Merge ~cjwatson/launchpad:stormify-bugtracker into launchpad:master
- Git
- lp:~cjwatson/launchpad
- stormify-bugtracker
- Merge into master
Proposed by
Colin Watson
Status: | Merged |
---|---|
Approved by: | Colin Watson |
Approved revision: | a158933f84ea0ff1bdf635b36b11f00337850c18 |
Merge reported by: | Otto Co-Pilot |
Merged at revision: | not available |
Proposed branch: | ~cjwatson/launchpad:stormify-bugtracker |
Merge into: | launchpad:master |
Diff against target: |
443 lines (+92/-83) 8 files modified
lib/lp/bugs/model/bug.py (+1/-1) lib/lp/bugs/model/bugtracker.py (+76/-58) lib/lp/bugs/model/bugwatch.py (+2/-3) lib/lp/bugs/stories/webservice/xx-bug.rst (+1/-1) lib/lp/bugs/templates/bugtracker-index.pt (+1/-1) lib/lp/bugs/vocabularies.py (+7/-7) lib/lp/registry/model/product.py (+2/-6) lib/lp/registry/model/projectgroup.py (+2/-6) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Guruprasad | Approve | ||
Review via email: mp+435977@code.launchpad.net |
Commit message
Convert BugTracker 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/bug.py b/lib/lp/bugs/model/bug.py |
2 | index 88f793d..18d6d7d 100644 |
3 | --- a/lib/lp/bugs/model/bug.py |
4 | +++ b/lib/lp/bugs/model/bug.py |
5 | @@ -402,7 +402,7 @@ class Bug(SQLBase, InformationTypeMixin): |
6 | "id", BugMessage.bug_id, order_by=BugMessage.index |
7 | ) |
8 | watches = SQLMultipleJoin( |
9 | - "BugWatch", joinColumn="bug", orderBy=["bugtracker", "remotebug"] |
10 | + "BugWatch", joinColumn="bug", orderBy=["bugtracker_id", "remotebug"] |
11 | ) |
12 | duplicates = SQLMultipleJoin("Bug", joinColumn="duplicateof", orderBy="id") |
13 | linked_bugbranches = ReferenceSet( |
14 | diff --git a/lib/lp/bugs/model/bugtracker.py b/lib/lp/bugs/model/bugtracker.py |
15 | index 8a8a94e..f589dc9 100644 |
16 | --- a/lib/lp/bugs/model/bugtracker.py |
17 | +++ b/lib/lp/bugs/model/bugtracker.py |
18 | @@ -12,12 +12,12 @@ __all__ = [ |
19 | |
20 | from datetime import datetime |
21 | from itertools import chain |
22 | +from operator import itemgetter |
23 | from urllib.parse import quote, urlsplit, urlunsplit |
24 | |
25 | -import six |
26 | from lazr.uri import URI |
27 | from pytz import timezone |
28 | -from storm.expr import Count, Desc, Not |
29 | +from storm.expr import Count, Desc, Not, Or |
30 | from storm.locals import SQL, Bool, Int, Reference, ReferenceSet, Unicode |
31 | from storm.store import Store |
32 | from zope.component import getUtility |
33 | @@ -45,17 +45,10 @@ from lp.bugs.model.bugwatch import BugWatch |
34 | from lp.registry.interfaces.person import IPersonSet, validate_public_person |
35 | from lp.registry.model.product import Product, ProductSet |
36 | from lp.registry.model.projectgroup import ProjectGroup |
37 | +from lp.services.database.decoratedresultset import DecoratedResultSet |
38 | from lp.services.database.enumcol import DBEnum |
39 | from lp.services.database.interfaces import IStore |
40 | -from lp.services.database.sqlbase import SQLBase, flush_database_updates |
41 | -from lp.services.database.sqlobject import ( |
42 | - OR, |
43 | - BoolCol, |
44 | - ForeignKey, |
45 | - SQLMultipleJoin, |
46 | - SQLObjectNotFound, |
47 | - StringCol, |
48 | -) |
49 | +from lp.services.database.sqlbase import flush_database_updates |
50 | from lp.services.database.stormbase import StormBase |
51 | from lp.services.helpers import shortlist |
52 | |
53 | @@ -286,7 +279,7 @@ class BugTrackerComponentGroup(StormBase): |
54 | |
55 | |
56 | @implementer(IBugTracker) |
57 | -class BugTracker(SQLBase): |
58 | +class BugTracker(StormBase): |
59 | """A class to access the BugTracker table in the database. |
60 | |
61 | Each BugTracker is a distinct instance of that bug tracking |
62 | @@ -295,35 +288,60 @@ class BugTracker(SQLBase): |
63 | distinct BugTrackers. |
64 | """ |
65 | |
66 | - _table = "BugTracker" |
67 | + __storm_table__ = "BugTracker" |
68 | |
69 | + id = Int(primary=True) |
70 | bugtrackertype = DBEnum( |
71 | name="bugtrackertype", enum=BugTrackerType, allow_none=False |
72 | ) |
73 | - name = StringCol(notNull=True, unique=True) |
74 | - title = StringCol(notNull=True) |
75 | - summary = StringCol(notNull=False) |
76 | - baseurl = StringCol(notNull=True) |
77 | + name = Unicode(name="name", allow_none=False) |
78 | + title = Unicode(name="title", allow_none=False) |
79 | + summary = Unicode(name="summary", allow_none=True) |
80 | + baseurl = Unicode(name="baseurl", allow_none=False) |
81 | active = Bool(name="active", allow_none=False, default=True) |
82 | |
83 | - owner = ForeignKey( |
84 | - dbName="owner", |
85 | - foreignKey="Person", |
86 | - storm_validator=validate_public_person, |
87 | - notNull=True, |
88 | - ) |
89 | - contactdetails = StringCol(notNull=False) |
90 | - has_lp_plugin = BoolCol(notNull=False, default=False) |
91 | - products = SQLMultipleJoin( |
92 | - "Product", joinColumn="bugtracker", orderBy="name" |
93 | + owner_id = Int( |
94 | + name="owner", validator=validate_public_person, allow_none=False |
95 | ) |
96 | - watches = SQLMultipleJoin( |
97 | - "BugWatch", |
98 | - joinColumn="bugtracker", |
99 | - orderBy="-datecreated", |
100 | - prejoins=["bug"], |
101 | + owner = Reference(owner_id, "Person.id") |
102 | + contactdetails = Unicode(name="contactdetails", allow_none=True) |
103 | + has_lp_plugin = Bool(name="has_lp_plugin", allow_none=True, default=False) |
104 | + products = ReferenceSet( |
105 | + "id", "Product.bugtracker_id", order_by="Product.name" |
106 | ) |
107 | |
108 | + @property |
109 | + def watches(self): |
110 | + return DecoratedResultSet( |
111 | + IStore(BugWatch) |
112 | + .find( |
113 | + (BugWatch, Bug), |
114 | + BugWatch.bugtracker == self, |
115 | + BugWatch.bugID == Bug.id, |
116 | + ) |
117 | + .order_by(Desc(BugWatch.datecreated)), |
118 | + result_decorator=itemgetter(0), |
119 | + ) |
120 | + |
121 | + def __init__( |
122 | + self, |
123 | + bugtrackertype, |
124 | + name, |
125 | + title, |
126 | + baseurl, |
127 | + owner, |
128 | + summary=None, |
129 | + contactdetails=None, |
130 | + ): |
131 | + super().__init__() |
132 | + self.bugtrackertype = bugtrackertype |
133 | + self.name = name |
134 | + self.title = title |
135 | + self.baseurl = baseurl |
136 | + self.owner = owner |
137 | + self.summary = summary |
138 | + self.contactdetails = contactdetails |
139 | + |
140 | _filing_url_patterns = { |
141 | BugTrackerType.BUGZILLA: ( |
142 | "%(base_url)s/enter_bug.cgi?product=%(remote_product)s" |
143 | @@ -548,7 +566,7 @@ class BugTracker(SQLBase): |
144 | .find( |
145 | Bug, |
146 | BugWatch.bugID == Bug.id, |
147 | - BugWatch.bugtrackerID == self.id, |
148 | + BugWatch.bugtracker == self, |
149 | BugWatch.remotebug == remotebug, |
150 | ) |
151 | .config(distinct=True) |
152 | @@ -590,9 +608,7 @@ class BugTracker(SQLBase): |
153 | |
154 | # Join to return a list of BugTrackerAliases relating to this |
155 | # BugTracker. |
156 | - _bugtracker_aliases = ReferenceSet( |
157 | - "<primary key>", "BugTrackerAlias.bugtracker_id" |
158 | - ) |
159 | + _bugtracker_aliases = ReferenceSet("id", "BugTrackerAlias.bugtracker_id") |
160 | |
161 | def _get_aliases(self): |
162 | """See `IBugTracker.aliases`.""" |
163 | @@ -646,7 +662,7 @@ class BugTracker(SQLBase): |
164 | .find( |
165 | BugMessage, |
166 | BugMessage.bugwatch_id == BugWatch.id, |
167 | - BugWatch.bugtrackerID == self.id, |
168 | + BugWatch.bugtracker == self, |
169 | ) |
170 | .order_by(BugMessage.id) |
171 | ) |
172 | @@ -807,7 +823,7 @@ class BugTracker(SQLBase): |
173 | IStore(Product) |
174 | .find( |
175 | Product, |
176 | - Product.bugtrackerID == self.id, |
177 | + Product.bugtracker == self, |
178 | Product.active == True, |
179 | ProductSet.getProductPrivacyFilter(user), |
180 | ) |
181 | @@ -817,13 +833,16 @@ class BugTracker(SQLBase): |
182 | IStore(ProjectGroup) |
183 | .find( |
184 | ProjectGroup, |
185 | - ProjectGroup.bugtrackerID == self.id, |
186 | + ProjectGroup.bugtracker == self, |
187 | ProjectGroup.active == True, |
188 | ) |
189 | .order_by(ProjectGroup.name) |
190 | ) |
191 | return groups, products |
192 | |
193 | + def destroySelf(self): |
194 | + IStore(self).remove(self) |
195 | + |
196 | |
197 | @implementer(IBugTrackerSet) |
198 | class BugTrackerSet: |
199 | @@ -831,53 +850,52 @@ class BugTrackerSet: |
200 | either the full set in the db, or a subset. |
201 | """ |
202 | |
203 | - table = BugTracker |
204 | - |
205 | def __init__(self): |
206 | self.title = "Bug trackers registered in Launchpad" |
207 | |
208 | def get(self, bugtracker_id, default=None): |
209 | """See `IBugTrackerSet`.""" |
210 | - try: |
211 | - return BugTracker.get(bugtracker_id) |
212 | - except SQLObjectNotFound: |
213 | + bugtracker = IStore(BugTracker).get(BugTracker, bugtracker_id) |
214 | + if bugtracker is None: |
215 | return default |
216 | + return bugtracker |
217 | |
218 | def getByName(self, name, default=None): |
219 | """See `IBugTrackerSet`.""" |
220 | - return self.table.selectOne(self.table.q.name == name) |
221 | + return IStore(BugTracker).find(BugTracker, name=name).one() |
222 | |
223 | def __getitem__(self, name): |
224 | - item = self.table.selectOne(self.table.q.name == name) |
225 | + item = IStore(BugTracker).find(BugTracker, name=name).one() |
226 | if item is None: |
227 | raise NotFoundError(name) |
228 | else: |
229 | return item |
230 | |
231 | def __iter__(self): |
232 | - yield from self.table.select(orderBy="title") |
233 | + yield from IStore(BugTracker).find(BugTracker).order_by("title") |
234 | |
235 | def queryByBaseURL(self, baseurl): |
236 | """See `IBugTrackerSet`.""" |
237 | # All permutations we'll search for. |
238 | permutations = base_url_permutations(baseurl) |
239 | # Construct the search. All the important parts in the next |
240 | - # expression are lazily evaluated. SQLObject queries do not |
241 | + # expression are lazily evaluated. Storm queries do not |
242 | # execute any SQL until results are pulled, so the first query |
243 | # to return a match will be the last query executed. |
244 | matching_bugtrackers = chain( |
245 | # Search for any permutation in BugTracker. |
246 | - BugTracker.select( |
247 | - OR(*(BugTracker.q.baseurl == url for url in permutations)) |
248 | + IStore(BugTracker).find( |
249 | + BugTracker, |
250 | + Or(*(BugTracker.baseurl == url for url in permutations)), |
251 | ), |
252 | # Search for any permutation in BugTrackerAlias. |
253 | ( |
254 | alias.bugtracker |
255 | for alias in IStore(BugTrackerAlias).find( |
256 | BugTrackerAlias, |
257 | - OR( |
258 | + Or( |
259 | *( |
260 | - BugTrackerAlias.base_url == six.ensure_text(url) |
261 | + BugTrackerAlias.base_url == url |
262 | for url in permutations |
263 | ) |
264 | ), |
265 | @@ -891,7 +909,7 @@ class BugTrackerSet: |
266 | |
267 | def search(self): |
268 | """See `IBugTrackerSet`.""" |
269 | - return BugTracker.select() |
270 | + return IStore(BugTracker).find(BugTracker) |
271 | |
272 | def getAllTrackers(self, active=None): |
273 | if active is not None: |
274 | @@ -945,17 +963,17 @@ class BugTrackerSet: |
275 | |
276 | @property |
277 | def count(self): |
278 | - return IStore(self.table).find(self.table).count() |
279 | + return IStore(BugTracker).find(BugTracker).count() |
280 | |
281 | @property |
282 | def names(self): |
283 | - return IStore(self.table).find(self.table).values(self.table.name) |
284 | + return IStore(BugTracker).find(BugTracker).values(BugTracker.name) |
285 | |
286 | def getMostActiveBugTrackers(self, limit=None): |
287 | """See `IBugTrackerSet`.""" |
288 | return ( |
289 | IStore(BugTracker) |
290 | - .find(BugTracker, BugTracker.id == BugWatch.bugtrackerID) |
291 | + .find(BugTracker, BugTracker.id == BugWatch.bugtracker_id) |
292 | .group_by(BugTracker) |
293 | .order_by(Desc(Count(BugWatch))) |
294 | .config(limit=limit) |
295 | @@ -968,7 +986,7 @@ class BugTrackerSet: |
296 | IStore(Product) |
297 | .find( |
298 | Product, |
299 | - Product.bugtrackerID.is_in(ids), |
300 | + Product.bugtracker_id.is_in(ids), |
301 | Product.active == True, |
302 | ProductSet.getProductPrivacyFilter(user), |
303 | ) |
304 | @@ -978,7 +996,7 @@ class BugTrackerSet: |
305 | IStore(ProjectGroup) |
306 | .find( |
307 | ProjectGroup, |
308 | - ProjectGroup.bugtrackerID.is_in(ids), |
309 | + ProjectGroup.bugtracker_id.is_in(ids), |
310 | ProjectGroup.active == True, |
311 | ) |
312 | .order_by(ProjectGroup.name) |
313 | diff --git a/lib/lp/bugs/model/bugwatch.py b/lib/lp/bugs/model/bugwatch.py |
314 | index 2bbef55..c617a24 100644 |
315 | --- a/lib/lp/bugs/model/bugwatch.py |
316 | +++ b/lib/lp/bugs/model/bugwatch.py |
317 | @@ -105,9 +105,8 @@ class BugWatch(SQLBase): |
318 | |
319 | _table = "BugWatch" |
320 | bug = ForeignKey(dbName="bug", foreignKey="Bug", notNull=True) |
321 | - bugtracker = ForeignKey( |
322 | - dbName="bugtracker", foreignKey="BugTracker", notNull=True |
323 | - ) |
324 | + bugtracker_id = Int(name="bugtracker", allow_none=False) |
325 | + bugtracker = Reference(bugtracker_id, "BugTracker.id") |
326 | remotebug = StringCol(notNull=True) |
327 | remotestatus = StringCol(notNull=False, default=None) |
328 | remote_importance = StringCol(notNull=False, default=None) |
329 | diff --git a/lib/lp/bugs/stories/webservice/xx-bug.rst b/lib/lp/bugs/stories/webservice/xx-bug.rst |
330 | index 5423f01..cbd273f 100644 |
331 | --- a/lib/lp/bugs/stories/webservice/xx-bug.rst |
332 | +++ b/lib/lp/bugs/stories/webservice/xx-bug.rst |
333 | @@ -1510,7 +1510,7 @@ Non-admins can't disable a bugtracker through the API. |
334 | ... ) |
335 | HTTP/1.1 401 Unauthorized |
336 | ... |
337 | - (<BugTracker at ...>, 'active', 'launchpad.Admin') |
338 | + (<...BugTracker object at ...>, 'active', 'launchpad.Admin') |
339 | |
340 | Admins can, however. |
341 | |
342 | diff --git a/lib/lp/bugs/templates/bugtracker-index.pt b/lib/lp/bugs/templates/bugtracker-index.pt |
343 | index 5320747..b47fcfd 100644 |
344 | --- a/lib/lp/bugs/templates/bugtracker-index.pt |
345 | +++ b/lib/lp/bugs/templates/bugtracker-index.pt |
346 | @@ -44,7 +44,7 @@ |
347 | <div tal:replace="structure context/@@+portlet-components" /> |
348 | </div> |
349 | <div class="yui-u" |
350 | - tal:condition="context/watches"> |
351 | + tal:condition="not: context/watches/is_empty"> |
352 | <div tal:replace="structure context/@@+portlet-watches" /> |
353 | </div> |
354 | </div> |
355 | diff --git a/lib/lp/bugs/vocabularies.py b/lib/lp/bugs/vocabularies.py |
356 | index ed5f2f6..10a96f9 100644 |
357 | --- a/lib/lp/bugs/vocabularies.py |
358 | +++ b/lib/lp/bugs/vocabularies.py |
359 | @@ -48,7 +48,7 @@ from lp.registry.model.milestone import milestone_sort_key |
360 | from lp.registry.model.productseries import ProductSeries |
361 | from lp.registry.vocabularies import DistributionVocabulary |
362 | from lp.services.database.interfaces import IStore |
363 | -from lp.services.database.sqlobject import CONTAINSSTRING, OR |
364 | +from lp.services.database.sqlobject import OR |
365 | from lp.services.helpers import shortlist |
366 | from lp.services.webapp.escaping import html_escape, structured |
367 | from lp.services.webapp.interfaces import ILaunchBag |
368 | @@ -57,6 +57,7 @@ from lp.services.webapp.vocabulary import ( |
369 | IHugeVocabulary, |
370 | NamedSQLObjectVocabulary, |
371 | SQLObjectVocabularyBase, |
372 | + StormVocabularyBase, |
373 | ) |
374 | |
375 | |
376 | @@ -91,14 +92,13 @@ class BugVocabulary(SQLObjectVocabularyBase): |
377 | |
378 | |
379 | @implementer(IHugeVocabulary) |
380 | -class BugTrackerVocabulary(SQLObjectVocabularyBase): |
381 | +class BugTrackerVocabulary(StormVocabularyBase): |
382 | """All web and email based external bug trackers.""" |
383 | |
384 | displayname = "Select a bug tracker" |
385 | step_title = "Search" |
386 | _table = BugTracker |
387 | _filter = True |
388 | - _orderBy = "title" |
389 | _order_by = [BugTracker.title] |
390 | |
391 | def toTerm(self, obj): |
392 | @@ -125,10 +125,10 @@ class BugTrackerVocabulary(SQLObjectVocabularyBase): |
393 | self._filter, |
394 | BugTracker.active == True, |
395 | Or( |
396 | - CONTAINSSTRING(BugTracker.name, query), |
397 | - CONTAINSSTRING(BugTracker.title, query), |
398 | - CONTAINSSTRING(BugTracker.summary, query), |
399 | - CONTAINSSTRING(BugTracker.baseurl, query), |
400 | + BugTracker.name.contains_string(query), |
401 | + BugTracker.title.contains_string(query), |
402 | + BugTracker.summary.contains_string(query), |
403 | + BugTracker.baseurl.contains_string(query), |
404 | ), |
405 | ), |
406 | ) |
407 | diff --git a/lib/lp/registry/model/product.py b/lib/lp/registry/model/product.py |
408 | index 8a941d8..12f1035 100644 |
409 | --- a/lib/lp/registry/model/product.py |
410 | +++ b/lib/lp/registry/model/product.py |
411 | @@ -348,12 +348,8 @@ class Product( |
412 | notNull=False, |
413 | default=None, |
414 | ) |
415 | - bugtracker = ForeignKey( |
416 | - foreignKey="BugTracker", |
417 | - dbName="bugtracker", |
418 | - notNull=False, |
419 | - default=None, |
420 | - ) |
421 | + bugtracker_id = Int(name="bugtracker", allow_none=True, default=None) |
422 | + bugtracker = Reference(bugtracker_id, "BugTracker.id") |
423 | official_answers = BoolCol( |
424 | dbName="official_answers", notNull=True, default=False |
425 | ) |
426 | diff --git a/lib/lp/registry/model/projectgroup.py b/lib/lp/registry/model/projectgroup.py |
427 | index 135c433..46cd5cb 100644 |
428 | --- a/lib/lp/registry/model/projectgroup.py |
429 | +++ b/lib/lp/registry/model/projectgroup.py |
430 | @@ -164,12 +164,8 @@ class ProjectGroup( |
431 | ) |
432 | active = BoolCol(dbName="active", notNull=True, default=True) |
433 | reviewed = BoolCol(dbName="reviewed", notNull=True, default=False) |
434 | - bugtracker = ForeignKey( |
435 | - foreignKey="BugTracker", |
436 | - dbName="bugtracker", |
437 | - notNull=False, |
438 | - default=None, |
439 | - ) |
440 | + bugtracker_id = Int(name="bugtracker", allow_none=True, default=None) |
441 | + bugtracker = Reference(bugtracker_id, "BugTracker.id") |
442 | bug_reporting_guidelines = StringCol(default=None) |
443 | bug_reported_acknowledgement = StringCol(default=None) |
444 |
LGTM 👍