Merge lp:~stevenk/launchpad/kill-entitlements into lp:launchpad
- kill-entitlements
- Merge into devel
Proposed by
Steve Kowalik
Status: | Merged |
---|---|
Approved by: | William Grant |
Approved revision: | no longer in the source branch. |
Merged at revision: | 14793 |
Proposed branch: | lp:~stevenk/launchpad/kill-entitlements |
Merge into: | lp:launchpad |
Diff against target: |
1532 lines (+0/-1430) 12 files modified
database/sampledata/current-dev.sql (+0/-11) database/sampledata/current.sql (+0/-11) lib/lp/registry/configure.zcml (+0/-23) lib/lp/registry/doc/entitlement.txt (+0/-333) lib/lp/registry/interfaces/entitlement.py (+0/-242) lib/lp/registry/interfaces/person.py (+0/-2) lib/lp/registry/model/entitlement.py (+0/-189) lib/lp/registry/model/person.py (+0/-1) lib/lp/registry/scripts/entitlement.py (+0/-330) lib/lp/registry/tests/test_entitlementimport.py (+0/-184) lib/lp/security.py (+0/-21) scripts/entitlements-to-lp.py (+0/-83) |
To merge this branch: | bzr merge lp:~stevenk/launchpad/kill-entitlements |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
William Grant | code | Approve | |
Review via email: mp+92888@code.launchpad.net |
Commit message
[r=wgrant][no-qa] Kill the never used entitlements interface/model and supporting scripts.
Description of the change
Kill the never used entitlements interface/model and supporting scripts.
The database table will be dropped in a separate branch.
To post a comment you must log in.
Revision history for this message
William Grant (wgrant) : | # |
review:
Approve
(code)
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'database/sampledata/current-dev.sql' |
2 | --- database/sampledata/current-dev.sql 2012-01-27 09:29:19 +0000 |
3 | +++ database/sampledata/current-dev.sql 2012-02-14 00:28:18 +0000 |
4 | @@ -4032,17 +4032,6 @@ |
5 | ALTER TABLE emailaddress ENABLE TRIGGER ALL; |
6 | |
7 | |
8 | -ALTER TABLE entitlement DISABLE TRIGGER ALL; |
9 | - |
10 | -INSERT INTO entitlement (id, person, entitlement_type, quota, amount_used, date_starts, date_expires, registrant, date_created, approved_by, date_approved, state, whiteboard, is_dirty, distribution, product, project) VALUES (1, 17, 10, 100, 0, '2007-06-11 12:00:00', '2008-06-11 12:00:00', NULL, '2007-06-10 12:00:00', NULL, NULL, 30, NULL, true, NULL, NULL, NULL); |
11 | -INSERT INTO entitlement (id, person, entitlement_type, quota, amount_used, date_starts, date_expires, registrant, date_created, approved_by, date_approved, state, whiteboard, is_dirty, distribution, product, project) VALUES (2, 17, 20, 200, 0, '2007-06-11 12:00:00', '2008-06-11 12:00:00', NULL, '2007-06-10 12:00:00', NULL, NULL, 30, NULL, true, NULL, NULL, NULL); |
12 | -INSERT INTO entitlement (id, person, entitlement_type, quota, amount_used, date_starts, date_expires, registrant, date_created, approved_by, date_approved, state, whiteboard, is_dirty, distribution, product, project) VALUES (4, 18, 10, 5, 0, '2007-06-11 12:00:00', '2007-06-11 00:00:00', NULL, '2007-06-11 00:44:19.267601', NULL, NULL, 30, NULL, true, NULL, NULL, NULL); |
13 | -INSERT INTO entitlement (id, person, entitlement_type, quota, amount_used, date_starts, date_expires, registrant, date_created, approved_by, date_approved, state, whiteboard, is_dirty, distribution, product, project) VALUES (5, 18, 10, 3, 0, '2007-06-11 12:00:00', '2007-06-11 00:00:00', NULL, '2007-06-11 01:02:48.538842', NULL, NULL, 30, NULL, true, NULL, NULL, NULL); |
14 | - |
15 | - |
16 | -ALTER TABLE entitlement ENABLE TRIGGER ALL; |
17 | - |
18 | - |
19 | ALTER TABLE faq DISABLE TRIGGER ALL; |
20 | |
21 | INSERT INTO faq (id, title, tags, content, product, distribution, owner, date_created, last_updated_by, date_last_updated, fti) VALUES (1, 'Wireless Networking Documentation', 'wifi', 'Installation instructions for many wireless cards can be found at: |
22 | |
23 | === modified file 'database/sampledata/current.sql' |
24 | --- database/sampledata/current.sql 2012-01-27 09:29:19 +0000 |
25 | +++ database/sampledata/current.sql 2012-02-14 00:28:18 +0000 |
26 | @@ -3965,17 +3965,6 @@ |
27 | ALTER TABLE emailaddress ENABLE TRIGGER ALL; |
28 | |
29 | |
30 | -ALTER TABLE entitlement DISABLE TRIGGER ALL; |
31 | - |
32 | -INSERT INTO entitlement (id, person, entitlement_type, quota, amount_used, date_starts, date_expires, registrant, date_created, approved_by, date_approved, state, whiteboard, is_dirty, distribution, product, project) VALUES (1, 17, 10, 100, 0, '2007-06-11 12:00:00', '2008-06-11 12:00:00', NULL, '2007-06-10 12:00:00', NULL, NULL, 30, NULL, true, NULL, NULL, NULL); |
33 | -INSERT INTO entitlement (id, person, entitlement_type, quota, amount_used, date_starts, date_expires, registrant, date_created, approved_by, date_approved, state, whiteboard, is_dirty, distribution, product, project) VALUES (2, 17, 20, 200, 0, '2007-06-11 12:00:00', '2008-06-11 12:00:00', NULL, '2007-06-10 12:00:00', NULL, NULL, 30, NULL, true, NULL, NULL, NULL); |
34 | -INSERT INTO entitlement (id, person, entitlement_type, quota, amount_used, date_starts, date_expires, registrant, date_created, approved_by, date_approved, state, whiteboard, is_dirty, distribution, product, project) VALUES (4, 18, 10, 5, 0, '2007-06-11 12:00:00', '2007-06-11 00:00:00', NULL, '2007-06-11 00:44:19.267601', NULL, NULL, 30, NULL, true, NULL, NULL, NULL); |
35 | -INSERT INTO entitlement (id, person, entitlement_type, quota, amount_used, date_starts, date_expires, registrant, date_created, approved_by, date_approved, state, whiteboard, is_dirty, distribution, product, project) VALUES (5, 18, 10, 3, 0, '2007-06-11 12:00:00', '2007-06-11 00:00:00', NULL, '2007-06-11 01:02:48.538842', NULL, NULL, 30, NULL, true, NULL, NULL, NULL); |
36 | - |
37 | - |
38 | -ALTER TABLE entitlement ENABLE TRIGGER ALL; |
39 | - |
40 | - |
41 | ALTER TABLE faq DISABLE TRIGGER ALL; |
42 | |
43 | INSERT INTO faq (id, title, tags, content, product, distribution, owner, date_created, last_updated_by, date_last_updated, fti) VALUES (1, 'Wireless Networking Documentation', 'wifi', 'Installation instructions for many wireless cards can be found at: |
44 | |
45 | === modified file 'lib/lp/registry/configure.zcml' |
46 | --- lib/lp/registry/configure.zcml 2012-02-13 05:50:16 +0000 |
47 | +++ lib/lp/registry/configure.zcml 2012-02-14 00:28:18 +0000 |
48 | @@ -1710,29 +1710,6 @@ |
49 | permission="launchpad.Edit" |
50 | interface="lp.registry.interfaces.productrelease.IProductReleaseFileEditRestricted"/> |
51 | </class> |
52 | - <class |
53 | - class="lp.registry.model.entitlement.Entitlement"> |
54 | - <require |
55 | - permission="launchpad.View" |
56 | - interface="lp.registry.interfaces.entitlement.IEntitlement"/> |
57 | - <require |
58 | - permission="launchpad.Admin" |
59 | - set_schema="lp.registry.interfaces.entitlement.IEntitlement"/> |
60 | - </class> |
61 | - |
62 | - <!-- EntitlementSet --> |
63 | - |
64 | - <class |
65 | - class="lp.registry.model.entitlement.EntitlementSet"> |
66 | - <allow |
67 | - interface="lp.registry.interfaces.entitlement.IEntitlementSet"/> |
68 | - </class> |
69 | - <securedutility |
70 | - class="lp.registry.model.entitlement.EntitlementSet" |
71 | - provides="lp.registry.interfaces.entitlement.IEntitlementSet"> |
72 | - <allow |
73 | - interface="lp.registry.interfaces.entitlement.IEntitlementSet"/> |
74 | - </securedutility> |
75 | |
76 | <!-- SuiteSourcePackage --> |
77 | <class |
78 | |
79 | === removed file 'lib/lp/registry/doc/entitlement.txt' |
80 | --- lib/lp/registry/doc/entitlement.txt 2011-12-30 06:14:56 +0000 |
81 | +++ lib/lp/registry/doc/entitlement.txt 1970-01-01 00:00:00 +0000 |
82 | @@ -1,333 +0,0 @@ |
83 | -= Entitlements = |
84 | - |
85 | -Entitlements are a means of recording features and privileges |
86 | -Launchpad users have been assigned. The business model for these |
87 | -assignments is not important, but it could be through a commercial |
88 | -arrangement or through a grant that applies to everyone. |
89 | - |
90 | - >>> from datetime import datetime |
91 | - >>> import pytz |
92 | - >>> from lp.services.database.sqlbase import flush_database_updates |
93 | - >>> from lp.testing import login |
94 | - >>> from lp.services.webapp.testing import verifyObject |
95 | - >>> from lp.registry.interfaces.entitlement import ( |
96 | - ... EntitlementQuota, |
97 | - ... EntitlementState, |
98 | - ... EntitlementType, |
99 | - ... IEntitlement, |
100 | - ... IEntitlementSet, |
101 | - ... ) |
102 | - >>> from lp.registry.interfaces.person import IPersonSet |
103 | - |
104 | -The logged in user must be an admin in order to change the quota. |
105 | - |
106 | - >>> from lp.app.interfaces.launchpad import ILaunchpadCelebrities |
107 | - >>> login("foo.bar@canonical.com") |
108 | - >>> foobar = getUtility(ILaunchBag).user |
109 | - >>> lp_admins = getUtility(ILaunchpadCelebrities).admin |
110 | - >>> foobar.inTeam(lp_admins) |
111 | - True |
112 | - >>> entitlements = getUtility(IEntitlementSet) |
113 | - >>> entitlements_in_sample_data = entitlements.count() |
114 | - |
115 | -Find a team with an entitlement. |
116 | - |
117 | - >>> personset = getUtility(IPersonSet) |
118 | - >>> ubuntu_team = personset.getByName('ubuntu-team') |
119 | - |
120 | -An entitlement grants privilege to use a restricted feature. The |
121 | -'quota' is the number of instances being granted and the |
122 | -entitlement_type is the feature being enabled. The 'person' is the |
123 | -person or team being granted the right. The state for a granted |
124 | -entitlement is ACTIVE and the expiration and start dates can be specified. |
125 | - |
126 | - >>> UTC = pytz.timezone('UTC') |
127 | - >>> entitlement = getUtility(IEntitlementSet).new( |
128 | - ... quota=5, |
129 | - ... entitlement_type=EntitlementType.PRIVATE_BUGS, |
130 | - ... person=ubuntu_team, |
131 | - ... state=EntitlementState.ACTIVE, |
132 | - ... date_expires=datetime(2038, 6, 11, tzinfo=UTC), |
133 | - ... amount_used=0) |
134 | - >>> verifyObject(IEntitlement, entitlement) |
135 | - True |
136 | - |
137 | -The person can be accessed and shown to be correct. |
138 | - |
139 | - >>> print entitlement.person.name |
140 | - ubuntu-team |
141 | - |
142 | -If the current date is after the start date (if not None) or before |
143 | -the expiration date (if not None) then the in_date_range test will |
144 | -pass. |
145 | - |
146 | - >>> entitlement.in_date_range |
147 | - True |
148 | - |
149 | -== Quota checking == |
150 | - |
151 | -If the amount used is less than the quota value the exceeded_quota |
152 | -test will be false. |
153 | - |
154 | - >>> entitlement.quota = 100 |
155 | - >>> entitlement.amount_used = 0 |
156 | - >>> entitlement.exceeded_quota |
157 | - False |
158 | - |
159 | -The quota is not exceeded, the date is in range, and the state is |
160 | -ACTIVE, so the entitlement is valid. |
161 | - |
162 | - >>> entitlement.is_valid |
163 | - True |
164 | - |
165 | -If the amount used is equal to the quota 'exceeded_quota' should still |
166 | -be false and 'is_valid' is still true. |
167 | - |
168 | - >>> entitlement.quota = 100 |
169 | - >>> entitlement.amount_used = 100 |
170 | - >>> entitlement.exceeded_quota |
171 | - False |
172 | - >>> entitlement.is_valid |
173 | - True |
174 | - |
175 | -However if the amount_used to exceeds the quota, the exceeded quota is |
176 | -true and the entitlement is no longer valid. |
177 | - |
178 | - >>> entitlement.quota = 100 |
179 | - >>> entitlement.amount_used = 101 |
180 | - >>> entitlement.exceeded_quota |
181 | - True |
182 | - >>> entitlement.is_valid |
183 | - False |
184 | - |
185 | -If the quota is UNLIMITED the exceeded quota test is false even when |
186 | -using a really large value for amount used. The entitlement remains |
187 | -valid. |
188 | - |
189 | -We use math.pow here, since __builtin__.pow is overridden by twisted.conch. |
190 | - |
191 | - >>> import math |
192 | - >>> entitlement.quota = EntitlementQuota.UNLIMITED |
193 | - >>> entitlement.amount_used = math.pow(10,9) |
194 | - >>> entitlement.exceeded_quota |
195 | - False |
196 | - >>> entitlement.is_valid |
197 | - True |
198 | - |
199 | -Calling incrementAmountUsed increases the value by one. |
200 | - |
201 | - >>> entitlement.quota = 10 |
202 | - >>> entitlement.amount_used = 9 |
203 | - >>> entitlement.incrementAmountUsed() |
204 | - >>> entitlement.amount_used |
205 | - 10 |
206 | - |
207 | -Attempting to increment past the quota will cause an error. |
208 | - |
209 | - >>> entitlement.incrementAmountUsed() |
210 | - Traceback (most recent call last): |
211 | - ... |
212 | - EntitlementQuotaExceededError: ... |
213 | - |
214 | -If the entitlement is invalid for any reason, attempting to increment |
215 | -the amount used will cause an error. |
216 | - |
217 | - >>> entitlement.state = EntitlementState.INACTIVE |
218 | - >>> entitlement.quota = 50 |
219 | - >>> entitlement.incrementAmountUsed() |
220 | - Traceback (most recent call last): |
221 | - ... |
222 | - EntitlementInvalidError: ... |
223 | - |
224 | - |
225 | -== Date range checking == |
226 | - |
227 | -The 'in_date_range' attribute can be used to tell whether the |
228 | -entitlement is valid with respect to the start date and expires date. |
229 | -When the current date is out of range the entitlement is considered |
230 | -invalid. |
231 | - |
232 | -When the entitlement does not have a start date or an expiration date |
233 | -then those values are not checked. If no start date is given the |
234 | -expiration date is still used, and vice versa. If both start date and |
235 | -expiration date are None then no date range checking is performed and |
236 | -the entitlement is valid with respect to dates. |
237 | - |
238 | -Remove the start and expiration dates and the date range will be |
239 | -valid. |
240 | - |
241 | - >>> entitlement.state = EntitlementState.ACTIVE |
242 | - >>> entitlement.date_starts = None |
243 | - >>> entitlement.date_expires = None |
244 | - >>> entitlement.in_date_range |
245 | - True |
246 | - >>> entitlement.is_valid |
247 | - True |
248 | - |
249 | - >>> from datetime import datetime, timedelta |
250 | - >>> from pytz import UTC |
251 | - >>> now = datetime.now(UTC) |
252 | - >>> yesterday = now - timedelta(days = 1) |
253 | - >>> tomorrow = now + timedelta(days = 1) |
254 | - |
255 | -If the start date is in the past and the expiration date is not set |
256 | -this condition is considered an open-ended entitlement and we are |
257 | -still in its range. |
258 | - |
259 | - >>> entitlement.date_starts = yesterday |
260 | - >>> entitlement.in_date_range |
261 | - True |
262 | - >>> entitlement.is_valid |
263 | - True |
264 | - |
265 | -If the start date is in the future the date is not in range and the |
266 | -entitlement is invalid. |
267 | - |
268 | - >>> entitlement.date_starts = tomorrow |
269 | - >>> entitlement.in_date_range |
270 | - False |
271 | - >>> entitlement.is_valid |
272 | - False |
273 | - |
274 | -If the expires date is in the past, we are not in its date range and |
275 | -the entitlement is not valid. |
276 | - |
277 | - >>> entitlement.date_starts = None |
278 | - >>> entitlement.date_expires = yesterday |
279 | - >>> entitlement.in_date_range |
280 | - False |
281 | - >>> entitlement.is_valid |
282 | - False |
283 | - |
284 | -If the expiration date is in the future the date range is satisfied |
285 | -and the entitlement is valid. |
286 | - |
287 | - >>> entitlement.date_expires = tomorrow |
288 | - >>> entitlement.in_date_range |
289 | - True |
290 | - >>> entitlement.is_valid |
291 | - True |
292 | - |
293 | -If the start date is in the past and the expires date is in the future |
294 | -the date range is again satisfied and the entitlement is valid. |
295 | - |
296 | - >>> entitlement.date_starts = yesterday |
297 | - >>> entitlement.date_expires = tomorrow |
298 | - >>> entitlement.in_date_range |
299 | - True |
300 | - >>> entitlement.is_valid |
301 | - True |
302 | - |
303 | - |
304 | -== Security == |
305 | - |
306 | -Viewing the entitlement's attributes is restricted to the |
307 | -entitlement's owner, the entitlement's registrant (or to a member of |
308 | -these teams), and to Launchpad administrators. |
309 | - |
310 | -If logged in as an anonymous user the entitlement values cannot be |
311 | -viewed. A security-wrapped entitlement must be used to exercise the |
312 | -security adapters. |
313 | - |
314 | - >>> entitlement = ubuntu_team.entitlements[0] |
315 | - >>> entitlement.quota = 50 |
316 | - >>> login(ANONYMOUS) |
317 | - >>> entitlement.quota |
318 | - Traceback (most recent call last): |
319 | - ... |
320 | - Unauthorized: (<Entitlement..., 'quota', 'launchpad.View') |
321 | - |
322 | - |
323 | - >>> entitlement.quota=10 |
324 | - Traceback (most recent call last): |
325 | - ... |
326 | - Unauthorized: (<Entitlement..., 'quota', 'launchpad.Admin') |
327 | - |
328 | -Authenticated Launchpad users who are not the owner or team member are |
329 | -similarly restricted. |
330 | - |
331 | - >>> login('no-priv@canonical.com') |
332 | - >>> entitlement.quota |
333 | - Traceback (most recent call last): |
334 | - ... |
335 | - Unauthorized: (<Entitlement..., 'quota', 'launchpad.View') |
336 | - |
337 | - |
338 | - >>> entitlement.quota=10 |
339 | - Traceback (most recent call last): |
340 | - ... |
341 | - Unauthorized: (<Entitlement..., 'quota', 'launchpad.Admin') |
342 | - |
343 | -Add the current user to the team and the user can now access |
344 | -attributes. Since ubuntu-team is restricted, an administrator must be |
345 | -listed as a reviewer for the membership to go through. |
346 | - |
347 | - >>> from lp.registry.interfaces.teammembership import TeamMembershipStatus |
348 | - >>> nopriv = getUtility(ILaunchBag).user |
349 | - >>> mark = personset.getByName('mark') |
350 | - >>> nopriv.join(ubuntu_team) |
351 | - |
352 | - # Login as the team's owner in order to be able to change its memberships. |
353 | - >>> login(ubuntu_team.teamowner.preferredemail.email) |
354 | - >>> ubuntu_team.setMembershipData(nopriv, TeamMembershipStatus.APPROVED, mark) |
355 | - >>> login('no-priv@canonical.com') |
356 | - |
357 | - >>> flush_database_updates() |
358 | - >>> nopriv in ubuntu_team.activemembers |
359 | - True |
360 | - >>> nopriv.inTeam(ubuntu_team) |
361 | - True |
362 | - >>> entitlement.quota |
363 | - 50 |
364 | - |
365 | - >>> entitlement.quota=10 |
366 | - Traceback (most recent call last): |
367 | - ... |
368 | - Unauthorized: (<Entitlement..., 'quota', 'launchpad.Admin') |
369 | - |
370 | - |
371 | -= EntitlementSet = |
372 | - |
373 | -All of the entitlements can be obtained using IEntitlementSet. |
374 | - |
375 | - >>> login("foo.bar@canonical.com") |
376 | - >>> entitlements = getUtility(IEntitlementSet) |
377 | - |
378 | -One entitlement was created in the previous examples. |
379 | - |
380 | - >>> entitlements.count() - entitlements_in_sample_data |
381 | - 1 |
382 | - |
383 | -To look up an entitlement from the set, simply use the 'get' with the id. |
384 | - |
385 | - >>> entitlement2 = entitlements.get(entitlement.id) |
386 | - >>> entitlement2.id == entitlement.id |
387 | - True |
388 | - |
389 | -Get all of the valid entitlements for ubuntu_team. |
390 | - |
391 | - >>> valid_list = getUtility(IEntitlementSet).getValidForPerson(ubuntu_team) |
392 | - >>> len(valid_list) |
393 | - 1 |
394 | - |
395 | -A new entitlement for the ubuntu_team can be created, increasing the |
396 | -number of valid entitlements. |
397 | - |
398 | - >>> entitlement = getUtility(IEntitlementSet).new( |
399 | - ... quota=50, |
400 | - ... entitlement_type=EntitlementType.PRIVATE_BRANCHES, |
401 | - ... person=ubuntu_team, |
402 | - ... state=EntitlementState.ACTIVE, |
403 | - ... date_expires=datetime(2038, 6, 11, tzinfo=UTC), |
404 | - ... amount_used=0) |
405 | - >>> valid_list = getUtility(IEntitlementSet).getValidForPerson(ubuntu_team) |
406 | - >>> len(valid_list) |
407 | - 2 |
408 | - |
409 | -Setting the original entitlement to INACTIVE will now reduce the |
410 | -number of valid entitlements. |
411 | - |
412 | - >>> entitlement.state = EntitlementState.INACTIVE |
413 | - >>> valid_list = getUtility(IEntitlementSet).getValidForPerson(ubuntu_team) |
414 | - >>> len(valid_list) |
415 | - 1 |
416 | |
417 | === removed file 'lib/lp/registry/interfaces/entitlement.py' |
418 | --- lib/lp/registry/interfaces/entitlement.py 2011-12-24 16:54:44 +0000 |
419 | +++ lib/lp/registry/interfaces/entitlement.py 1970-01-01 00:00:00 +0000 |
420 | @@ -1,242 +0,0 @@ |
421 | -# Copyright 2009 Canonical Ltd. This software is licensed under the |
422 | -# GNU Affero General Public License version 3 (see the file LICENSE). |
423 | - |
424 | -# pylint: disable-msg=E0211,E0213 |
425 | - |
426 | -"""Entitlement interfaces.""" |
427 | - |
428 | -__metaclass__ = type |
429 | - |
430 | -__all__ = [ |
431 | - 'EntitlementInvalidError', |
432 | - 'EntitlementQuota', |
433 | - 'EntitlementQuotaExceededError', |
434 | - 'EntitlementState', |
435 | - 'EntitlementType', |
436 | - 'IEntitlement', |
437 | - 'IEntitlementSet', |
438 | - ] |
439 | - |
440 | -from lazr.enum import ( |
441 | - DBEnumeratedType, |
442 | - DBItem, |
443 | - ) |
444 | -from zope.interface import ( |
445 | - Attribute, |
446 | - Interface, |
447 | - ) |
448 | -from zope.schema import ( |
449 | - Bool, |
450 | - Choice, |
451 | - Datetime, |
452 | - Int, |
453 | - ) |
454 | - |
455 | -from lp import _ |
456 | -from lp.services.fields import Whiteboard |
457 | - |
458 | - |
459 | -class EntitlementQuotaExceededError(Exception): |
460 | - """The quota has been exceeded for the entitlement.""" |
461 | - |
462 | - |
463 | -class EntitlementInvalidError(Exception): |
464 | - """The entitlement is not valid.""" |
465 | - |
466 | - |
467 | -class EntitlementType(DBEnumeratedType): |
468 | - """The set of features supported via entitlements. |
469 | - |
470 | - The listed features may be enabled by the granting of an entitlement. |
471 | - """ |
472 | - |
473 | - PRIVATE_BRANCHES = DBItem(10, """ |
474 | - Private Branches |
475 | - |
476 | - The ability to create branches which are only visible to the team. |
477 | - """) |
478 | - |
479 | - PRIVATE_BUGS = DBItem(20, """ |
480 | - Private Bugs |
481 | - |
482 | - The ability to create private bugs which are only visible to the team. |
483 | - """) |
484 | - |
485 | - PRIVATE_TEAMS = DBItem(30, """ |
486 | - Private Teams |
487 | - |
488 | - The ability to create private teams which are only visible to parent |
489 | - teams. |
490 | - """) |
491 | - |
492 | - |
493 | -class EntitlementState(DBEnumeratedType): |
494 | - """States for an entitlement. |
495 | - |
496 | - The entitlement may start life as a REQUEST that is then granted and |
497 | - made ACTIVE. At some point the entitlement may be revoked by marking |
498 | - as INACTIVE. |
499 | - """ |
500 | - |
501 | - REQUESTED = DBItem(10, """ |
502 | - Entitlement has been requested. |
503 | - |
504 | - The entitlement is inactive in this state. |
505 | - """) |
506 | - |
507 | - ACTIVE = DBItem(20, """ |
508 | - The entitlement is active. |
509 | - |
510 | - The entitlement is approved in Launchpad or was imported in the |
511 | - active state. |
512 | - """) |
513 | - |
514 | - INACTIVE = DBItem(30, """ |
515 | - The entitlement is inactive. |
516 | - |
517 | - The entitlement has be deactivated. |
518 | - """) |
519 | - |
520 | - |
521 | -class IEntitlement(Interface): |
522 | - """An entitlement the right to use a specific feature in Launchpad. |
523 | - |
524 | - Entitlements can be granted in an unlimited quantity or with a given |
525 | - quota. They have a start date and optionally an expiration date. An |
526 | - entitlement is invalid if it is not active, the quota is exceeded, or if |
527 | - it is expired. |
528 | - """ |
529 | - |
530 | - id = Int( |
531 | - title=_("Entitlement id"), |
532 | - required=True, |
533 | - readonly=True) |
534 | - person = Choice( |
535 | - title=_('Person'), |
536 | - required=True, |
537 | - readonly=True, |
538 | - vocabulary='ValidPersonOrTeam', |
539 | - description=_("Person or team to whom the entitlements is assigned.")) |
540 | - date_created = Datetime( |
541 | - title=_("Date Created"), |
542 | - description=_("The date on which this entitlement was created."), |
543 | - required=True, |
544 | - readonly=True) |
545 | - date_starts = Datetime( |
546 | - title=_("Date Starts"), |
547 | - description=_("The date on which this entitlement starts."), |
548 | - readonly=False) |
549 | - date_expires = Datetime( |
550 | - title=_("Date Expires"), |
551 | - description=_("The date on which this entitlement expires."), |
552 | - readonly=False) |
553 | - entitlement_type = Choice( |
554 | - title=_("Type of entitlement."), |
555 | - required=True, |
556 | - vocabulary='EntitlementType', |
557 | - description=_("Type of feature for this entitlement."), |
558 | - readonly=True) |
559 | - quota = Int( |
560 | - title=_("Allocated quota."), |
561 | - required=True, |
562 | - description=_( |
563 | - "A quota is the number of a feature allowed by this entitlement, " |
564 | - "for instance 50 private bugs.")) |
565 | - amount_used = Int( |
566 | - title=_("Amount used."), |
567 | - description=_( |
568 | - "The amount used is the number of instances of a feature " |
569 | - "the person has used so far.")) |
570 | - registrant = Choice( |
571 | - title=_('Registrant'), |
572 | - vocabulary='ValidPersonOrTeam', |
573 | - description=_( |
574 | - "Person who registered the entitlement. " |
575 | - "May be None if created automatically."), |
576 | - readonly=True) |
577 | - approved_by = Choice( |
578 | - title=_('Approved By'), |
579 | - vocabulary='ValidPersonOrTeam', |
580 | - description=_( |
581 | - "Person who approved the entitlement. " |
582 | - "May be None if created automatically."), |
583 | - readonly=True) |
584 | - state = Choice( |
585 | - title=_("State"), |
586 | - required=True, |
587 | - vocabulary='EntitlementState', |
588 | - description = _("Current state of the entitlement.")) |
589 | - |
590 | - whiteboard = Whiteboard(title=_('Whiteboard'), required=False, |
591 | - description=_('Notes on the current status of the entitlement.')) |
592 | - |
593 | - is_dirty = Bool( |
594 | - title=_("Dirty?"), |
595 | - description=_( |
596 | - "Is the entitlement 'dirty', i.e. has been written since the " |
597 | - "most recent update to an external system?")) |
598 | - |
599 | - is_valid = Attribute( |
600 | - "Is this entitlement valid?") |
601 | - |
602 | - exceeded_quota = Attribute( |
603 | - "If the quota is not unlimited, is it exceeded?") |
604 | - |
605 | - in_date_range = Attribute( |
606 | - "Has the start date passed but not the expiration date?") |
607 | - |
608 | - def incrementAmountUsed(): |
609 | - """Add one to the amount used.""" |
610 | - |
611 | - |
612 | -class IEntitlementSet(Interface): |
613 | - """Interface representing a set of Entitlements.""" |
614 | - |
615 | - def __getitem__(entitlement_id): |
616 | - """Return the entitlement with the given id. |
617 | - |
618 | - Raise NotFoundError if there is no such entitlement. |
619 | - """ |
620 | - |
621 | - def __iter__(): |
622 | - """Return an iterator that will go through all entitlements.""" |
623 | - |
624 | - def count(): |
625 | - """Return the number of entitlements in the database.""" |
626 | - |
627 | - def get(entitlement_id, default=None): |
628 | - """Return the entitlement with the given id. |
629 | - |
630 | - Return the default value if there is no such entitlement. |
631 | - """ |
632 | - |
633 | - def getForPerson(person): |
634 | - """Return the entitlements for the person or team. |
635 | - |
636 | - Get all entitlements for a person. |
637 | - """ |
638 | - |
639 | - def getValidForPerson(person): |
640 | - """Return a list of valid entitlements for the person or team. |
641 | - |
642 | - Get all valid entitlements for a person. None is returned if no valid |
643 | - entitlements are found. |
644 | - """ |
645 | - |
646 | - def getDirty(): |
647 | - """Return the entitlements that have the dirty bit set. |
648 | - |
649 | - Get all entitlements that are marked as dirty. |
650 | - """ |
651 | - |
652 | - def new(person, quota, entitlement_type, state, |
653 | - is_dirty=True, date_created=None, date_expires=None, |
654 | - date_starts=None, amount_used=None, registrant=None, |
655 | - approved_by=None): |
656 | - """Create a new entitlement.""" |
657 | - |
658 | - |
659 | -class EntitlementQuota: |
660 | - """This class stores constants for entitlements quotas.""" |
661 | - |
662 | - UNLIMITED = 0 |
663 | |
664 | === modified file 'lib/lp/registry/interfaces/person.py' |
665 | --- lib/lp/registry/interfaces/person.py 2012-02-13 23:02:42 +0000 |
666 | +++ lib/lp/registry/interfaces/person.py 2012-02-14 00:28:18 +0000 |
667 | @@ -1022,8 +1022,6 @@ |
668 | # Really IArchive, see archive.py |
669 | value_type=Reference(schema=Interface))) |
670 | |
671 | - entitlements = Attribute("List of Entitlements for this person or team.") |
672 | - |
673 | structural_subscriptions = Attribute( |
674 | "The structural subscriptions for this person.") |
675 | |
676 | |
677 | === removed file 'lib/lp/registry/model/entitlement.py' |
678 | --- lib/lp/registry/model/entitlement.py 2011-12-30 06:14:56 +0000 |
679 | +++ lib/lp/registry/model/entitlement.py 1970-01-01 00:00:00 +0000 |
680 | @@ -1,189 +0,0 @@ |
681 | -# Copyright 2009 Canonical Ltd. This software is licensed under the |
682 | -# GNU Affero General Public License version 3 (see the file LICENSE). |
683 | - |
684 | -# pylint: disable-msg=E0611,W0212 |
685 | - |
686 | -__metaclass__ = type |
687 | -__all__ = ['Entitlement', 'EntitlementSet'] |
688 | - |
689 | -from datetime import datetime |
690 | - |
691 | -import pytz |
692 | -from sqlobject import ( |
693 | - BoolCol, |
694 | - ForeignKey, |
695 | - IntCol, |
696 | - SQLObjectNotFound, |
697 | - StringCol, |
698 | - ) |
699 | -from zope.interface import implements |
700 | - |
701 | -from lp.app.errors import NotFoundError |
702 | -from lp.registry.interfaces.entitlement import ( |
703 | - EntitlementInvalidError, |
704 | - EntitlementQuota, |
705 | - EntitlementQuotaExceededError, |
706 | - EntitlementState, |
707 | - EntitlementType, |
708 | - IEntitlement, |
709 | - IEntitlementSet, |
710 | - ) |
711 | -from lp.services.database.constants import DEFAULT |
712 | -from lp.services.database.datetimecol import UtcDateTimeCol |
713 | -from lp.services.database.enumcol import EnumCol |
714 | -from lp.services.database.sqlbase import SQLBase |
715 | - |
716 | - |
717 | -class Entitlement(SQLBase): |
718 | - """A table recording the entitlements for a person or team.""" |
719 | - |
720 | - implements(IEntitlement) |
721 | - _table = 'Entitlement' |
722 | - _defaultOrder = ['person', '-date_expires'] |
723 | - |
724 | - person = ForeignKey( |
725 | - dbName='person', foreignKey='Person', |
726 | - default=None, notNull=True) |
727 | - date_created = UtcDateTimeCol(notNull=True, default=DEFAULT) |
728 | - date_starts = UtcDateTimeCol(notNull=False, default=None) |
729 | - date_expires = UtcDateTimeCol(notNull=False, default=None) |
730 | - |
731 | - entitlement_type = EnumCol( |
732 | - dbName='entitlement_type', |
733 | - notNull=True, |
734 | - enum=EntitlementType, |
735 | - default=EntitlementType.PRIVATE_BUGS) |
736 | - quota = IntCol(notNull=True) |
737 | - amount_used = IntCol(notNull=True, default=0) |
738 | - registrant = ForeignKey( |
739 | - dbName='registrant', foreignKey='Person', |
740 | - default=None, notNull=False) |
741 | - approved_by = ForeignKey( |
742 | - dbName='approved_by', foreignKey='Person', |
743 | - default=None, notNull=False) |
744 | - state = EnumCol( |
745 | - dbName='state', |
746 | - notNull=True, |
747 | - enum=EntitlementState, |
748 | - default=EntitlementState.INACTIVE) |
749 | - whiteboard = StringCol(notNull=False, default=None) |
750 | - is_dirty = BoolCol(dbName="is_dirty", notNull=True, default=True) |
751 | - |
752 | - @property |
753 | - def exceeded_quota(self): |
754 | - """See IEntitlement.""" |
755 | - if self.quota == EntitlementQuota.UNLIMITED: |
756 | - return False |
757 | - else: |
758 | - return self.amount_used > self.quota |
759 | - |
760 | - def _isExpired(self, now=None): |
761 | - if now is None: |
762 | - now = datetime.now(pytz.timezone('UTC')) |
763 | - if self.date_expires is None: |
764 | - return False |
765 | - else: |
766 | - return now > self.date_expires |
767 | - |
768 | - def _hasNotYetStarted(self, now=None): |
769 | - if now is None: |
770 | - now = datetime.now(pytz.timezone('UTC')) |
771 | - if self.date_starts is None: |
772 | - return False |
773 | - else: |
774 | - return now < self.date_starts |
775 | - |
776 | - @property |
777 | - def in_date_range(self): |
778 | - """See IEntitlement.""" |
779 | - now = datetime.now(pytz.timezone('UTC')) |
780 | - too_late = self._isExpired(now) |
781 | - too_early = self._hasNotYetStarted(now) |
782 | - just_right = not (too_late or too_early) |
783 | - return just_right |
784 | - |
785 | - @property |
786 | - def is_valid(self): |
787 | - """See IEntitlement.""" |
788 | - if self.state != EntitlementState.ACTIVE: |
789 | - return False |
790 | - else: |
791 | - return self.in_date_range and not self.exceeded_quota |
792 | - |
793 | - def incrementAmountUsed(self): |
794 | - """See IEntitlement.""" |
795 | - if not self.is_valid: |
796 | - raise EntitlementInvalidError( |
797 | - "This entitlement is invalid and cannot be used.") |
798 | - |
799 | - self.amount_used += 1 |
800 | - |
801 | - if self.exceeded_quota: |
802 | - self.amount_used -= 1 |
803 | - raise EntitlementQuotaExceededError( |
804 | - "The quota for this entitlement has been exceeded.") |
805 | - |
806 | - |
807 | -class EntitlementSet: |
808 | - """The set of all entitlements.""" |
809 | - |
810 | - implements(IEntitlementSet) |
811 | - |
812 | - def __getitem__(self, entitlement_id): |
813 | - """See `IEntitlementSet`.""" |
814 | - entitlement = self.get(entitlement_id) |
815 | - if entitlement is None: |
816 | - raise NotFoundError(entitlement_id) |
817 | - return entitlement |
818 | - |
819 | - def __iter__(self): |
820 | - """See `IEntitlementSet`.""" |
821 | - return iter(Entitlement.select()) |
822 | - |
823 | - def count(self): |
824 | - """See `IEntitlementSet`.""" |
825 | - return Entitlement.select().count() |
826 | - |
827 | - def get(self, entitlement_id, default=None): |
828 | - """See `IEntitlementSet`.""" |
829 | - try: |
830 | - return Entitlement.get(entitlement_id) |
831 | - except SQLObjectNotFound: |
832 | - return default |
833 | - |
834 | - def getForPerson(self, person): |
835 | - """See `IEntitlementSet`.""" |
836 | - return Entitlement.selectBy(person=person) |
837 | - |
838 | - def getValidForPerson(self, person): |
839 | - """See `IEntitlementSet`.""" |
840 | - entitlements = self.getForPerson(person) |
841 | - return [entitlement for entitlement in entitlements |
842 | - if entitlement.is_valid] |
843 | - |
844 | - def getDirty(self): |
845 | - """See `IEntitlementSet`.""" |
846 | - return Entitlement.selectBy(is_dirty=True) |
847 | - |
848 | - def new(self, person, quota, entitlement_type, |
849 | - state, is_dirty=True, date_created=None, date_starts=None, |
850 | - date_expires=None, amount_used=0, registrant=None, |
851 | - approved_by=None, whiteboard=None): |
852 | - """See `IEntitlementSet`.""" |
853 | - |
854 | - if date_created is None: |
855 | - date_created = datetime.now(pytz.timezone('UTC')) |
856 | - |
857 | - return Entitlement( |
858 | - person=person, |
859 | - quota=quota, |
860 | - entitlement_type=entitlement_type, |
861 | - state=state, |
862 | - is_dirty=is_dirty, |
863 | - date_created=date_created, |
864 | - date_starts=date_starts, |
865 | - date_expires=date_expires, |
866 | - amount_used=amount_used, |
867 | - registrant=registrant, |
868 | - approved_by=approved_by, |
869 | - whiteboard=whiteboard) |
870 | |
871 | === modified file 'lib/lp/registry/model/person.py' |
872 | --- lib/lp/registry/model/person.py 2012-02-13 23:02:42 +0000 |
873 | +++ lib/lp/registry/model/person.py 2012-02-14 00:28:18 +0000 |
874 | @@ -595,7 +595,6 @@ |
875 | _ircnicknames = SQLMultipleJoin('IrcID', joinColumn='person') |
876 | jabberids = SQLMultipleJoin('JabberID', joinColumn='person') |
877 | |
878 | - entitlements = SQLMultipleJoin('Entitlement', joinColumn='person') |
879 | visibility = EnumCol( |
880 | enum=PersonVisibility, |
881 | default=PersonVisibility.PUBLIC, |
882 | |
883 | === removed file 'lib/lp/registry/scripts/entitlement.py' |
884 | --- lib/lp/registry/scripts/entitlement.py 2011-12-19 23:38:16 +0000 |
885 | +++ lib/lp/registry/scripts/entitlement.py 1970-01-01 00:00:00 +0000 |
886 | @@ -1,330 +0,0 @@ |
887 | -# Copyright 2009-2011 Canonical Ltd. This software is licensed under the |
888 | -# GNU Affero General Public License version 3 (see the file LICENSE). |
889 | - |
890 | -"""Import entitlements from Salesforce. |
891 | - |
892 | -Provide a class that allows the writing and reading of entitlement exchange |
893 | -files and a class to create and update entitlements in Launchpad. |
894 | -""" |
895 | - |
896 | -__metaclass__ = type |
897 | -__all__ = [ |
898 | - 'EntitlementExchange', |
899 | - 'EntitlementImporter', |
900 | - 'InvalidFormat', |
901 | - 'NoSuchEntitlement', |
902 | - 'UnsupportedVersion', |
903 | - ] |
904 | - |
905 | -import cStringIO |
906 | -import csv |
907 | -import datetime |
908 | -import re |
909 | -import time |
910 | - |
911 | -import pytz |
912 | -from zope.component import getUtility |
913 | - |
914 | -from lp.app.errors import NotFoundError |
915 | -from lp.registry.interfaces.entitlement import ( |
916 | - EntitlementState, |
917 | - EntitlementType, |
918 | - IEntitlementSet, |
919 | - ) |
920 | -from lp.registry.interfaces.person import IPersonSet |
921 | -from lp.services.unicode_csv import ( |
922 | - UnicodeDictReader, |
923 | - UnicodeDictWriter, |
924 | - ) |
925 | - |
926 | - |
927 | -COMMENT = '#' |
928 | -COMMA = ',' |
929 | - |
930 | - |
931 | -class NoSuchEntitlement(Exception): |
932 | - """Used if a non-existent entitlement is specified.""" |
933 | - |
934 | - |
935 | -class UnsupportedVersion(Exception): |
936 | - """Used if the version is not supported.""" |
937 | - |
938 | - |
939 | -class InvalidFormat(Exception): |
940 | - """Used if the file format is not as expected.""" |
941 | - |
942 | - |
943 | -class RequiredValueMissing(Exception): |
944 | - """Used if a required value was not provided.""" |
945 | - |
946 | - |
947 | -class EntitlementExchange: |
948 | - """Define the exchange format for entitlement data. |
949 | - |
950 | - Writers of entitlement data should use the 'writerFactory' method to |
951 | - obtain a writer object. Readers should use the 'readerFactory'. They |
952 | - return a UnicodeDictWriter and UnicodeDictReader respectively. |
953 | - |
954 | - Any changes to the list of fieldnames or their order will require an |
955 | - increment in the version value. |
956 | - """ |
957 | - |
958 | - file_header = "%s Entitlement exchange format version" % COMMENT |
959 | - version = 1 |
960 | - version_re = re.compile( |
961 | - "^%s (\d+)" % file_header) |
962 | - |
963 | - fieldnames = [ |
964 | - 'id', 'ext_id', 'person_name', 'entitlement_type', 'quota', |
965 | - 'amount_used', 'date_starts', 'date_expires', 'date_created', |
966 | - 'registrant_name', 'approved_by_name', 'state', 'whiteboard', |
967 | - ] |
968 | - |
969 | - @staticmethod |
970 | - def _preprocessData(in_file): |
971 | - """Verify the version and remove comments.""" |
972 | - version_line = in_file.readline() |
973 | - match = EntitlementExchange.version_re.search(version_line) |
974 | - if not match: |
975 | - raise InvalidFormat( |
976 | - "The first line does not have valid version information.") |
977 | - read_version = int(match.group(1)) |
978 | - if EntitlementExchange.version != read_version: |
979 | - raise UnsupportedVersion( |
980 | - "Version %d of the file format is not supported." % |
981 | - read_version) |
982 | - lines= [line for line in in_file.readlines() |
983 | - if not line.lstrip().startswith(COMMENT)] |
984 | - return "".join(lines) |
985 | - |
986 | - @staticmethod |
987 | - def readerFactory(in_file): |
988 | - """Return a properly provisioned reader. |
989 | - |
990 | - Assumes data in the file is UTF-8 encoded. |
991 | - """ |
992 | - |
993 | - filedata = EntitlementExchange._preprocessData(in_file) |
994 | - return UnicodeDictReader(cStringIO.StringIO(filedata), |
995 | - EntitlementExchange.fieldnames, |
996 | - skipinitialspace=True, |
997 | - quoting=csv.QUOTE_ALL) |
998 | - |
999 | - @staticmethod |
1000 | - def writerFactory(filedescriptor): |
1001 | - """Return a properly provisioned writer. |
1002 | - |
1003 | - Data in the file will be UTF-8 encoded. |
1004 | - """ |
1005 | - |
1006 | - filedescriptor.write( |
1007 | - "%s %d\n" % (EntitlementExchange.file_header, |
1008 | - EntitlementExchange.version)) |
1009 | - filedescriptor.write( |
1010 | - "%s %s\n" % (COMMENT, |
1011 | - COMMA.join(EntitlementExchange.fieldnames))) |
1012 | - writer = UnicodeDictWriter(filedescriptor, |
1013 | - EntitlementExchange.fieldnames, |
1014 | - skipinitialspace=True, |
1015 | - quoting=csv.QUOTE_ALL) |
1016 | - return writer |
1017 | - |
1018 | - |
1019 | -class EntitlementImporter: |
1020 | - """Class for writing and updating entitlement data. |
1021 | - |
1022 | - Methods create_entitlements and update_entitlements are called with a list |
1023 | - of dictionaries representing entitlement data. |
1024 | - """ |
1025 | - def __init__(self, logger): |
1026 | - self.logger = logger |
1027 | - |
1028 | - def _replacePersonName(self, entitlement, old_key, new_key, |
1029 | - row_no, required=False): |
1030 | - """Replace a person's name with a Person object in the entitlement. |
1031 | - |
1032 | - Raise RequiredValueMissing if the old_key is not found in the |
1033 | - entitlement dictionary and required is True. |
1034 | - Raise NotFoundError if no matching person can be found. |
1035 | - """ |
1036 | - person_name = entitlement.get(old_key, '') |
1037 | - del entitlement[old_key] |
1038 | - if person_name == '': |
1039 | - if required: |
1040 | - raise RequiredValueMissing( |
1041 | - "'person_name' not supplied in row %d." % row_no) |
1042 | - else: |
1043 | - return entitlement |
1044 | - person = getUtility(IPersonSet).getByName(person_name) |
1045 | - if person is None: |
1046 | - self.logger.error( |
1047 | - "[E%d] Person '%s' is not found." % ( |
1048 | - row_no, person_name)) |
1049 | - raise NotFoundError( |
1050 | - "Person '%s' not supplied in row %d." % ( |
1051 | - person_name, row_no)) |
1052 | - entitlement[new_key] = person |
1053 | - return entitlement |
1054 | - |
1055 | - def _normalizeEntitlement( |
1056 | - self, entitlement, row_no, person_required=True): |
1057 | - """Normalize a dictionary representing an entitlement. |
1058 | - |
1059 | - Convert names of people and teams to database objects and |
1060 | - convert string representations of numerics to the correct type. |
1061 | - Remove any keys in the dictionary that do not correspond to attributes |
1062 | - on an Entitlement. |
1063 | - """ |
1064 | - entitlement = self._replacePersonName( |
1065 | - entitlement, 'person_name', 'person', row_no, person_required) |
1066 | - entitlement = self._replacePersonName( |
1067 | - entitlement, 'registrant_name', 'registrant', row_no) |
1068 | - entitlement = self._replacePersonName( |
1069 | - entitlement, 'approved_by_name', 'approved_by', row_no) |
1070 | - |
1071 | - # Remove the 'ext_id' since it is not part of the Launchpad data. |
1072 | - del entitlement['ext_id'] |
1073 | - |
1074 | - # Convert numeric data from string to int. |
1075 | - for field in ['id', 'quota', 'entitlement_type', 'state', 'amount_used']: |
1076 | - if entitlement[field]: |
1077 | - entitlement[field] = int(entitlement[field]) |
1078 | - |
1079 | - # Convert strings to dates. |
1080 | - for field in ['date_starts', 'date_expires', 'date_created']: |
1081 | - if entitlement[field]: |
1082 | - date_string = entitlement[field] |
1083 | - if len(date_string) == len('YYYY-mm-dd'): |
1084 | - year, month, day, hour, minute, second = time.strptime( |
1085 | - date_string, '%Y-%m-%d')[:6] |
1086 | - elif len(date_string) == len('YYYY-mm-dd HH:MM:SS'): |
1087 | - year, month, day, hour, minute, second = time.strptime( |
1088 | - date_string, '%Y-%m-%d %H:%M:%S')[:6] |
1089 | - else: |
1090 | - raise AssertionError( |
1091 | - 'Unknown date format: %s' % date_string) |
1092 | - entitlement[field] = datetime.datetime( |
1093 | - year, month, day, hour, minute, second, |
1094 | - tzinfo=pytz.timezone('UTC')) |
1095 | - |
1096 | - # Convert the entitlement_type and state to the corresponding |
1097 | - # database objects. |
1098 | - if entitlement['entitlement_type']: |
1099 | - entitlement_type = entitlement['entitlement_type'] |
1100 | - entitlement['entitlement_type'] = ( |
1101 | - EntitlementType.items.mapping[entitlement_type]) |
1102 | - |
1103 | - if entitlement['state']: |
1104 | - state = entitlement['state'] |
1105 | - entitlement['state'] = ( |
1106 | - EntitlementState.items.mapping[state]) |
1107 | - |
1108 | - # Remove the entries from the dictionary that only have placeholder |
1109 | - # data. |
1110 | - for key, value in entitlement.items(): |
1111 | - if value == '': |
1112 | - del entitlement[key] |
1113 | - return entitlement |
1114 | - |
1115 | - def _checkRequired(self, entitlement, required, row_no): |
1116 | - """Check to see that all required keys are in the entitlement.""" |
1117 | - for key in required: |
1118 | - val = entitlement.get(key, '') |
1119 | - # Test for None or ''. No boolean variable are expected. |
1120 | - if val == '': |
1121 | - self.logger.error( |
1122 | - "[E%d] A required key is missing: %s." % (row_no, key)) |
1123 | - return False |
1124 | - return True |
1125 | - |
1126 | - def createEntitlements(self, entitlements): |
1127 | - """Create a new entitlement for each in the list. |
1128 | - |
1129 | - Return a list of sparsely populated dictionaries suitable for writing |
1130 | - as a return CSV file. |
1131 | - """ |
1132 | - |
1133 | - required = ['ext_id', 'person_name', 'quota', 'entitlement_type', |
1134 | - 'state'] |
1135 | - entitlement_set = getUtility(IEntitlementSet) |
1136 | - new_entitlements = [] |
1137 | - for row_no, entitlement in enumerate(entitlements): |
1138 | - if self._checkRequired(entitlement, required, row_no) is False: |
1139 | - continue |
1140 | - ext_id = entitlement.get('ext_id') |
1141 | - try: |
1142 | - normalized_entitlement = self._normalizeEntitlement( |
1143 | - entitlement, row_no) |
1144 | - except NotFoundError: |
1145 | - continue |
1146 | - except RequiredValueMissing: |
1147 | - continue |
1148 | - |
1149 | - new_entitlement = entitlement_set.new(**normalized_entitlement) |
1150 | - |
1151 | - if new_entitlement is not None: |
1152 | - # Add a dictionary with id and ext_id to the list of |
1153 | - # new entitlements. |
1154 | - new_entitlements.append(dict(id=str(new_entitlement.id), |
1155 | - ext_id=ext_id)) |
1156 | - return new_entitlements |
1157 | - |
1158 | - def updateEntitlements(self, entitlements): |
1159 | - """Update an existing entitlement. |
1160 | - |
1161 | - The entitlement must already exist. A list of dictionaries with the |
1162 | - ids of the entitlments that were modified is returned. |
1163 | - """ |
1164 | - |
1165 | - modified = [] |
1166 | - required = ['id'] |
1167 | - for row_no, upd_entitlement in enumerate(entitlements): |
1168 | - if not self._checkRequired(upd_entitlement, required, row_no): |
1169 | - continue |
1170 | - # The ext_id must be cached before the data is normalized. |
1171 | - ext_id = upd_entitlement.get('ext_id') |
1172 | - |
1173 | - try: |
1174 | - norm_entitlement = self._normalizeEntitlement( |
1175 | - upd_entitlement, row_no, person_required=False) |
1176 | - except NotFoundError: |
1177 | - continue |
1178 | - except RequiredValueMissing: |
1179 | - continue |
1180 | - |
1181 | - lp_id = norm_entitlement.get('id') |
1182 | - entitlement_set = getUtility(IEntitlementSet) |
1183 | - |
1184 | - existing = entitlement_set.get(lp_id) |
1185 | - if existing is None: |
1186 | - self.logger.error( |
1187 | - "[E%d] Invalid entitlement id: %d" % (row_no, |
1188 | - lp_id)) |
1189 | - continue |
1190 | - |
1191 | - succeeded = True |
1192 | - for (key, val) in norm_entitlement.items(): |
1193 | - if key == 'id': |
1194 | - pass |
1195 | - elif key == 'person': |
1196 | - self.logger.info( |
1197 | - "[E%d] You may not change the person for the " |
1198 | - "entitlement." % (row_no)) |
1199 | - succeeded = False |
1200 | - break |
1201 | - elif key == 'whiteboard': |
1202 | - # Append the whiteboard value rather than replacing it. |
1203 | - existing.whiteboard = "%s\n%s" % (existing.whiteboard, |
1204 | - val) |
1205 | - elif key in ['entitlement_type', 'quota', 'amount_used', |
1206 | - 'date_starts', 'date_expires', 'date_created', |
1207 | - 'registrant', 'approved_by', 'state']: |
1208 | - setattr(existing, key, val) |
1209 | - else: |
1210 | - self.logger.error( |
1211 | - "[E%d] Unrecognized key: %s." % (row_no, key)) |
1212 | - succeeded = False |
1213 | - break |
1214 | - if succeeded: |
1215 | - modified.append(dict(id=str(lp_id))) |
1216 | - return modified |
1217 | |
1218 | === removed file 'lib/lp/registry/tests/test_entitlementimport.py' |
1219 | --- lib/lp/registry/tests/test_entitlementimport.py 2012-01-01 02:58:52 +0000 |
1220 | +++ lib/lp/registry/tests/test_entitlementimport.py 1970-01-01 00:00:00 +0000 |
1221 | @@ -1,184 +0,0 @@ |
1222 | -# Copyright 2009 Canonical Ltd. This software is licensed under the |
1223 | -# GNU Affero General Public License version 3 (see the file LICENSE). |
1224 | - |
1225 | -"""Test EntitlementExchange and EntitlementImporter.""" |
1226 | - |
1227 | -__metaclass__ = type |
1228 | - |
1229 | -from cStringIO import StringIO |
1230 | -import logging |
1231 | -import unittest |
1232 | - |
1233 | -from zope.testing.loghandler import Handler |
1234 | - |
1235 | -from lp.registry.scripts.entitlement import ( |
1236 | - EntitlementExchange, |
1237 | - EntitlementImporter, |
1238 | - InvalidFormat, |
1239 | - UnsupportedVersion, |
1240 | - ) |
1241 | -from lp.testing.layers import LaunchpadZopelessLayer |
1242 | - |
1243 | - |
1244 | -class EntitlementExchangeTestCase(unittest.TestCase): |
1245 | - """Test EntitlementExchange methods.""" |
1246 | - layer = LaunchpadZopelessLayer |
1247 | - |
1248 | - def test_preprocessData(self): |
1249 | - """The preprocessor verifies the header and removes comments.""" |
1250 | - # Wrong header |
1251 | - data = ("# bad format data\n" |
1252 | - "more data") |
1253 | - in_file = StringIO(data) |
1254 | - self.assertRaises( |
1255 | - InvalidFormat, EntitlementExchange._preprocessData, in_file) |
1256 | - |
1257 | - # Invalid version |
1258 | - data = ("# Entitlement exchange format version 0\n" |
1259 | - "more data") |
1260 | - in_file = StringIO(data) |
1261 | - self.assertRaises( |
1262 | - UnsupportedVersion, EntitlementExchange._preprocessData, in_file) |
1263 | - |
1264 | - # Only one line should remain after processing |
1265 | - data = ("# Entitlement exchange format version 1\n" |
1266 | - "# comment line\n" |
1267 | - " # another comment\n" |
1268 | - "'name', 'date' #valid line") |
1269 | - in_file = StringIO(data) |
1270 | - processed = EntitlementExchange._preprocessData(in_file) |
1271 | - self.assertEqual(len(processed.split('\n')), 1) |
1272 | - |
1273 | -class EntitlementImporterTestCase(unittest.TestCase): |
1274 | - """Test EntitlementImport methods.""" |
1275 | - layer = LaunchpadZopelessLayer |
1276 | - |
1277 | - def setUp(self): |
1278 | - """Setup the test environment and retrieve useful instances.""" |
1279 | - self.log = logging.getLogger("test_entitlement") |
1280 | - self.log.setLevel(logging.INFO) |
1281 | - self.handler = Handler(self) |
1282 | - self.handler.add(self.log.name) |
1283 | - |
1284 | - def tearDown(self): |
1285 | - """Teardown the test environment.""" |
1286 | - self.layer.txn.commit() |
1287 | - self.handler.close() |
1288 | - |
1289 | - def _getImporterAndReader(self, data): |
1290 | - in_file = StringIO(data) |
1291 | - importer = EntitlementImporter(self.log) |
1292 | - return importer, EntitlementExchange.readerFactory(in_file) |
1293 | - |
1294 | - def _testCreate(self, data): |
1295 | - importer, reader = self._getImporterAndReader(data) |
1296 | - return importer.createEntitlements(reader) |
1297 | - |
1298 | - def _testUpdate(self, data): |
1299 | - importer, reader = self._getImporterAndReader(data) |
1300 | - return importer.updateEntitlements(reader) |
1301 | - |
1302 | - def test_manipulateEntitlement(self): |
1303 | - """Test creating and updating entitlements.""" |
1304 | - |
1305 | - def test_wrongVersion(self): |
1306 | - """Wrong version.""" |
1307 | - data = r"""# Entitlement exchange format version 0""" |
1308 | - in_file = StringIO(data) |
1309 | - importer = EntitlementImporter(self.log) |
1310 | - self.assertRaises(UnsupportedVersion, |
1311 | - EntitlementExchange.readerFactory, |
1312 | - in_file) |
1313 | - |
1314 | - def test_successfulInsert(self): |
1315 | - """Successfully insert an entitlement.""" |
1316 | - data = ("# Entitlement exchange format version 1\n" |
1317 | - ",A-100, lifeless, 10, 100, 0, 2007-06-14, , , " |
1318 | - "keybuk, keybuk, 20,") |
1319 | - results = self._testCreate(data) |
1320 | - self.assertEqual(len(results), 1) |
1321 | - |
1322 | - def test_insertUsingNonExistentPerson(self): |
1323 | - """Attempt to insert using a non-existent person.""" |
1324 | - data = ("# Entitlement exchange format version 1\n" |
1325 | - ",A-100, persona_non_grata, 10, 100, 0, 2007-06-14, , , " |
1326 | - "keybuk, keybuk, 20,") |
1327 | - results = self._testCreate(data) |
1328 | - self.handler.assertLogsMessage( |
1329 | - "[E0] Person 'persona_non_grata' " |
1330 | - "is not found.", level=logging.ERROR) |
1331 | - |
1332 | - def test_omitQuota(self): |
1333 | - """Omit the quota and get an error.""" |
1334 | - data = ("# Entitlement exchange format version 1\n" |
1335 | - ",A-100, lifeless, 10, , 0, 2007-06-14, , , " |
1336 | - "keybuk, keybuk, 20,") |
1337 | - results = self._testCreate(data) |
1338 | - self.handler.assertLogsMessage( |
1339 | - "[E0] A required key is missing: quota.", |
1340 | - level=logging.ERROR) |
1341 | - |
1342 | - def test_omitPerson(self): |
1343 | - """Omit the person and get an error.""" |
1344 | - data = ("# Entitlement exchange format version 1\n" |
1345 | - ",A-100, , 10, 100, 0, 2007-06-14, , , " |
1346 | - "keybuk, keybuk, 20,") |
1347 | - results = self._testCreate(data) |
1348 | - self.handler.assertLogsMessage( |
1349 | - "[E0] A required key is missing: person_name.", |
1350 | - level=logging.ERROR) |
1351 | - |
1352 | - def test_omitExtId(self): |
1353 | - """Omit the ext_id and get an error.""" |
1354 | - data = ("# Entitlement exchange format version 1\n" |
1355 | - ",, lifeless, 10, 100, 0, 2007-06-14, , , " |
1356 | - "keybuk, keybuk, 20,") |
1357 | - results = self._testCreate(data) |
1358 | - self.handler.assertLogsMessage( |
1359 | - "[E0] A required key is missing: ext_id.", |
1360 | - level=logging.ERROR) |
1361 | - |
1362 | - def test_omitEntitlementType(self): |
1363 | - """Omit the entitlement_type and get an error.""" |
1364 | - data = ("# Entitlement exchange format version 1\n" |
1365 | - ",A-100, lifeless, , 100, 0, 2007-06-14, , , " |
1366 | - "keybuk, keybuk, 20,") |
1367 | - results = self._testCreate(data) |
1368 | - self.handler.assertLogsMessage( |
1369 | - "[E0] A required key is missing: entitlement_type.", |
1370 | - level=logging.ERROR) |
1371 | - |
1372 | - def test_omitState(self): |
1373 | - """Omit the state and get an error.""" |
1374 | - data = ("# Entitlement exchange format version 1\n" |
1375 | - ",A-100, lifeless, 10, 100, 0, 2007-06-14, , , " |
1376 | - "keybuk, keybuk, ,") |
1377 | - results = self._testCreate(data) |
1378 | - self.handler.assertLogsMessage( |
1379 | - "[E0] A required key is missing: state.", |
1380 | - level=logging.ERROR) |
1381 | - |
1382 | - def test_updateWithInvalidId(self): |
1383 | - """Update using an invalid entitlement id.""" |
1384 | - data = ("# Entitlement exchange format version 1\n" |
1385 | - "9999,A-100,kiko, ,1500, , , , , , , , ") |
1386 | - results = self._testUpdate(data) |
1387 | - self.handler.assertLogsMessage( |
1388 | - "[E0] Invalid entitlement id: 9999", |
1389 | - level=logging.ERROR) |
1390 | - |
1391 | - def test_updateChangingPerson(self): |
1392 | - """Changing the person, which isn't allowed.""" |
1393 | - data = ("# Entitlement exchange format version 1\n" |
1394 | - ",A-100, lifeless, 10, 100, 0, 2007-06-14, , , " |
1395 | - "keybuk, keybuk, 20,") |
1396 | - results = self._testCreate(data) |
1397 | - self.assertEqual(len(results), 1) |
1398 | - valid_id = results[0].get('id') |
1399 | - data = ("# Entitlement exchange format version 1\n" |
1400 | - " %s, A-100, kiko, , 1500, , , , , , , , " % |
1401 | - valid_id) |
1402 | - results = self._testUpdate(data) |
1403 | - self.handler.assertLogsMessage( |
1404 | - "[E0] You may not change the person for the entitlement.", |
1405 | - level=logging.INFO) |
1406 | |
1407 | === modified file 'lib/lp/security.py' |
1408 | --- lib/lp/security.py 2012-02-08 05:07:20 +0000 |
1409 | +++ lib/lp/security.py 2012-02-14 00:28:18 +0000 |
1410 | @@ -103,7 +103,6 @@ |
1411 | IDistroSeriesDifferenceEdit, |
1412 | ) |
1413 | from lp.registry.interfaces.distroseriesparent import IDistroSeriesParent |
1414 | -from lp.registry.interfaces.entitlement import IEntitlement |
1415 | from lp.registry.interfaces.gpg import IGPGKey |
1416 | from lp.registry.interfaces.irc import IIrcID |
1417 | from lp.registry.interfaces.location import IPersonLocation |
1418 | @@ -2249,26 +2248,6 @@ |
1419 | user.inTeam(self.obj.target_branch.reviewer)) |
1420 | |
1421 | |
1422 | -class ViewEntitlement(AuthorizationBase): |
1423 | - """Permissions to view IEntitlement objects. |
1424 | - |
1425 | - Allow the owner of the entitlement, the entitlement registrant, |
1426 | - or any member of the team or any admin to view the entitlement. |
1427 | - """ |
1428 | - permission = 'launchpad.View' |
1429 | - usedfor = IEntitlement |
1430 | - |
1431 | - def checkAuthenticated(self, user): |
1432 | - """Is the user able to view an Entitlement attribute? |
1433 | - |
1434 | - Any team member can edit a branch subscription for their team. |
1435 | - Launchpad Admins can also edit any branch subscription. |
1436 | - """ |
1437 | - return (user.inTeam(self.obj.person) or |
1438 | - user.inTeam(self.obj.registrant) or |
1439 | - user.in_admin) |
1440 | - |
1441 | - |
1442 | class AdminDistroSeriesLanguagePacks( |
1443 | OnlyRosettaExpertsAndAdmins, |
1444 | EditDistroSeriesByReleaseManagerOrDistroOwnersOrAdmins): |
1445 | |
1446 | === removed file 'scripts/entitlements-to-lp.py' |
1447 | --- scripts/entitlements-to-lp.py 2012-01-01 03:13:08 +0000 |
1448 | +++ scripts/entitlements-to-lp.py 1970-01-01 00:00:00 +0000 |
1449 | @@ -1,83 +0,0 @@ |
1450 | -#!/usr/bin/python -S |
1451 | -# |
1452 | -# Copyright 2009 Canonical Ltd. This software is licensed under the |
1453 | -# GNU Affero General Public License version 3 (see the file LICENSE). |
1454 | - |
1455 | -# pylint: disable-msg=W0403 |
1456 | -import _pythonpath |
1457 | - |
1458 | -import logging |
1459 | -import sys |
1460 | - |
1461 | -from lp.registry.scripts.entitlement import ( |
1462 | - EntitlementExchange, |
1463 | - EntitlementImporter, |
1464 | - ) |
1465 | -from lp.services.scripts.base import LaunchpadScript |
1466 | - |
1467 | - |
1468 | -class ImportEntitlementsScript(LaunchpadScript): |
1469 | - """Script for to import entitlement data into Launchpad.""" |
1470 | - |
1471 | - description = "Create or update entitlements." |
1472 | - usage = ("usage: %s [-c|--create | -u|--update] file_name" % |
1473 | - sys.argv[0]) |
1474 | - |
1475 | - loglevel = logging.INFO |
1476 | - |
1477 | - def add_my_options(self): |
1478 | - """See `LaunchpadScript`.""" |
1479 | - self.parser.add_option( |
1480 | - '-c', '--create', action='store_const', const='create', |
1481 | - help='Create new entitlements', dest='action') |
1482 | - self.parser.add_option( |
1483 | - '-u', '--update', action='store_const', const='update', |
1484 | - help='Update existing entitlements', dest='action') |
1485 | - self.parser.add_option( |
1486 | - '-f', '--infile', action='store', default='-', |
1487 | - help='Input file name ("-" for stdin)', dest='in_file_name') |
1488 | - self.parser.add_option( |
1489 | - '-o', '--outfile', action='store', default='-', |
1490 | - help='Output file name ("-" for stdout)', dest='out_file_name') |
1491 | - |
1492 | - def main(self): |
1493 | - """See `LaunchpadScript`.""" |
1494 | - |
1495 | - action = self.options.action |
1496 | - |
1497 | - if self.options.in_file_name == '-': |
1498 | - in_file = sys.stdin |
1499 | - else: |
1500 | - in_file = open(self.options.in_file_name, "rb") |
1501 | - |
1502 | - if self.options.out_file_name == '-': |
1503 | - out_file = sys.stdout |
1504 | - else: |
1505 | - out_file = open(self.options.out_file_name, "wb") |
1506 | - |
1507 | - # get a reader and writer |
1508 | - reader = EntitlementExchange.readerFactory(in_file) |
1509 | - entitlement_writer = EntitlementImporter(self.logger) |
1510 | - importer = EntitlementImporter(self.logger) |
1511 | - if action == 'create': |
1512 | - out_data = importer.createEntitlements(reader) |
1513 | - elif action == 'update': |
1514 | - out_data = importer.updateEntitlements(reader) |
1515 | - elif action is None: |
1516 | - self.logger.error("No action specified. Use either -c or -u.") |
1517 | - return 1 |
1518 | - else: |
1519 | - self.logger.error("Invalid action: %s\n" % action) |
1520 | - return 1 |
1521 | - |
1522 | - self.txn.commit() |
1523 | - |
1524 | - if out_data: |
1525 | - writer = EntitlementExchange.writerFactory(out_file) |
1526 | - writer.writerows(out_data) |
1527 | - return 0 |
1528 | - |
1529 | -if __name__ == '__main__': |
1530 | - script = ImportEntitlementsScript( |
1531 | - 'lp.services.scripts.entitlements') |
1532 | - script.run() |