Merge lp:~jcsackett/launchpad/add-enums-to-models into lp:launchpad

Proposed by j.c.sackett
Status: Merged
Approved by: Brad Crittenden
Approved revision: no longer in the source branch.
Merged at revision: 11410
Proposed branch: lp:~jcsackett/launchpad/add-enums-to-models
Merge into: lp:launchpad
Prerequisite: lp:~bac/launchpad/progress-enums
Diff against target: 643 lines (+351/-54)
7 files modified
lib/lp/registry/browser/product.py (+3/-17)
lib/lp/registry/configure.zcml (+4/-1)
lib/lp/registry/doc/distribution.txt (+16/-3)
lib/lp/registry/doc/product.txt (+0/-4)
lib/lp/registry/model/distribution.py (+87/-21)
lib/lp/registry/model/product.py (+67/-8)
lib/lp/registry/tests/test_service_usage.py (+174/-0)
To merge this branch: bzr merge lp:~jcsackett/launchpad/add-enums-to-models
Reviewer Review Type Date Requested Status
Brad Crittenden (community) code Approve
Review via email: mp+33255@code.launchpad.net

Commit message

Wraps the usage_enums (e.g. official_translations) on product and distribution in a property that makes use of and sets data on the official_ attrs (e.g. official_rosetta) to help transition over to the usage_enums.

Description of the change

= Summary =

IServiceUsage provides enums to give better data on how a product or distribution uses launchpad, in place of the old bool fields (e.g. official_rosetta). However, when we roll out these enums, they will default to UNKNOWN until we can migrate data.

This branch creates properties using the enums (moved to _[ENUM] format) to use the old bool data until data is set for the enum, so we can start using the enums in code.

== Proposed fix ==

Create properties using getters and setters to intelligent find the data from the new and old attributes, and set them properly moving forward.

== Pre-implementation notes ==

Talked with Curtis.

== Implementation details ==

As above.

This branch incorporates Brad's work on the progress bar, as they need to land together. ~bac/launchpad/progress-enums
== Tests ==

bin/test -vvc -t UsageEnums

== Demo and Q/A ==

Go to a project on launchpad.dev and ensure the configuration and status
links are correct.

= Launchpad lint =

= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/registry/configure.zcml
  lib/lp/registry/browser/product.py
  lib/lp/registry/browser/tests/pillar-views.txt
  lib/lp/registry/interfaces/product.py
  lib/lp/registry/model/distribution.py
  lib/lp/registry/model/product.py
  lib/lp/registry/tests/test_distribution.py
  lib/lp/registry/tests/test_product.py

./lib/lp/registry/interfaces/product.py
     901: E301 expected 1 blank line, found 2
./lib/lp/registry/model/distribution.py
    1181: E231 missing whitespace after ','
    1185: E231 missing whitespace after ','
    1197: E231 missing whitespace after ','

E301 from comment issue
E231 from creation of tuple (something = ('some tuple',))

To post a comment you must log in.
Revision history for this message
Brad Crittenden (bac) wrote :

Hi Jon,

In def test_codehosting_usage(self): why do you make a new distribution instead of using self.distribution?

Same question for def test_bug_tracking_usage(self):

We discussed on IRC refactoring the tests since they are mostly the same.

Otherwise this branch rocks. Look forward to seeing it landed.

review: Approve (code)
Revision history for this message
j.c.sackett (jcsackett) wrote :
Download full text (18.8 KiB)

> In def test_codehosting_usage(self): why do you make a new distribution instead of using self.distribution?
> Same question for def test_bug_tracking_usage(self):

A bad job of refactoring early on? I must have just missed it when I moved the call to makeDistribution up into setUp.

>
> We discussed on IRC refactoring the tests since they are mostly the same.

There is now one BaseTestCase that does most of the work, with subclass that define the target to product or distribution.

Thanks for the pointers, Brad.

=== modified file 'lib/lp/registry/tests/test_distribution.py'
--- lib/lp/registry/tests/test_distribution.py 2010-08-20 18:37:10 +0000
+++ lib/lp/registry/tests/test_distribution.py 2010-08-20 21:48:02 +0000
@@ -10,155 +10,17 @@
 from lazr.lifecycle.snapshot import Snapshot
 from lp.registry.tests.test_distroseries import (
     TestDistroSeriesCurrentSourceReleases)
-from lp.app.enums import ServiceUsage
 from lp.registry.interfaces.distroseries import NoSuchDistroSeries
 from lp.registry.interfaces.series import SeriesStatus
 from lp.registry.interfaces.distribution import IDistribution

 from lp.soyuz.interfaces.distributionsourcepackagerelease import (
     IDistributionSourcePackageRelease)
-from lp.testing import (
- login_person,
- TestCaseWithFactory,
- )
+from lp.testing import TestCaseWithFactory
 from canonical.testing.layers import (
     DatabaseFunctionalLayer, LaunchpadFunctionalLayer)

-class TestDistributionUsageEnums(TestCaseWithFactory):
- """Tests the usage enums for the distribution."""
-
- layer = DatabaseFunctionalLayer
-
- def setUp(self):
- super(TestDistributionUsageEnums, self).setUp()
- self.distribution = self.factory.makeDistribution()
-
- def test_answers_usage_no_data(self):
- # By default, we don't know anything about a distribution
- self.assertEqual(
- ServiceUsage.UNKNOWN,
- self.distribution.answers_usage)
-
- def test_answers_usage_using_bool(self):
- # If the old bool says they use Launchpad, return LAUNCHPAD
- # if the ServiceUsage is unknown.
- login_person(self.distribution.owner)
- self.distribution.official_answers = True
- self.assertEqual(
- ServiceUsage.LAUNCHPAD,
- self.distribution.answers_usage)
-
- def test_answers_usage_with_enum_data(self):
- # If the enum has something other than UNKNOWN as its status,
- # use that.
- login_person(self.distribution.owner)
- self.distribution.answers_usage = ServiceUsage.EXTERNAL
- self.assertEqual(
- ServiceUsage.EXTERNAL,
- self.distribution.answers_usage)
-
- def test_answers_setter(self):
- login_person(self.distribution.owner)
- self.distribution.official_answers = True
- self.distribution.answers_usage = ServiceUsage.EXTERNAL
- self.assertEqual(
- False,
- self.distribution.official_answers)
- self.distribution.answers_usage = ServiceUsage.LAUNCHPAD
- self.assertEqual(
- True,
- self.distribution.official_answers)
-
- def test_codehosting_usage(self):
- ...

Revision history for this message
Robert Collins (lifeless) wrote :

On Sat, Aug 21, 2010 at 9:58 AM, j.c.sackett
<email address hidden> wrote:

> +
> +# Manually create the TestLoader list, because the UsageEnumsBaseTestCase
> +# shouldn't run.
> +test_list = [
> +    __name__ + ".TestDistributionUsageEnums",
> +    __name__ + ".TestProductUsageEnums",
> +    ]
> +
> +
> +def test_suite():
> +    return unittest.TestLoader().loadTestsFromNames(test_list)

There are a few slightly better ways of doing this.

Class UsageEnumsMixinIn
....stuff

class TestDistributionUsageEnums(TestCaseWithFactory, UsageEnumsMixin):
 ...

class TestProductUsageEnums(TestCaseWithFactory, UsageEnumsMixin):
...

is one.

Another is to use testscenarios, and your parameterisation would be
more explicit, but we don't use that in lp today AFAIK, so I won't
suggest adding it in (but you could get much the same helpers from
bzrlib.testing).

These are better because they don't have a class that looks-to-loaders
like a valid test case, but isn't.

-Rob

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/registry/browser/product.py'
--- lib/lp/registry/browser/product.py 2010-08-22 20:27:48 +0000
+++ lib/lp/registry/browser/product.py 2010-08-22 20:27:49 +0000
@@ -50,8 +50,6 @@
50 )50 )
51from operator import attrgetter51from operator import attrgetter
5252
53from lazr.delegates import delegates
54from lazr.restful.interface import copy_field
55import pytz53import pytz
56from z3c.ptcompat import ViewPageTemplateFile54from z3c.ptcompat import ViewPageTemplateFile
57from zope.app.form.browser import (55from zope.app.form.browser import (
@@ -79,7 +77,8 @@
7977
80from canonical.cachedproperty import cachedproperty78from canonical.cachedproperty import cachedproperty
81from canonical.config import config79from canonical.config import config
82<<<<<<< TREE80from lazr.delegates import delegates
81from lazr.restful.interface import copy_field
83from canonical.launchpad import (82from canonical.launchpad import (
84 _,83 _,
85 helpers,84 helpers,
@@ -89,20 +88,6 @@
89 MultiStepView,88 MultiStepView,
90 StepView,89 StepView,
91 )90 )
92=======
93from lazr.delegates import delegates
94from lazr.restful.interface import copy_field
95from canonical.launchpad import _
96from lp.services.fields import PillarAliases, PublicPersonChoice
97from lp.app.enums import ServiceUsage
98from lp.app.errors import NotFoundError
99from lp.app.interfaces.headings import IEditableContextTitle
100from lp.blueprints.browser.specificationtarget import (
101 HasSpecificationsMenuMixin)
102from lp.bugs.interfaces.bugtask import RESOLVED_BUGTASK_STATUSES
103from lp.code.browser.sourcepackagerecipelisting import HasRecipesMenuMixin
104from lp.services.worlddata.interfaces.country import ICountry
105>>>>>>> MERGE-SOURCE
106from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities91from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
107from canonical.launchpad.interfaces.librarian import ILibraryFileAliasSet92from canonical.launchpad.interfaces.librarian import ILibraryFileAliasSet
108from canonical.launchpad.mail import (93from canonical.launchpad.mail import (
@@ -157,6 +142,7 @@
157 QuestionTargetFacetMixin,142 QuestionTargetFacetMixin,
158 QuestionTargetTraversalMixin,143 QuestionTargetTraversalMixin,
159 )144 )
145from lp.app.enums import ServiceUsage
160from lp.app.errors import NotFoundError146from lp.app.errors import NotFoundError
161from lp.app.interfaces.headings import IEditableContextTitle147from lp.app.interfaces.headings import IEditableContextTitle
162from lp.blueprints.browser.specificationtarget import (148from lp.blueprints.browser.specificationtarget import (
163149
=== modified file 'lib/lp/registry/configure.zcml'
--- lib/lp/registry/configure.zcml 2010-08-22 20:27:48 +0000
+++ lib/lp/registry/configure.zcml 2010-08-22 20:27:49 +0000
@@ -1090,7 +1090,7 @@
1090 interface="lp.registry.interfaces.product.IProductEditRestricted"/>1090 interface="lp.registry.interfaces.product.IProductEditRestricted"/>
1091 <require1091 <require
1092 permission="launchpad.Edit"1092 permission="launchpad.Edit"
1093 set_schema="lp.app.interfaces.launchpad.IServiceUsage"/>1093 set_schema="lp.app.interfaces.launchpad.IServiceUsage"/>
1094 <require1094 <require
1095 permission="launchpad.Edit"1095 permission="launchpad.Edit"
1096 set_attributes="bug_reporting_guidelines1096 set_attributes="bug_reporting_guidelines
@@ -1405,6 +1405,9 @@
1405 permission="launchpad.Edit"1405 permission="launchpad.Edit"
1406 interface="lp.registry.interfaces.distribution.IDistributionEditRestricted"/>1406 interface="lp.registry.interfaces.distribution.IDistributionEditRestricted"/>
1407 <require1407 <require
1408 permission="launchpad.Edit"
1409 set_schema="lp.app.interfaces.launchpad.IServiceUsage"/>
1410 <require
1408 permission="launchpad.Moderate"1411 permission="launchpad.Moderate"
1409 interface="lp.registry.interfaces.distribution.IDistributionDriverRestricted"/>1412 interface="lp.registry.interfaces.distribution.IDistributionDriverRestricted"/>
1410 <require1413 <require
14111414
=== modified file 'lib/lp/registry/doc/distribution.txt'
--- lib/lp/registry/doc/distribution.txt 2010-08-06 14:49:35 +0000
+++ lib/lp/registry/doc/distribution.txt 2010-08-22 20:27:49 +0000
@@ -397,13 +397,26 @@
397 >>> print ubuntu.bug_tracking_usage.name397 >>> print ubuntu.bug_tracking_usage.name
398 LAUNCHPAD398 LAUNCHPAD
399399
400While the other attributes are not yet used.400While the other attributes track the other official_ attributes.
401401
402 >>> print ubuntu.official_rosetta
403 True
402 >>> print ubuntu.translations_usage.name404 >>> print ubuntu.translations_usage.name
403 UNKNOWN405 LAUNCHPAD
406 >>> print ubuntu.official_answers
407 True
404 >>> print ubuntu.answers_usage.name408 >>> print ubuntu.answers_usage.name
405 UNKNOWN409 LAUNCHPAD
410 >>> print ubuntu.official_blueprints
411 True
406 >>> print ubuntu.blueprints_usage.name412 >>> print ubuntu.blueprints_usage.name
413 LAUNCHPAD
414
415If the official_ attributes are False, the usage enums don't know anything.
416
417 >>> login_person(ubuntu.owner.teamowner)
418 >>> ubuntu.official_rosetta = False
419 >>> print ubuntu.translations_usage.name
407 UNKNOWN420 UNKNOWN
408421
409A distribution *cannot* specify that it uses codehosting. Currently there's422A distribution *cannot* specify that it uses codehosting. Currently there's
410423
=== modified file 'lib/lp/registry/doc/product.txt'
--- lib/lp/registry/doc/product.txt 2010-08-06 14:49:35 +0000
+++ lib/lp/registry/doc/product.txt 2010-08-22 20:27:49 +0000
@@ -501,15 +501,11 @@
501 False501 False
502 >>> print firefox.codehosting_usage.name502 >>> print firefox.codehosting_usage.name
503 UNKNOWN503 UNKNOWN
504 >>> print firefox.uses_launchpad
505 False
506 >>> firefox.development_focus.branch = factory.makeBranch(product=firefox)504 >>> firefox.development_focus.branch = factory.makeBranch(product=firefox)
507 >>> print firefox.official_codehosting505 >>> print firefox.official_codehosting
508 True506 True
509 >>> print firefox.codehosting_usage.name507 >>> print firefox.codehosting_usage.name
510 LAUNCHPAD508 LAUNCHPAD
511 >>> print firefox.uses_launchpad
512 True
513509
514We can also find all the products that have branches.510We can also find all the products that have branches.
515511
516512
=== modified file 'lib/lp/registry/model/distribution.py'
--- lib/lp/registry/model/distribution.py 2010-08-20 20:31:18 +0000
+++ lib/lp/registry/model/distribution.py 2010-08-22 20:27:49 +0000
@@ -63,6 +63,11 @@
63 ILaunchpadCelebrities,63 ILaunchpadCelebrities,
64 )64 )
65from canonical.launchpad.interfaces.lpstorm import IStore65from canonical.launchpad.interfaces.lpstorm import IStore
66from lp.registry.model.announcement import MakesAnnouncements
67from lp.soyuz.model.archive import Archive
68from lp.soyuz.model.binarypackagename import BinaryPackageName
69from lp.soyuz.model.binarypackagerelease import (
70 BinaryPackageRelease)
66from canonical.launchpad.validators.name import (71from canonical.launchpad.validators.name import (
67 sanitize_name,72 sanitize_name,
68 valid_name,73 valid_name,
@@ -273,7 +278,6 @@
273 else:278 else:
274 alsoProvides(self, IDerivativeDistribution)279 alsoProvides(self, IDerivativeDistribution)
275280
276
277 @property281 @property
278 def uploaders(self):282 def uploaders(self):
279 """See `IDistribution`."""283 """See `IDistribution`."""
@@ -310,21 +314,85 @@
310 return True in (self.official_malone, self.official_rosetta,314 return True in (self.official_malone, self.official_rosetta,
311 self.official_blueprints, self.official_answers)315 self.official_blueprints, self.official_answers)
312316
313 answers_usage = EnumCol(317 _answers_usage = EnumCol(
314 dbName="answers_usage", notNull=True,318 dbName="answers_usage", notNull=True,
315 schema=ServiceUsage,319 schema=ServiceUsage,
316 default=ServiceUsage.UNKNOWN)320 default=ServiceUsage.UNKNOWN)
317 blueprints_usage = EnumCol(321
322 def _get_answers_usage(self):
323 if self._answers_usage != ServiceUsage.UNKNOWN:
324 # If someone has set something with the enum, use it.
325 return self._answers_usage
326 elif self.official_answers:
327 return ServiceUsage.LAUNCHPAD
328 return self._answers_usage
329
330 def _set_answers_usage(self, val):
331 self._answers_usage = val
332 if val == ServiceUsage.LAUNCHPAD:
333 self.official_answers = True
334 else:
335 self.official_answers = False
336
337 answers_usage = property(
338 _get_answers_usage,
339 _set_answers_usage,
340 doc="Indicates if the product uses the answers service.")
341
342 _blueprints_usage = EnumCol(
318 dbName="blueprints_usage", notNull=True,343 dbName="blueprints_usage", notNull=True,
319 schema=ServiceUsage,344 schema=ServiceUsage,
320 default=ServiceUsage.UNKNOWN)345 default=ServiceUsage.UNKNOWN)
321 translations_usage = EnumCol(346
347 def _get_blueprints_usage(self):
348 if self._blueprints_usage != ServiceUsage.UNKNOWN:
349 # If someone has set something with the enum, use it.
350 return self._blueprints_usage
351 elif self.official_blueprints:
352 return ServiceUsage.LAUNCHPAD
353 return self._blueprints_usage
354
355 def _set_blueprints_usage(self, val):
356 self._blueprints_usage = val
357 if val == ServiceUsage.LAUNCHPAD:
358 self.official_blueprints = True
359 else:
360 self.official_blueprints = False
361
362 blueprints_usage = property(
363 _get_blueprints_usage,
364 _set_blueprints_usage,
365 doc="Indicates if the product uses the blueprints service.")
366
367 _translations_usage = EnumCol(
322 dbName="translations_usage", notNull=True,368 dbName="translations_usage", notNull=True,
323 schema=ServiceUsage,369 schema=ServiceUsage,
324 default=ServiceUsage.UNKNOWN)370 default=ServiceUsage.UNKNOWN)
371
372 def _get_translations_usage(self):
373 if self._translations_usage != ServiceUsage.UNKNOWN:
374 # If someone has set something with the enum, use it.
375 return self._translations_usage
376 elif self.official_rosetta:
377 return ServiceUsage.LAUNCHPAD
378 return self._translations_usage
379
380 def _set_translations_usage(self, val):
381 self._translations_usage = val
382 if val == ServiceUsage.LAUNCHPAD:
383 self.official_rosetta = True
384 else:
385 self.official_rosetta = False
386
387 translations_usage = property(
388 _get_translations_usage,
389 _set_translations_usage,
390 doc="Indicates if the product uses the translations service.")
391
325 @property392 @property
326 def codehosting_usage(self):393 def codehosting_usage(self):
327 return ServiceUsage.NOT_APPLICABLE394 return ServiceUsage.NOT_APPLICABLE
395
328 @property396 @property
329 def bug_tracking_usage(self):397 def bug_tracking_usage(self):
330 if not self.official_malone:398 if not self.official_malone:
@@ -551,9 +619,9 @@
551 if not self.full_functionality:619 if not self.full_functionality:
552 return None620 return None
553621
554 urls = {'http_base_url' : http_base_url,622 urls = {'http_base_url': http_base_url,
555 'ftp_base_url' : ftp_base_url,623 'ftp_base_url': ftp_base_url,
556 'rsync_base_url' : rsync_base_url}624 'rsync_base_url': rsync_base_url}
557 for name, value in urls.items():625 for name, value in urls.items():
558 if value is not None:626 if value is not None:
559 urls[name] = IDistributionMirror[name].normalize(value)627 urls[name] = IDistributionMirror[name].normalize(value)
@@ -766,7 +834,7 @@
766834
767 # filter based on completion. see the implementation of835 # filter based on completion. see the implementation of
768 # Specification.is_complete() for more details836 # Specification.is_complete() for more details
769 completeness = Specification.completeness_clause837 completeness = Specification.completeness_clause
770838
771 if SpecificationFilter.COMPLETE in filter:839 if SpecificationFilter.COMPLETE in filter:
772 query += ' AND ( %s ) ' % completeness840 query += ' AND ( %s ) ' % completeness
@@ -1091,15 +1159,15 @@
1091 find_spec = (1159 find_spec = (
1092 DistributionSourcePackageCache,1160 DistributionSourcePackageCache,
1093 SourcePackageName,1161 SourcePackageName,
1094 SQL('rank(fti, ftq(%s)) AS rank' % sqlvalues(text))1162 SQL('rank(fti, ftq(%s)) AS rank' % sqlvalues(text)),
1095 )1163 )
1096 origin = [1164 origin = [
1097 DistributionSourcePackageCache,1165 DistributionSourcePackageCache,
1098 Join(1166 Join(
1099 SourcePackageName,1167 SourcePackageName,
1100 DistributionSourcePackageCache.sourcepackagename ==1168 DistributionSourcePackageCache.sourcepackagename ==
1101 SourcePackageName.id1169 SourcePackageName.id,
1102 )1170 ),
1103 ]1171 ]
11041172
1105 publishing_condition = ''1173 publishing_condition = ''
@@ -1140,8 +1208,7 @@
1140 quote(text), quote_like(text), has_packaging_condition,1208 quote(text), quote_like(text), has_packaging_condition,
1141 publishing_condition)1209 publishing_condition)
1142 dsp_caches_with_ranks = store.using(*origin).find(1210 dsp_caches_with_ranks = store.using(*origin).find(
1143 find_spec, condition1211 find_spec, condition).order_by('rank DESC')
1144 ).order_by('rank DESC')
11451212
1146 return dsp_caches_with_ranks1213 return dsp_caches_with_ranks
11471214
@@ -1160,8 +1227,7 @@
1160 cache, source_package_name, rank = result1227 cache, source_package_name, rank = result
1161 return DistributionSourcePackage(1228 return DistributionSourcePackage(
1162 self,1229 self,
1163 source_package_name1230 source_package_name)
1164 )
11651231
1166 # Return the decorated result set so the consumer of these1232 # Return the decorated result set so the consumer of these
1167 # results will only see DSPs1233 # results will only see DSPs
@@ -1225,7 +1291,7 @@
1225 extra_clauses = (1291 extra_clauses = (
1226 BinaryPackageRelease.binarypackagenameID ==1292 BinaryPackageRelease.binarypackagenameID ==
1227 DistroSeriesPackageCache.binarypackagenameID,1293 DistroSeriesPackageCache.binarypackagenameID,
1228 Match(search_vector_column, query_function)1294 Match(search_vector_column, query_function),
1229 )1295 )
1230 where_spec = (self._binaryPackageSearchClause + extra_clauses)1296 where_spec = (self._binaryPackageSearchClause + extra_clauses)
12311297
@@ -1481,11 +1547,11 @@
1481 # XXX Julian 2007-08-161547 # XXX Julian 2007-08-16
1482 # These component names should be Soyuz-wide constants.1548 # These component names should be Soyuz-wide constants.
1483 componentMapToArchivePurpose = {1549 componentMapToArchivePurpose = {
1484 'main' : ArchivePurpose.PRIMARY,1550 'main': ArchivePurpose.PRIMARY,
1485 'restricted' : ArchivePurpose.PRIMARY,1551 'restricted': ArchivePurpose.PRIMARY,
1486 'universe' : ArchivePurpose.PRIMARY,1552 'universe': ArchivePurpose.PRIMARY,
1487 'multiverse' : ArchivePurpose.PRIMARY,1553 'multiverse': ArchivePurpose.PRIMARY,
1488 'partner' : ArchivePurpose.PARTNER,1554 'partner': ArchivePurpose.PARTNER,
1489 'contrib': ArchivePurpose.PRIMARY,1555 'contrib': ArchivePurpose.PRIMARY,
1490 'non-free': ArchivePurpose.PRIMARY,1556 'non-free': ArchivePurpose.PRIMARY,
1491 }1557 }
14921558
=== modified file 'lib/lp/registry/model/product.py'
--- lib/lp/registry/model/product.py 2010-08-22 20:27:48 +0000
+++ lib/lp/registry/model/product.py 2010-08-22 20:27:49 +0000
@@ -39,10 +39,6 @@
39from zope.security.proxy import removeSecurityProxy39from zope.security.proxy import removeSecurityProxy
4040
41from canonical.cachedproperty import cachedproperty41from canonical.cachedproperty import cachedproperty
42<<<<<<< TREE
43=======
44from lazr.delegates import delegates
45>>>>>>> MERGE-SOURCE
46from canonical.database.constants import UTC_NOW42from canonical.database.constants import UTC_NOW
47from canonical.database.datetimecol import UtcDateTimeCol43from canonical.database.datetimecol import UtcDateTimeCol
48from canonical.database.enumcol import EnumCol44from canonical.database.enumcol import EnumCol
@@ -67,7 +63,6 @@
67 MAIN_STORE,63 MAIN_STORE,
68 )64 )
69from canonical.launchpad.webapp.sorting import sorted_version_numbers65from canonical.launchpad.webapp.sorting import sorted_version_numbers
70from canonical.lazr.utils import safe_hasattr
71from lp.answers.interfaces.faqtarget import IFAQTarget66from lp.answers.interfaces.faqtarget import IFAQTarget
72from lp.answers.interfaces.questioncollection import (67from lp.answers.interfaces.questioncollection import (
73 QUESTION_STATUS_DEFAULT_SEARCH,68 QUESTION_STATUS_DEFAULT_SEARCH,
@@ -323,24 +318,26 @@
323 # XXX Need to remove official_codehosting column from Product318 # XXX Need to remove official_codehosting column from Product
324 # table.319 # table.
325 return self.development_focus.branch is not None320 return self.development_focus.branch is not None
321
326 @property322 @property
327 def official_anything(self):323 def official_anything(self):
328 return True in (self.official_malone, self.official_rosetta,324 return True in (self.official_malone, self.official_rosetta,
329 self.official_blueprints, self.official_answers,325 self.official_blueprints, self.official_answers,
330 self.official_codehosting)326 self.official_codehosting)
331327
332 answers_usage = EnumCol(328 _answers_usage = EnumCol(
333 dbName="answers_usage", notNull=True,329 dbName="answers_usage", notNull=True,
334 schema=ServiceUsage,330 schema=ServiceUsage,
335 default=ServiceUsage.UNKNOWN)331 default=ServiceUsage.UNKNOWN)
336 blueprints_usage = EnumCol(332 _blueprints_usage = EnumCol(
337 dbName="blueprints_usage", notNull=True,333 dbName="blueprints_usage", notNull=True,
338 schema=ServiceUsage,334 schema=ServiceUsage,
339 default=ServiceUsage.UNKNOWN)335 default=ServiceUsage.UNKNOWN)
340 translations_usage = EnumCol(336 _translations_usage = EnumCol(
341 dbName="translations_usage", notNull=True,337 dbName="translations_usage", notNull=True,
342 schema=ServiceUsage,338 schema=ServiceUsage,
343 default=ServiceUsage.UNKNOWN)339 default=ServiceUsage.UNKNOWN)
340
344 @property341 @property
345 def codehosting_usage(self):342 def codehosting_usage(self):
346 if self.development_focus.branch is None:343 if self.development_focus.branch is None:
@@ -350,6 +347,7 @@
350 elif self.development_focus.branch.branch_type == BranchType.MIRRORED:347 elif self.development_focus.branch.branch_type == BranchType.MIRRORED:
351 return ServiceUsage.EXTERNAL348 return ServiceUsage.EXTERNAL
352 return ServiceUsage.NOT_APPLICABLE349 return ServiceUsage.NOT_APPLICABLE
350
353 @property351 @property
354 def bug_tracking_usage(self):352 def bug_tracking_usage(self):
355 if self.official_malone:353 if self.official_malone:
@@ -358,6 +356,7 @@
358 return ServiceUsage.UNKNOWN356 return ServiceUsage.UNKNOWN
359 else:357 else:
360 return ServiceUsage.EXTERNAL358 return ServiceUsage.EXTERNAL
359
361 @property360 @property
362 def uses_launchpad(self):361 def uses_launchpad(self):
363 """Does this distribution actually use Launchpad?"""362 """Does this distribution actually use Launchpad?"""
@@ -573,6 +572,66 @@
573 super(Product, self).__storm_invalidated__()572 super(Product, self).__storm_invalidated__()
574 self._cached_licenses = None573 self._cached_licenses = None
575574
575 def _get_answers_usage(self):
576 if self._answers_usage != ServiceUsage.UNKNOWN:
577 # If someone has set something with the enum, use it.
578 return self._answers_usage
579 elif self.official_answers:
580 return ServiceUsage.LAUNCHPAD
581 return self._answers_usage
582
583 def _set_answers_usage(self, val):
584 self._answers_usage = val
585 if val == ServiceUsage.LAUNCHPAD:
586 self.official_answers = True
587 else:
588 self.official_answers = False
589
590 answers_usage = property(
591 _get_answers_usage,
592 _set_answers_usage,
593 doc="Indicates if the product uses the answers service.")
594
595 def _get_blueprints_usage(self):
596 if self._blueprints_usage != ServiceUsage.UNKNOWN:
597 # If someone has set something with the enum, use it.
598 return self._blueprints_usage
599 elif self.official_blueprints:
600 return ServiceUsage.LAUNCHPAD
601 return self._blueprints_usage
602
603 def _set_blueprints_usage(self, val):
604 self._blueprints_usage = val
605 if val == ServiceUsage.LAUNCHPAD:
606 self.official_blueprints = True
607 else:
608 self.official_blueprints = False
609
610 blueprints_usage = property(
611 _get_blueprints_usage,
612 _set_blueprints_usage,
613 doc="Indicates if the product uses the blueprints service.")
614
615 def _get_translations_usage(self):
616 if self._translations_usage != ServiceUsage.UNKNOWN:
617 # If someone has set something with the enum, use it.
618 return self._translations_usage
619 elif self.official_rosetta:
620 return ServiceUsage.LAUNCHPAD
621 return self._translations_usage
622
623 def _set_translations_usage(self, val):
624 self._translations_usage = val
625 if val == ServiceUsage.LAUNCHPAD:
626 self.official_rosetta = True
627 else:
628 self.official_rosetta = False
629
630 translations_usage = property(
631 _get_translations_usage,
632 _set_translations_usage,
633 doc="Indicates if the product uses the translations service.")
634
576 def _getLicenses(self):635 def _getLicenses(self):
577 """Get the licenses as a tuple."""636 """Get the licenses as a tuple."""
578 if self._cached_licenses is None:637 if self._cached_licenses is None:
579638
=== added file 'lib/lp/registry/tests/test_service_usage.py'
--- lib/lp/registry/tests/test_service_usage.py 1970-01-01 00:00:00 +0000
+++ lib/lp/registry/tests/test_service_usage.py 2010-08-22 20:27:49 +0000
@@ -0,0 +1,174 @@
1# Copyright 2010 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4__metaclass__ = type
5
6import unittest
7
8from canonical.testing import DatabaseFunctionalLayer
9
10from lp.app.enums import ServiceUsage
11from lp.testing import (
12 login_person,
13 TestCaseWithFactory,
14 )
15
16
17class UsageEnumsMixin(object):
18 """Base class for testing the UsageEnums on their pillars."""
19
20 def setUp(self):
21 self.target = None
22
23 def test_answers_usage_no_data(self):
24 # By default, we don't know anything about a target
25 self.assertEqual(
26 ServiceUsage.UNKNOWN,
27 self.target.answers_usage)
28
29 def test_answers_usage_using_bool(self):
30 # If the old bool says they use Launchpad, return LAUNCHPAD
31 # if the ServiceUsage is unknown.
32 login_person(self.target.owner)
33 self.target.official_answers = True
34 self.assertEqual(
35 ServiceUsage.LAUNCHPAD,
36 self.target.answers_usage)
37
38 def test_answers_usage_with_enum_data(self):
39 # If the enum has something other than UNKNOWN as its status,
40 # use that.
41 login_person(self.target.owner)
42 self.target.answers_usage = ServiceUsage.EXTERNAL
43 self.assertEqual(
44 ServiceUsage.EXTERNAL,
45 self.target.answers_usage)
46
47 def test_answers_setter(self):
48 login_person(self.target.owner)
49 self.target.official_answers = True
50 self.target.answers_usage = ServiceUsage.EXTERNAL
51 self.assertEqual(
52 False,
53 self.target.official_answers)
54 self.target.answers_usage = ServiceUsage.LAUNCHPAD
55 self.assertEqual(
56 True,
57 self.target.official_answers)
58
59 def test_codehosting_usage(self):
60 # Only test get for codehosting; this has no setter because the
61 # state is derived from other data.
62 self.assertEqual(
63 ServiceUsage.UNKNOWN,
64 self.target.codehosting_usage)
65
66 def test_translations_usage_no_data(self):
67 # By default, we don't know anything about a target
68 self.assertEqual(
69 ServiceUsage.UNKNOWN,
70 self.target.translations_usage)
71
72 def test_translations_usage_using_bool(self):
73 # If the old bool says they use Launchpad, return LAUNCHPAD
74 # if the ServiceUsage is unknown.
75 login_person(self.target.owner)
76 self.target.official_rosetta = True
77 self.assertEqual(
78 ServiceUsage.LAUNCHPAD,
79 self.target.translations_usage)
80
81 def test_translations_usage_with_enum_data(self):
82 # If the enum has something other than UNKNOWN as its status,
83 # use that.
84 login_person(self.target.owner)
85 self.target.translations_usage = ServiceUsage.EXTERNAL
86 self.assertEqual(
87 ServiceUsage.EXTERNAL,
88 self.target.translations_usage)
89
90 def test_translations_setter(self):
91 login_person(self.target.owner)
92 self.target.official_rosetta = True
93 self.target.translations_usage = ServiceUsage.EXTERNAL
94 self.assertEqual(
95 False,
96 self.target.official_rosetta)
97 self.target.translations_usage = ServiceUsage.LAUNCHPAD
98 self.assertEqual(
99 True,
100 self.target.official_rosetta)
101
102 def test_bug_tracking_usage(self):
103 # Only test get for bug_tracking; this has no setter because the
104 # state is derived from other data.
105 self.assertEqual(
106 ServiceUsage.UNKNOWN,
107 self.target.bug_tracking_usage)
108
109 def test_blueprints_usage_no_data(self):
110 # By default, we don't know anything about a target
111 self.assertEqual(
112 ServiceUsage.UNKNOWN,
113 self.target.blueprints_usage)
114
115 def test_blueprints_usage_using_bool(self):
116 # If the old bool says they use Launchpad, return LAUNCHPAD
117 # if the ServiceUsage is unknown.
118 login_person(self.target.owner)
119 self.target.official_blueprints = True
120 self.assertEqual(
121 ServiceUsage.LAUNCHPAD,
122 self.target.blueprints_usage)
123
124 def test_blueprints_usage_with_enum_data(self):
125 # If the enum has something other than UNKNOWN as its status,
126 # use that.
127 login_person(self.target.owner)
128 self.target.blueprints_usage = ServiceUsage.EXTERNAL
129 self.assertEqual(
130 ServiceUsage.EXTERNAL,
131 self.target.blueprints_usage)
132
133 def test_blueprints_setter(self):
134 login_person(self.target.owner)
135 self.target.official_blueprints = True
136 self.target.blueprints_usage = ServiceUsage.EXTERNAL
137 self.assertEqual(
138 False,
139 self.target.official_blueprints)
140 self.target.blueprints_usage = ServiceUsage.LAUNCHPAD
141 self.assertEqual(
142 True,
143 self.target.official_blueprints)
144
145
146class TestDistributionUsageEnums(TestCaseWithFactory, UsageEnumsMixin):
147 """Tests the usage enums for the distribution."""
148
149 layer = DatabaseFunctionalLayer
150
151 def setUp(self):
152 super(TestDistributionUsageEnums, self).setUp()
153 self.target = self.factory.makeDistribution()
154
155 def test_codehosting_usage(self):
156 # This method must be changed for Distribution, because its
157 # enum defaults to different data.
158 self.assertEqual(
159 ServiceUsage.NOT_APPLICABLE,
160 self.target.codehosting_usage)
161
162
163class TestProductUsageEnums(TestCaseWithFactory, UsageEnumsMixin):
164 """Tests the usage enums for the product."""
165
166 layer = DatabaseFunctionalLayer
167
168 def setUp(self):
169 super(TestProductUsageEnums, self).setUp()
170 self.target = self.factory.makeProduct()
171
172
173def test_suite():
174 return unittest.TestLoader().loadTestsFromName(__name__)