← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~stevenk/launchpad/branch-information_type-garbo into lp:launchpad

 

Steve Kowalik has proposed merging lp:~stevenk/launchpad/branch-information_type-garbo into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  Bug #933768 in Launchpad itself: "Update branch to use information_visibility_policy"
  https://bugs.launchpad.net/launchpad/+bug/933768

For more details, see:
https://code.launchpad.net/~stevenk/launchpad/branch-information_type-garbo/+merge/104682

Add a garbo job to populate Branch.information_type. Be very sneaky and use an SQL CASE statement to set it for purely speed reasons.
-- 
https://code.launchpad.net/~stevenk/launchpad/branch-information_type-garbo/+merge/104682
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~stevenk/launchpad/branch-information_type-garbo into lp:launchpad.
=== modified file 'lib/lp/code/interfaces/branch.py'
--- lib/lp/code/interfaces/branch.py	2012-04-06 17:28:25 +0000
+++ lib/lp/code/interfaces/branch.py	2012-05-04 06:06:20 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2010 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2012 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 # pylint: disable-msg=E0211,E0213,F0401,W0611
@@ -90,6 +90,7 @@
 from lp.code.interfaces.hasbranches import IHasMergeProposals
 from lp.code.interfaces.hasrecipes import IHasRecipes
 from lp.code.interfaces.linkedbranch import ICanHasLinkedBranch
+from lp.registry.enums import InformationType
 from lp.registry.interfaces.person import IPerson
 from lp.registry.interfaces.pocket import PackagePublishingPocket
 from lp.registry.interfaces.role import IHasOwner
@@ -247,11 +248,16 @@
             title=_('Date Last Modified'),
             required=True,
             readonly=False))
-
     explicitly_private = Bool(
         title=_("Explicitly Private"),
         description=_("This branch is explicitly marked private as opposed "
         "to being private because it is stacked on a private branch."))
+    information_type = exported(
+        Choice(
+            title=_('Information Type'), vocabulary=InformationType,
+            required=True, readonly=True, default=InformationType.PUBLIC,
+            description=_(
+                'The type of information contained in this branch.')))
 
 
 class IBranchAnyone(Interface):
@@ -1114,6 +1120,13 @@
         :raise: CannotDeleteBranch if the branch cannot be deleted.
         """
 
+    def transitionToInformationType(information_type, who):
+         """Set the information type for this branch.
+
+        :information_type: The `InformationType` to transition to.
+        :who: The `IPerson` who is making the change.
+        """
+
 
 class IMergeQueueable(Interface):
     """An interface for branches that can be queued."""

=== modified file 'lib/lp/code/model/branch.py'
--- lib/lp/code/model/branch.py	2012-04-06 17:28:25 +0000
+++ lib/lp/code/model/branch.py	2012-05-04 06:06:20 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2011 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2012 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 # pylint: disable-msg=E0611,W0212,W0141,F0401
@@ -128,6 +128,7 @@
     )
 from lp.code.model.seriessourcepackagebranch import SeriesSourcePackageBranch
 from lp.codehosting.safe_open import safe_open
+from lp.registry.enums import InformationType
 from lp.registry.interfaces.person import (
     validate_person,
     validate_public_person,
@@ -181,6 +182,8 @@
     # transitively private branch. The value of this attribute is maintained
     # by a database trigger.
     transitively_private = BoolCol(dbName='transitively_private')
+    information_type = EnumCol(
+        enum=InformationType, default=InformationType.PUBLIC)
 
     @property
     def private(self):
@@ -203,8 +206,14 @@
         # otherwise we need to reload the value.
         if private:
             self.transitively_private = True
+            self.information_type = InformationType.USERDATA
         else:
             self.transitively_private = AutoReload
+            self.information_type = InformationType.PUBLIC
+
+    def transitionToInformationType(self, information_type, who):
+        """See `IBranch`."""
+        self.information_type = information_type
 
     registrant = ForeignKey(
         dbName='registrant', foreignKey='Person',

=== modified file 'lib/lp/code/model/branchnamespace.py'
--- lib/lp/code/model/branchnamespace.py	2011-12-30 06:14:56 +0000
+++ lib/lp/code/model/branchnamespace.py	2012-05-04 06:06:20 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2012 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Implementations of `IBranchNamespace`."""
@@ -44,6 +44,7 @@
     )
 from lp.code.interfaces.branchtarget import IBranchTarget
 from lp.code.model.branch import Branch
+from lp.registry.enums import InformationType
 from lp.registry.errors import (
     NoSuchDistroSeries,
     NoSuchSourcePackageName,
@@ -108,15 +109,19 @@
 
         # If branches can be private, make them private initially.
         private = self.areNewBranchesPrivate()
+        if private:
+            information_type = InformationType.USERDATA
+        else:
+            information_type = InformationType.PUBLIC
 
         branch = Branch(
             registrant=registrant,
             name=name, owner=self.owner, product=product, url=url,
             title=title, lifecycle_status=lifecycle_status, summary=summary,
             whiteboard=whiteboard, explicitly_private=private,
-            date_created=date_created, branch_type=branch_type,
-            date_last_modified=date_created, branch_format=branch_format,
-            repository_format=repository_format,
+            information_type=information_type, date_created=date_created,
+            branch_type=branch_type, date_last_modified=date_created,
+            branch_format=branch_format, repository_format=repository_format,
             control_format=control_format, distroseries=distroseries,
             sourcepackagename=sourcepackagename)
 

=== modified file 'lib/lp/code/model/tests/test_branch.py'
--- lib/lp/code/model/tests/test_branch.py	2012-04-13 18:31:35 +0000
+++ lib/lp/code/model/tests/test_branch.py	2012-05-04 06:06:20 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2011 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2012 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 # pylint: disable-msg=F0401,E1002
@@ -109,6 +109,7 @@
 from lp.code.tests.helpers import add_revision_to_branch
 from lp.codehosting.safe_open import BadUrl
 from lp.codehosting.vfs.branchfs import get_real_branch_path
+from lp.registry.enums import InformationType
 from lp.registry.interfaces.person import PersonVisibility
 from lp.registry.interfaces.pocket import PackagePublishingPocket
 from lp.registry.model.sourcepackage import SourcePackage
@@ -2350,6 +2351,7 @@
         team = self.factory.makeTeam(visibility=PersonVisibility.PRIVATE)
         branch = self.factory.makePersonalBranch(owner=team)
         self.assertTrue(branch.private)
+        self.assertEqual(InformationType.USERDATA, branch.information_type)
 
 
 class TestBranchSetPrivate(TestCaseWithFactory):
@@ -2380,6 +2382,7 @@
         self.assertTrue(branch.private)
         self.assertTrue(removeSecurityProxy(branch).transitively_private)
         self.assertTrue(branch.explicitly_private)
+        self.assertEqual(InformationType.USERDATA, branch.information_type)
 
     def test_public_to_private_not_allowed(self):
         # If there are no privacy policies allowing private branches, then
@@ -2418,6 +2421,7 @@
         self.assertFalse(branch.private)
         self.assertFalse(removeSecurityProxy(branch).transitively_private)
         self.assertFalse(branch.explicitly_private)
+        self.assertEqual(InformationType.PUBLIC, branch.information_type)
 
     def test_private_to_public_not_allowed(self):
         # If the namespace policy does not allow public branches, attempting

=== modified file 'lib/lp/code/model/tests/test_branchnamespace.py'
--- lib/lp/code/model/tests/test_branchnamespace.py	2012-01-01 02:58:52 +0000
+++ lib/lp/code/model/tests/test_branchnamespace.py	2012-05-04 06:06:20 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2011 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2012 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Tests for `IBranchNamespace` implementations."""
@@ -35,6 +35,7 @@
     PersonalNamespace,
     ProductNamespace,
     )
+from lp.registry.enums import InformationType
 from lp.registry.errors import (
     NoSuchDistroSeries,
     NoSuchSourcePackageName,
@@ -79,6 +80,7 @@
             BranchType.HOSTED, branch_name, registrant)
         self.assertEqual(
             expected_unique_name, branch.unique_name)
+        self.assertEqual(InformationType.PUBLIC, branch.information_type)
 
     def test_createBranch_passes_through(self):
         # createBranch takes all the arguments that the `Branch` constructor
@@ -373,6 +375,20 @@
             team, BranchVisibilityRule.PRIVATE)
         self.assertEqual(team, namespace.getPrivacySubscriber())
 
+    def test_information_type_private_team(self):
+        # Given a privacy policy for a namespace, branches are created with
+        # the correct information type.
+        person = self.factory.makePerson()
+        team = self.factory.makeTeam(owner=person)
+        product = self.factory.makeProduct()
+        namespace = ProductNamespace(team, product)
+        product.setBranchVisibilityTeamPolicy(
+            team, BranchVisibilityRule.PRIVATE)
+        branch = namespace.createBranch(
+            BranchType.HOSTED, self.factory.getUniqueString(),
+            namespace.owner)
+        self.assertEqual(InformationType.USERDATA, branch.information_type)
+
     def test_subscriber_private_team_namespace(self):
         # If there is a private policy for a namespace owner, then there is no
         # privacy subscriber.

=== modified file 'lib/lp/scripts/garbo.py'
--- lib/lp/scripts/garbo.py	2012-04-25 14:10:24 +0000
+++ lib/lp/scripts/garbo.py	2012-05-04 06:06:20 +0000
@@ -51,6 +51,7 @@
     MAX_SAMPLE_SIZE,
     )
 from lp.code.interfaces.revision import IRevisionSet
+from lp.code.model.branch import Branch
 from lp.code.model.codeimportevent import CodeImportEvent
 from lp.code.model.codeimportresult import CodeImportResult
 from lp.code.model.revision import (
@@ -58,6 +59,7 @@
     RevisionCache,
     )
 from lp.hardwaredb.model.hwdb import HWSubmission
+from lp.registry.enums import InformationType
 from lp.registry.model.person import Person
 from lp.services.config import config
 from lp.services.database import postgresql
@@ -835,6 +837,30 @@
         transaction.commit()
 
 
+class BranchInformationTypeMigrator(TunableLoop):
+    """A `TunableLoop` to populate information_type for all branches."""
+
+    maximum_chunk_size = 5000
+
+    def __init__(self, log, abort_time=None):
+        super(BranchInformationTypeMigrator, self).__init__(log, abort_time)
+        self.transaction = transaction
+        self.store = IMasterStore(Branch)
+
+    def findBranches(self):
+        return self.store.find(Branch, Branch.information_type == None)
+
+    def isDone(self):
+        return self.findBranches().is_empty()
+
+    def __call__(self, chunk_size):
+        self.findBranches()[:chunk_size].set(
+            information_type=SQL("CASE WHEN transitively_private THEN %s "
+                "ELSE %s END" % sqlvalues(
+                    InformationType.USERDATA, InformationType.PUBLIC)))
+        self.transaction.commit()
+
+
 class BugWatchActivityPruner(BulkPruner):
     """A TunableLoop to prune BugWatchActivity entries."""
     target_table_class = BugWatchActivity
@@ -1382,6 +1408,7 @@
         DuplicateSessionPruner,
         BugHeatUpdater,
         BugTaskFlattener,
+        BranchInformationTypeMigrator,
         ]
     experimental_tunable_loops = []
 

=== modified file 'lib/lp/scripts/tests/test_garbo.py'
--- lib/lp/scripts/tests/test_garbo.py	2012-04-02 07:10:56 +0000
+++ lib/lp/scripts/tests/test_garbo.py	2012-05-04 06:06:20 +0000
@@ -55,6 +55,7 @@
     )
 from lp.code.model.codeimportevent import CodeImportEvent
 from lp.code.model.codeimportresult import CodeImportResult
+from lp.registry.enums import InformationType
 from lp.registry.interfaces.distribution import IDistributionSet
 from lp.registry.interfaces.person import IPersonSet
 from lp.scripts.garbo import (
@@ -1134,6 +1135,16 @@
                 'SELECT bugtask FROM BugTaskFlat WHERE bugtask = ?',
                 (task.id,)).get_one())
 
+    def test_BranchInformationTypeMigrator(self):
+        # A non-migrated branch will have information_type set correctly.
+        switch_dbuser('testadmin')
+        branch = self.factory.makeBranch(private=True)
+        # Since creating a branch will set information_type, unset it.
+        removeSecurityProxy(branch).information_type = None
+        transaction.commit()
+        self.runHourly()
+        self.assertEqual(InformationType.USERDATA, branch.information_type)
+
 
 class TestGarboTasks(TestCaseWithFactory):
     layer = LaunchpadZopelessLayer