Merge lp:~abentley/launchpad/model-product-info-type into lp:launchpad

Proposed by Aaron Bentley on 2012-10-02
Status: Merged
Approved by: Aaron Bentley on 2012-10-02
Approved revision: no longer in the source branch.
Merged at revision: 16083
Proposed branch: lp:~abentley/launchpad/model-product-info-type
Merge into: lp:launchpad
Diff against target: 336 lines (+68/-37)
9 files modified
lib/lp/app/interfaces/informationtype.py (+5/-2)
lib/lp/app/model/launchpad.py (+10/-0)
lib/lp/blueprints/model/specification.py (+2/-6)
lib/lp/bugs/model/bug.py (+2/-5)
lib/lp/registry/configure.zcml (+5/-0)
lib/lp/registry/interfaces/product.py (+2/-9)
lib/lp/registry/model/product.py (+12/-13)
lib/lp/registry/tests/test_product.py (+26/-0)
lib/lp/testing/factory.py (+4/-2)
To merge this branch: bzr merge lp:~abentley/launchpad/model-product-info-type
Reviewer Review Type Date Requested Status
Deryck Hodge (community) 2012-10-02 Approve on 2012-10-02
Review via email: mp+127558@code.launchpad.net

Commit Message

Implement Product.information_type in Storm

Description of the Change

= Summary =
Provide Product.information_type via Storm

== Proposed fix ==
Add Product.information_type. Implement IInformationType, remove IProduct.information_type

== Pre-implementation notes ==
None

== LOC Rationale ==
Part of Private Projects

== Implementation details ==
The database column has already landed; this adds support to Storm.

The information_type move is to reduce duplication and to simplify security policy implementation.

information_type is nullable because the database permits this and existing data contains NULLs. Once a garbo job has removed all NULLs, it can be updated to prohibit nulls.

== Tests ==
bin/test -t test_product_information_type test_product

== Demo and Q/A ==
View a product page. If nothing's broken, it's good.

= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/app/model/launchpad.py
  lib/lp/blueprints/model/specification.py
  lib/lp/bugs/model/bug.py
  lib/lp/registry/configure.zcml
  lib/lp/registry/interfaces/product.py
  lib/lp/registry/model/product.py
  lib/lp/registry/tests/test_product.py
  lib/lp/testing/factory.py

To post a comment you must log in.
Deryck Hodge (deryck) wrote :

Looks good.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/app/interfaces/informationtype.py'
2--- lib/lp/app/interfaces/informationtype.py 2012-09-18 19:41:02 +0000
3+++ lib/lp/app/interfaces/informationtype.py 2012-10-03 01:18:22 +0000
4@@ -6,6 +6,7 @@
5 'IInformationType',
6 ]
7
8+from lazr.restful.declarations import exported
9 from zope.schema import Choice
10
11 from lp import _
12@@ -15,7 +16,9 @@
13
14 class IInformationType(IPrivacy):
15
16- information_type = Choice(
17+ information_type = exported(Choice(
18 title=_('Information Type'),
19 vocabulary=InformationType,
20- )
21+ required=True,
22+ description=_('The type of data contained in this item.')
23+ ))
24
25=== modified file 'lib/lp/app/model/launchpad.py'
26--- lib/lp/app/model/launchpad.py 2012-02-28 04:24:19 +0000
27+++ lib/lp/app/model/launchpad.py 2012-10-03 01:18:22 +0000
28@@ -7,6 +7,7 @@
29
30 __all__ = [
31 'ExceptionPrivacy',
32+ 'InformationTypeMixin',
33 'Privacy',
34 ]
35
36@@ -17,6 +18,7 @@
37 Unauthorized,
38 )
39
40+from lp.app.enums import PRIVATE_INFORMATION_TYPES
41 from lp.app.interfaces.launchpad import IPrivacy
42
43
44@@ -38,3 +40,11 @@
45 else:
46 private = False
47 super(ExceptionPrivacy, self).__init__(error, private)
48+
49+
50+class InformationTypeMixin:
51+ """"Common functionality for classes implementing IInformationType."""
52+
53+ @property
54+ def private(self):
55+ return self.information_type in PRIVATE_INFORMATION_TYPES
56
57=== modified file 'lib/lp/blueprints/model/specification.py'
58--- lib/lp/blueprints/model/specification.py 2012-09-27 14:30:40 +0000
59+++ lib/lp/blueprints/model/specification.py 2012-10-03 01:18:22 +0000
60@@ -47,11 +47,11 @@
61
62 from lp.app.enums import (
63 InformationType,
64- PRIVATE_INFORMATION_TYPES,
65 PUBLIC_INFORMATION_TYPES,
66 )
67 from lp.app.errors import UserCannotUnsubscribePerson
68 from lp.app.interfaces.informationtype import IInformationType
69+from lp.app.model.launchpad import InformationTypeMixin
70 from lp.blueprints.adapters import SpecificationDelta
71 from lp.blueprints.enums import (
72 NewSpecificationDefinitionStatus,
73@@ -157,7 +157,7 @@
74 }
75
76
77-class Specification(SQLBase, BugLinkTargetMixin):
78+class Specification(SQLBase, BugLinkTargetMixin, InformationTypeMixin):
79 """See ISpecification."""
80
81 implements(ISpecification, IBugLinkTarget, IInformationType)
82@@ -891,10 +891,6 @@
83 reconcile_access_for_artifact(self, information_type, [self.target])
84 return True
85
86- @property
87- def private(self):
88- return self.information_type in PRIVATE_INFORMATION_TYPES
89-
90 @cachedproperty
91 def _known_viewers(self):
92 """A set of known persons able to view the specifcation."""
93
94=== modified file 'lib/lp/bugs/model/bug.py'
95--- lib/lp/bugs/model/bug.py 2012-09-28 06:15:58 +0000
96+++ lib/lp/bugs/model/bug.py 2012-10-03 01:18:22 +0000
97@@ -93,6 +93,7 @@
98 )
99 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
100 from lp.app.interfaces.services import IService
101+from lp.app.model.launchpad import InformationTypeMixin
102 from lp.app.validators import LaunchpadValidationError
103 from lp.bugs.adapters.bug import convert_to_information_type
104 from lp.bugs.adapters.bugchange import (
105@@ -303,7 +304,7 @@
106 self.user = user
107
108
109-class Bug(SQLBase):
110+class Bug(SQLBase, InformationTypeMixin):
111 """A bug."""
112
113 implements(IBug)
114@@ -363,10 +364,6 @@
115 latest_patch_uploaded = UtcDateTimeCol(default=None)
116
117 @property
118- def private(self):
119- return self.information_type in PRIVATE_INFORMATION_TYPES
120-
121- @property
122 def security_related(self):
123 return self.information_type in SECURITY_INFORMATION_TYPES
124
125
126=== modified file 'lib/lp/registry/configure.zcml'
127--- lib/lp/registry/configure.zcml 2012-09-25 04:29:34 +0000
128+++ lib/lp/registry/configure.zcml 2012-10-03 01:18:22 +0000
129@@ -1241,6 +1241,11 @@
130 <require
131 permission="launchpad.Edit"
132 interface="lp.registry.interfaces.product.IProductEditRestricted"/>
133+ <allow
134+ interface="lp.app.interfaces.informationtype.IInformationType"/>
135+ <require
136+ permission="launchpad.Edit"
137+ set_schema="lp.app.interfaces.informationtype.IInformationType"/>
138 <require
139 permission="launchpad.Edit"
140 set_attributes="answers_usage blueprints_usage codehosting_usage
141
142=== modified file 'lib/lp/registry/interfaces/product.py'
143--- lib/lp/registry/interfaces/product.py 2012-09-28 06:15:58 +0000
144+++ lib/lp/registry/interfaces/product.py 2012-10-03 01:18:22 +0000
145@@ -73,9 +73,9 @@
146
147 from lp import _
148 from lp.answers.interfaces.questiontarget import IQuestionTarget
149-from lp.app.enums import InformationType
150 from lp.app.errors import NameLookupFailed
151 from lp.app.interfaces.headings import IRootContext
152+from lp.app.interfaces.informationtype import IInformationType
153 from lp.app.interfaces.launchpad import (
154 IHasIcon,
155 IHasLogo,
156@@ -450,13 +450,6 @@
157 'and security policy will apply to this project.')),
158 exported_as='project_group')
159
160- information_type = exported(
161- Choice(
162- title=_('Information Type'), vocabulary=InformationType,
163- required=True, readonly=True,
164- description=_(
165- 'The type of of data contained in this project.')))
166-
167 owner = exported(
168 PersonChoice(
169 title=_('Maintainer'),
170@@ -933,7 +926,7 @@
171 IHasBugSupervisor, IProductEditRestricted,
172 IProductModerateRestricted, IProductDriverRestricted,
173 IProductPublic, IQuestionTarget, IRootContext,
174- IStructuralSubscriptionTarget):
175+ IStructuralSubscriptionTarget, IInformationType):
176 """A Product.
177
178 The Launchpad Registry describes the open source world as ProjectGroups
179
180=== modified file 'lib/lp/registry/model/product.py'
181--- lib/lp/registry/model/product.py 2012-09-28 19:59:35 +0000
182+++ lib/lp/registry/model/product.py 2012-10-03 01:18:22 +0000
183@@ -81,6 +81,7 @@
184 ILaunchpadUsage,
185 IServiceUsage,
186 )
187+from lp.app.model.launchpad import InformationTypeMixin
188 from lp.blueprints.enums import (
189 SpecificationDefinitionStatus,
190 SpecificationFilter,
191@@ -323,7 +324,8 @@
192 HasAliasMixin, StructuralSubscriptionTargetMixin,
193 HasMilestonesMixin, OfficialBugTagTargetMixin, HasBranchesMixin,
194 HasCustomLanguageCodesMixin, HasMergeProposalsMixin,
195- HasCodeImportsMixin, TranslationPolicyMixin):
196+ HasCodeImportsMixin, InformationTypeMixin,
197+ TranslationPolicyMixin):
198 """A Product."""
199
200 implements(
201@@ -405,7 +407,7 @@
202 """
203 return None
204
205- @date_next_suggest_packaging.setter
206+ @date_next_suggest_packaging.setter # pyflakes:ignore
207 def date_next_suggest_packaging(self, value):
208 """See `IProduct`
209
210@@ -413,15 +415,8 @@
211 """
212 pass
213
214- @property
215- def information_type(self):
216- """See `IProduct`
217-
218- Place holder for a db column.
219- XXX: rharding 2012-09-10 bug=1048720: Waiting on db patch to connect
220- into place.
221- """
222- pass
223+ information_type = EnumCol(
224+ enum=InformationType, default=InformationType.PUBLIC)
225
226 security_contact = None
227
228@@ -1599,12 +1594,15 @@
229 sourceforgeproject=None, programminglang=None,
230 project_reviewed=False, mugshot=None, logo=None,
231 icon=None, licenses=None, license_info=None,
232- registrant=None, bug_supervisor=None, driver=None):
233+ registrant=None, bug_supervisor=None, driver=None,
234+ information_type=None):
235 """See `IProductSet`."""
236 if registrant is None:
237 registrant = owner
238 if licenses is None:
239 licenses = set()
240+ if information_type is None:
241+ information_type = InformationType.PUBLIC
242 product = Product(
243 owner=owner, registrant=registrant, name=name,
244 displayname=displayname, title=title, project=project,
245@@ -1615,7 +1613,8 @@
246 programminglang=programminglang,
247 project_reviewed=project_reviewed,
248 icon=icon, logo=logo, mugshot=mugshot, license_info=license_info,
249- bug_supervisor=bug_supervisor, driver=driver)
250+ bug_supervisor=bug_supervisor, driver=driver,
251+ information_type=information_type)
252
253 # Set up the sharing policies and product licence.
254 bug_sharing_policy_to_use = BugSharingPolicy.PUBLIC
255
256=== modified file 'lib/lp/registry/tests/test_product.py'
257--- lib/lp/registry/tests/test_product.py 2012-09-28 06:15:58 +0000
258+++ lib/lp/registry/tests/test_product.py 2012-10-03 01:18:22 +0000
259@@ -7,6 +7,7 @@
260 import datetime
261
262 import pytz
263+from storm.locals import Store
264 from testtools.matchers import MatchesAll
265 import transaction
266 from zope.component import getUtility
267@@ -28,6 +29,7 @@
268 ILaunchpadUsage,
269 IServiceUsage,
270 )
271+from lp.app.interfaces.informationtype import IInformationType
272 from lp.app.interfaces.services import IService
273 from lp.bugs.interfaces.bugsummary import IBugSummaryDimension
274 from lp.bugs.interfaces.bugsupervisor import IHasBugSupervisor
275@@ -111,6 +113,7 @@
276 IHasLogo,
277 IHasMugshot,
278 IHasOOPSReferences,
279+ IInformationType,
280 ILaunchpadUsage,
281 IServiceUsage,
282 ]
283@@ -390,6 +393,29 @@
284 expected = [InformationType.PROPRIETARY]
285 self.assertContentEqual(expected, [policy.type for policy in aps])
286
287+ def test_product_information_type(self):
288+ # Product is created with specified information_type
289+ product = self.factory.makeProduct(
290+ information_type=InformationType.EMBARGOED)
291+ self.assertEqual(InformationType.EMBARGOED, product.information_type)
292+ # Owner can set information_type
293+ with person_logged_in(product.owner):
294+ product.information_type = InformationType.PROPRIETARY
295+ self.assertEqual(InformationType.PROPRIETARY, product.information_type)
296+ # Database persists information_type value
297+ store = Store.of(product)
298+ store.flush()
299+ store.reset()
300+ product = store.get(Product, product.id)
301+ self.assertEqual(InformationType.PROPRIETARY, product.information_type)
302+
303+ def test_product_information_type_default(self):
304+ # Default information_type is PUBLIC
305+ owner = self.factory.makePerson()
306+ product = getUtility(IProductSet).createProduct(
307+ owner, 'fnord', 'Fnord', 'Fnord', 'test 1', 'test 2')
308+ self.assertEqual(InformationType.PUBLIC, product.information_type)
309+
310
311 class TestProductBugInformationTypes(TestCaseWithFactory):
312
313
314=== modified file 'lib/lp/testing/factory.py'
315--- lib/lp/testing/factory.py 2012-10-02 07:08:13 +0000
316+++ lib/lp/testing/factory.py 2012-10-03 01:18:22 +0000
317@@ -964,7 +964,8 @@
318 title=None, summary=None, official_malone=None,
319 translations_usage=None, bug_supervisor=None, private_bugs=False,
320 driver=None, icon=None, bug_sharing_policy=None,
321- branch_sharing_policy=None, specification_sharing_policy=None):
322+ branch_sharing_policy=None, specification_sharing_policy=None,
323+ information_type=None):
324 """Create and return a new, arbitrary Product."""
325 if owner is None:
326 owner = self.makePerson()
327@@ -993,7 +994,8 @@
328 licenses=licenses,
329 project=project,
330 registrant=registrant,
331- icon=icon)
332+ icon=icon,
333+ information_type=information_type)
334 naked_product = removeSecurityProxy(product)
335 if official_malone is not None:
336 naked_product.official_malone = official_malone