← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~michael.nelson/launchpad/distro-series-difference-model into lp:launchpad

 

Michael Nelson has proposed merging lp:~michael.nelson/launchpad/distro-series-difference-model into lp:launchpad with lp:~michael.nelson/launchpad/distro-series-difference-message as a prerequisite.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  #623388 Add model for derived distroseries differences
  https://bugs.launchpad.net/bugs/623388


Overview
========

This branch ensures that comments can only be added to a DistroSeriesDifference by people with launchpad.Edit (currently people in the owning team of the derived series).

It also adds a getComments() method, and a method to update the status and/or type of difference based on any new publishings.

This branch follows on from:
https://code.edge.launchpad.net/~michael.nelson/launchpad/distro-series-difference-message/+merge/33929

Test:
bin/test -vv -m test_distroseriesdifference -m test_distroseriesdifferencecomment

No lint.

-- 
https://code.launchpad.net/~michael.nelson/launchpad/distro-series-difference-model/+merge/34086
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~michael.nelson/launchpad/distro-series-difference-model into lp:launchpad.
=== modified file 'lib/lp/registry/configure.zcml'
--- lib/lp/registry/configure.zcml	2010-08-30 16:51:47 +0000
+++ lib/lp/registry/configure.zcml	2010-08-30 16:51:49 +0000
@@ -111,7 +111,10 @@
     </securedutility>
     <class
         class="lp.registry.model.distroseriesdifference.DistroSeriesDifference">
-        <allow interface="lp.registry.interfaces.distroseriesdifference.IDistroSeriesDifference"/>
+        <allow interface="lp.registry.interfaces.distroseriesdifference.IDistroSeriesDifferencePublic"/>
+        <require
+            permission="launchpad.Edit"
+            interface="lp.registry.interfaces.distroseriesdifference.IDistroSeriesDifferenceEdit"/>
     </class>
 
     <!-- DistroSeriesDifferenceComment -->

=== modified file 'lib/lp/registry/interfaces/distroseriesdifference.py'
--- lib/lp/registry/interfaces/distroseriesdifference.py	2010-08-30 16:51:47 +0000
+++ lib/lp/registry/interfaces/distroseriesdifference.py	2010-08-30 16:51:49 +0000
@@ -8,6 +8,8 @@
 
 __all__ = [
     'IDistroSeriesDifference',
+    'IDistroSeriesDifferencePublic',
+    'IDistroSeriesDifferenceEdit',
     'IDistroSeriesDifferenceSource',
     ]
 
@@ -16,7 +18,6 @@
 from zope.schema import (
     Choice,
     Int,
-    Text,
     TextLine,
     )
 
@@ -26,13 +27,15 @@
     DistroSeriesDifferenceType,
     )
 from lp.registry.interfaces.distroseries import IDistroSeries
+from lp.registry.interfaces.person import IPerson
 from lp.registry.interfaces.sourcepackagename import ISourcePackageName
+from lp.registry.interfaces.role import IHasOwner
 from lp.soyuz.interfaces.packagediff import IPackageDiff
 from lp.soyuz.interfaces.publishing import ISourcePackagePublishingHistory
 
 
-class IDistroSeriesDifference(Interface):
-    """An interface for a package difference between two distroseries."""
+class IDistroSeriesDifferencePublic(IHasOwner, Interface):
+    """The public interface for distro series differences."""
 
     id = Int(title=_('ID'), required=True, readonly=True)
 
@@ -54,10 +57,6 @@
         readonly=True, description=_(
             "The most recently generated package diff for this difference."))
 
-    activity_log = Text(
-        title=_('A log of activity and comments for this difference.'),
-        required=False, readonly=True)
-
     status = Choice(
         title=_('Distro series difference status.'),
         description=_('The current status of this difference.'),
@@ -76,20 +75,60 @@
         description=_(
             "The most recent published version in the derived series."))
 
+    source_version = TextLine(
+        title=_("Source version"), readonly=True,
+        description=_(
+            "The version of the most recent source publishing in the "
+            "derived series."))
+
     parent_source_pub = Reference(
         ISourcePackagePublishingHistory,
         title=_("Parent source pub"), readonly=True,
         description=_(
             "The most recent published version in the parent series."))
 
+    parent_source_version = TextLine(
+        title=_("Parent source version"), readonly=True,
+        description=_(
+            "The version of the most recent source publishing in the "
+            "parent series."))
+
+    owner = Reference(
+        IPerson, title=_("Owning team of the derived series"), readonly=True,
+        description=_(
+            "This attribute mirrors the owner of the derived series."))
+
     title = TextLine(
         title=_("Title"), readonly=True, required=False, description=_(
             "A human-readable name describing this difference."))
 
+    def updateStatusAndType():
+        """Checks that difference type and status matches current publishings.
+
+        If the record is updated, a relevant comment is added.
+
+        If there is no longer a difference (ie. the versions are
+        the same) then the status is updated to RESOLVED.
+
+        :return: True if the record was updated, False otherwise.
+        """
+
+    def getComments():
+        """Return a result set of the comments for this difference."""
+
+
+class IDistroSeriesDifferenceEdit(Interface):
+    """Difference attributes requiring launchpad.Edit."""
+
     def addComment(owner, comment):
         """Add a comment on this difference."""
 
 
+class IDistroSeriesDifference(IDistroSeriesDifferencePublic,
+                              IDistroSeriesDifferenceEdit):
+    """An interface for a package difference between two distroseries."""
+
+
 class IDistroSeriesDifferenceSource(Interface):
     """A utility of this interface can be used to create differences."""
 

=== modified file 'lib/lp/registry/interfaces/distroseriesdifferencecomment.py'
--- lib/lp/registry/interfaces/distroseriesdifferencecomment.py	2010-08-30 16:51:47 +0000
+++ lib/lp/registry/interfaces/distroseriesdifferencecomment.py	2010-08-30 16:51:49 +0000
@@ -55,3 +55,11 @@
         :param comment: The comment.
         :return: A new `DistroSeriesDifferenceComment` object.
         """
+
+    def getForDifference(distro_series_difference):
+        """Return a result set of comments for a difference.
+
+        :param distro_series_difference: The distribution series difference
+            for which comments are fetched.
+        :return: A result set of comments, ordered by id by default.
+        """

=== modified file 'lib/lp/registry/model/distroseriesdifference.py'
--- lib/lp/registry/model/distroseriesdifference.py	2010-08-30 16:51:47 +0000
+++ lib/lp/registry/model/distroseriesdifference.py	2010-08-30 16:51:49 +0000
@@ -9,7 +9,6 @@
     'DistroSeriesDifference',
     ]
 
-
 from storm.locals import (
     Int,
     Reference,
@@ -91,23 +90,24 @@
         return self._getLatestSourcePub(for_parent=True)
 
     @property
+    def owner(self):
+        """See `IDistroSeriesDifference`."""
+        return self.derived_series.owner
+
+    @property
     def title(self):
         """See `IDistroSeriesDifference`."""
         parent_name = self.derived_series.parent_series.displayname
         return ("Difference between distroseries '%(parent_name)s' and "
                 "'%(derived_name)s' for package '%(pkg_name)s' "
-                "(%(versions)s)" % {
+                "(%(parent_version)s/%(source_version)s)" % {
                     'parent_name': parent_name,
                     'derived_name': self.derived_series.displayname,
                     'pkg_name': self.source_package_name.name,
-                    'versions': self._getVersions(),
+                    'parent_version': self.parent_source_version,
+                    'source_version': self.source_version,
                     })
 
-    @property
-    def activity_log(self):
-        """See `IDistroSeriesDifference`."""
-        return u""
-
     def _getLatestSourcePub(self, for_parent=False):
         """Helper to keep source_pub/parent_source_pub DRY."""
         distro_series = self.derived_series
@@ -123,16 +123,53 @@
         else:
             return None
 
-    def _getVersions(self):
-        """Helper method returning versions string."""
-        src_pub_ver = parent_src_pub_ver = "-"
+    @property
+    def source_version(self):
+        """See `IDistroSeriesDifference`."""
         if self.source_pub:
-            src_pub_ver = self.source_pub.source_package_version
-        if self.parent_source_pub is not None:
-            parent_src_pub_ver = self.parent_source_pub.source_package_version
-        return parent_src_pub_ver + "/" + src_pub_ver
+            return self.source_pub.source_package_version
+        return ''
+
+    @property
+    def parent_source_version(self):
+        """See `IDistroSeriesDifference`."""
+        if self.parent_source_pub:
+            return self.parent_source_pub.source_package_version
+        return ''
+
+    def updateStatusAndType(self):
+        """See `IDistroSeriesDifference`."""
+        if self.source_pub is None:
+            new_type = DistroSeriesDifferenceType.MISSING_FROM_DERIVED_SERIES
+        elif self.parent_source_pub is None:
+            new_type = DistroSeriesDifferenceType.UNIQUE_TO_DERIVED_SERIES
+        else:
+            new_type = DistroSeriesDifferenceType.DIFFERENT_VERSIONS
+
+        updated = False
+        if new_type != self.difference_type:
+            updated = True
+            self.difference_type = new_type
+
+        version = self.source_version
+        parent_version = self.parent_source_version
+        if self.status == DistroSeriesDifferenceStatus.RESOLVED:
+            if version != parent_version:
+                updated = True
+                self.status = DistroSeriesDifferenceStatus.NEEDS_ATTENTION
+        else:
+            if version == parent_version:
+                updated = True
+                self.status = DistroSeriesDifferenceStatus.RESOLVED
+
+        return updated
 
     def addComment(self, owner, comment):
         """See `IDistroSeriesDifference`."""
         return getUtility(IDistroSeriesDifferenceCommentSource).new(
             self, owner, comment)
+
+    def getComments(self):
+        """See `IDistroSeriesDifference`."""
+        comment_source = getUtility(IDistroSeriesDifferenceCommentSource)
+        return comment_source.getForDifference(self)

=== modified file 'lib/lp/registry/model/distroseriesdifferencecomment.py'
--- lib/lp/registry/model/distroseriesdifferencecomment.py	2010-08-30 16:51:47 +0000
+++ lib/lp/registry/model/distroseriesdifferencecomment.py	2010-08-30 16:51:49 +0000
@@ -65,3 +65,13 @@
         dsd_comment.message = message
 
         return store.add(dsd_comment)
+
+    @staticmethod
+    def getForDifference(distro_series_difference):
+        """See `IDistroSeriesDifferenceCommentSource`."""
+        DSDComment = DistroSeriesDifferenceComment
+        from canonical.launchpad.interfaces.lpstorm import IStore
+        comments = IStore(DSDComment).find(
+            DistroSeriesDifferenceComment,
+            DSDComment.distro_series_difference == distro_series_difference)
+        return comments.order_by(DSDComment.id)

=== modified file 'lib/lp/registry/tests/test_distroseriesdifference.py'
--- lib/lp/registry/tests/test_distroseriesdifference.py	2010-08-30 16:51:47 +0000
+++ lib/lp/registry/tests/test_distroseriesdifference.py	2010-08-30 16:51:49 +0000
@@ -3,17 +3,27 @@
 
 """Model tests for the DistroSeriesDifference class."""
 
+from __future__ import with_statement
+
 __metaclass__ = type
 
 import unittest
 
 from storm.store import Store
 from zope.component import getUtility
+from zope.security.interfaces import Unauthorized
 
+from canonical.launchpad.webapp.authorization import check_permission
 from canonical.launchpad.webapp.testing import verifyObject
 from canonical.testing import DatabaseFunctionalLayer
-from lp.testing import TestCaseWithFactory
-from lp.registry.enum import DistroSeriesDifferenceType
+from lp.testing import (
+    person_logged_in,
+    TestCaseWithFactory,
+    )
+from lp.registry.enum import (
+    DistroSeriesDifferenceStatus,
+    DistroSeriesDifferenceType,
+    )
 from lp.registry.exceptions import NotADerivedSeriesError
 from lp.registry.interfaces.distroseriesdifference import (
     IDistroSeriesDifference,
@@ -105,6 +115,116 @@
 
         self.assertEqual(pending_pub, ds_diff.parent_source_pub)
 
+    def test_source_version(self):
+        # The version of the source in the derived series is returned.
+        ds_diff = self.factory.makeDistroSeriesDifference(
+            source_package_name_str="foonew")
+
+        self.assertEqual(
+            ds_diff.source_pub.source_package_version, ds_diff.source_version)
+
+    def test_source_version_none(self):
+        # None is returned for source_version when there is no source pub.
+        ds_diff = self.factory.makeDistroSeriesDifference(
+            source_package_name_str="foonew",
+            difference_type=(
+                DistroSeriesDifferenceType.MISSING_FROM_DERIVED_SERIES))
+
+        self.assertEqual('', ds_diff.source_version)
+
+    def test_updateStatusAndType_resolves_difference(self):
+        # Status is set to resolved when versions match.
+        ds_diff = self.factory.makeDistroSeriesDifference(
+            source_package_name_str="foonew",
+            versions={
+                'parent': '1.0',
+                'derived': '0.9',
+                })
+        new_derived_pub = self.factory.makeSourcePackagePublishingHistory(
+            sourcepackagename=ds_diff.source_package_name,
+            distroseries=ds_diff.derived_series,
+            status=PackagePublishingStatus.PENDING,
+            version='1.0')
+
+        was_updated = ds_diff.updateStatusAndType()
+
+        self.assertIs(True, was_updated)
+        self.assertEqual(
+            DistroSeriesDifferenceStatus.RESOLVED,
+            ds_diff.status)
+
+    def test_updateStatusAndType_re_opens_difference(self):
+        # The status of a resolved difference will updated with new
+        # uploads.
+        ds_diff = self.factory.makeDistroSeriesDifference(
+            source_package_name_str="foonew",
+            versions={
+                'parent': '1.0',
+                'derived': '1.0',
+                },
+            status=DistroSeriesDifferenceStatus.RESOLVED)
+        new_derived_pub = self.factory.makeSourcePackagePublishingHistory(
+            sourcepackagename=ds_diff.source_package_name,
+            distroseries=ds_diff.derived_series,
+            status=PackagePublishingStatus.PENDING,
+            version='1.1')
+
+        was_updated = ds_diff.updateStatusAndType()
+
+        self.assertIs(True, was_updated)
+        self.assertEqual(
+            DistroSeriesDifferenceStatus.NEEDS_ATTENTION,
+            ds_diff.status)
+
+    def test_updateStatusAndType_new_version_no_change(self):
+        # Uploading a new (different) version does not necessarily
+        # update the record.
+        # In this case, a new version is uploaded, but there is still a
+        # difference needing attention.
+        ds_diff = self.factory.makeDistroSeriesDifference(
+            source_package_name_str="foonew",
+            versions={
+                'parent': '1.0',
+                'derived': '0.9',
+                })
+        new_derived_pub = self.factory.makeSourcePackagePublishingHistory(
+            sourcepackagename=ds_diff.source_package_name,
+            distroseries=ds_diff.derived_series,
+            status=PackagePublishingStatus.PENDING,
+            version='1.1')
+
+        was_updated = ds_diff.updateStatusAndType()
+
+        self.assertIs(False, was_updated)
+        self.assertEqual(
+            DistroSeriesDifferenceStatus.NEEDS_ATTENTION,
+            ds_diff.status)
+
+    def test_updateStatusAndType_changes_type(self):
+        # The type of difference is updated when appropriate.
+        # In this case, a package that was previously only in the
+        # derived series (UNIQUE_TO_DERIVED_SERIES), is uploaded
+        # to the parent series with a different version.
+        ds_diff = self.factory.makeDistroSeriesDifference(
+            source_package_name_str="foonew",
+            versions={
+                'derived': '0.9',
+                },
+            difference_type=(
+                DistroSeriesDifferenceType.UNIQUE_TO_DERIVED_SERIES))
+        new_parent_pub = self.factory.makeSourcePackagePublishingHistory(
+            sourcepackagename=ds_diff.source_package_name,
+            distroseries=ds_diff.derived_series.parent_series,
+            status=PackagePublishingStatus.PENDING,
+            version='1.1')
+
+        was_updated = ds_diff.updateStatusAndType()
+
+        self.assertIs(True, was_updated)
+        self.assertEqual(
+            DistroSeriesDifferenceType.DIFFERENT_VERSIONS,
+            ds_diff.difference_type)
+
     def test_title(self):
         # The title is a friendly description of the difference.
         parent_series = self.factory.makeDistroSeries(name="lucid")
@@ -126,11 +246,45 @@
         # Adding a comment creates a new DistroSeriesDifferenceComment
         ds_diff = self.factory.makeDistroSeriesDifference(
             source_package_name_str="foonew")
+
+        with person_logged_in(ds_diff.owner):
+            dsd_comment = ds_diff.addComment(
+                ds_diff.owner, "Wait until version 2.1")
+
+        self.assertEqual(ds_diff, dsd_comment.distro_series_difference)
+
+    def test_getComments(self):
+        # All comments for this difference are returned.
+        ds_diff = self.factory.makeDistroSeriesDifference()
+
+        with person_logged_in(ds_diff.owner):
+            dsd_comment = ds_diff.addComment(
+                ds_diff.owner, "Wait until version 2.1")
+            dsd_comment_2 = ds_diff.addComment(
+                ds_diff.owner, "Wait until version 2.1")
+
+        self.assertEqual(
+            [dsd_comment, dsd_comment_2], list(ds_diff.getComments()))
+
+    def test_addComment_not_public(self):
+        # Comments cannot be added with launchpad.View.
+        ds_diff = self.factory.makeDistroSeriesDifference()
         person = self.factory.makePerson()
 
-        dsd_comment = ds_diff.addComment(person, "Wait until version 2.1")
-
-        self.assertEqual(ds_diff, dsd_comment.distro_series_difference)
+        with person_logged_in(person):
+            self.assertTrue(check_permission('launchpad.View', ds_diff))
+            self.assertFalse(check_permission('launchpad.Edit', ds_diff))
+            self.assertRaises(Unauthorized, getattr, ds_diff, 'addComment')
+
+    def test_addComment_for_owners(self):
+        # Comments can be added by any of the owners of the derived
+        # series.
+        ds_diff = self.factory.makeDistroSeriesDifference()
+
+        with person_logged_in(ds_diff.owner):
+            self.assertTrue(check_permission('launchpad.Edit', ds_diff))
+            diff_comment = ds_diff.addComment(
+                ds_diff.derived_series.owner, "Boo")
 
 
 def test_suite():

=== modified file 'lib/lp/registry/tests/test_distroseriesdifferencecomment.py'
--- lib/lp/registry/tests/test_distroseriesdifferencecomment.py	2010-08-30 16:51:47 +0000
+++ lib/lp/registry/tests/test_distroseriesdifferencecomment.py	2010-08-30 16:51:49 +0000
@@ -6,11 +6,13 @@
 __metaclass__ = type
 
 from storm.store import Store
+from zope.component import getUtility
 
 from canonical.launchpad.webapp.testing import verifyObject
 from canonical.testing import DatabaseFunctionalLayer
 from lp.registry.interfaces.distroseriesdifferencecomment import (
     IDistroSeriesDifferenceComment,
+    IDistroSeriesDifferenceCommentSource,
     )
 from lp.testing import TestCaseWithFactory
 
@@ -42,3 +44,21 @@
         self.assertEqual(
             dsd_comment.distro_series_difference.title,
             dsd_comment.message.subject)
+
+
+class DifferenceCommentSourceTestCase(TestCaseWithFactory):
+
+    layer = DatabaseFunctionalLayer
+
+    def test_getForDifference(self):
+        # Returns all comments for a difference ordered by id.
+        dsd_comment = self.factory.makeDistroSeriesDifferenceComment()
+        dsd_comment_2 = self.factory.makeDistroSeriesDifferenceComment(
+            dsd_comment.distro_series_difference)
+        dsd_other_comment = self.factory.makeDistroSeriesDifferenceComment()
+
+        comment_source = getUtility(IDistroSeriesDifferenceCommentSource)
+        comments = comment_source.getForDifference(
+            distro_series_difference=dsd_comment.distro_series_difference)
+
+        self.assertEqual([dsd_comment, dsd_comment_2], list(comments))

=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py	2010-08-30 16:51:47 +0000
+++ lib/lp/testing/factory.py	2010-08-30 16:51:49 +0000
@@ -154,7 +154,10 @@
     IHWSubmissionDeviceSet,
     IHWSubmissionSet,
     )
-from lp.registry.enum import DistroSeriesDifferenceType
+from lp.registry.enum import (
+    DistroSeriesDifferenceStatus,
+    DistroSeriesDifferenceType,
+    )
 from lp.registry.interfaces.distribution import IDistributionSet
 from lp.registry.interfaces.distributionmirror import (
     MirrorContent,
@@ -1827,7 +1830,8 @@
     def makeDistroSeriesDifference(
         self, derived_series=None, source_package_name_str=None,
         versions=None,
-        difference_type=DistroSeriesDifferenceType.DIFFERENT_VERSIONS):
+        difference_type=DistroSeriesDifferenceType.DIFFERENT_VERSIONS,
+        status=DistroSeriesDifferenceStatus.NEEDS_ATTENTION):
         """Create a new distro series source package difference."""
         if derived_series is None:
             parent_series = self.makeDistroSeries()
@@ -1860,7 +1864,8 @@
                 sourcepackagename=source_package_name)
 
         return getUtility(IDistroSeriesDifferenceSource).new(
-            derived_series, source_package_name, difference_type)
+            derived_series, source_package_name, difference_type,
+            status=status)
 
     def makeDistroSeriesDifferenceComment(
         self, distro_series_difference=None, owner=None, comment=None):


Follow ups