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
=== modified file 'lib/lp/registry/adapters.py'
--- lib/lp/registry/adapters.py 2010-09-13 12:09:30 +0000
+++ lib/lp/registry/adapters.py 2010-09-22 00:36:48 +0000
@@ -13,19 +13,11 @@
13 ]13 ]
1414
1515
16from zope.component import (16from zope.component import getUtility
17 adapter,
18 getUtility,
19 )
20from zope.component.interfaces import ComponentLookupError17from zope.component.interfaces import ComponentLookupError
21from zope.interface import (18from zope.interface import implements
22 implementer,
23 implements,
24 )
2519
26from canonical.launchpad.webapp.interfaces import ILaunchpadPrincipal20from canonical.launchpad.webapp.interfaces import ILaunchpadPrincipal
27from lp.app.interfaces.launchpad import IServiceUsage
28from lp.registry.interfaces.distroseries import IDistroSeries
29from lp.registry.interfaces.poll import (21from lp.registry.interfaces.poll import (
30 IPollSet,22 IPollSet,
31 IPollSubset,23 IPollSubset,
@@ -34,15 +26,11 @@
34 )26 )
3527
3628
37@implementer(IServiceUsage)29def distroseries_to_distribution(distroseries):
38@adapter(IDistroSeries)30 """Adapts `IDistroSeries` object to `IDistribution`.
39def distroseries_to_serviceusage(distroseries):31
40 """Adapts `IDistroSeries` object to `IServiceUsage`."""32 This is useful for adapting to `IServiceUsage`
41 return distroseries.distribution33 or `ILaunchpadUsage`."""
42
43
44def distroseries_to_launchpadusage(distroseries):
45 """Adapts `IDistroSeries` object to `ILaunchpadUsage`."""
46 return distroseries.distribution34 return distroseries.distribution
4735
4836
4937
=== modified file 'lib/lp/registry/configure.zcml'
--- lib/lp/registry/configure.zcml 2010-09-18 00:17:07 +0000
+++ lib/lp/registry/configure.zcml 2010-09-22 00:36:48 +0000
@@ -142,6 +142,9 @@
142 interface="canonical.launchpad.interfaces.ICanPublishPackages"/>142 interface="canonical.launchpad.interfaces.ICanPublishPackages"/>
143 <require143 <require
144 permission="launchpad.Edit"144 permission="launchpad.Edit"
145 set_schema="lp.app.interfaces.launchpad.IServiceUsage"/>
146 <require
147 permission="launchpad.Edit"
145 interface="lp.registry.interfaces.distroseries.IDistroSeriesEditRestricted"/>148 interface="lp.registry.interfaces.distroseries.IDistroSeriesEditRestricted"/>
146 <require149 <require
147 permission="launchpad.TranslationsAdmin"150 permission="launchpad.TranslationsAdmin"
@@ -194,11 +197,9 @@
194 <adapter197 <adapter
195 provides="lp.app.interfaces.launchpad.ILaunchpadUsage"198 provides="lp.app.interfaces.launchpad.ILaunchpadUsage"
196 for="lp.registry.interfaces.distroseries.IDistroSeries"199 for="lp.registry.interfaces.distroseries.IDistroSeries"
197 factory="lp.registry.adapters.distroseries_to_launchpadusage"200 factory="lp.registry.adapters.distroseries_to_distribution"
198 permission="zope.Public"/>201 permission="zope.Public"/>
199 <adapter202 <adapter
200 factory="lp.registry.adapters.distroseries_to_serviceusage" />
201 <adapter
202 provides="canonical.launchpad.webapp.interfaces.IBreadcrumb"203 provides="canonical.launchpad.webapp.interfaces.IBreadcrumb"
203 for="lp.registry.interfaces.distroseries.IDistroSeries"204 for="lp.registry.interfaces.distroseries.IDistroSeries"
204 factory="lp.registry.browser.distroseries.DistroSeriesBreadcrumb"205 factory="lp.registry.browser.distroseries.DistroSeriesBreadcrumb"
@@ -1374,6 +1375,9 @@
1374 interface="lp.translations.interfaces.translationimportqueue.IHasTranslationImports"/>1375 interface="lp.translations.interfaces.translationimportqueue.IHasTranslationImports"/>
1375 <require1376 <require
1376 permission="launchpad.Edit"1377 permission="launchpad.Edit"
1378 set_schema="lp.app.interfaces.launchpad.IServiceUsage"/>
1379 <require
1380 permission="launchpad.Edit"
1377 set_attributes="product name owner driver summary branch1381 set_attributes="product name owner driver summary branch
1378 translations_branch status releasefileglob1382 translations_branch status releasefileglob
1379 translations_autoimport_mode"/>1383 translations_autoimport_mode"/>
@@ -1407,11 +1411,6 @@
1407 factory="lp.registry.adapters.productseries_to_product"1411 factory="lp.registry.adapters.productseries_to_product"
1408 permission="zope.Public"/>1412 permission="zope.Public"/>
1409 <adapter1413 <adapter
1410 provides="lp.app.interfaces.launchpad.IServiceUsage"
1411 for="lp.registry.interfaces.productseries.IProductSeries"
1412 factory="lp.registry.adapters.productseries_to_product"
1413 permission="zope.Public"/>
1414 <adapter
1415 provides="lp.app.interfaces.launchpad.ILaunchpadUsage"1414 provides="lp.app.interfaces.launchpad.ILaunchpadUsage"
1416 for="lp.registry.interfaces.productseries.IProductSeries"1415 for="lp.registry.interfaces.productseries.IProductSeries"
1417 factory="lp.registry.adapters.productseries_to_product"1416 factory="lp.registry.adapters.productseries_to_product"
14181417
=== modified file 'lib/lp/registry/interfaces/distroseries.py'
--- lib/lp/registry/interfaces/distroseries.py 2010-08-30 19:06:34 +0000
+++ lib/lp/registry/interfaces/distroseries.py 2010-09-22 00:36:48 +0000
@@ -52,6 +52,7 @@
52from canonical.launchpad.validators.name import name_validator52from canonical.launchpad.validators.name import name_validator
53from canonical.launchpad.validators.version import sane_version53from canonical.launchpad.validators.version import sane_version
54from lp.app.errors import NameLookupFailed54from lp.app.errors import NameLookupFailed
55from lp.app.interfaces.launchpad import IServiceUsage
55from lp.blueprints.interfaces.specificationtarget import ISpecificationGoal56from lp.blueprints.interfaces.specificationtarget import ISpecificationGoal
56from lp.bugs.interfaces.bugtarget import (57from lp.bugs.interfaces.bugtarget import (
57 IBugTarget,58 IBugTarget,
@@ -173,7 +174,7 @@
173class IDistroSeriesPublic(174class IDistroSeriesPublic(
174 ISeriesMixin, IHasAppointedDriver, IHasOwner, IBugTarget,175 ISeriesMixin, IHasAppointedDriver, IHasOwner, IBugTarget,
175 ISpecificationGoal, IHasMilestones, IHasOfficialBugTags,176 ISpecificationGoal, IHasMilestones, IHasOfficialBugTags,
176 IHasBuildRecords, IHasTranslationTemplates):177 IHasBuildRecords, IHasTranslationTemplates, IServiceUsage):
177 """Public IDistroSeries properties."""178 """Public IDistroSeries properties."""
178179
179 id = Attribute("The distroseries's unique number.")180 id = Attribute("The distroseries's unique number.")
180181
=== modified file 'lib/lp/registry/interfaces/productseries.py'
--- lib/lp/registry/interfaces/productseries.py 2010-08-31 00:02:42 +0000
+++ lib/lp/registry/interfaces/productseries.py 2010-09-22 00:36:48 +0000
@@ -48,6 +48,7 @@
48from canonical.launchpad.validators.name import name_validator48from canonical.launchpad.validators.name import name_validator
49from canonical.launchpad.webapp.url import urlparse49from canonical.launchpad.webapp.url import urlparse
50from lp.app.errors import NameLookupFailed50from lp.app.errors import NameLookupFailed
51from lp.app.interfaces.launchpad import IServiceUsage
51from lp.blueprints.interfaces.specificationtarget import ISpecificationGoal52from lp.blueprints.interfaces.specificationtarget import ISpecificationGoal
52from lp.bugs.interfaces.bugtarget import (53from lp.bugs.interfaces.bugtarget import (
53 IBugTarget,54 IBugTarget,
@@ -120,7 +121,7 @@
120class IProductSeriesPublic(121class IProductSeriesPublic(
121 ISeriesMixin, IHasAppointedDriver, IHasOwner, IBugTarget,122 ISeriesMixin, IHasAppointedDriver, IHasOwner, IBugTarget,
122 ISpecificationGoal, IHasMilestones, IHasOfficialBugTags,123 ISpecificationGoal, IHasMilestones, IHasOfficialBugTags,
123 IHasTranslationTemplates):124 IHasTranslationTemplates, IServiceUsage):
124 """Public IProductSeries properties."""125 """Public IProductSeries properties."""
125 # XXX Mark Shuttleworth 2004-10-14: Would like to get rid of id in126 # XXX Mark Shuttleworth 2004-10-14: Would like to get rid of id in
126 # interfaces, as soon as SQLobject allows using the object directly127 # interfaces, as soon as SQLobject allows using the object directly
127128
=== modified file 'lib/lp/registry/model/distroseries.py'
--- lib/lp/registry/model/distroseries.py 2010-09-03 15:02:39 +0000
+++ lib/lp/registry/model/distroseries.py 2010-09-22 00:36:48 +0000
@@ -63,6 +63,10 @@
63 SLAVE_FLAVOR,63 SLAVE_FLAVOR,
64 )64 )
65from lp.app.errors import NotFoundError65from lp.app.errors import NotFoundError
66from lp.app.enums import (
67 ServiceUsage,
68 service_uses_launchpad)
69from lp.app.interfaces.launchpad import IServiceUsage
66from lp.blueprints.interfaces.specification import (70from lp.blueprints.interfaces.specification import (
67 SpecificationFilter,71 SpecificationFilter,
68 SpecificationGoalStatus,72 SpecificationGoalStatus,
@@ -186,7 +190,7 @@
186 """A particular series of a distribution."""190 """A particular series of a distribution."""
187 implements(191 implements(
188 ICanPublishPackages, IDistroSeries, IHasBugHeat, IHasBuildRecords,192 ICanPublishPackages, IDistroSeries, IHasBugHeat, IHasBuildRecords,
189 IHasQueueItems)193 IHasQueueItems, IServiceUsage)
190194
191 _table = 'DistroSeries'195 _table = 'DistroSeries'
192 _defaultOrder = ['distribution', 'version']196 _defaultOrder = ['distribution', 'version']
@@ -263,6 +267,51 @@
263 """ % self.id,267 """ % self.id,
264 clauseTables=["ComponentSelection"])268 clauseTables=["ComponentSelection"])
265269
270 @property
271 def answers_usage(self):
272 """See `IServiceUsage.`"""
273 return self.distribution.answers_usage
274
275 @property
276 def blueprints_usage(self):
277 """See `IServiceUsage.`"""
278 return self.distribution.blueprints_usage
279
280 @property
281 def translations_usage(self):
282 """See `IServiceUsage.`"""
283 # If translations_usage is set for the Distribution, respect it.
284 usage = self.distribution.translations_usage
285 if usage != ServiceUsage.UNKNOWN:
286 return usage
287
288 # If not, usage is based on the presence of current translation
289 # templates for the series.
290 if self.getCurrentTranslationTemplates().count() > 0:
291 return ServiceUsage.LAUNCHPAD
292 else:
293 return ServiceUsage.UNKNOWN
294
295 @property
296 def codehosting_usage(self):
297 """See `IServiceUsage.`"""
298 return self.distribution.codehosting_usage
299
300 @property
301 def bug_tracking_usage(self):
302 """See `IServiceUsage.`"""
303 return self.distribution.bug_tracking_usage
304
305 @property
306 def uses_launchpad(self):
307 """ See `IServiceUsage.`"""
308 return (
309 service_uses_launchpad(self.blueprints_usage) or
310 service_uses_launchpad(self.translations_usage) or
311 service_uses_launchpad(self.answers_usage) or
312 service_uses_launchpad(self.codehosting_usage) or
313 service_uses_launchpad(self.bug_tracking_usage))
314
266 # DistroArchSeries lookup properties/methods.315 # DistroArchSeries lookup properties/methods.
267 architectures = SQLMultipleJoin(316 architectures = SQLMultipleJoin(
268 'DistroArchSeries', joinColumn='distroseries',317 'DistroArchSeries', joinColumn='distroseries',
269318
=== modified file 'lib/lp/registry/model/productseries.py'
--- lib/lp/registry/model/productseries.py 2010-08-31 00:02:42 +0000
+++ lib/lp/registry/model/productseries.py 2010-09-22 00:36:48 +0000
@@ -43,6 +43,10 @@
43from canonical.launchpad.webapp.publisher import canonical_url43from canonical.launchpad.webapp.publisher import canonical_url
44from canonical.launchpad.webapp.sorting import sorted_dotted_numbers44from canonical.launchpad.webapp.sorting import sorted_dotted_numbers
45from lp.app.errors import NotFoundError45from lp.app.errors import NotFoundError
46from lp.app.enums import (
47 ServiceUsage,
48 service_uses_launchpad)
49from lp.app.interfaces.launchpad import IServiceUsage
46from lp.blueprints.interfaces.specification import (50from lp.blueprints.interfaces.specification import (
47 SpecificationDefinitionStatus,51 SpecificationDefinitionStatus,
48 SpecificationFilter,52 SpecificationFilter,
@@ -115,7 +119,7 @@
115 HasTranslationImportsMixin, HasTranslationTemplatesMixin,119 HasTranslationImportsMixin, HasTranslationTemplatesMixin,
116 StructuralSubscriptionTargetMixin, SeriesMixin):120 StructuralSubscriptionTargetMixin, SeriesMixin):
117 """A series of product releases."""121 """A series of product releases."""
118 implements(IHasBugHeat, IProductSeries)122 implements(IHasBugHeat, IProductSeries, IServiceUsage)
119123
120 _table = 'ProductSeries'124 _table = 'ProductSeries'
121125
@@ -151,6 +155,52 @@
151 packagings = SQLMultipleJoin('Packaging', joinColumn='productseries',155 packagings = SQLMultipleJoin('Packaging', joinColumn='productseries',
152 orderBy=['-id'])156 orderBy=['-id'])
153157
158 @property
159 def answers_usage(self):
160 """See `IServiceUsage.`"""
161 return self.product.answers_usage
162
163 @property
164 def blueprints_usage(self):
165 """See `IServiceUsage.`"""
166 return self.product.blueprints_usage
167
168 @property
169 def translations_usage(self):
170 """See `IServiceUsage.`"""
171 # If translations_usage is set for the Product, respect it.
172 usage = self.product.translations_usage
173 if usage != ServiceUsage.UNKNOWN:
174 return usage
175
176 # If not, usage is based on the presence of current translation
177 # templates for the series.
178 if self.potemplate_count > 0:
179 return ServiceUsage.LAUNCHPAD
180 else:
181 return ServiceUsage.UNKNOWN
182
183 @property
184 def codehosting_usage(self):
185 """See `IServiceUsage.`"""
186 return self.product.codehosting_usage
187
188 @property
189 def bug_tracking_usage(self):
190 """See `IServiceUsage.`"""
191 return self.product.bug_tracking_usage
192
193 @property
194 def uses_launchpad(self):
195 """ See `IServiceUsage.`"""
196 return (
197 service_uses_launchpad(self.blueprints_usage) or
198 service_uses_launchpad(self.translations_usage) or
199 service_uses_launchpad(self.answers_usage) or
200 service_uses_launchpad(self.codehosting_usage) or
201 service_uses_launchpad(self.bug_tracking_usage))
202
203
154 def _getMilestoneCondition(self):204 def _getMilestoneCondition(self):
155 """See `HasMilestonesMixin`."""205 """See `HasMilestonesMixin`."""
156 return (Milestone.productseries == self)206 return (Milestone.productseries == self)
157207
=== modified file 'lib/lp/registry/tests/test_service_usage.py'
--- lib/lp/registry/tests/test_service_usage.py 2010-08-22 17:21:46 +0000
+++ lib/lp/registry/tests/test_service_usage.py 2010-09-22 00:36:48 +0000
@@ -143,6 +143,31 @@
143 self.target.official_blueprints)143 self.target.official_blueprints)
144144
145145
146class SeriesUsageEnumsMixin(object):
147 """Mixin to test the usage attributes on product and distro series."""
148 def setUp(self):
149 self.series = None
150 self.series_pillar = None
151
152 def _addCurrentPOTemplate(self):
153 raise NotImplementedError("Child class must provide _addPOTTemplate.")
154
155 def test_translations_usage_pillar(self):
156 # The translations_usage enum should determine usage based on
157 # pillar usage enum and existence of current templates.
158 self.assertEqual(
159 ServiceUsage.UNKNOWN,
160 self.series_pillar.translations_usage)
161 self.assertEqual(
162 ServiceUsage.UNKNOWN,
163 self.series.translations_usage)
164
165 self._addCurrentPOTemplate()
166 self.assertEqual(
167 ServiceUsage.LAUNCHPAD,
168 self.series.translations_usage)
169
170
146class TestDistributionUsageEnums(TestCaseWithFactory, UsageEnumsMixin):171class TestDistributionUsageEnums(TestCaseWithFactory, UsageEnumsMixin):
147 """Tests the usage enums for the distribution."""172 """Tests the usage enums for the distribution."""
148173
@@ -170,5 +195,56 @@
170 self.target = self.factory.makeProduct()195 self.target = self.factory.makeProduct()
171196
172197
198class TestProductSeriesUsageEnums(
199 TestCaseWithFactory,
200 SeriesUsageEnumsMixin):
201
202 layer = DatabaseFunctionalLayer
203
204 def setUp(self):
205 super(TestProductSeriesUsageEnums, self).setUp()
206 self.series_pillar = self.factory.makeProduct()
207 self.series = self.factory.makeProductSeries(
208 product=self.series_pillar)
209 login_person(self.series_pillar.owner)
210
211 def _addCurrentPOTemplate(self):
212 self.factory.makePOTemplate(productseries=self.series)
213 # XXX j.c.sackett 2010-09-21 bug=605924: Right now for it to
214 # be current, the series pillar must be marked as using
215 # using translations.
216 self.series_pillar.translations_usage = ServiceUsage.LAUNCHPAD
217
218
219class TestDistroSeriesUsageEnums(
220 TestCaseWithFactory,
221 SeriesUsageEnumsMixin):
222
223 layer = DatabaseFunctionalLayer
224
225 def setUp(self):
226 super(TestDistroSeriesUsageEnums, self).setUp()
227 self.series_pillar = self.factory.makeDistribution()
228 self.series = self.factory.makeDistroSeries(
229 distribution=self.series_pillar)
230 login_person(self.series_pillar.owner)
231
232 def _addCurrentPOTemplate(self):
233 # Adding POTemplates are much more complicated for distribution
234 # than product; specifically, a sourcepackage needs to be setup.
235 sp_name = self.factory.makeSourcePackageName()
236 self.factory.makeSourcePackage(
237 sourcepackagename=sp_name,
238 distroseries=self.series)
239 self.factory.makePOTemplate(
240 distroseries=self.series,
241 sourcepackagename=sp_name)
242
243 # XXX j.c.sackett 2010-09-21 bug=605924: Right now for it to
244 # be current, the series pillar must be marked as using
245 # using translations.
246 self.series_pillar.translations_usage = ServiceUsage.LAUNCHPAD
247
248
173def test_suite():249def test_suite():
174 return unittest.TestLoader().loadTestsFromName(__name__)250 return unittest.TestLoader().loadTestsFromName(__name__)