Merge ~cjwatson/launchpad:stormify-person into launchpad:master
- Git
- lp:~cjwatson/launchpad
- stormify-person
- Merge into master
Proposed by
Colin Watson
Status: | Merged |
---|---|
Approved by: | Colin Watson |
Approved revision: | 589e261816f7bd622560f2293397fd1f2f2fddd3 |
Merge reported by: | Otto Co-Pilot |
Merged at revision: | not available |
Proposed branch: | ~cjwatson/launchpad:stormify-person |
Merge into: | launchpad:master |
Diff against target: |
976 lines (+188/-171) 14 files modified
lib/lp/bugs/model/bugtasksearch.py (+32/-36) lib/lp/registry/browser/person.py (+1/-1) lib/lp/registry/doc/person-merge.rst (+4/-0) lib/lp/registry/doc/vocabularies.rst (+0/-1) lib/lp/registry/interfaces/person.py (+5/-5) lib/lp/registry/model/distroseries.py (+1/-1) lib/lp/registry/model/person.py (+124/-103) lib/lp/registry/personmerge.py (+1/-2) lib/lp/registry/scripts/closeaccount.py (+1/-1) lib/lp/registry/vocabularies.py (+14/-16) lib/lp/translations/model/poexportrequest.py (+1/-1) lib/lp/translations/model/pofile.py (+1/-1) lib/lp/translations/model/translationgroup.py (+1/-1) lib/lp/translations/scripts/remove_translations.py (+2/-2) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Simone Pelosi | Approve | ||
Review via email: mp+451654@code.launchpad.net |
Commit message
Convert Person to Storm
Description of the change
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/lib/lp/bugs/model/bugtasksearch.py b/lib/lp/bugs/model/bugtasksearch.py |
2 | index 8de8c55..08fe8dc 100644 |
3 | --- a/lib/lp/bugs/model/bugtasksearch.py |
4 | +++ b/lib/lp/bugs/model/bugtasksearch.py |
5 | @@ -29,8 +29,9 @@ from storm.expr import ( |
6 | Row, |
7 | Select, |
8 | Union, |
9 | + With, |
10 | ) |
11 | -from storm.info import ClassAlias |
12 | +from storm.info import ClassAlias, get_cls_info |
13 | from storm.references import Reference |
14 | from zope.component import getUtility |
15 | from zope.security.proxy import isinstance as zope_isinstance |
16 | @@ -77,10 +78,6 @@ from lp.registry.model.teammembership import TeamParticipation |
17 | from lp.services.database.bulk import load |
18 | from lp.services.database.decoratedresultset import DecoratedResultSet |
19 | from lp.services.database.interfaces import IStore |
20 | -from lp.services.database.sqlbase import ( |
21 | - convert_storm_clause_to_string, |
22 | - sqlvalues, |
23 | -) |
24 | from lp.services.database.stormexpr import ( |
25 | ArrayAgg, |
26 | ArrayIntersects, |
27 | @@ -224,10 +221,10 @@ def search_bugs(pre_iter_hook, alternatives, just_bug_ids=False): |
28 | clauseTables, |
29 | bugtask_decorator, |
30 | join_tables, |
31 | - with_clause, |
32 | + with_clauses, |
33 | ] = _build_query(alternatives[0]) |
34 | - if with_clause: |
35 | - store = store.with_(with_clause) |
36 | + if with_clauses: |
37 | + store = store.with_(with_clauses) |
38 | decorators.append(bugtask_decorator) |
39 | origin = _build_origin( |
40 | join_tables + orderby_joins, clauseTables, start |
41 | @@ -242,12 +239,12 @@ def search_bugs(pre_iter_hook, alternatives, just_bug_ids=False): |
42 | clauseTables, |
43 | decorator, |
44 | join_tables, |
45 | - with_clause, |
46 | + with_clauses, |
47 | ] = _build_query(params) |
48 | origin = _build_origin(join_tables, clauseTables, start) |
49 | localstore = store |
50 | - if with_clause: |
51 | - localstore = store.with_(with_clause) |
52 | + if with_clauses: |
53 | + localstore = store.with_(with_clauses) |
54 | next_result = localstore.using(*origin).find(BugTaskFlat, query) |
55 | results.append(next_result) |
56 | # NB: assumes the decorators are all compatible. |
57 | @@ -492,9 +489,16 @@ def _build_query(params): |
58 | |
59 | if params.structural_subscriber is not None: |
60 | with_clauses.append( |
61 | - """ss as (SELECT * from StructuralSubscription |
62 | - WHERE StructuralSubscription.subscriber = %s)""" |
63 | - % sqlvalues(params.structural_subscriber) |
64 | + With( |
65 | + "ss", |
66 | + Select( |
67 | + get_cls_info(StructuralSubscription).columns, |
68 | + where=( |
69 | + StructuralSubscription.subscriber |
70 | + == params.structural_subscriber |
71 | + ), |
72 | + ), |
73 | + ) |
74 | ) |
75 | |
76 | class StructuralSubscriptionCTE(StructuralSubscription): |
77 | @@ -761,27 +765,23 @@ def _build_query(params): |
78 | ) |
79 | store = IStore(Bug) |
80 | with_clauses.append( |
81 | - convert_storm_clause_to_string( |
82 | - WithMaterialized( |
83 | - "commented_bug_ids", |
84 | - store, |
85 | - Union(commented_messages, commented_activities), |
86 | - ) |
87 | + WithMaterialized( |
88 | + "commented_bug_ids", |
89 | + store, |
90 | + Union(commented_messages, commented_activities), |
91 | ) |
92 | ) |
93 | with_clauses.append( |
94 | - convert_storm_clause_to_string( |
95 | - WithMaterialized( |
96 | - "commented_bugtask_ids", |
97 | - store, |
98 | - Select( |
99 | - BugTaskFlat.bugtask_id, |
100 | - tables=[BugTaskFlat], |
101 | - where=BugTaskFlat.bug_id.is_in( |
102 | - Select(Column("bug", "commented_bug_ids")) |
103 | - ), |
104 | + WithMaterialized( |
105 | + "commented_bugtask_ids", |
106 | + store, |
107 | + Select( |
108 | + BugTaskFlat.bugtask_id, |
109 | + tables=[BugTaskFlat], |
110 | + where=BugTaskFlat.bug_id.is_in( |
111 | + Select(Column("bug", "commented_bug_ids")) |
112 | ), |
113 | - ) |
114 | + ), |
115 | ) |
116 | ) |
117 | extra_clauses.append( |
118 | @@ -921,11 +921,7 @@ def _build_query(params): |
119 | obj = decor(obj) |
120 | return obj |
121 | |
122 | - if with_clauses: |
123 | - with_clause = SQL(", ".join(with_clauses)) |
124 | - else: |
125 | - with_clause = None |
126 | - return (query, clauseTables, decorator, join_tables, with_clause) |
127 | + return (query, clauseTables, decorator, join_tables, with_clauses) |
128 | |
129 | |
130 | def _process_order_by(params): |
131 | diff --git a/lib/lp/registry/browser/person.py b/lib/lp/registry/browser/person.py |
132 | index d14004b..4b4e920 100644 |
133 | --- a/lib/lp/registry/browser/person.py |
134 | +++ b/lib/lp/registry/browser/person.py |
135 | @@ -2099,7 +2099,7 @@ class PersonParticipationView(LaunchpadView): |
136 | # The member is a direct member; use the membership data. |
137 | datejoined = membership.datejoined |
138 | dateexpires = membership.dateexpires |
139 | - if membership.person_id == team.teamownerID: |
140 | + if membership.person_id == team.teamowner_id: |
141 | role = "Owner" |
142 | elif membership.status == TeamMembershipStatus.ADMIN: |
143 | role = "Admin" |
144 | diff --git a/lib/lp/registry/doc/person-merge.rst b/lib/lp/registry/doc/person-merge.rst |
145 | index f50b89c..65d98e4 100644 |
146 | --- a/lib/lp/registry/doc/person-merge.rst |
147 | +++ b/lib/lp/registry/doc/person-merge.rst |
148 | @@ -278,6 +278,7 @@ create, and then delete, the needed two people. |
149 | |
150 | >>> from lp.registry.model.person import PersonSet, Person |
151 | >>> from lp.registry.interfaces.person import PersonCreationRationale |
152 | + >>> from lp.services.database.interfaces import IStore |
153 | >>> personset = PersonSet() |
154 | |
155 | >>> skip = [] |
156 | @@ -312,11 +313,14 @@ create, and then delete, the needed two people. |
157 | ... display_name="Merge Winner", |
158 | ... creation_rationale=lp, |
159 | ... ) |
160 | + ... IStore(Person).add(winner) |
161 | ... loser = Person( |
162 | ... name=name + ".loser", |
163 | ... display_name="Merge Loser", |
164 | ... creation_rationale=lp, |
165 | ... ) |
166 | + ... IStore(Person).add(loser) |
167 | + ... IStore(Person).flush() |
168 | ... yield winner, loser |
169 | ... |
170 | >>> endless_supply_of_players = new_players() |
171 | diff --git a/lib/lp/registry/doc/vocabularies.rst b/lib/lp/registry/doc/vocabularies.rst |
172 | index ad6bf4b..ab7f5c5 100644 |
173 | --- a/lib/lp/registry/doc/vocabularies.rst |
174 | +++ b/lib/lp/registry/doc/vocabularies.rst |
175 | @@ -611,7 +611,6 @@ Any person that's already merged is not part of this vocabulary: |
176 | |
177 | >>> naked_cprov = removeSecurityProxy(cprov) |
178 | >>> naked_cprov.merged = 1 |
179 | - >>> naked_cprov.syncUpdate() |
180 | >>> cprov in vocab |
181 | False |
182 | |
183 | diff --git a/lib/lp/registry/interfaces/person.py b/lib/lp/registry/interfaces/person.py |
184 | index fe77d12..4e66cfd 100644 |
185 | --- a/lib/lp/registry/interfaces/person.py |
186 | +++ b/lib/lp/registry/interfaces/person.py |
187 | @@ -220,7 +220,7 @@ def validate_membership_policy(obj, attr, value): |
188 | return None |
189 | |
190 | # If we are just creating a new team, it can have any membership policy. |
191 | - if getattr(obj, "_SO_creating", True): |
192 | + if getattr(obj, "_creating", True): |
193 | return value |
194 | |
195 | team = obj |
196 | @@ -792,7 +792,7 @@ class IPersonLimitedView(IHasIcon, IHasLogo): |
197 | "in listings of bugs or on a person's membership table." |
198 | ), |
199 | ) |
200 | - iconID = Int(title=_("Icon ID"), required=True, readonly=True) |
201 | + icon_id = Int(title=_("Icon ID"), required=True, readonly=True) |
202 | logo = exported( |
203 | LogoImageUpload( |
204 | title=_("Logo"), |
205 | @@ -806,7 +806,7 @@ class IPersonLimitedView(IHasIcon, IHasLogo): |
206 | ), |
207 | ) |
208 | ) |
209 | - logoID = Int(title=_("Logo ID"), required=True, readonly=True) |
210 | + logo_id = Int(title=_("Logo ID"), required=True, readonly=True) |
211 | # title is required for the Launchpad Page Layout main template |
212 | title = Attribute("Person Page Title") |
213 | is_probationary = exported( |
214 | @@ -890,7 +890,7 @@ class IPersonViewRestricted( |
215 | ), |
216 | ) |
217 | ) |
218 | - mugshotID = Int(title=_("Mugshot ID"), required=True, readonly=True) |
219 | + mugshot_id = Int(title=_("Mugshot ID"), required=True, readonly=True) |
220 | |
221 | languages = exported( |
222 | CollectionField( |
223 | @@ -1111,7 +1111,7 @@ class IPersonViewRestricted( |
224 | ), |
225 | exported_as="team_owner", |
226 | ) |
227 | - teamownerID = Int( |
228 | + teamowner_id = Int( |
229 | title=_("The Team Owner's ID or None"), required=False, readonly=True |
230 | ) |
231 | preferredemail = exported( |
232 | diff --git a/lib/lp/registry/model/distroseries.py b/lib/lp/registry/model/distroseries.py |
233 | index 22d2895..4119824 100644 |
234 | --- a/lib/lp/registry/model/distroseries.py |
235 | +++ b/lib/lp/registry/model/distroseries.py |
236 | @@ -1580,7 +1580,7 @@ class DistroSeries( |
237 | POTemplate.distroseries == self, |
238 | POTemplate.iscurrent == True, |
239 | ) |
240 | - contributors = contributors.order_by(*Person._storm_sortingColumns) |
241 | + contributors = contributors.order_by(*Person._sortingColumns) |
242 | contributors = contributors.config(distinct=True) |
243 | return contributors |
244 | |
245 | diff --git a/lib/lp/registry/model/person.py b/lib/lp/registry/model/person.py |
246 | index 81f4187..dcf118e 100644 |
247 | --- a/lib/lp/registry/model/person.py |
248 | +++ b/lib/lp/registry/model/person.py |
249 | @@ -62,7 +62,7 @@ from storm.expr import ( |
250 | With, |
251 | ) |
252 | from storm.info import ClassAlias |
253 | -from storm.locals import Int, Reference, ReferenceSet, Unicode |
254 | +from storm.locals import Bool, DateTime, Int, Reference, ReferenceSet, Unicode |
255 | from storm.store import EmptyResultSet, Store |
256 | from twisted.conch.ssh.common import getNS |
257 | from twisted.conch.ssh.keys import Key |
258 | @@ -193,24 +193,16 @@ from lp.registry.model.teammembership import ( |
259 | ) |
260 | from lp.services.config import config |
261 | from lp.services.database import bulk, postgresql |
262 | -from lp.services.database.constants import UTC_NOW |
263 | -from lp.services.database.datetimecol import UtcDateTimeCol |
264 | +from lp.services.database.constants import DEFAULT, UTC_NOW |
265 | from lp.services.database.decoratedresultset import DecoratedResultSet |
266 | from lp.services.database.enumcol import DBEnum |
267 | from lp.services.database.interfaces import IStore |
268 | from lp.services.database.policy import PrimaryDatabasePolicy |
269 | from lp.services.database.sqlbase import ( |
270 | - SQLBase, |
271 | convert_storm_clause_to_string, |
272 | cursor, |
273 | sqlvalues, |
274 | ) |
275 | -from lp.services.database.sqlobject import ( |
276 | - BoolCol, |
277 | - ForeignKey, |
278 | - IntCol, |
279 | - StringCol, |
280 | -) |
281 | from lp.services.database.stormbase import StormBase |
282 | from lp.services.database.stormexpr import WithMaterialized, fti_search |
283 | from lp.services.helpers import backslashreplace, shortlist |
284 | @@ -282,7 +274,7 @@ class TeamInvitationEvent: |
285 | self.team = team |
286 | |
287 | |
288 | -class ValidPersonCache(SQLBase): |
289 | +class ValidPersonCache(StormBase): |
290 | """Flags if a Person is active and usable in Launchpad. |
291 | |
292 | This is readonly, as this is a view in the database. |
293 | @@ -294,6 +286,10 @@ class ValidPersonCache(SQLBase): |
294 | corroborating information. |
295 | """ |
296 | |
297 | + __storm_table__ = "ValidPersonCache" |
298 | + |
299 | + id = Int(primary=True) |
300 | + |
301 | |
302 | def validate_person_visibility(person, attr, value): |
303 | """Validate changes in visibility. |
304 | @@ -355,14 +351,14 @@ class PersonSettings(StormBase): |
305 | |
306 | __storm_table__ = "PersonSettings" |
307 | |
308 | - personID = Int("person", default=None, primary=True) |
309 | - person = Reference(personID, "Person.id") |
310 | + person_id = Int("person", default=None, primary=True) |
311 | + person = Reference(person_id, "Person.id") |
312 | |
313 | - selfgenerated_bugnotifications = BoolCol(notNull=True, default=False) |
314 | + selfgenerated_bugnotifications = Bool(allow_none=False, default=False) |
315 | |
316 | - expanded_notification_footers = BoolCol(notNull=False, default=False) |
317 | + expanded_notification_footers = Bool(allow_none=True, default=False) |
318 | |
319 | - require_strong_email_authentication = BoolCol(notNull=False, default=False) |
320 | + require_strong_email_authentication = Bool(allow_none=True, default=False) |
321 | |
322 | |
323 | def readonly_settings(message, interface): |
324 | @@ -420,7 +416,7 @@ _readonly_person_settings = readonly_settings( |
325 | @implementer(IPerson) |
326 | @delegate_to(IPersonSettings, context="_person_settings") |
327 | class Person( |
328 | - SQLBase, |
329 | + StormBase, |
330 | HasBugsBase, |
331 | HasSpecificationsMixin, |
332 | HasTranslationImportsMixin, |
333 | @@ -431,14 +427,54 @@ class Person( |
334 | ): |
335 | """A Person.""" |
336 | |
337 | - def __init__(self, *args, **kwargs): |
338 | - super().__init__(*args, **kwargs) |
339 | - # Initialize our PersonSettings object/record. |
340 | + __storm_table__ = "Person" |
341 | + |
342 | + id = Int(primary=True) |
343 | + |
344 | + _creating = False |
345 | + |
346 | + def __init__( |
347 | + self, |
348 | + name, |
349 | + display_name, |
350 | + account=None, |
351 | + teamowner=None, |
352 | + description=None, |
353 | + membership_policy=DEFAULT, |
354 | + defaultrenewalperiod=None, |
355 | + defaultmembershipperiod=None, |
356 | + creation_rationale=None, |
357 | + creation_comment=None, |
358 | + registrant=None, |
359 | + hide_email_addresses=False, |
360 | + ): |
361 | + super().__init__() |
362 | + self._creating = True |
363 | + self.name = name |
364 | + self.display_name = display_name |
365 | + self.account = account |
366 | + self.teamowner = teamowner |
367 | + self.description = description |
368 | + self.membership_policy = membership_policy |
369 | + self.defaultrenewalperiod = defaultrenewalperiod |
370 | + self.defaultmembershipperiod = defaultmembershipperiod |
371 | + self.creation_rationale = creation_rationale |
372 | + self.creation_comment = creation_comment |
373 | + self.registrant = registrant |
374 | + self.hide_email_addresses = hide_email_addresses |
375 | if not self.is_team: |
376 | - # This is a Person, not a team. Teams may want a TeamSettings |
377 | - # in the future. |
378 | + # Initialize our PersonSettings object/record. This is a |
379 | + # Person, not a team. Teams may want a TeamSettings in the |
380 | + # future. |
381 | settings = PersonSettings() |
382 | settings.person = self |
383 | + self.__storm_loaded__() |
384 | + del self._creating |
385 | + |
386 | + def __storm_loaded__(self): |
387 | + """Mark the person as a team when created or fetched from database.""" |
388 | + if self.is_team: |
389 | + alsoProvides(self, ITeam) |
390 | |
391 | @cachedproperty |
392 | def _person_settings(self): |
393 | @@ -462,13 +498,11 @@ class Person( |
394 | return self.id |
395 | |
396 | sortingColumns = SQL("person_sort_key(Person.displayname, Person.name)") |
397 | - # Redefine the default ordering into Storm syntax. |
398 | - _storm_sortingColumns = ("Person.displayname", "Person.name") |
399 | # When doing any sort of set operations (union, intersect, except_) with |
400 | - # SQLObject we can't use sortingColumns because the table name Person is |
401 | - # not available in that context, so we use this one. |
402 | + # Storm we can't use sortingColumns because the table name Person is not |
403 | + # available in that context, so we use this one. |
404 | _sortingColumnsForSetOperations = SQL("person_sort_key(displayname, name)") |
405 | - _defaultOrder = sortingColumns |
406 | + __storm_order__ = sortingColumns |
407 | _visibility_warning_cache_key = None |
408 | _visibility_warning_cache = None |
409 | |
410 | @@ -490,7 +524,7 @@ class Person( |
411 | else: |
412 | mailing_list = getUtility(IMailingListSet).get(self.name) |
413 | can_rename = ( |
414 | - self._SO_creating |
415 | + self._creating |
416 | or not self.is_team |
417 | or mailing_list is None |
418 | or mailing_list.status == MailingListStatus.PURGED |
419 | @@ -499,35 +533,27 @@ class Person( |
420 | # Everything's okay, so let SQLObject do the normal thing. |
421 | return value |
422 | |
423 | - name = StringCol( |
424 | - dbName="name", |
425 | - alternateID=True, |
426 | - notNull=True, |
427 | - storm_validator=_validate_name, |
428 | - ) |
429 | + name = Unicode(name="name", allow_none=False, validator=_validate_name) |
430 | |
431 | def __repr__(self): |
432 | displayname = backslashreplace(self.displayname) |
433 | return "<Person %s (%s)>" % (self.name, displayname) |
434 | |
435 | - display_name = StringCol(dbName="displayname", notNull=True) |
436 | + display_name = Unicode(name="displayname", allow_none=False) |
437 | |
438 | @property |
439 | def displayname(self): |
440 | return self.display_name |
441 | |
442 | - teamdescription = StringCol(dbName="teamdescription", default=None) |
443 | - homepage_content = StringCol(default=None) |
444 | - _description = StringCol(dbName="description", default=None) |
445 | - icon = ForeignKey( |
446 | - dbName="icon", foreignKey="LibraryFileAlias", default=None |
447 | - ) |
448 | - logo = ForeignKey( |
449 | - dbName="logo", foreignKey="LibraryFileAlias", default=None |
450 | - ) |
451 | - mugshot = ForeignKey( |
452 | - dbName="mugshot", foreignKey="LibraryFileAlias", default=None |
453 | - ) |
454 | + teamdescription = Unicode(name="teamdescription", default=None) |
455 | + homepage_content = Unicode(default=None) |
456 | + _description = Unicode(name="description", default=None) |
457 | + icon_id = Int(name="icon", allow_none=True, default=None) |
458 | + icon = Reference(icon_id, "LibraryFileAlias.id") |
459 | + logo_id = Int(name="logo", allow_none=True, default=None) |
460 | + logo = Reference(logo_id, "LibraryFileAlias.id") |
461 | + mugshot_id = Int(name="mugshot", allow_none=True, default=None) |
462 | + mugshot = Reference(mugshot_id, "LibraryFileAlias.id") |
463 | |
464 | @property |
465 | def account_status(self): |
466 | @@ -546,12 +572,13 @@ class Person( |
467 | raise NoAccountError() |
468 | self.account.setStatus(status, user, comment) |
469 | |
470 | - teamowner = ForeignKey( |
471 | - dbName="teamowner", |
472 | - foreignKey="Person", |
473 | + teamowner_id = Int( |
474 | + name="teamowner", |
475 | + validator=validate_public_person, |
476 | + allow_none=True, |
477 | default=None, |
478 | - storm_validator=validate_public_person, |
479 | ) |
480 | + teamowner = Reference(teamowner_id, "Person.id") |
481 | |
482 | sshkeys = ReferenceSet("id", "SSHKey.person_id") |
483 | |
484 | @@ -565,30 +592,32 @@ class Person( |
485 | default=TeamMembershipPolicy.RESTRICTED, |
486 | validator=validate_membership_policy, |
487 | ) |
488 | - defaultrenewalperiod = IntCol(dbName="defaultrenewalperiod", default=None) |
489 | - defaultmembershipperiod = IntCol( |
490 | - dbName="defaultmembershipperiod", default=None |
491 | - ) |
492 | + defaultrenewalperiod = Int(name="defaultrenewalperiod", default=None) |
493 | + defaultmembershipperiod = Int(name="defaultmembershipperiod", default=None) |
494 | mailing_list_auto_subscribe_policy = DBEnum( |
495 | enum=MailingListAutoSubscribePolicy, |
496 | default=MailingListAutoSubscribePolicy.ON_REGISTRATION, |
497 | ) |
498 | |
499 | - merged = ForeignKey(dbName="merged", foreignKey="Person", default=None) |
500 | + merged_id = Int(name="merged", allow_none=True, default=None) |
501 | + merged = Reference(merged_id, "Person.id") |
502 | |
503 | - datecreated = UtcDateTimeCol(notNull=True, default=UTC_NOW) |
504 | + datecreated = DateTime( |
505 | + allow_none=False, default=UTC_NOW, tzinfo=timezone.utc |
506 | + ) |
507 | creation_rationale = DBEnum(enum=PersonCreationRationale, default=None) |
508 | - creation_comment = StringCol(default=None) |
509 | - registrant = ForeignKey( |
510 | - dbName="registrant", |
511 | - foreignKey="Person", |
512 | + creation_comment = Unicode(default=None) |
513 | + registrant_id = Int( |
514 | + name="registrant", |
515 | + validator=validate_public_person, |
516 | + allow_none=True, |
517 | default=None, |
518 | - storm_validator=validate_public_person, |
519 | ) |
520 | - hide_email_addresses = BoolCol(notNull=True, default=False) |
521 | - verbose_bugnotifications = BoolCol(notNull=True, default=True) |
522 | + registrant = Reference(registrant_id, "Person.id") |
523 | + hide_email_addresses = Bool(allow_none=False, default=False) |
524 | + verbose_bugnotifications = Bool(allow_none=False, default=True) |
525 | |
526 | - signedcocs = ReferenceSet("<primary key>", "SignedCodeOfConduct.owner_id") |
527 | + signedcocs = ReferenceSet("id", "SignedCodeOfConduct.owner_id") |
528 | _ircnicknames = ReferenceSet("id", "IrcID.person_id") |
529 | jabberids = ReferenceSet("id", "JabberID.person_id") |
530 | |
531 | @@ -604,7 +633,7 @@ class Person( |
532 | allow_none=False, |
533 | ) |
534 | |
535 | - personal_standing_reason = StringCol(default=None) |
536 | + personal_standing_reason = Unicode(default=None) |
537 | |
538 | @property |
539 | def description(self): |
540 | @@ -703,12 +732,6 @@ class Person( |
541 | person_language.delete() |
542 | self.deleteLanguagesCache() |
543 | |
544 | - def _init(self, *args, **kw): |
545 | - """Mark the person as a team when created or fetched from database.""" |
546 | - SQLBase._init(self, *args, **kw) |
547 | - if self.teamownerID is not None: |
548 | - alsoProvides(self, ITeam) |
549 | - |
550 | def convertToTeam(self, team_owner): |
551 | """See `IPerson`.""" |
552 | if self.is_team: |
553 | @@ -1009,7 +1032,7 @@ class Person( |
554 | @property |
555 | def is_team(self): |
556 | """See `IPerson`.""" |
557 | - return self.teamownerID is not None |
558 | + return self.teamowner_id is not None |
559 | |
560 | @property |
561 | def mailing_list(self): |
562 | @@ -1117,7 +1140,7 @@ class Person( |
563 | OR product.bug_supervisor = %(person)s |
564 | ) |
565 | """ % sqlvalues( |
566 | - person=self |
567 | + person=self.id |
568 | ) |
569 | |
570 | return "%s AND (%s)" % ( |
571 | @@ -1157,7 +1180,7 @@ class Person( |
572 | ) _pillar |
573 | ON PillarName.name = _pillar.name |
574 | """ |
575 | - % sqlvalues(person=self) |
576 | + % sqlvalues(person=self.id) |
577 | ) |
578 | |
579 | results = IStore(self).using(SQL(origin)).find(find_spec) |
580 | @@ -1260,7 +1283,6 @@ class Person( |
581 | CommercialSubscription, |
582 | ) |
583 | from lp.registry.model.distribution import Distribution |
584 | - from lp.registry.model.person import Person |
585 | from lp.registry.model.product import Product |
586 | from lp.registry.model.teammembership import TeamParticipation |
587 | |
588 | @@ -1599,7 +1621,6 @@ class Person( |
589 | def getAssignedSpecificationWorkItemsDueBefore(self, date, user): |
590 | """See `IPerson`.""" |
591 | from lp.registry.model.distribution import Distribution |
592 | - from lp.registry.model.person import Person |
593 | from lp.registry.model.product import Product |
594 | |
595 | store = Store.of(self) |
596 | @@ -1811,7 +1832,7 @@ class Person( |
597 | And( |
598 | TeamParticipation.team_id == self.id, |
599 | TeamParticipation.person_id != self.id, |
600 | - Person.teamownerID != None, |
601 | + IsNot(Person.teamowner_id, None), |
602 | ), |
603 | need_api=True, |
604 | ) |
605 | @@ -2061,7 +2082,7 @@ class Person( |
606 | Select( |
607 | Person.id, |
608 | tables=[Person], |
609 | - where=Person.teamownerID.is_in(team_select), |
610 | + where=Person.teamowner_id.is_in(team_select), |
611 | ), |
612 | Select( |
613 | TeamMembership.team_id, |
614 | @@ -2942,7 +2963,7 @@ class Person( |
615 | Person, |
616 | Person.id == TeamParticipation.team_id, |
617 | TeamParticipation.person == self, |
618 | - IsNot(Person.teamownerID, None), |
619 | + IsNot(Person.teamowner_id, None), |
620 | ) |
621 | .order_by(Person.sortingColumns) |
622 | ) |
623 | @@ -2950,11 +2971,10 @@ class Person( |
624 | @property |
625 | def teams_indirectly_participated_in(self): |
626 | """See `IPerson`.""" |
627 | - Team = ClassAlias(Person, "Team") |
628 | store = Store.of(self) |
629 | origin = [ |
630 | - Team, |
631 | - Join(TeamParticipation, Team.id == TeamParticipation.team_id), |
632 | + Person, |
633 | + Join(TeamParticipation, Person.id == TeamParticipation.team_id), |
634 | LeftJoin( |
635 | TeamMembership, |
636 | And( |
637 | @@ -2969,9 +2989,8 @@ class Person( |
638 | ), |
639 | ), |
640 | ] |
641 | - find_objects = Team |
642 | return store.using(*origin).find( |
643 | - find_objects, |
644 | + Person, |
645 | And( |
646 | TeamParticipation.person == self.id, |
647 | TeamParticipation.person != TeamParticipation.team_id, |
648 | @@ -2988,8 +3007,8 @@ class Person( |
649 | Person, |
650 | Person.id == TeamParticipation.team_id, |
651 | TeamParticipation.person == self, |
652 | - IsNot(Person.teamownerID, None), |
653 | - IsNot(Person.iconID, None), |
654 | + IsNot(Person.teamowner_id, None), |
655 | + IsNot(Person.icon_id, None), |
656 | TeamParticipation.team != self, |
657 | ) |
658 | .order_by(Person.sortingColumns) |
659 | @@ -4155,6 +4174,9 @@ class PersonSet: |
660 | defaultrenewalperiod=defaultrenewalperiod, |
661 | membership_policy=membership_policy, |
662 | ) |
663 | + store = IStore(Person) |
664 | + store.add(team) |
665 | + store.flush() |
666 | notify(ObjectCreatedEvent(team)) |
667 | # Here we add the owner as a team admin manually because we know what |
668 | # we're doing (so we don't need to do any sanity checks) and we don't |
669 | @@ -4267,19 +4289,18 @@ class PersonSet: |
670 | if not displayname: |
671 | displayname = name.capitalize() |
672 | |
673 | - if account is None: |
674 | - account_id = None |
675 | - else: |
676 | - account_id = account.id |
677 | person = Person( |
678 | name=name, |
679 | display_name=displayname, |
680 | - account_id=account_id, |
681 | + account=account, |
682 | creation_rationale=rationale, |
683 | creation_comment=comment, |
684 | hide_email_addresses=hide_email_addresses, |
685 | registrant=registrant, |
686 | ) |
687 | + store = IStore(Person) |
688 | + store.add(person) |
689 | + store.flush() |
690 | return person |
691 | |
692 | def ensurePerson( |
693 | @@ -4309,7 +4330,7 @@ class PersonSet: |
694 | """See `IPersonSet`.""" |
695 | clauses = [Person.name == name] |
696 | if ignore_merged: |
697 | - clauses.append(Is(Person.mergedID, None)) |
698 | + clauses.append(Is(Person.merged_id, None)) |
699 | return IStore(Person).find(Person, *clauses).one() |
700 | |
701 | def getByAccount(self, account): |
702 | @@ -4323,8 +4344,8 @@ class PersonSet: |
703 | IStore(Person) |
704 | .find( |
705 | Person, |
706 | - Is(Person.teamownerID, None), |
707 | - Is(Person.mergedID, None), |
708 | + Is(Person.teamowner_id, None), |
709 | + Is(Person.merged_id, None), |
710 | ) |
711 | .count() |
712 | ) |
713 | @@ -4334,8 +4355,8 @@ class PersonSet: |
714 | IStore(Person) |
715 | .find( |
716 | Person, |
717 | - IsNot(Person.teamownerID, None), |
718 | - Is(Person.mergedID, None), |
719 | + IsNot(Person.teamowner_id, None), |
720 | + Is(Person.merged_id, None), |
721 | ) |
722 | .count() |
723 | ) |
724 | @@ -4601,15 +4622,15 @@ class PersonSet: |
725 | """See `IPersonSet`.""" |
726 | aliases = [] |
727 | aliases.extend( |
728 | - person.iconID for person in people if person.iconID is not None |
729 | + person.icon_id for person in people if person.icon_id is not None |
730 | ) |
731 | aliases.extend( |
732 | - person.logoID for person in people if person.logoID is not None |
733 | + person.logo_id for person in people if person.logo_id is not None |
734 | ) |
735 | aliases.extend( |
736 | - person.mugshotID |
737 | + person.mugshot_id |
738 | for person in people |
739 | - if person.mugshotID is not None |
740 | + if person.mugshot_id is not None |
741 | ) |
742 | if not aliases: |
743 | return |
744 | @@ -4805,7 +4826,7 @@ class PersonSet: |
745 | |
746 | def preload_for_people(rows): |
747 | if need_teamowner or need_api: |
748 | - bulk.load(Person, [row[0].teamownerID for row in rows]) |
749 | + bulk.load(Person, [row[0].teamowner_id for row in rows]) |
750 | |
751 | def prepopulate_person(row): |
752 | result = row[0] |
753 | @@ -5559,7 +5580,7 @@ def _get_recipients_for_team(team): |
754 | EmailAddress.person != None, |
755 | Account.status == AccountStatus.ACTIVE, |
756 | ), |
757 | - Person.teamownerID != None, |
758 | + IsNot(Person.teamowner_id, None), |
759 | ), |
760 | ).config(distinct=True) |
761 | next_ids = [] |
762 | diff --git a/lib/lp/registry/personmerge.py b/lib/lp/registry/personmerge.py |
763 | index cec406c..2787bc2 100644 |
764 | --- a/lib/lp/registry/personmerge.py |
765 | +++ b/lib/lp/registry/personmerge.py |
766 | @@ -1230,8 +1230,7 @@ def merge_people(from_person, to_person, reviewer, delete=False): |
767 | cur.execute("SELECT id FROM Person WHERE name = %s" % sqlvalues(name)) |
768 | i += 1 |
769 | cur.execute( |
770 | - "UPDATE Person SET name = %s WHERE id = %s" |
771 | - % sqlvalues(name, from_person) |
772 | + "UPDATE Person SET name = %s WHERE id = %s" % sqlvalues(name, from_id) |
773 | ) |
774 | |
775 | # Since we've updated the database behind Storm's back, |
776 | diff --git a/lib/lp/registry/scripts/closeaccount.py b/lib/lp/registry/scripts/closeaccount.py |
777 | index c7d4c55..7df78d0 100644 |
778 | --- a/lib/lp/registry/scripts/closeaccount.py |
779 | +++ b/lib/lp/registry/scripts/closeaccount.py |
780 | @@ -236,7 +236,7 @@ def close_account(username, log): |
781 | # Keep the corresponding PersonSettings row, but reset everything to the |
782 | # defaults. |
783 | table_notification("PersonSettings") |
784 | - store.find(PersonSettings, PersonSettings.personID == person.id).set( |
785 | + store.find(PersonSettings, PersonSettings.person == person).set( |
786 | selfgenerated_bugnotifications=DEFAULT, |
787 | # XXX cjwatson 2018-11-29: These two columns have NULL defaults, but |
788 | # perhaps shouldn't? |
789 | diff --git a/lib/lp/registry/vocabularies.py b/lib/lp/registry/vocabularies.py |
790 | index 56453ca..0104a33 100644 |
791 | --- a/lib/lp/registry/vocabularies.py |
792 | +++ b/lib/lp/registry/vocabularies.py |
793 | @@ -71,6 +71,8 @@ from storm.expr import ( |
794 | And, |
795 | Column, |
796 | Desc, |
797 | + Is, |
798 | + IsNot, |
799 | Join, |
800 | LeftJoin, |
801 | Not, |
802 | @@ -176,7 +178,6 @@ from lp.services.webapp.vocabulary import ( |
803 | IHugeVocabulary, |
804 | NamedStormHugeVocabulary, |
805 | NamedStormVocabulary, |
806 | - SQLObjectVocabularyBase, |
807 | StormVocabularyBase, |
808 | VocabularyFilter, |
809 | ) |
810 | @@ -208,7 +209,6 @@ class BasePersonVocabulary: |
811 | If the token contains an '@', treat it like an email. Otherwise, |
812 | treat it like a name. |
813 | """ |
814 | - token = six.ensure_text(token) |
815 | if "@" in token: |
816 | # This looks like an email token, so let's do an object |
817 | # lookup based on that. |
818 | @@ -369,11 +369,11 @@ def project_products_vocabulary_factory(context): |
819 | ) |
820 | |
821 | |
822 | -class UserTeamsParticipationVocabulary(SQLObjectVocabularyBase): |
823 | +class UserTeamsParticipationVocabulary(StormVocabularyBase): |
824 | """Describes the public teams in which the current user participates.""" |
825 | |
826 | _table = Person |
827 | - _orderBy = "display_name" |
828 | + _order_by = "display_name" |
829 | |
830 | INCLUDE_PRIVATE_TEAM = False |
831 | |
832 | @@ -401,7 +401,7 @@ class UserTeamsParticipationVocabulary(SQLObjectVocabularyBase): |
833 | teams = list( |
834 | IStore(Person) |
835 | .find(Person, *clauses) |
836 | - .order_by(Person._storm_sortingColumns) |
837 | + .order_by(Person._sortingColumns) |
838 | ) |
839 | # Users can view all the teams they belong to. |
840 | precache_permission_for_objects( |
841 | @@ -428,7 +428,7 @@ class UserTeamsParticipationVocabulary(SQLObjectVocabularyBase): |
842 | |
843 | @implementer(IHugeVocabulary) |
844 | class NonMergedPeopleAndTeamsVocabulary( |
845 | - BasePersonVocabulary, SQLObjectVocabularyBase |
846 | + BasePersonVocabulary, StormVocabularyBase |
847 | ): |
848 | """The set of all non-merged people and teams. |
849 | |
850 | @@ -437,7 +437,7 @@ class NonMergedPeopleAndTeamsVocabulary( |
851 | a preferred email address, that is, unvalidated person profiles. |
852 | """ |
853 | |
854 | - _orderBy = ["display_name"] |
855 | + _order_by = ["display_name"] |
856 | displayname = "Select a Person or Team" |
857 | step_title = "Search" |
858 | |
859 | @@ -449,7 +449,7 @@ class NonMergedPeopleAndTeamsVocabulary( |
860 | return getUtility(IPersonSet).find(text) |
861 | |
862 | def search(self, text, vocab_filter=None): |
863 | - """See `SQLObjectVocabularyBase`. |
864 | + """See `StormVocabularyBase`. |
865 | |
866 | Return people/teams whose fti or email address match :text. |
867 | """ |
868 | @@ -461,7 +461,7 @@ class NonMergedPeopleAndTeamsVocabulary( |
869 | |
870 | @implementer(IHugeVocabulary) |
871 | class PersonAccountToMergeVocabulary( |
872 | - BasePersonVocabulary, SQLObjectVocabularyBase |
873 | + BasePersonVocabulary, StormVocabularyBase |
874 | ): |
875 | """The set of all non-merged people with at least one email address. |
876 | |
877 | @@ -469,7 +469,7 @@ class PersonAccountToMergeVocabulary( |
878 | accounts to merge. You *don't* want to use it. |
879 | """ |
880 | |
881 | - _orderBy = ["display_name"] |
882 | + _order_by = ["display_name"] |
883 | displayname = "Select a Person to Merge" |
884 | step_title = "Search" |
885 | must_have_email = True |
886 | @@ -486,7 +486,7 @@ class PersonAccountToMergeVocabulary( |
887 | ) |
888 | |
889 | def search(self, text, vocab_filter=None): |
890 | - """See `SQLObjectVocabularyBase`. |
891 | + """See `StormVocabularyBase`. |
892 | |
893 | Return people whose fti or email address match :text. |
894 | """ |
895 | @@ -516,7 +516,7 @@ class VocabularyFilterPerson(VocabularyFilter): |
896 | |
897 | @property |
898 | def filter_terms(self): |
899 | - return [Person.teamownerID == None] |
900 | + return [Is(Person.teamowner_id, None)] |
901 | |
902 | |
903 | class VocabularyFilterTeam(VocabularyFilter): |
904 | @@ -529,13 +529,11 @@ class VocabularyFilterTeam(VocabularyFilter): |
905 | |
906 | @property |
907 | def filter_terms(self): |
908 | - return [Person.teamownerID != None] |
909 | + return [IsNot(Person.teamowner_id, None)] |
910 | |
911 | |
912 | @implementer(IHugeVocabulary) |
913 | -class ValidPersonOrTeamVocabulary( |
914 | - BasePersonVocabulary, SQLObjectVocabularyBase |
915 | -): |
916 | +class ValidPersonOrTeamVocabulary(BasePersonVocabulary, StormVocabularyBase): |
917 | """The set of valid, viewable Persons/Teams in Launchpad. |
918 | |
919 | A Person is considered valid if they have a preferred email address, and |
920 | diff --git a/lib/lp/translations/model/poexportrequest.py b/lib/lp/translations/model/poexportrequest.py |
921 | index 3313457..9dd9ec5 100644 |
922 | --- a/lib/lp/translations/model/poexportrequest.py |
923 | +++ b/lib/lp/translations/model/poexportrequest.py |
924 | @@ -92,7 +92,7 @@ class POExportRequestSet: |
925 | ) |
926 | |
927 | query_params = { |
928 | - "person": quote(person), |
929 | + "person": quote(person.id), |
930 | "format": quote(format), |
931 | "templates": potemplate_ids, |
932 | "pofiles": pofile_ids, |
933 | diff --git a/lib/lp/translations/model/pofile.py b/lib/lp/translations/model/pofile.py |
934 | index 14626b1..623fb69 100644 |
935 | --- a/lib/lp/translations/model/pofile.py |
936 | +++ b/lib/lp/translations/model/pofile.py |
937 | @@ -456,7 +456,7 @@ class POFile(StormBase, POFileMixIn): |
938 | ) |
939 | .config(distinct=True) |
940 | ) |
941 | - contributors = contributors.order_by(*Person._storm_sortingColumns) |
942 | + contributors = contributors.order_by(*Person._sortingColumns) |
943 | contributors = contributors.config(distinct=True) |
944 | return contributors |
945 | |
946 | diff --git a/lib/lp/translations/model/translationgroup.py b/lib/lp/translations/model/translationgroup.py |
947 | index 943b38e..c8dc2dd 100644 |
948 | --- a/lib/lp/translations/model/translationgroup.py |
949 | +++ b/lib/lp/translations/model/translationgroup.py |
950 | @@ -175,7 +175,7 @@ class TranslationGroup(StormBase): |
951 | Translator, |
952 | Language, |
953 | Person, |
954 | - LeftJoin(LibraryFileAlias, LibraryFileAlias.id == Person.iconID), |
955 | + LeftJoin(LibraryFileAlias, LibraryFileAlias.id == Person.icon_id), |
956 | LeftJoin( |
957 | LibraryFileContent, |
958 | LibraryFileContent.id == LibraryFileAlias.contentID, |
959 | diff --git a/lib/lp/translations/scripts/remove_translations.py b/lib/lp/translations/scripts/remove_translations.py |
960 | index c042b8a..e854640 100644 |
961 | --- a/lib/lp/translations/scripts/remove_translations.py |
962 | +++ b/lib/lp/translations/scripts/remove_translations.py |
963 | @@ -444,11 +444,11 @@ def remove_translations( |
964 | conditions = set() |
965 | if submitter is not None: |
966 | conditions.add( |
967 | - "TranslationMessage.submitter = %s" % sqlvalues(submitter) |
968 | + "TranslationMessage.submitter = %s" % sqlvalues(submitter.id) |
969 | ) |
970 | if reviewer is not None: |
971 | conditions.add( |
972 | - "TranslationMessage.reviewer = %s" % sqlvalues(reviewer) |
973 | + "TranslationMessage.reviewer = %s" % sqlvalues(reviewer.id) |
974 | ) |
975 | if date_created is not None: |
976 | conditions.add( |
LGTM!