← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~stevenk/launchpad/merge-recipes-during-merge into lp:launchpad

 

Steve Kowalik has proposed merging lp:~stevenk/launchpad/merge-recipes-during-merge into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  #506630 account merge code for branches doesn't handle source package branches
  https://bugs.launchpad.net/bugs/506630

For more details, see:
https://code.launchpad.net/~stevenk/launchpad/merge-recipes-during-merge/+merge/48264

Readd the code that deals with recipes on merge -- but merges them into the target person, rather than just deleting them. I also refactored the branch merging code to deal with package branches at the same time.
-- 
https://code.launchpad.net/~stevenk/launchpad/merge-recipes-during-merge/+merge/48264
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~stevenk/launchpad/merge-recipes-during-merge into lp:launchpad.
=== modified file 'lib/lp/registry/model/person.py'
--- lib/lp/registry/model/person.py	2011-02-01 01:40:34 +0000
+++ lib/lp/registry/model/person.py	2011-02-02 03:07:59 +0000
@@ -3340,37 +3340,36 @@
             (decorator_table.lower(), person_pointer_column.lower()))
 
     def _mergeBranches(self, cur, from_id, to_id):
-        # XXX MichaelHudson 2010-01-13 bug=506630: This code does not account
-        # for package branches.
-        cur.execute('''
-            SELECT product, name FROM Branch WHERE owner = %(to_id)d
-            ''' % vars())
-        possible_conflicts = set(tuple(r) for r in cur.fetchall())
-        cur.execute('''
-            SELECT id, product, name FROM Branch WHERE owner = %(from_id)d
-            ORDER BY id
-            ''' % vars())
-        for id, product, name in list(cur.fetchall()):
-            new_name = name
-            suffix = 1
-            while (product, new_name) in possible_conflicts:
-                new_name = '%s-%d' % (name, suffix)
-                suffix += 1
-            possible_conflicts.add((product, new_name))
-            new_name = new_name.encode('US-ASCII')
-            name = name.encode('US-ASCII')
-            cur.execute('''
-                UPDATE Branch SET owner = %(to_id)s, name = %(new_name)s
-                WHERE owner = %(from_id)s AND name = %(name)s
-                    AND (%(product)s IS NULL OR product = %(product)s)
-                ''', dict(to_id=to_id, from_id=from_id,
-                          name=name, new_name=new_name, product=product))
+        # Avoid circular imports.
+        from lp.code.model.branch import Branch
+        store = IMasterStore(Branch)
+        branches = store.find(Branch, Branch.owner == from_id)
+        for branch in branches:
+            branch.setOwner(to_id, to_id)
 
     def _mergeBranchMergeQueues(self, cur, from_id, to_id):
         cur.execute('''
             UPDATE BranchMergeQueue SET owner = %(to_id)s WHERE owner =
             %(from_id)s''', dict(to_id=to_id, from_id=from_id))
 
+    def _mergeSourcePackageRecipes(self, cur, from_id, to_id):
+        from lp.code.model.sourcepackagerecipe import SourcePackageRecipe
+        store = IMasterStore(SourcePackageRecipe)
+        recipes = store.find(
+            SourcePackageRecipe, SourcePackageRecipe.owner == from_id)
+        to_recipes = store.find(
+            SourcePackageRecipe, SourcePackageRecipe.owner == to_id)
+        existing_names = [r.name for r in to_recipes]
+        for recipe in recipes:
+            new_name = recipe.name
+            count = 1
+            while new_name in existing_names:
+                new_name = '%s-%s' % (recipe.name, count)
+                count += 1
+            nr = removeSecurityProxy(recipe)
+            nr.owner = to_id
+            nr.name = new_name
+
     def _mergeMailingListSubscriptions(self, cur, from_id, to_id):
         # Update MailingListSubscription. Note that since all the from_id
         # email addresses are set to NEW, all the subscriptions must be
@@ -3865,14 +3864,13 @@
 
         # Update the Branches that will not conflict, and fudge the names of
         # ones that *do* conflict.
-        self._mergeBranches(cur, from_id, to_id)
+        self._mergeBranches(cur, from_person, to_person)
         skip.append(('branch', 'owner'))
 
         self._mergeBranchMergeQueues(cur, from_id, to_id)
         skip.append(('branchmergequeue', 'owner'))
 
-        # XXX MichaelHudson 2010-01-13: Write _mergeSourcePackageRecipes!
-        #self._mergeSourcePackageRecipes(cur, from_id, to_id))
+        self._mergeSourcePackageRecipes(cur, from_id, to_id)
         skip.append(('sourcepackagerecipe', 'owner'))
 
         self._mergeMailingListSubscriptions(cur, from_id, to_id)

=== modified file 'lib/lp/registry/tests/test_person.py'
--- lib/lp/registry/tests/test_person.py	2011-02-01 01:40:34 +0000
+++ lib/lp/registry/tests/test_person.py	2011-02-02 03:07:59 +0000
@@ -17,10 +17,6 @@
 from canonical.database.sqlbase import cursor
 from canonical.launchpad.database.account import Account
 from canonical.launchpad.database.emailaddress import EmailAddress
-from canonical.launchpad.ftests import (
-    ANONYMOUS,
-    login,
-    )
 from canonical.launchpad.interfaces.account import (
     AccountCreationRationale,
     AccountStatus,
@@ -62,9 +58,14 @@
 from lp.registry.model.person import Person
 from lp.registry.model.structuralsubscription import StructuralSubscription
 from lp.services.openid.model.openididentifier import OpenIdIdentifier
-from lp.soyuz.enums import ArchivePurpose
+from lp.soyuz.enums import (
+    ArchivePurpose,
+    ArchiveStatus,
+    )
 from lp.testing import (
+    ANONYMOUS,
     celebrity_logged_in,
+    login,
     login_person,
     logout,
     person_logged_in,
@@ -681,6 +682,66 @@
             test_team,
             target_team)
 
+    def test_merge_moves_branches(self):
+        # When person/teams are merged, branches owned by the from person
+        # are copied.
+        person = self.factory.makePerson()
+        branch = self.factory.makeBranch()
+        self._do_premerge(branch.owner, person)
+        login_person(person)
+        self.person_set.merge(branch.owner, person)
+        branches = person.getBranches()
+        self.assertEqual(1, branches.count())
+
+    def test_merge_with_duplicated_branches(self):
+        # When person/teams are merged, if the from person has a branch with
+        # the same name as the to person, the from person's branches are
+        # renamed.
+        product = self.factory.makeProduct()
+        from_branch = self.factory.makeBranch(name='foo', product=product)
+        to_branch = self.factory.makeBranch(name='foo', product=product)
+        mergee = to_branch.owner
+        self._do_premerge(from_branch.owner, mergee)
+        login_person(mergee)
+        self.person_set.merge(from_branch.owner, mergee)
+        branches = [b.name for b in mergee.getBranches()]
+        self.assertEqual(2, len(branches))
+        self.assertEqual([u'foo', u'foo-1'], branches)
+
+    def test_merge_moves_recipes(self):
+        # When person/teams are merged, recipes owned by the from person are
+        # copied.
+        person = self.factory.makePerson()
+        recipe = self.factory.makeSourcePackageRecipe()
+        # Disable the recipe owners PPA
+        with person_logged_in(recipe.owner):
+            recipe.owner.archive.status = ArchiveStatus.DELETED
+        self._do_premerge(recipe.owner, person)
+        login_person(person)
+        self.person_set.merge(recipe.owner, person)
+        self.assertEqual(1, person.getRecipes().count())
+
+    def test_merge_with_duplicated_recipes(self):
+        # When person/teams are merged, if the from person has a recipe with
+        # the same name as the to person, the from person's recipes are
+        # renamed.
+        merge_from = self.factory.makeSourcePackageRecipe(
+            name=u'foo', description=u'FROM')
+        merge_to = self.factory.makeSourcePackageRecipe(
+            name=u'foo', description=u'TO')
+        mergee = merge_to.owner
+        # Disable merge_from's PPA
+        with person_logged_in(merge_from.owner):
+            merge_from.owner.archive.status = ArchiveStatus.DELETED
+        self._do_premerge(merge_from.owner, mergee)
+        login_person(mergee)
+        self.person_set.merge(merge_from.owner, merge_to.owner)
+        recipes = mergee.getRecipes()
+        self.assertEqual(2, recipes.count())
+        descriptions = [r.description for r in recipes]
+        self.assertEqual([u'TO', u'FROM'], descriptions)
+        self.assertEqual(u'foo-1', recipes[1].name)
+
 
 class TestPersonSetCreateByOpenId(TestCaseWithFactory):
     layer = DatabaseFunctionalLayer