Merge ~cjwatson/launchpad:stormify-bugtracker into launchpad: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)
Reviewer Review Type Date Requested Status
Guruprasad Approve
Review via email: mp+435977@code.launchpad.net

Commit message

Convert BugTracker to Storm

To post a comment you must log in.
Revision history for this message
Guruprasad (lgp171188) wrote :

LGTM 👍

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/lib/lp/bugs/model/bug.py b/lib/lp/bugs/model/bug.py
2index 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(
14diff --git a/lib/lp/bugs/model/bugtracker.py b/lib/lp/bugs/model/bugtracker.py
15index 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)
313diff --git a/lib/lp/bugs/model/bugwatch.py b/lib/lp/bugs/model/bugwatch.py
314index 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)
329diff --git a/lib/lp/bugs/stories/webservice/xx-bug.rst b/lib/lp/bugs/stories/webservice/xx-bug.rst
330index 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
342diff --git a/lib/lp/bugs/templates/bugtracker-index.pt b/lib/lp/bugs/templates/bugtracker-index.pt
343index 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>
355diff --git a/lib/lp/bugs/vocabularies.py b/lib/lp/bugs/vocabularies.py
356index 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 )
407diff --git a/lib/lp/registry/model/product.py b/lib/lp/registry/model/product.py
408index 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 )
426diff --git a/lib/lp/registry/model/projectgroup.py b/lib/lp/registry/model/projectgroup.py
427index 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

Subscribers

People subscribed via source and target branches

to status/vote changes: