← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~allenap/launchpad/selected-differences-vocab-bug-817408 into lp:launchpad

 

Gavin Panella has proposed merging lp:~allenap/launchpad/selected-differences-vocab-bug-817408 into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  Bug #817408 in Launchpad itself: "IDifferencesFormSchema.selected_differences vocabulary is too restrictive"
  https://bugs.launchpad.net/launchpad/+bug/817408

For more details, see:
https://code.launchpad.net/~allenap/launchpad/selected-differences-vocab-bug-817408/+merge/70048

This adds a new vocabulary called "DistroSeriesDifferences" which represents all DistroSeriesDifference records for a given derived series. This is then used in place of the hand-crafted vocabulary in IDifferencesFormSchema.
-- 
https://code.launchpad.net/~allenap/launchpad/selected-differences-vocab-bug-817408/+merge/70048
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~allenap/launchpad/selected-differences-vocab-bug-817408 into lp:launchpad.
=== modified file 'lib/lp/registry/browser/distroseries.py'
--- lib/lp/registry/browser/distroseries.py	2011-07-29 20:05:40 +0000
+++ lib/lp/registry/browser/distroseries.py	2011-08-01 16:34:19 +0000
@@ -792,7 +792,7 @@
 
     selected_differences = List(
         title=_('Selected differences'),
-        value_type=Choice(vocabulary=SimpleVocabulary([])),
+        value_type=Choice(vocabulary="DistroSeriesDifferences"),
         description=_("Select the differences for syncing."),
         required=True)
 
@@ -859,15 +859,6 @@
         self.form_fields = (
             self.setupPackageFilterRadio() +
             self.form_fields)
-        check_permission('launchpad.Edit', self.context)
-        terms = [
-            SimpleTerm(diff, diff.id)
-                    for diff in self.cached_differences.batch]
-        diffs_vocabulary = SimpleVocabulary(terms)
-        # XXX: GavinPanella 2011-07-13 bug=809985: Good thing the app servers
-        # are running single threaded...
-        choice = self.form_fields['selected_differences'].field.value_type
-        choice.vocabulary = diffs_vocabulary
 
     def _sync_sources(self, action, data):
         """Synchronise packages from the parent series to this one."""

=== modified file 'lib/lp/registry/tests/test_distroseries_vocabularies.py'
--- lib/lp/registry/tests/test_distroseries_vocabularies.py	2011-07-08 14:09:11 +0000
+++ lib/lp/registry/tests/test_distroseries_vocabularies.py	2011-08-01 16:34:19 +0000
@@ -13,19 +13,29 @@
 from pytz import utc
 from testtools.matchers import Equals
 from zope.component import getUtility
-from zope.schema.interfaces import IVocabularyFactory
+from zope.schema.interfaces import (
+    ITokenizedTerm,
+    IVocabularyFactory,
+    )
 from zope.security.proxy import removeSecurityProxy
 
 from canonical.database.sqlbase import flush_database_caches
 from canonical.launchpad.webapp.vocabulary import IHugeVocabulary
 from canonical.testing.layers import DatabaseFunctionalLayer
 from lp.registry.interfaces.distroseries import IDistroSeriesSet
-from lp.registry.vocabularies import DistroSeriesDerivationVocabulary
+from lp.registry.vocabularies import (
+    DistroSeriesDerivationVocabulary,
+    DistroSeriesDifferencesVocabulary,
+    )
 from lp.testing import (
     StormStatementRecorder,
     TestCaseWithFactory,
     )
-from lp.testing.matchers import HasQueryCount
+from lp.testing.matchers import (
+    Contains,
+    HasQueryCount,
+    Provides,
+    )
 
 
 class TestDistroSeriesDerivationVocabulary(TestCaseWithFactory):
@@ -47,9 +57,8 @@
             getUtility(IVocabularyFactory, name="DistroSeriesDerivation"),
             DistroSeriesDerivationVocabulary)
 
-    def test_interfaces(self):
-        # DistroSeriesDerivationVocabulary instances provide
-        # IVocabulary and IVocabularyTokenized.
+    def test_interface(self):
+        # DistroSeriesDerivationVocabulary instances provide IHugeVocabulary.
         distroseries = self.factory.makeDistroSeries()
         vocabulary = DistroSeriesDerivationVocabulary(distroseries)
         self.assertProvides(vocabulary, IHugeVocabulary)
@@ -248,3 +257,108 @@
         self.assertContentEqual(
             expected_distroseries,
             observed_distroseries)
+
+
+class TestDistroSeriesDifferencesVocabulary(TestCaseWithFactory):
+    """Tests for `DistroSeriesDifferencesVocabulary`."""
+
+    layer = DatabaseFunctionalLayer
+
+    def test_registration(self):
+        # DistroSeriesDifferencesVocabulary is registered as a named utility
+        # for IVocabularyFactory.
+        self.assertEqual(
+            getUtility(IVocabularyFactory, name="DistroSeriesDifferences"),
+            DistroSeriesDifferencesVocabulary)
+
+    def test_interface(self):
+        # DistroSeriesDifferencesVocabulary instances provide IHugeVocabulary.
+        distroseries = self.factory.makeDistroSeries()
+        vocabulary = DistroSeriesDifferencesVocabulary(distroseries)
+        self.assertProvides(vocabulary, IHugeVocabulary)
+
+    def test_non_derived_distroseries(self):
+        # The vocabulary is empty for a non-derived series.
+        distroseries = self.factory.makeDistroSeries()
+        vocabulary = DistroSeriesDifferencesVocabulary(distroseries)
+        self.assertContentEqual([], vocabulary)
+
+    def test_derived_distroseries(self):
+        # The vocabulary contains all DSDs for a derived series.
+        distroseries = self.factory.makeDistroSeries()
+        dsds = [
+            self.factory.makeDistroSeriesDifference(
+                derived_series=distroseries),
+            self.factory.makeDistroSeriesDifference(
+                derived_series=distroseries),
+            ]
+        vocabulary = DistroSeriesDifferencesVocabulary(distroseries)
+        self.assertContentEqual(
+            dsds, (term.value for term in vocabulary))
+
+    def test_derived_distroseries_not_other_distroseries(self):
+        # The vocabulary contains all DSDs for a derived series and not for
+        # another series.
+        distroseries1 = self.factory.makeDistroSeries()
+        distroseries2 = self.factory.makeDistroSeries()
+        dsds = [
+            self.factory.makeDistroSeriesDifference(
+                derived_series=distroseries1),
+            self.factory.makeDistroSeriesDifference(
+                derived_series=distroseries1),
+            self.factory.makeDistroSeriesDifference(
+                derived_series=distroseries2),
+            self.factory.makeDistroSeriesDifference(
+                derived_series=distroseries2),
+            ]
+        vocabulary = DistroSeriesDifferencesVocabulary(distroseries1)
+        self.assertContentEqual(
+            (dsd for dsd in dsds if dsd.derived_series == distroseries1),
+            (term.value for term in vocabulary))
+
+    def test_contains(self):
+        # The vocabulary can be tested for membership.
+        difference = self.factory.makeDistroSeriesDifference()
+        vocabulary = DistroSeriesDifferencesVocabulary(
+            difference.derived_series)
+        self.assertThat(vocabulary, Contains(difference))
+
+    def test_size(self):
+        # The vocabulary can report its size.
+        difference = self.factory.makeDistroSeriesDifference()
+        vocabulary = DistroSeriesDifferencesVocabulary(
+            difference.derived_series)
+        self.assertEqual(1, len(vocabulary))
+
+    def test_getTerm(self):
+        # A term can be obtained from a given value.
+        difference = self.factory.makeDistroSeriesDifference()
+        vocabulary = DistroSeriesDifferencesVocabulary(
+            difference.derived_series)
+        term = vocabulary.getTerm(difference)
+        self.assertThat(term, Provides(ITokenizedTerm))
+        self.assertEqual(difference, term.value)
+        self.assertEqual(str(difference.id), term.token)
+
+    def test_getTermByToken(self):
+        # A term can be obtained from a given token.
+        difference = self.factory.makeDistroSeriesDifference()
+        vocabulary = DistroSeriesDifferencesVocabulary(
+            difference.derived_series)
+        term = vocabulary.getTermByToken(str(difference.id))
+        self.assertEqual(difference, term.value)
+
+    def test_getTermByToken_not_found(self):
+        # LookupError is raised when the token cannot be found.
+        distroseries = self.factory.makeDistroSeries()
+        difference = self.factory.makeDistroSeriesDifference()
+        vocabulary = DistroSeriesDifferencesVocabulary(distroseries)
+        self.assertRaises(
+            LookupError, vocabulary.getTermByToken, str(difference.id))
+
+    def test_getTermByToken_invalid(self):
+        # LookupError is raised when the token is not valid (i.e. a string
+        # containing only digits).
+        distroseries = self.factory.makeDistroSeries()
+        vocabulary = DistroSeriesDifferencesVocabulary(distroseries)
+        self.assertRaises(LookupError, vocabulary.getTermByToken, "foobar")

=== modified file 'lib/lp/registry/vocabularies.py'
--- lib/lp/registry/vocabularies.py	2011-07-26 19:09:48 +0000
+++ lib/lp/registry/vocabularies.py	2011-08-01 16:34:19 +0000
@@ -33,6 +33,7 @@
     'DistributionSourcePackageVocabulary',
     'DistributionVocabulary',
     'DistroSeriesDerivationVocabulary',
+    'DistroSeriesDifferencesVocabulary',
     'DistroSeriesVocabulary',
     'FeaturedProjectVocabulary',
     'FilteredDistroSeriesVocabulary',
@@ -167,6 +168,7 @@
 from lp.registry.interfaces.sourcepackage import ISourcePackage
 from lp.registry.model.distribution import Distribution
 from lp.registry.model.distroseries import DistroSeries
+from lp.registry.model.distroseriesdifference import DistroSeriesDifference
 from lp.registry.model.distroseriesparent import DistroSeriesParent
 from lp.registry.model.featuredproject import FeaturedProject
 from lp.registry.model.karma import KarmaCategory
@@ -194,9 +196,7 @@
 from lp.soyuz.model.binarypackagename import BinaryPackageName
 from lp.soyuz.model.binarypackagerelease import BinaryPackageRelease
 from lp.soyuz.model.distroarchseries import DistroArchSeries
-from lp.soyuz.model.publishing import (
-    SourcePackagePublishingHistory,
-    )
+from lp.soyuz.model.publishing import SourcePackagePublishingHistory
 from lp.soyuz.model.sourcepackagerelease import SourcePackageRelease
 
 
@@ -1866,6 +1866,75 @@
             return self.find_terms(where)
 
 
+class DistroSeriesDifferencesVocabulary:
+    """A vocabulary source for differences relating to a series.
+
+    Specifically, all `DistroSeriesDifference`s relating to a derived series.
+    """
+
+    implements(IHugeVocabulary)
+
+    displayname = "Choose a difference"
+    step_title = 'Search'
+
+    def __init__(self, context):
+        """Create a new vocabulary for the context.
+
+        :type context: `IDistroSeries`.
+        """
+        assert IDistroSeries.providedBy(context)
+        self.distroseries = context
+
+    def __len__(self):
+        """See `IIterableVocabulary`."""
+        return self.searchForDifferences().count()
+
+    def __iter__(self):
+        """See `IIterableVocabulary`."""
+        for difference in self.searchForDifferences():
+            yield self.toTerm(difference)
+
+    def __contains__(self, value):
+        """See `IVocabulary`."""
+        return value.derived_series == self.distroseries
+
+    def getTerm(self, value):
+        """See `IVocabulary`."""
+        if value not in self:
+            raise LookupError(value)
+        return self.toTerm(value)
+
+    def getTermByToken(self, token):
+        """See `IVocabularyTokenized`."""
+        if not token.isdigit():
+            raise LookupError(token)
+        difference = IStore(DistroSeriesDifference).get(
+            DistroSeriesDifference, int(token))
+        if difference is None:
+            raise LookupError(token)
+        elif difference.derived_series != self.distroseries:
+            raise LookupError(token)
+        else:
+            return self.toTerm(difference)
+
+    @staticmethod
+    def toTerm(dsd):
+        """Return the term for a `DistroSeriesDifference`."""
+        return SimpleTerm(dsd, dsd.id)
+
+    def searchForTerms(self, query=None):
+        """See `IHugeVocabulary`."""
+        results = self.searchForDifferences()
+        return CountableIterator(results.count(), results, self.toTerm)
+
+    def searchForDifferences(self):
+        """The set of `DistroSeriesDifference`s related to the context.
+
+        :return: `IResultSet` yielding `IDistroSeriesDifference`.
+        """
+        return DistroSeriesDifference.getForDistroSeries(self.distroseries)
+
+
 class PillarVocabularyBase(NamedSQLObjectHugeVocabulary):
     """Active `IPillar` objects vocabulary."""
     displayname = 'Needs to be overridden'

=== modified file 'lib/lp/registry/vocabularies.zcml'
--- lib/lp/registry/vocabularies.zcml	2011-07-13 01:49:53 +0000
+++ lib/lp/registry/vocabularies.zcml	2011-08-01 16:34:19 +0000
@@ -42,6 +42,18 @@
 
 
   <securedutility
+      name="DistroSeriesDifferences"
+      component=".vocabularies.DistroSeriesDifferencesVocabulary"
+      provides="zope.schema.interfaces.IVocabularyFactory">
+    <allow interface="zope.schema.interfaces.IVocabularyFactory"/>
+  </securedutility>
+
+  <class class=".vocabularies.DistroSeriesDifferencesVocabulary">
+    <allow interface="canonical.launchpad.webapp.vocabulary.IHugeVocabulary"/>
+  </class>
+
+
+  <securedutility
     name="FeaturedProject"
     component="lp.registry.vocabularies.FeaturedProjectVocabulary"
     provides="zope.schema.interfaces.IVocabularyFactory"