launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #00785
lp:~michael.nelson/launchpad/distro-series-difference-basic-model into lp:launchpad
Michael Nelson has proposed merging lp:~michael.nelson/launchpad/distro-series-difference-basic-model into lp:launchpad with lp:~michael.nelson/launchpad/distro-series-difference-schema as a prerequisite.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Overview
========
This branch continues on from:
https://code.edge.launchpad.net/~michael.nelson/launchpad/distro-series-difference-schema/+merge/33515
and implements the basic model, enums and tests for representing the distroseriesdifference pages such as:
https://dev.launchpad.net/LEP/DerivativeDistributions?action=AttachFile&do=get&target=derived-series-diffs_5.png
Details
=======
I've set the status and activity_log fields as readonly as a following branch will adds more functionality to the model including security for the status and activity_log fields.
Test with:
bin/test -vvm lp.registry.tests.test_distroseriesdifference
--
https://code.launchpad.net/~michael.nelson/launchpad/distro-series-difference-basic-model/+merge/33885
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~michael.nelson/launchpad/distro-series-difference-basic-model into lp:launchpad.
=== modified file 'lib/lp/registry/configure.zcml'
--- lib/lp/registry/configure.zcml 2010-08-23 03:25:20 +0000
+++ lib/lp/registry/configure.zcml 2010-08-27 09:27:48 +0000
@@ -102,6 +102,17 @@
date_last_modified
person"/>
</class>
+
+ <!-- DistroSeriesDifference -->
+ <securedutility
+ component="lp.registry.model.distroseriesdifference.DistroSeriesDifference"
+ provides="lp.registry.interfaces.distroseriesdifference.IDistroSeriesDifferenceSource">
+ <allow interface="lp.registry.interfaces.distroseriesdifference.IDistroSeriesDifferenceSource"/>
+ </securedutility>
+ <class
+ class="lp.registry.model.distroseriesdifference.DistroSeriesDifference">
+ <allow interface="lp.registry.interfaces.distroseriesdifference.IDistroSeriesDifference"/>
+ </class>
<class
class="lp.registry.model.distroseries.DistroSeries">
<allow
=== modified file 'lib/lp/registry/enum.py'
--- lib/lp/registry/enum.py 2010-08-20 20:31:18 +0000
+++ lib/lp/registry/enum.py 2010-08-27 09:27:48 +0000
@@ -6,6 +6,8 @@
__metaclass__ = type
__all__ = [
'BugNotificationLevel',
+ 'DistroSeriesDifferenceStatus',
+ 'DistroSeriesDifferenceType',
]
from lazr.enum import (
@@ -47,3 +49,58 @@
notifications about new events in the bugs's discussion, like new
comments.
""")
+
+
+class DistroSeriesDifferenceStatus(DBEnumeratedType):
+ """Distribution series difference status.
+
+ The status of a package difference between two DistroSeries.
+ """
+
+ NEEDS_ATTENTION = DBItem(1, """
+ Needs attention
+
+ This difference is current and needs attention.
+ """)
+
+ IGNORED = DBItem(2, """
+ Ignored
+
+ This difference is being ignored until a new package is uploaded
+ or the status is manually updated.
+ """)
+
+ IGNORED_ALWAYS = DBItem(3, """
+ Ignored always
+
+ This difference should always be ignored.
+ """)
+
+ RESOLVED = DBItem(4, """
+ Resolved
+
+ This difference has been resolved and versions are now equal.
+ """)
+
+class DistroSeriesDifferenceType(DBEnumeratedType):
+ """Distribution series difference type."""
+
+ UNIQUE_TO_DERIVED_SERIES = DBItem(1, """
+ Unique to derived series
+
+ This package is present in the derived series but not the parent
+ series.
+ """)
+
+ MISSING_FROM_DERIVED_SERIES = DBItem(2, """
+ Missing from derived series
+
+ This package is present in the parent series but missing from the
+ derived series.
+ """)
+
+ DIFFERENT_VERSIONS = DBItem(3, """
+ Different versions
+
+ This package is present in both series with different versions.
+ """)
=== added file 'lib/lp/registry/exceptions.py'
--- lib/lp/registry/exceptions.py 1970-01-01 00:00:00 +0000
+++ lib/lp/registry/exceptions.py 2010-08-27 09:27:48 +0000
@@ -0,0 +1,17 @@
+# Copyright 2010 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Exceptions for the Registry app."""
+
+__metaclass__ = type
+__all__ = [
+ 'NotADerivedSeriesError',
+ ]
+
+
+class NotADerivedSeriesError(Exception):
+ """A distro series difference must be created with a derived series.
+
+ This is raised when a DistroSeriesDifference is created with a
+ non-derived series - that is, a distroseries with a null Parent."""
+
=== added file 'lib/lp/registry/interfaces/distroseriesdifference.py'
--- lib/lp/registry/interfaces/distroseriesdifference.py 1970-01-01 00:00:00 +0000
+++ lib/lp/registry/interfaces/distroseriesdifference.py 2010-08-27 09:27:48 +0000
@@ -0,0 +1,108 @@
+# Copyright 2010 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Interface classes for a difference between two distribution series."""
+
+__metaclass__ = type
+
+
+__all__ = [
+ 'IDistroSeriesDifference',
+ 'IDistroSeriesDifferenceSource',
+ ]
+
+from lazr.restful.fields import Reference
+from zope.interface import Interface
+from zope.schema import (
+ Choice,
+ Int,
+ Text,
+ )
+
+from canonical.launchpad import _
+from lp.registry.enum import (
+ DistroSeriesDifferenceStatus,
+ DistroSeriesDifferenceType,
+ )
+from lp.registry.interfaces.distroseries import IDistroSeries
+from lp.registry.interfaces.sourcepackagename import ISourcePackageName
+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."""
+
+ id = Int(title=_('ID'), required=True, readonly=True)
+
+ derived_series = Reference(
+ IDistroSeries, title=_("Derived series"), required=True,
+ readonly=True, description=_(
+ "The distribution series which, together with its parent, "
+ "identifies the two series with the difference."))
+
+ source_package_name = Reference(
+ ISourcePackageName,
+ title=_("Source package name"), required=True, readonly=True,
+ description=_(
+ "The package with a difference between the derived series "
+ "and its parent."))
+
+ last_package_diff = Reference(
+ IPackageDiff, title=_("Last package diff"), required=False,
+ 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=False)
+
+ status = Choice(
+ title=_('Distro series difference status.'),
+ description=_('The current status of this difference.'),
+ vocabulary=DistroSeriesDifferenceStatus,
+ required=True, readonly=False)
+
+ difference_type = Choice(
+ title=_('Difference type'),
+ description=_('The type of difference for this package.'),
+ vocabulary=DistroSeriesDifferenceType,
+ required=True, readonly=False)
+
+ source_pub = Reference(
+ ISourcePackagePublishingHistory,
+ title=_("Derived source pub"), readonly=True,
+ description=_(
+ "The most recent published version 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."))
+
+
+class IDistroSeriesDifferenceSource(Interface):
+ """A utility of this interface can be used to create differences."""
+
+ def new(derived_series, source_package_name, difference_type,
+ status=DistroSeriesDifferenceStatus.NEEDS_ATTENTION):
+ """Create an `IDistroSeriesDifference`.
+
+ :param derived_series: The distribution series which was derived
+ from a parent. If a series without a parent is passed an
+ exception is raised.
+ :type derived_series: `IDistroSeries`.
+ :param source_package_name: A source package name identifying the
+ package with a difference.
+ :type source_package_name: `ISourcePackageName`.
+ :param difference_type: Indicates the type of difference represented
+ by this record.
+ :type difference_type: `DistroSeriesDifferenceType`.
+ :param status: The current status of this difference.
+ :type status: `DistorSeriesDifferenceStatus`.
+ :raises NotADerivedSeriesError: When the passed distro series
+ is not a derived series.
+ :return: A new `DistroSeriesDifference` object.
+ """
+
=== added file 'lib/lp/registry/model/distroseriesdifference.py'
--- lib/lp/registry/model/distroseriesdifference.py 1970-01-01 00:00:00 +0000
+++ lib/lp/registry/model/distroseriesdifference.py 2010-08-27 09:27:48 +0000
@@ -0,0 +1,113 @@
+# Copyright 2010 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Database classes for a difference between two distribution series."""
+
+__metaclass__ = type
+
+__all__ = [
+ 'DistroSeriesDifference',
+ ]
+
+
+from storm.locals import (
+ Int,
+ Reference,
+ Storm,
+ Unicode,
+ )
+from zope.interface import (
+ classProvides,
+ implements,
+ )
+
+from canonical.database.enumcol import DBEnum
+from canonical.launchpad.interfaces.lpstorm import IMasterStore
+from lp.registry.enum import (
+ DistroSeriesDifferenceStatus,
+ DistroSeriesDifferenceType,
+ )
+from lp.registry.exceptions import NotADerivedSeriesError
+from lp.registry.interfaces.distroseriesdifference import (
+ IDistroSeriesDifference,
+ IDistroSeriesDifferenceSource,
+ )
+
+
+class DistroSeriesDifference(Storm):
+ """See `DistroSeriesDifference`."""
+ implements(IDistroSeriesDifference)
+ classProvides(IDistroSeriesDifferenceSource)
+ __storm_table__ = 'DistroSeriesDifference'
+
+ id = Int(primary=True)
+
+ derived_series_id = Int(name='derived_series', allow_none=False)
+ derived_series = Reference(
+ derived_series_id, 'DistroSeries.id')
+
+ source_package_name_id = Int(
+ name='source_package_name', allow_none=False)
+ source_package_name = Reference(
+ source_package_name_id, 'SourcePackageName.id')
+
+ last_package_diff_id = Int(
+ name='last_package_diff', allow_none=True)
+ last_package_diff = Reference(
+ last_package_diff_id, 'PackageDiff.id')
+
+ activity_log = Unicode(name='activity_log', allow_none=True)
+ status = DBEnum(name='status', allow_none=False,
+ enum=DistroSeriesDifferenceStatus)
+ difference_type = DBEnum(name='difference_type', allow_none=False,
+ enum=DistroSeriesDifferenceType)
+
+ @staticmethod
+ def new(derived_series, source_package_name, difference_type,
+ status=DistroSeriesDifferenceStatus.NEEDS_ATTENTION):
+ """See `IDistroSeriesDifferenceSource`."""
+ if derived_series.parent_series is None:
+ raise NotADerivedSeriesError()
+
+ store = IMasterStore(DistroSeriesDifference)
+ diff = DistroSeriesDifference()
+ diff.derived_series = derived_series
+ diff.source_package_name = source_package_name
+ diff.status = status
+ diff.difference_type = difference_type
+
+ return store.add(diff)
+
+ @property
+ def source_pub(self):
+ """See `IDistroSeriesDifference`."""
+ return self._getLatestSourcePub()
+
+ @property
+ def parent_source_pub(self):
+ """See `IDistroSeriesDifference`."""
+ return self._getLatestSourcePub(for_parent=True)
+
+ def _getLatestSourcePub(self, for_parent=False):
+ """Helper to keep source_pub/parent_source_pub DRY."""
+ distro_series = self.derived_series
+ if for_parent:
+ distro_series = self.derived_series.parent_series
+
+ pubs = distro_series.getPublishedSources(
+ self.source_package_name, include_pending=True)
+
+ # The most recent published source is the first one.
+ if pubs:
+ return pubs[0]
+ else:
+ return None
+
+ def _getVersions(self):
+ """Helper method returning versions string."""
+ src_pub_ver = parent_src_pub_ver = "-"
+ 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
=== added file 'lib/lp/registry/tests/test_distroseriesdifference.py'
--- lib/lp/registry/tests/test_distroseriesdifference.py 1970-01-01 00:00:00 +0000
+++ lib/lp/registry/tests/test_distroseriesdifference.py 2010-08-27 09:27:48 +0000
@@ -0,0 +1,105 @@
+# Copyright 2010 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Model tests for the DistroSeriesDifference class."""
+
+__metaclass__ = type
+
+import unittest
+
+from storm.store import Store
+from zope.component import getUtility
+
+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.registry.exceptions import NotADerivedSeriesError
+from lp.registry.interfaces.distroseriesdifference import (
+ IDistroSeriesDifference,
+ IDistroSeriesDifferenceSource,
+ )
+from lp.soyuz.interfaces.publishing import PackagePublishingStatus
+
+
+class DistroSeriesDifferenceTestCase(TestCaseWithFactory):
+
+ layer = DatabaseFunctionalLayer
+
+ def test_implements_interface(self):
+ # The implementation implements the interface correctly.
+ ds_diff = self.factory.makeDistroSeriesDifference()
+ # Flush the store to ensure db constraints are triggered.
+ Store.of(ds_diff).flush()
+
+ verifyObject(IDistroSeriesDifference, ds_diff)
+
+ def test_source_implements_interface(self):
+ # The utility for creating differences implements its interface.
+ utility = getUtility(IDistroSeriesDifferenceSource)
+
+ verifyObject(IDistroSeriesDifferenceSource, utility)
+
+ def test_new_non_derived_series(self):
+ # A DistroSeriesDifference cannot be created with a non-derived
+ # series.
+ distro_series = self.factory.makeDistroSeries()
+ self.assertRaises(
+ NotADerivedSeriesError,
+ self.factory.makeDistroSeriesDifference,
+ derived_series=distro_series)
+
+ def test_source_pub(self):
+ # The related source pub is returned for the derived series.
+ ds_diff = self.factory.makeDistroSeriesDifference(
+ source_package_name_str="foonew")
+
+ self.assertEqual(
+ 'foonew', ds_diff.source_pub.source_package_name)
+ self.assertEqual(
+ ds_diff.derived_series, ds_diff.source_pub.distroseries)
+
+ def test_source_pub_gets_latest_pending(self):
+ # The most recent publication is always returned, even if its pending.
+ ds_diff = self.factory.makeDistroSeriesDifference(
+ source_package_name_str="foonew")
+ pending_pub = self.factory.makeSourcePackagePublishingHistory(
+ sourcepackagename=ds_diff.source_package_name,
+ distroseries=ds_diff.derived_series,
+ status=PackagePublishingStatus.PENDING)
+
+ self.assertEqual(pending_pub, ds_diff.source_pub)
+
+ def test_source_pub_returns_none(self):
+ # None is returned when there is no source pub.
+ ds_diff = self.factory.makeDistroSeriesDifference(
+ difference_type=(
+ DistroSeriesDifferenceType.MISSING_FROM_DERIVED_SERIES))
+
+ self.assertIs(None, ds_diff.source_pub)
+
+ def test_parent_source_pub(self):
+ # The related source pub for the parent distro series is returned.
+ ds_diff = self.factory.makeDistroSeriesDifference(
+ source_package_name_str="foonew")
+
+ self.assertEqual(
+ 'foonew', ds_diff.parent_source_pub.source_package_name)
+ self.assertEqual(
+ ds_diff.derived_series.parent_series,
+ ds_diff.parent_source_pub.distroseries)
+
+ def test_paren_source_pub_gets_latest_pending(self):
+ # The most recent publication is always returned, even if its pending.
+ ds_diff = self.factory.makeDistroSeriesDifference(
+ source_package_name_str="foonew")
+ pending_pub = self.factory.makeSourcePackagePublishingHistory(
+ sourcepackagename=ds_diff.source_package_name,
+ distroseries=ds_diff.derived_series.parent_series,
+ status=PackagePublishingStatus.PENDING)
+
+ self.assertEqual(pending_pub, ds_diff.parent_source_pub)
+
+
+def test_suite():
+ return unittest.TestLoader().loadTestsFromName(__name__)
=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py 2010-08-27 04:33:52 +0000
+++ lib/lp/testing/factory.py 2010-08-27 09:27:48 +0000
@@ -152,15 +152,15 @@
IHWSubmissionDeviceSet,
IHWSubmissionSet,
)
+from lp.registry.enum import DistroSeriesDifferenceType
from lp.registry.interfaces.distribution import IDistributionSet
from lp.registry.interfaces.distributionmirror import (
MirrorContent,
MirrorSpeed,
)
from lp.registry.interfaces.distroseries import IDistroSeries
-from lp.registry.interfaces.gpg import (
- GPGKeyAlgorithm,
- IGPGKeySet,
+from lp.registry.interfaces.distroseriesdifference import (
+ IDistroSeriesDifferenceSource,
)
from lp.registry.interfaces.mailinglist import (
IMailingListSet,
@@ -1813,6 +1813,44 @@
# Most people think of distro releases as distro series.
makeDistroSeries = makeDistroRelease
+ def makeDistroSeriesDifference(
+ self, derived_series=None, source_package_name_str=None,
+ versions=None,
+ difference_type=DistroSeriesDifferenceType.DIFFERENT_VERSIONS):
+ """Create a new distro series source package difference."""
+ if derived_series is None:
+ parent_series = self.makeDistroSeries()
+ derived_series = self.makeDistroSeries(
+ parent_series=parent_series)
+
+ if source_package_name_str is None:
+ source_package_name_str = self.getUniqueString('src-name')
+
+ source_package_name = self.getOrMakeSourcePackageName(
+ source_package_name_str)
+
+ if versions is None:
+ versions = {}
+
+ if difference_type is not (
+ DistroSeriesDifferenceType.MISSING_FROM_DERIVED_SERIES):
+
+ source_pub = self.makeSourcePackagePublishingHistory(
+ distroseries=derived_series,
+ version=versions.get('derived'),
+ sourcepackagename=source_package_name)
+
+ if difference_type is not (
+ DistroSeriesDifferenceType.UNIQUE_TO_DERIVED_SERIES):
+
+ source_pub = self.makeSourcePackagePublishingHistory(
+ distroseries=derived_series.parent_series,
+ version=versions.get('parent'),
+ sourcepackagename=source_package_name)
+
+ return getUtility(IDistroSeriesDifferenceSource).new(
+ derived_series, source_package_name, difference_type)
+
def makeDistroArchSeries(self, distroseries=None,
architecturetag=None, processorfamily=None,
official=True, owner=None,
Follow ups