Merge lp:~jcsackett/launchpad/series-need-usage-attributes-643902 into lp:launchpad

Proposed by j.c.sackett
Status: Merged
Approved by: Graham Binns
Approved revision: no longer in the source branch.
Merged at revision: 11604
Proposed branch: lp:~jcsackett/launchpad/series-need-usage-attributes-643902
Merge into: lp:launchpad
Diff against target: 383 lines (+195/-31)
7 files modified
lib/lp/registry/adapters.py (+7/-19)
lib/lp/registry/configure.zcml (+7/-8)
lib/lp/registry/interfaces/distroseries.py (+2/-1)
lib/lp/registry/interfaces/productseries.py (+2/-1)
lib/lp/registry/model/distroseries.py (+50/-1)
lib/lp/registry/model/productseries.py (+51/-1)
lib/lp/registry/tests/test_service_usage.py (+76/-0)
To merge this branch: bzr merge lp:~jcsackett/launchpad/series-need-usage-attributes-643902
Reviewer Review Type Date Requested Status
Graham Binns (community) code Approve
Review via email: mp+36145@code.launchpad.net

Commit message

Adds usage attributes (e.g. blueprints_usage, codehosting_usage) to productseries and distroseries.

Description of the change

Summary
=======

Adds the usage attributes to productseries and distroseries, largely as a mirror of the attributes on the series related pillar.

Proposed Fix
============

Add the usage attributes as properties to the series.

Pre-implementation notes
========================

Spoke with Curtis Hovey (sinzui) and jtv about what was needed in usage attributes on the series, especially for translations.

Implementation details
======================

As in proposed fix.

Translations use the current status of templates to determine usage; however, that is overridden by requirements in the translations application (see bug 605924). That requirement will be resolve by lp:~jcsackett/launchpad/unknown-translations-service-643545, which is dependent on this branch.

Tests
=====

bin/test -t test_service_usage

Demo and Q/A
============

Currently usage of the various series should see no change from this branch.

Lint
====
Output:

= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/registry/adapters.py
  lib/lp/registry/configure.zcml
  lib/lp/registry/interfaces/distroseries.py
  lib/lp/registry/interfaces/productseries.py
  lib/lp/registry/model/distroseries.py
  lib/lp/registry/model/productseries.py
  lib/lp/registry/tests/test_service_usage.py

./lib/lp/registry/interfaces/distroseries.py
     417: E301 expected 1 blank line, found 2
     458: E301 expected 1 blank line, found 0
./lib/lp/registry/model/distroseries.py
     364: E301 expected 1 blank line, found 2
     708: E301 expected 1 blank line, found 2

Both errors related to lint's issue with comments and blank lines

To post a comment you must log in.
Revision history for this message
Graham Binns (gmb) wrote :

From IRC:

<gmb> jcsackett, On line 177 you say "# If translations_usage is set for the Product, respect it" but then you reference self.distribution.translations_usage. I'm assuming you need to s/Product/Distribution in the comment.
<jcsackett> gmb: you're correct. making the change.
<gmb> Same on line 241
 jcsackett, You need to add some comments or docstrings to the start of your tests explaining what they test (you should phrase this as a statement of expected behaviour, e.g. "The frobnob goes boing." rather than "Test that the frobnob goes boing."
 Other than that, r=me.

review: Approve (code)
Revision history for this message
j.c.sackett (jcsackett) wrote :

> From IRC:
>
> <gmb> jcsackett, On line 177 you say "# If translations_usage is set for the
> Product, respect it" but then you reference
> self.distribution.translations_usage. I'm assuming you need to
> s/Product/Distribution in the comment.
> <jcsackett> gmb: you're correct. making the change.
> <gmb> Same on line 241

Line 241 actually does mean product; it's in the productseries and looks for the information on the product for the productseries.

> jcsackett, You need to add some comments or docstrings to the start of your
> tests explaining what they test (you should phrase this as a statement of
> expected behaviour, e.g. "The frobnob goes boing." rather than "Test that the
> frobnob goes boing."
> Other than that, r=me.

Done. Thanks, Graham.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/registry/adapters.py'
2--- lib/lp/registry/adapters.py 2010-09-13 12:09:30 +0000
3+++ lib/lp/registry/adapters.py 2010-09-22 00:36:48 +0000
4@@ -13,19 +13,11 @@
5 ]
6
7
8-from zope.component import (
9- adapter,
10- getUtility,
11- )
12+from zope.component import getUtility
13 from zope.component.interfaces import ComponentLookupError
14-from zope.interface import (
15- implementer,
16- implements,
17- )
18+from zope.interface import implements
19
20 from canonical.launchpad.webapp.interfaces import ILaunchpadPrincipal
21-from lp.app.interfaces.launchpad import IServiceUsage
22-from lp.registry.interfaces.distroseries import IDistroSeries
23 from lp.registry.interfaces.poll import (
24 IPollSet,
25 IPollSubset,
26@@ -34,15 +26,11 @@
27 )
28
29
30-@implementer(IServiceUsage)
31-@adapter(IDistroSeries)
32-def distroseries_to_serviceusage(distroseries):
33- """Adapts `IDistroSeries` object to `IServiceUsage`."""
34- return distroseries.distribution
35-
36-
37-def distroseries_to_launchpadusage(distroseries):
38- """Adapts `IDistroSeries` object to `ILaunchpadUsage`."""
39+def distroseries_to_distribution(distroseries):
40+ """Adapts `IDistroSeries` object to `IDistribution`.
41+
42+ This is useful for adapting to `IServiceUsage`
43+ or `ILaunchpadUsage`."""
44 return distroseries.distribution
45
46
47
48=== modified file 'lib/lp/registry/configure.zcml'
49--- lib/lp/registry/configure.zcml 2010-09-18 00:17:07 +0000
50+++ lib/lp/registry/configure.zcml 2010-09-22 00:36:48 +0000
51@@ -142,6 +142,9 @@
52 interface="canonical.launchpad.interfaces.ICanPublishPackages"/>
53 <require
54 permission="launchpad.Edit"
55+ set_schema="lp.app.interfaces.launchpad.IServiceUsage"/>
56+ <require
57+ permission="launchpad.Edit"
58 interface="lp.registry.interfaces.distroseries.IDistroSeriesEditRestricted"/>
59 <require
60 permission="launchpad.TranslationsAdmin"
61@@ -194,11 +197,9 @@
62 <adapter
63 provides="lp.app.interfaces.launchpad.ILaunchpadUsage"
64 for="lp.registry.interfaces.distroseries.IDistroSeries"
65- factory="lp.registry.adapters.distroseries_to_launchpadusage"
66+ factory="lp.registry.adapters.distroseries_to_distribution"
67 permission="zope.Public"/>
68 <adapter
69- factory="lp.registry.adapters.distroseries_to_serviceusage" />
70- <adapter
71 provides="canonical.launchpad.webapp.interfaces.IBreadcrumb"
72 for="lp.registry.interfaces.distroseries.IDistroSeries"
73 factory="lp.registry.browser.distroseries.DistroSeriesBreadcrumb"
74@@ -1374,6 +1375,9 @@
75 interface="lp.translations.interfaces.translationimportqueue.IHasTranslationImports"/>
76 <require
77 permission="launchpad.Edit"
78+ set_schema="lp.app.interfaces.launchpad.IServiceUsage"/>
79+ <require
80+ permission="launchpad.Edit"
81 set_attributes="product name owner driver summary branch
82 translations_branch status releasefileglob
83 translations_autoimport_mode"/>
84@@ -1407,11 +1411,6 @@
85 factory="lp.registry.adapters.productseries_to_product"
86 permission="zope.Public"/>
87 <adapter
88- provides="lp.app.interfaces.launchpad.IServiceUsage"
89- for="lp.registry.interfaces.productseries.IProductSeries"
90- factory="lp.registry.adapters.productseries_to_product"
91- permission="zope.Public"/>
92- <adapter
93 provides="lp.app.interfaces.launchpad.ILaunchpadUsage"
94 for="lp.registry.interfaces.productseries.IProductSeries"
95 factory="lp.registry.adapters.productseries_to_product"
96
97=== modified file 'lib/lp/registry/interfaces/distroseries.py'
98--- lib/lp/registry/interfaces/distroseries.py 2010-08-30 19:06:34 +0000
99+++ lib/lp/registry/interfaces/distroseries.py 2010-09-22 00:36:48 +0000
100@@ -52,6 +52,7 @@
101 from canonical.launchpad.validators.name import name_validator
102 from canonical.launchpad.validators.version import sane_version
103 from lp.app.errors import NameLookupFailed
104+from lp.app.interfaces.launchpad import IServiceUsage
105 from lp.blueprints.interfaces.specificationtarget import ISpecificationGoal
106 from lp.bugs.interfaces.bugtarget import (
107 IBugTarget,
108@@ -173,7 +174,7 @@
109 class IDistroSeriesPublic(
110 ISeriesMixin, IHasAppointedDriver, IHasOwner, IBugTarget,
111 ISpecificationGoal, IHasMilestones, IHasOfficialBugTags,
112- IHasBuildRecords, IHasTranslationTemplates):
113+ IHasBuildRecords, IHasTranslationTemplates, IServiceUsage):
114 """Public IDistroSeries properties."""
115
116 id = Attribute("The distroseries's unique number.")
117
118=== modified file 'lib/lp/registry/interfaces/productseries.py'
119--- lib/lp/registry/interfaces/productseries.py 2010-08-31 00:02:42 +0000
120+++ lib/lp/registry/interfaces/productseries.py 2010-09-22 00:36:48 +0000
121@@ -48,6 +48,7 @@
122 from canonical.launchpad.validators.name import name_validator
123 from canonical.launchpad.webapp.url import urlparse
124 from lp.app.errors import NameLookupFailed
125+from lp.app.interfaces.launchpad import IServiceUsage
126 from lp.blueprints.interfaces.specificationtarget import ISpecificationGoal
127 from lp.bugs.interfaces.bugtarget import (
128 IBugTarget,
129@@ -120,7 +121,7 @@
130 class IProductSeriesPublic(
131 ISeriesMixin, IHasAppointedDriver, IHasOwner, IBugTarget,
132 ISpecificationGoal, IHasMilestones, IHasOfficialBugTags,
133- IHasTranslationTemplates):
134+ IHasTranslationTemplates, IServiceUsage):
135 """Public IProductSeries properties."""
136 # XXX Mark Shuttleworth 2004-10-14: Would like to get rid of id in
137 # interfaces, as soon as SQLobject allows using the object directly
138
139=== modified file 'lib/lp/registry/model/distroseries.py'
140--- lib/lp/registry/model/distroseries.py 2010-09-03 15:02:39 +0000
141+++ lib/lp/registry/model/distroseries.py 2010-09-22 00:36:48 +0000
142@@ -63,6 +63,10 @@
143 SLAVE_FLAVOR,
144 )
145 from lp.app.errors import NotFoundError
146+from lp.app.enums import (
147+ ServiceUsage,
148+ service_uses_launchpad)
149+from lp.app.interfaces.launchpad import IServiceUsage
150 from lp.blueprints.interfaces.specification import (
151 SpecificationFilter,
152 SpecificationGoalStatus,
153@@ -186,7 +190,7 @@
154 """A particular series of a distribution."""
155 implements(
156 ICanPublishPackages, IDistroSeries, IHasBugHeat, IHasBuildRecords,
157- IHasQueueItems)
158+ IHasQueueItems, IServiceUsage)
159
160 _table = 'DistroSeries'
161 _defaultOrder = ['distribution', 'version']
162@@ -263,6 +267,51 @@
163 """ % self.id,
164 clauseTables=["ComponentSelection"])
165
166+ @property
167+ def answers_usage(self):
168+ """See `IServiceUsage.`"""
169+ return self.distribution.answers_usage
170+
171+ @property
172+ def blueprints_usage(self):
173+ """See `IServiceUsage.`"""
174+ return self.distribution.blueprints_usage
175+
176+ @property
177+ def translations_usage(self):
178+ """See `IServiceUsage.`"""
179+ # If translations_usage is set for the Distribution, respect it.
180+ usage = self.distribution.translations_usage
181+ if usage != ServiceUsage.UNKNOWN:
182+ return usage
183+
184+ # If not, usage is based on the presence of current translation
185+ # templates for the series.
186+ if self.getCurrentTranslationTemplates().count() > 0:
187+ return ServiceUsage.LAUNCHPAD
188+ else:
189+ return ServiceUsage.UNKNOWN
190+
191+ @property
192+ def codehosting_usage(self):
193+ """See `IServiceUsage.`"""
194+ return self.distribution.codehosting_usage
195+
196+ @property
197+ def bug_tracking_usage(self):
198+ """See `IServiceUsage.`"""
199+ return self.distribution.bug_tracking_usage
200+
201+ @property
202+ def uses_launchpad(self):
203+ """ See `IServiceUsage.`"""
204+ return (
205+ service_uses_launchpad(self.blueprints_usage) or
206+ service_uses_launchpad(self.translations_usage) or
207+ service_uses_launchpad(self.answers_usage) or
208+ service_uses_launchpad(self.codehosting_usage) or
209+ service_uses_launchpad(self.bug_tracking_usage))
210+
211 # DistroArchSeries lookup properties/methods.
212 architectures = SQLMultipleJoin(
213 'DistroArchSeries', joinColumn='distroseries',
214
215=== modified file 'lib/lp/registry/model/productseries.py'
216--- lib/lp/registry/model/productseries.py 2010-08-31 00:02:42 +0000
217+++ lib/lp/registry/model/productseries.py 2010-09-22 00:36:48 +0000
218@@ -43,6 +43,10 @@
219 from canonical.launchpad.webapp.publisher import canonical_url
220 from canonical.launchpad.webapp.sorting import sorted_dotted_numbers
221 from lp.app.errors import NotFoundError
222+from lp.app.enums import (
223+ ServiceUsage,
224+ service_uses_launchpad)
225+from lp.app.interfaces.launchpad import IServiceUsage
226 from lp.blueprints.interfaces.specification import (
227 SpecificationDefinitionStatus,
228 SpecificationFilter,
229@@ -115,7 +119,7 @@
230 HasTranslationImportsMixin, HasTranslationTemplatesMixin,
231 StructuralSubscriptionTargetMixin, SeriesMixin):
232 """A series of product releases."""
233- implements(IHasBugHeat, IProductSeries)
234+ implements(IHasBugHeat, IProductSeries, IServiceUsage)
235
236 _table = 'ProductSeries'
237
238@@ -151,6 +155,52 @@
239 packagings = SQLMultipleJoin('Packaging', joinColumn='productseries',
240 orderBy=['-id'])
241
242+ @property
243+ def answers_usage(self):
244+ """See `IServiceUsage.`"""
245+ return self.product.answers_usage
246+
247+ @property
248+ def blueprints_usage(self):
249+ """See `IServiceUsage.`"""
250+ return self.product.blueprints_usage
251+
252+ @property
253+ def translations_usage(self):
254+ """See `IServiceUsage.`"""
255+ # If translations_usage is set for the Product, respect it.
256+ usage = self.product.translations_usage
257+ if usage != ServiceUsage.UNKNOWN:
258+ return usage
259+
260+ # If not, usage is based on the presence of current translation
261+ # templates for the series.
262+ if self.potemplate_count > 0:
263+ return ServiceUsage.LAUNCHPAD
264+ else:
265+ return ServiceUsage.UNKNOWN
266+
267+ @property
268+ def codehosting_usage(self):
269+ """See `IServiceUsage.`"""
270+ return self.product.codehosting_usage
271+
272+ @property
273+ def bug_tracking_usage(self):
274+ """See `IServiceUsage.`"""
275+ return self.product.bug_tracking_usage
276+
277+ @property
278+ def uses_launchpad(self):
279+ """ See `IServiceUsage.`"""
280+ return (
281+ service_uses_launchpad(self.blueprints_usage) or
282+ service_uses_launchpad(self.translations_usage) or
283+ service_uses_launchpad(self.answers_usage) or
284+ service_uses_launchpad(self.codehosting_usage) or
285+ service_uses_launchpad(self.bug_tracking_usage))
286+
287+
288 def _getMilestoneCondition(self):
289 """See `HasMilestonesMixin`."""
290 return (Milestone.productseries == self)
291
292=== modified file 'lib/lp/registry/tests/test_service_usage.py'
293--- lib/lp/registry/tests/test_service_usage.py 2010-08-22 17:21:46 +0000
294+++ lib/lp/registry/tests/test_service_usage.py 2010-09-22 00:36:48 +0000
295@@ -143,6 +143,31 @@
296 self.target.official_blueprints)
297
298
299+class SeriesUsageEnumsMixin(object):
300+ """Mixin to test the usage attributes on product and distro series."""
301+ def setUp(self):
302+ self.series = None
303+ self.series_pillar = None
304+
305+ def _addCurrentPOTemplate(self):
306+ raise NotImplementedError("Child class must provide _addPOTTemplate.")
307+
308+ def test_translations_usage_pillar(self):
309+ # The translations_usage enum should determine usage based on
310+ # pillar usage enum and existence of current templates.
311+ self.assertEqual(
312+ ServiceUsage.UNKNOWN,
313+ self.series_pillar.translations_usage)
314+ self.assertEqual(
315+ ServiceUsage.UNKNOWN,
316+ self.series.translations_usage)
317+
318+ self._addCurrentPOTemplate()
319+ self.assertEqual(
320+ ServiceUsage.LAUNCHPAD,
321+ self.series.translations_usage)
322+
323+
324 class TestDistributionUsageEnums(TestCaseWithFactory, UsageEnumsMixin):
325 """Tests the usage enums for the distribution."""
326
327@@ -170,5 +195,56 @@
328 self.target = self.factory.makeProduct()
329
330
331+class TestProductSeriesUsageEnums(
332+ TestCaseWithFactory,
333+ SeriesUsageEnumsMixin):
334+
335+ layer = DatabaseFunctionalLayer
336+
337+ def setUp(self):
338+ super(TestProductSeriesUsageEnums, self).setUp()
339+ self.series_pillar = self.factory.makeProduct()
340+ self.series = self.factory.makeProductSeries(
341+ product=self.series_pillar)
342+ login_person(self.series_pillar.owner)
343+
344+ def _addCurrentPOTemplate(self):
345+ self.factory.makePOTemplate(productseries=self.series)
346+ # XXX j.c.sackett 2010-09-21 bug=605924: Right now for it to
347+ # be current, the series pillar must be marked as using
348+ # using translations.
349+ self.series_pillar.translations_usage = ServiceUsage.LAUNCHPAD
350+
351+
352+class TestDistroSeriesUsageEnums(
353+ TestCaseWithFactory,
354+ SeriesUsageEnumsMixin):
355+
356+ layer = DatabaseFunctionalLayer
357+
358+ def setUp(self):
359+ super(TestDistroSeriesUsageEnums, self).setUp()
360+ self.series_pillar = self.factory.makeDistribution()
361+ self.series = self.factory.makeDistroSeries(
362+ distribution=self.series_pillar)
363+ login_person(self.series_pillar.owner)
364+
365+ def _addCurrentPOTemplate(self):
366+ # Adding POTemplates are much more complicated for distribution
367+ # than product; specifically, a sourcepackage needs to be setup.
368+ sp_name = self.factory.makeSourcePackageName()
369+ self.factory.makeSourcePackage(
370+ sourcepackagename=sp_name,
371+ distroseries=self.series)
372+ self.factory.makePOTemplate(
373+ distroseries=self.series,
374+ sourcepackagename=sp_name)
375+
376+ # XXX j.c.sackett 2010-09-21 bug=605924: Right now for it to
377+ # be current, the series pillar must be marked as using
378+ # using translations.
379+ self.series_pillar.translations_usage = ServiceUsage.LAUNCHPAD
380+
381+
382 def test_suite():
383 return unittest.TestLoader().loadTestsFromName(__name__)