Merge ~cjwatson/launchpad:stormify-bug into launchpad:master
- Git
- lp:~cjwatson/launchpad
- stormify-bug
- Merge into master
Proposed by
Colin Watson
Status: | Merged |
---|---|
Approved by: | Colin Watson |
Approved revision: | 781668475e3065ef27a871302ea3b82ad5ae1c1c |
Merge reported by: | Otto Co-Pilot |
Merged at revision: | not available |
Proposed branch: | ~cjwatson/launchpad:stormify-bug |
Merge into: | launchpad:master |
Diff against target: |
602 lines (+107/-91) 21 files modified
lib/lp/answers/doc/karma.rst (+1/-1) lib/lp/bugs/browser/bug.py (+2/-5) lib/lp/bugs/browser/buglisting.py (+1/-1) lib/lp/bugs/browser/bugtask.py (+1/-1) lib/lp/bugs/doc/bug.rst (+1/-1) lib/lp/bugs/doc/bugnotificationrecipients.rst (+3/-2) lib/lp/bugs/doc/bugnotifications.rst (+1/-1) lib/lp/bugs/doc/cve.rst (+2/-1) lib/lp/bugs/interfaces/bug.py (+1/-1) lib/lp/bugs/model/bug.py (+69/-52) lib/lp/bugs/model/bugtask.py (+2/-2) lib/lp/bugs/model/bugtasksearch.py (+1/-1) lib/lp/bugs/model/personsubscriptioninfo.py (+1/-1) lib/lp/bugs/stories/bugs/xx-bug-text-pages.rst (+4/-3) lib/lp/bugs/templates/bug-portlet-duplicates.pt (+1/-1) lib/lp/bugs/tests/bugs-emailinterface.rst (+8/-8) lib/lp/bugs/vocabularies.py (+2/-3) lib/lp/registry/tests/test_person.py (+1/-1) lib/lp/scripts/harness.py (+2/-2) lib/lp/services/database/tests/test_bulk.py (+1/-1) lib/lp/services/statistics/model/statistics.py (+2/-2) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Guruprasad | Approve | ||
Review via email: mp+447358@code.launchpad.net |
Commit message
Convert Bug to Storm
Description of the change
To post a comment you must log in.
Revision history for this message
Colin Watson (cjwatson) : | # |
Revision history for this message
Guruprasad (lgp171188) : | # |
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/lib/lp/answers/doc/karma.rst b/lib/lp/answers/doc/karma.rst | |||
2 | index 5bc61ef..488a2a3 100644 | |||
3 | --- a/lib/lp/answers/doc/karma.rst | |||
4 | +++ b/lib/lp/answers/doc/karma.rst | |||
5 | @@ -216,7 +216,7 @@ Linking to a bug | |||
6 | 216 | ................ | 216 | ................ |
7 | 217 | 217 | ||
8 | 218 | >>> from lp.bugs.model.bug import Bug | 218 | >>> from lp.bugs.model.bug import Bug |
10 | 219 | >>> questionbug = firefox_question.linkBug(Bug.get(5)) | 219 | >>> questionbug = firefox_question.linkBug(IStore(Bug).get(Bug, 5)) |
11 | 220 | Karma added: action=questionlinkedtobug, product=firefox, person=name12 | 220 | Karma added: action=questionlinkedtobug, product=firefox, person=name12 |
12 | 221 | 221 | ||
13 | 222 | 222 | ||
14 | diff --git a/lib/lp/bugs/browser/bug.py b/lib/lp/bugs/browser/bug.py | |||
15 | index 3a34c23..190ba78 100644 | |||
16 | --- a/lib/lp/bugs/browser/bug.py | |||
17 | +++ b/lib/lp/bugs/browser/bug.py | |||
18 | @@ -1202,11 +1202,8 @@ class BugTextView(LaunchpadView): | |||
19 | 1202 | else: | 1202 | else: |
20 | 1203 | text.append("duplicate-of: ") | 1203 | text.append("duplicate-of: ") |
21 | 1204 | 1204 | ||
27 | 1205 | if bug.duplicates: | 1205 | dupes = " ".join(str(dupe.id) for dupe in bug.duplicates) |
28 | 1206 | dupes = " ".join(str(dupe.id) for dupe in bug.duplicates) | 1206 | text.append("duplicates: %s" % dupes) |
24 | 1207 | text.append("duplicates: %s" % dupes) | ||
25 | 1208 | else: | ||
26 | 1209 | text.append("duplicates: ") | ||
29 | 1210 | 1207 | ||
30 | 1211 | if bug.private: | 1208 | if bug.private: |
31 | 1212 | # XXX kiko 2007-10-31: this could include date_made_private and | 1209 | # XXX kiko 2007-10-31: this could include date_made_private and |
32 | diff --git a/lib/lp/bugs/browser/buglisting.py b/lib/lp/bugs/browser/buglisting.py | |||
33 | index cef7d6e..3b579c7 100644 | |||
34 | --- a/lib/lp/bugs/browser/buglisting.py | |||
35 | +++ b/lib/lp/bugs/browser/buglisting.py | |||
36 | @@ -682,7 +682,7 @@ class BugTaskListingItem: | |||
37 | 682 | assignee = None | 682 | assignee = None |
38 | 683 | if self.assignee_id is not None: | 683 | if self.assignee_id is not None: |
39 | 684 | assignee = self.people[self.assignee_id].displayname | 684 | assignee = self.people[self.assignee_id].displayname |
41 | 685 | reporter = self.people[self.bug.ownerID] | 685 | reporter = self.people[self.bug.owner_id] |
42 | 686 | 686 | ||
43 | 687 | # the case that there is no target context (e.g. viewing bug that | 687 | # the case that there is no target context (e.g. viewing bug that |
44 | 688 | # are related to a user account) is intercepted | 688 | # are related to a user account) is intercepted |
45 | diff --git a/lib/lp/bugs/browser/bugtask.py b/lib/lp/bugs/browser/bugtask.py | |||
46 | index ad3a7a2..adedd6b 100644 | |||
47 | --- a/lib/lp/bugs/browser/bugtask.py | |||
48 | +++ b/lib/lp/bugs/browser/bugtask.py | |||
49 | @@ -435,7 +435,7 @@ class BugTaskView(LaunchpadView, BugViewMixin, FeedsMixin): | |||
50 | 435 | self.context = context | 435 | self.context = context |
51 | 436 | list( | 436 | list( |
52 | 437 | getUtility(IPersonSet).getPrecachedPersonsFromIDs( | 437 | getUtility(IPersonSet).getPrecachedPersonsFromIDs( |
54 | 438 | [self.context.bug.ownerID], need_validity=True | 438 | [self.context.bug.owner_id], need_validity=True |
55 | 439 | ) | 439 | ) |
56 | 440 | ) | 440 | ) |
57 | 441 | 441 | ||
58 | diff --git a/lib/lp/bugs/doc/bug.rst b/lib/lp/bugs/doc/bug.rst | |||
59 | index e1c54e5..7893e52 100644 | |||
60 | --- a/lib/lp/bugs/doc/bug.rst | |||
61 | +++ b/lib/lp/bugs/doc/bug.rst | |||
62 | @@ -263,7 +263,7 @@ private: | |||
63 | 263 | >>> from lp.bugs.model.bug import Bug | 263 | >>> from lp.bugs.model.bug import Bug |
64 | 264 | >>> from lp.services.database.interfaces import IStore | 264 | >>> from lp.services.database.interfaces import IStore |
65 | 265 | 265 | ||
67 | 266 | >>> all_bugs = set(IStore(Bug).find(Bug).values(Bug.id)) | 266 | >>> all_bugs = set(IStore(Bug).find(Bug.id)) |
68 | 267 | 267 | ||
69 | 268 | >>> taskset = getUtility(IBugTaskSet) | 268 | >>> taskset = getUtility(IBugTaskSet) |
70 | 269 | >>> def hidden_bugs(): | 269 | >>> def hidden_bugs(): |
71 | diff --git a/lib/lp/bugs/doc/bugnotificationrecipients.rst b/lib/lp/bugs/doc/bugnotificationrecipients.rst | |||
72 | index c633ea9..c3688c9 100644 | |||
73 | --- a/lib/lp/bugs/doc/bugnotificationrecipients.rst | |||
74 | +++ b/lib/lp/bugs/doc/bugnotificationrecipients.rst | |||
75 | @@ -14,7 +14,8 @@ action: | |||
76 | 14 | >>> from lp.bugs.model.bug import Bug | 14 | >>> from lp.bugs.model.bug import Bug |
77 | 15 | >>> from lp.registry.model.distribution import Distribution | 15 | >>> from lp.registry.model.distribution import Distribution |
78 | 16 | >>> from lp.registry.model.product import Product | 16 | >>> from lp.registry.model.product import Product |
80 | 17 | >>> bug_one = Bug.get(1) | 17 | >>> from lp.services.database.interfaces import IStore |
81 | 18 | >>> bug_one = IStore(Bug).get(Bug, 1) | ||
82 | 18 | >>> recipients = bug_one.getBugNotificationRecipients() | 19 | >>> recipients = bug_one.getBugNotificationRecipients() |
83 | 19 | 20 | ||
84 | 20 | The instance of BugNotificationRecipients we get back correctly | 21 | The instance of BugNotificationRecipients we get back correctly |
85 | @@ -124,7 +125,7 @@ additional step is involved. A BugNotificationRecipients instance is | |||
86 | 124 | created, annotating that it represents a master bug (of which we are a | 125 | created, annotating that it represents a master bug (of which we are a |
87 | 125 | duplicate of). | 126 | duplicate of). |
88 | 126 | 127 | ||
90 | 127 | >>> bug_two = Bug.get(2) | 128 | >>> bug_two = IStore(Bug).get(Bug, 2) |
91 | 128 | >>> recipients = BugNotificationRecipients(duplicateof=bug_two) | 129 | >>> recipients = BugNotificationRecipients(duplicateof=bug_two) |
92 | 129 | 130 | ||
93 | 130 | >>> foo_bar = personset.getByEmail("foo.bar@canonical.com") | 131 | >>> foo_bar = personset.getByEmail("foo.bar@canonical.com") |
94 | diff --git a/lib/lp/bugs/doc/bugnotifications.rst b/lib/lp/bugs/doc/bugnotifications.rst | |||
95 | index c7aaeb5..4b4d755 100644 | |||
96 | --- a/lib/lp/bugs/doc/bugnotifications.rst | |||
97 | +++ b/lib/lp/bugs/doc/bugnotifications.rst | |||
98 | @@ -354,7 +354,7 @@ this document: | |||
99 | 354 | ... status=CveStatus.ENTRY, | 354 | ... status=CveStatus.ENTRY, |
100 | 355 | ... ) | 355 | ... ) |
101 | 356 | >>> from lp.bugs.model.bug import Bug | 356 | >>> from lp.bugs.model.bug import Bug |
103 | 357 | >>> bug = Bug.get(1) | 357 | >>> bug = IStore(Bug).get(Bug, 1) |
104 | 358 | >>> bugcve = cve.linkBug(bug) # note this creates the event and notifies | 358 | >>> bugcve = cve.linkBug(bug) # note this creates the event and notifies |
105 | 359 | 359 | ||
106 | 360 | >>> latest_notification = ( | 360 | >>> latest_notification = ( |
107 | diff --git a/lib/lp/bugs/doc/cve.rst b/lib/lp/bugs/doc/cve.rst | |||
108 | index e06fe5f..86b2a3f 100644 | |||
109 | --- a/lib/lp/bugs/doc/cve.rst | |||
110 | +++ b/lib/lp/bugs/doc/cve.rst | |||
111 | @@ -75,7 +75,8 @@ You can link a CVE to a bug. You can also see which CVEs are currently | |||
112 | 75 | linked to a bug: | 75 | linked to a bug: |
113 | 76 | 76 | ||
114 | 77 | >>> from lp.bugs.model.bug import Bug | 77 | >>> from lp.bugs.model.bug import Bug |
116 | 78 | >>> b = Bug.get(1) | 78 | >>> from lp.services.database.interfaces import IStore |
117 | 79 | >>> b = IStore(Bug).get(Bug, 1) | ||
118 | 79 | >>> for c in b.cves: | 80 | >>> for c in b.cves: |
119 | 80 | ... print(c.displayname) | 81 | ... print(c.displayname) |
120 | 81 | ... | 82 | ... |
121 | diff --git a/lib/lp/bugs/interfaces/bug.py b/lib/lp/bugs/interfaces/bug.py | |||
122 | index e02f2ff..acf5c6e 100644 | |||
123 | --- a/lib/lp/bugs/interfaces/bug.py | |||
124 | +++ b/lib/lp/bugs/interfaces/bug.py | |||
125 | @@ -240,7 +240,7 @@ class IBugView(Interface): | |||
126 | 240 | max_length=50000, | 240 | max_length=50000, |
127 | 241 | ) | 241 | ) |
128 | 242 | ) | 242 | ) |
130 | 243 | ownerID = Int(title=_("Owner"), required=True, readonly=True) | 243 | owner_id = Int(title=_("Owner"), required=True, readonly=True) |
131 | 244 | owner = exported( | 244 | owner = exported( |
132 | 245 | Reference(IPerson, title=_("The owner's IPerson"), readonly=True) | 245 | Reference(IPerson, title=_("The owner's IPerson"), readonly=True) |
133 | 246 | ) | 246 | ) |
134 | diff --git a/lib/lp/bugs/model/bug.py b/lib/lp/bugs/model/bug.py | |||
135 | index dc2d66b..2db7afe 100644 | |||
136 | --- a/lib/lp/bugs/model/bug.py | |||
137 | +++ b/lib/lp/bugs/model/bug.py | |||
138 | @@ -33,6 +33,7 @@ from lazr.lifecycle.snapshot import Snapshot | |||
139 | 33 | from lazr.restful.declarations import error_status | 33 | from lazr.restful.declarations import error_status |
140 | 34 | from storm.expr import ( | 34 | from storm.expr import ( |
141 | 35 | SQL, | 35 | SQL, |
142 | 36 | Add, | ||
143 | 36 | And, | 37 | And, |
144 | 37 | Coalesce, | 38 | Coalesce, |
145 | 38 | Desc, | 39 | Desc, |
146 | @@ -170,19 +171,10 @@ from lp.registry.model.pillar import pillar_sort_key | |||
147 | 170 | from lp.registry.model.teammembership import TeamParticipation | 171 | from lp.registry.model.teammembership import TeamParticipation |
148 | 171 | from lp.services.config import config | 172 | from lp.services.config import config |
149 | 172 | from lp.services.database import bulk | 173 | from lp.services.database import bulk |
152 | 173 | from lp.services.database.constants import UTC_NOW | 174 | from lp.services.database.constants import DEFAULT, UTC_NOW |
151 | 174 | from lp.services.database.datetimecol import UtcDateTimeCol | ||
153 | 175 | from lp.services.database.decoratedresultset import DecoratedResultSet | 175 | from lp.services.database.decoratedresultset import DecoratedResultSet |
154 | 176 | from lp.services.database.enumcol import DBEnum | 176 | from lp.services.database.enumcol import DBEnum |
155 | 177 | from lp.services.database.interfaces import IStore | 177 | from lp.services.database.interfaces import IStore |
156 | 178 | from lp.services.database.sqlbase import SQLBase, sqlvalues | ||
157 | 179 | from lp.services.database.sqlobject import ( | ||
158 | 180 | ForeignKey, | ||
159 | 181 | IntCol, | ||
160 | 182 | SQLMultipleJoin, | ||
161 | 183 | SQLObjectNotFound, | ||
162 | 184 | StringCol, | ||
163 | 185 | ) | ||
164 | 186 | from lp.services.database.stormbase import StormBase | 178 | from lp.services.database.stormbase import StormBase |
165 | 187 | from lp.services.database.stormexpr import WithMaterialized | 179 | from lp.services.database.stormexpr import WithMaterialized |
166 | 188 | from lp.services.fields import DuplicateBug | 180 | from lp.services.fields import DuplicateBug |
167 | @@ -261,7 +253,6 @@ class BugTag(StormBase): | |||
168 | 261 | __storm_table__ = "BugTag" | 253 | __storm_table__ = "BugTag" |
169 | 262 | id = Int(primary=True) | 254 | id = Int(primary=True) |
170 | 263 | 255 | ||
171 | 264 | bug = ForeignKey(dbName="bug", foreignKey="Bug", notNull=True) | ||
172 | 265 | bug_id = Int(name="bug", allow_none=False) | 256 | bug_id = Int(name="bug", allow_none=False) |
173 | 266 | bug = Reference(bug_id, "Bug.id") | 257 | bug = Reference(bug_id, "Bug.id") |
174 | 267 | 258 | ||
175 | @@ -356,33 +347,36 @@ def update_bug_heat(bug_ids): | |||
176 | 356 | 347 | ||
177 | 357 | 348 | ||
178 | 358 | @implementer(IBug, IInformationType) | 349 | @implementer(IBug, IInformationType) |
180 | 359 | class Bug(SQLBase, InformationTypeMixin): | 350 | class Bug(StormBase, InformationTypeMixin): |
181 | 360 | """A bug.""" | 351 | """A bug.""" |
182 | 361 | 352 | ||
184 | 362 | _defaultOrder = "-id" | 353 | __storm_table__ = "Bug" |
185 | 354 | __storm_order__ = "-id" | ||
186 | 363 | 355 | ||
187 | 364 | # db field names | 356 | # db field names |
196 | 365 | name = StringCol(unique=True, default=None) | 357 | id = Int(primary=True) |
197 | 366 | title = StringCol(notNull=True) | 358 | name = Unicode(default=None) |
198 | 367 | description = StringCol(notNull=False, default=None) | 359 | title = Unicode(allow_none=False) |
199 | 368 | owner = ForeignKey( | 360 | description = Unicode(allow_none=True, default=None) |
200 | 369 | dbName="owner", | 361 | owner_id = Int( |
201 | 370 | foreignKey="Person", | 362 | name="owner", validator=validate_public_person, allow_none=False |
202 | 371 | storm_validator=validate_public_person, | 363 | ) |
203 | 372 | notNull=True, | 364 | owner = Reference(owner_id, "Person.id") |
204 | 365 | duplicateof_id = Int(name="duplicateof", default=None) | ||
205 | 366 | duplicateof = Reference(duplicateof_id, "Bug.id") | ||
206 | 367 | datecreated = DateTime( | ||
207 | 368 | allow_none=False, default=UTC_NOW, tzinfo=timezone.utc | ||
208 | 369 | ) | ||
209 | 370 | date_last_updated = DateTime( | ||
210 | 371 | allow_none=False, default=UTC_NOW, tzinfo=timezone.utc | ||
211 | 373 | ) | 372 | ) |
214 | 374 | duplicateof = ForeignKey( | 373 | date_made_private = DateTime( |
215 | 375 | dbName="duplicateof", foreignKey="Bug", default=None | 374 | allow_none=True, default=None, tzinfo=timezone.utc |
216 | 376 | ) | 375 | ) |
225 | 377 | datecreated = UtcDateTimeCol(notNull=True, default=UTC_NOW) | 376 | who_made_private_id = Int( |
226 | 378 | date_last_updated = UtcDateTimeCol(notNull=True, default=UTC_NOW) | 377 | name="who_made_private", validator=validate_public_person, default=None |
219 | 379 | date_made_private = UtcDateTimeCol(notNull=False, default=None) | ||
220 | 380 | who_made_private = ForeignKey( | ||
221 | 381 | dbName="who_made_private", | ||
222 | 382 | foreignKey="Person", | ||
223 | 383 | storm_validator=validate_public_person, | ||
224 | 384 | default=None, | ||
227 | 385 | ) | 378 | ) |
228 | 379 | who_made_private = Reference(who_made_private_id, "Person.id") | ||
229 | 386 | information_type = DBEnum( | 380 | information_type = DBEnum( |
230 | 387 | enum=InformationType, allow_none=False, default=InformationType.PUBLIC | 381 | enum=InformationType, allow_none=False, default=InformationType.PUBLIC |
231 | 388 | ) | 382 | ) |
232 | @@ -397,25 +391,44 @@ class Bug(SQLBase, InformationTypeMixin): | |||
233 | 397 | BugWatch.bug_id, | 391 | BugWatch.bug_id, |
234 | 398 | order_by=(BugWatch.bugtracker_id, BugWatch.remotebug), | 392 | order_by=(BugWatch.bugtracker_id, BugWatch.remotebug), |
235 | 399 | ) | 393 | ) |
237 | 400 | duplicates = SQLMultipleJoin("Bug", joinColumn="duplicateof", orderBy="id") | 394 | duplicates = ReferenceSet("id", "Bug.duplicateof_id", order_by="Bug.id") |
238 | 401 | linked_bugbranches = ReferenceSet( | 395 | linked_bugbranches = ReferenceSet( |
239 | 402 | "id", BugBranch.bug_id, order_by=BugBranch.id | 396 | "id", BugBranch.bug_id, order_by=BugBranch.id |
240 | 403 | ) | 397 | ) |
249 | 404 | date_last_message = UtcDateTimeCol(default=None) | 398 | date_last_message = DateTime(default=None, tzinfo=timezone.utc) |
250 | 405 | number_of_duplicates = IntCol(notNull=True, default=0) | 399 | number_of_duplicates = Int(allow_none=False, default=0) |
251 | 406 | message_count = IntCol(notNull=True, default=0) | 400 | message_count = Int(allow_none=False, default=0) |
252 | 407 | users_affected_count = IntCol(notNull=True, default=0) | 401 | users_affected_count = Int(allow_none=False, default=0) |
253 | 408 | users_unaffected_count = IntCol(notNull=True, default=0) | 402 | users_unaffected_count = Int(allow_none=False, default=0) |
254 | 409 | heat = IntCol(notNull=True, default=0) | 403 | heat = Int(allow_none=False, default=0) |
255 | 410 | heat_last_updated = UtcDateTimeCol(default=None) | 404 | heat_last_updated = DateTime(default=None, tzinfo=timezone.utc) |
256 | 411 | latest_patch_uploaded = UtcDateTimeCol(default=None) | 405 | latest_patch_uploaded = DateTime(default=None, tzinfo=timezone.utc) |
257 | 412 | lock_status = DBEnum( | 406 | lock_status = DBEnum( |
258 | 413 | name="lock_status", | 407 | name="lock_status", |
259 | 414 | enum=BugLockStatus, | 408 | enum=BugLockStatus, |
260 | 415 | allow_none=False, | 409 | allow_none=False, |
261 | 416 | default=BugLockStatus.UNLOCKED, | 410 | default=BugLockStatus.UNLOCKED, |
262 | 417 | ) | 411 | ) |
264 | 418 | lock_reason = StringCol(notNull=False, default=None) | 412 | lock_reason = Unicode(allow_none=True, default=None) |
265 | 413 | |||
266 | 414 | def __init__( | ||
267 | 415 | self, | ||
268 | 416 | title, | ||
269 | 417 | owner, | ||
270 | 418 | description=None, | ||
271 | 419 | datecreated=DEFAULT, | ||
272 | 420 | date_made_private=None, | ||
273 | 421 | who_made_private=None, | ||
274 | 422 | information_type=InformationType.PUBLIC, | ||
275 | 423 | ): | ||
276 | 424 | super().__init__() | ||
277 | 425 | self.title = title | ||
278 | 426 | self.owner = owner | ||
279 | 427 | self.description = description | ||
280 | 428 | self.datecreated = datecreated | ||
281 | 429 | self.date_made_private = date_made_private | ||
282 | 430 | self.who_made_private = who_made_private | ||
283 | 431 | self.information_type = information_type | ||
284 | 419 | 432 | ||
285 | 420 | @property | 433 | @property |
286 | 421 | def locked(self): | 434 | def locked(self): |
287 | @@ -1905,7 +1918,7 @@ class Bug(SQLBase, InformationTypeMixin): | |||
288 | 1905 | def _question_from_bug(self): | 1918 | def _question_from_bug(self): |
289 | 1906 | for question in self.questions: | 1919 | for question in self.questions: |
290 | 1907 | if ( | 1920 | if ( |
292 | 1908 | question.owner_id == self.ownerID | 1921 | question.owner_id == self.owner_id |
293 | 1909 | and question.datecreated == self.datecreated | 1922 | and question.datecreated == self.datecreated |
294 | 1910 | ): | 1923 | ): |
295 | 1911 | return question | 1924 | return question |
296 | @@ -1934,10 +1947,12 @@ class Bug(SQLBase, InformationTypeMixin): | |||
297 | 1934 | # range. | 1947 | # range. |
298 | 1935 | slices.append( | 1948 | slices.append( |
299 | 1936 | BugMessage.index | 1949 | BugMessage.index |
304 | 1937 | >= SQL( | 1950 | >= Add( |
305 | 1938 | "(select max(index) from " | 1951 | Select( |
306 | 1939 | "bugmessage where bug=%s) + 1 - %s" | 1952 | Max(BugMessage.index), |
307 | 1940 | % (sqlvalues(self.id, -slice.start)) | 1953 | where=(BugMessage.bug == self), |
308 | 1954 | ), | ||
309 | 1955 | slice.start + 1, | ||
310 | 1941 | ) | 1956 | ) |
311 | 1942 | ) | 1957 | ) |
312 | 1943 | else: | 1958 | else: |
313 | @@ -2397,7 +2412,7 @@ class Bug(SQLBase, InformationTypeMixin): | |||
314 | 2397 | try: | 2412 | try: |
315 | 2398 | if duplicate_of is not None: | 2413 | if duplicate_of is not None: |
316 | 2399 | field._validate(duplicate_of) | 2414 | field._validate(duplicate_of) |
318 | 2400 | if self.duplicates: | 2415 | if not self.duplicates.is_empty(): |
319 | 2401 | user = getUtility(ILaunchBag).user | 2416 | user = getUtility(ILaunchBag).user |
320 | 2402 | for duplicate in self.duplicates: | 2417 | for duplicate in self.duplicates: |
321 | 2403 | old_value = duplicate.duplicateof | 2418 | old_value = duplicate.duplicateof |
322 | @@ -3204,17 +3219,17 @@ class BugSet: | |||
323 | 3204 | 3219 | ||
324 | 3205 | def get(self, bugid): | 3220 | def get(self, bugid): |
325 | 3206 | """See `IBugSet`.""" | 3221 | """See `IBugSet`.""" |
329 | 3207 | try: | 3222 | bug = IStore(Bug).get(Bug, int(bugid)) |
330 | 3208 | return Bug.get(bugid) | 3223 | if bug is None: |
328 | 3209 | except SQLObjectNotFound: | ||
331 | 3210 | raise NotFoundError( | 3224 | raise NotFoundError( |
332 | 3211 | "Unable to locate bug with ID %s." % str(bugid) | 3225 | "Unable to locate bug with ID %s." % str(bugid) |
333 | 3212 | ) | 3226 | ) |
334 | 3227 | return bug | ||
335 | 3213 | 3228 | ||
336 | 3214 | def getByNameOrID(self, bugid): | 3229 | def getByNameOrID(self, bugid): |
337 | 3215 | """See `IBugSet`.""" | 3230 | """See `IBugSet`.""" |
338 | 3216 | if self.valid_bug_name_re.match(bugid): | 3231 | if self.valid_bug_name_re.match(bugid): |
340 | 3217 | bug = Bug.selectOneBy(name=bugid) | 3232 | bug = IStore(Bug).find(Bug, name=bugid).one() |
341 | 3218 | if bug is None: | 3233 | if bug is None: |
342 | 3219 | raise NotFoundError("Unable to locate bug with ID %s." % bugid) | 3234 | raise NotFoundError("Unable to locate bug with ID %s." % bugid) |
343 | 3220 | else: | 3235 | else: |
344 | @@ -3334,6 +3349,8 @@ class BugSet: | |||
345 | 3334 | if params.tags: | 3349 | if params.tags: |
346 | 3335 | bug.tags = params.tags | 3350 | bug.tags = params.tags |
347 | 3336 | 3351 | ||
348 | 3352 | Store.of(bug).flush() | ||
349 | 3353 | |||
350 | 3337 | # Link the bug to the message. | 3354 | # Link the bug to the message. |
351 | 3338 | BugMessage(bug=bug, message=params.msg, index=0) | 3355 | BugMessage(bug=bug, message=params.msg, index=0) |
352 | 3339 | 3356 | ||
353 | @@ -3497,7 +3514,7 @@ def generate_subscription_with(bug, person): | |||
354 | 3497 | BugSubscription, | 3514 | BugSubscription, |
355 | 3498 | Join(Bug, Bug.id == BugSubscription.bug_id), | 3515 | Join(Bug, Bug.id == BugSubscription.bug_id), |
356 | 3499 | ], | 3516 | ], |
358 | 3500 | where=Or(Bug.id == bug.id, Bug.duplicateofID == bug.id), | 3517 | where=Or(Bug.id == bug.id, Bug.duplicateof_id == bug.id), |
359 | 3501 | ), | 3518 | ), |
360 | 3502 | ), | 3519 | ), |
361 | 3503 | WithMaterialized( | 3520 | WithMaterialized( |
362 | diff --git a/lib/lp/bugs/model/bugtask.py b/lib/lp/bugs/model/bugtask.py | |||
363 | index 2a0dce7..2606bed 100644 | |||
364 | --- a/lib/lp/bugs/model/bugtask.py | |||
365 | +++ b/lib/lp/bugs/model/bugtask.py | |||
366 | @@ -1019,7 +1019,7 @@ class BugTask(StormBase): | |||
367 | 1019 | return True | 1019 | return True |
368 | 1020 | elif ( | 1020 | elif ( |
369 | 1021 | self.status == BugTaskStatus.FIXRELEASED | 1021 | self.status == BugTaskStatus.FIXRELEASED |
371 | 1022 | and user.id != self.bug.ownerID | 1022 | and user.id != self.bug.owner_id |
372 | 1023 | and not user.inTeam(self.bug.owner) | 1023 | and not user.inTeam(self.bug.owner) |
373 | 1024 | ): | 1024 | ): |
374 | 1025 | # The bug reporter can reopen a Fix Released bug. | 1025 | # The bug reporter can reopen a Fix Released bug. |
375 | @@ -1606,7 +1606,7 @@ class BugTaskSet: | |||
376 | 1606 | 1606 | ||
377 | 1607 | people_ids = set( | 1607 | people_ids = set( |
378 | 1608 | [bugtask.assignee_id for bugtask in bugtasks] | 1608 | [bugtask.assignee_id for bugtask in bugtasks] |
380 | 1609 | + [bugtask.bug.ownerID for bugtask in bugtasks] | 1609 | + [bugtask.bug.owner_id for bugtask in bugtasks] |
381 | 1610 | ) | 1610 | ) |
382 | 1611 | people = getUtility(IPersonSet).getPrecachedPersonsFromIDs(people_ids) | 1611 | people = getUtility(IPersonSet).getPrecachedPersonsFromIDs(people_ids) |
383 | 1612 | return {person.id: person for person in people} | 1612 | return {person.id: person for person in people} |
384 | diff --git a/lib/lp/bugs/model/bugtasksearch.py b/lib/lp/bugs/model/bugtasksearch.py | |||
385 | index 9ec4c25..7f7dd20 100644 | |||
386 | --- a/lib/lp/bugs/model/bugtasksearch.py | |||
387 | +++ b/lib/lp/bugs/model/bugtasksearch.py | |||
388 | @@ -1105,7 +1105,7 @@ def _build_exclude_conjoined_clause(milestone): | |||
389 | 1105 | """ | 1105 | """ |
390 | 1106 | # XXX: EdwinGrubbs 2010-12-15 bug=682989 | 1106 | # XXX: EdwinGrubbs 2010-12-15 bug=682989 |
391 | 1107 | # (ConjoinedPrimary.bug == X) produces the wrong sql, but | 1107 | # (ConjoinedPrimary.bug == X) produces the wrong sql, but |
393 | 1108 | # (ConjoinedPrimary.bugID == X) works right. This bug applies to | 1108 | # (ConjoinedPrimary.bug_id == X) works right. This bug applies to |
394 | 1109 | # all foreign keys on the ClassAlias. | 1109 | # all foreign keys on the ClassAlias. |
395 | 1110 | 1110 | ||
396 | 1111 | # Perform a LEFT JOIN to the conjoined primary bugtask. If the | 1111 | # Perform a LEFT JOIN to the conjoined primary bugtask. If the |
397 | diff --git a/lib/lp/bugs/model/personsubscriptioninfo.py b/lib/lp/bugs/model/personsubscriptioninfo.py | |||
398 | index 8e28966..aaff8d4 100644 | |||
399 | --- a/lib/lp/bugs/model/personsubscriptioninfo.py | |||
400 | +++ b/lib/lp/bugs/model/personsubscriptioninfo.py | |||
401 | @@ -184,7 +184,7 @@ class PersonSubscriptions: | |||
402 | 184 | # Preload bug owners, then all pillars. | 184 | # Preload bug owners, then all pillars. |
403 | 185 | list( | 185 | list( |
404 | 186 | getUtility(IPersonSet).getPrecachedPersonsFromIDs( | 186 | getUtility(IPersonSet).getPrecachedPersonsFromIDs( |
406 | 187 | [bug.ownerID for bug in bugs] | 187 | [bug.owner_id for bug in bugs] |
407 | 188 | ) | 188 | ) |
408 | 189 | ) | 189 | ) |
409 | 190 | all_tasks = load_referencing(BugTask, bugs, ["bug_id"]) | 190 | all_tasks = load_referencing(BugTask, bugs, ["bug_id"]) |
410 | diff --git a/lib/lp/bugs/stories/bugs/xx-bug-text-pages.rst b/lib/lp/bugs/stories/bugs/xx-bug-text-pages.rst | |||
411 | index bfa0b6e..116baaa 100644 | |||
412 | --- a/lib/lp/bugs/stories/bugs/xx-bug-text-pages.rst | |||
413 | +++ b/lib/lp/bugs/stories/bugs/xx-bug-text-pages.rst | |||
414 | @@ -9,14 +9,15 @@ To demonstrate this feature, we'll use bug 1. | |||
415 | 9 | We'll start by adding some attachments to the bug: | 9 | We'll start by adding some attachments to the bug: |
416 | 10 | 10 | ||
417 | 11 | >>> from io import BytesIO | 11 | >>> from io import BytesIO |
418 | 12 | >>> from lp.services.database.sqlbase import flush_database_updates | ||
419 | 13 | >>> from lp.testing import login, logout | ||
420 | 14 | >>> from lp.bugs.model.bug import Bug | 12 | >>> from lp.bugs.model.bug import Bug |
421 | 15 | >>> from lp.registry.model.person import Person | 13 | >>> from lp.registry.model.person import Person |
422 | 14 | >>> from lp.services.database.interfaces import IStore | ||
423 | 15 | >>> from lp.services.database.sqlbase import flush_database_updates | ||
424 | 16 | >>> from lp.testing import login, logout | ||
425 | 16 | >>> login("foo.bar@canonical.com") | 17 | >>> login("foo.bar@canonical.com") |
426 | 17 | >>> mark = Person.selectOneBy(name="mark") | 18 | >>> mark = Person.selectOneBy(name="mark") |
427 | 18 | >>> mark.display_name = "M\xe1rk Sh\xfattlew\xf2rth" | 19 | >>> mark.display_name = "M\xe1rk Sh\xfattlew\xf2rth" |
429 | 19 | >>> bug = Bug.get(1) | 20 | >>> bug = IStore(Bug).get(Bug, 1) |
430 | 20 | >>> content = BytesIO(b"<html><body>bogus</body></html>") | 21 | >>> content = BytesIO(b"<html><body>bogus</body></html>") |
431 | 21 | >>> a1 = bug.addAttachment( | 22 | >>> a1 = bug.addAttachment( |
432 | 22 | ... mark, | 23 | ... mark, |
433 | diff --git a/lib/lp/bugs/templates/bug-portlet-duplicates.pt b/lib/lp/bugs/templates/bug-portlet-duplicates.pt | |||
434 | index 0a11c96..a2f0afa 100644 | |||
435 | --- a/lib/lp/bugs/templates/bug-portlet-duplicates.pt | |||
436 | +++ b/lib/lp/bugs/templates/bug-portlet-duplicates.pt | |||
437 | @@ -3,7 +3,7 @@ | |||
438 | 3 | xmlns:metal="http://xml.zope.org/namespaces/metal" | 3 | xmlns:metal="http://xml.zope.org/namespaces/metal" |
439 | 4 | xmlns:i18n="http://xml.zope.org/namespaces/i18n" | 4 | xmlns:i18n="http://xml.zope.org/namespaces/i18n" |
440 | 5 | class="portlet" id="portlet-duplicates" | 5 | class="portlet" id="portlet-duplicates" |
442 | 6 | tal:condition="context/duplicates"> | 6 | tal:condition="not: context/duplicates/is_empty"> |
443 | 7 | <h2>Duplicates of this bug</h2> | 7 | <h2>Duplicates of this bug</h2> |
444 | 8 | <ul> | 8 | <ul> |
445 | 9 | <li tal:repeat="dupe view/duplicates"> | 9 | <li tal:repeat="dupe view/duplicates"> |
446 | diff --git a/lib/lp/bugs/tests/bugs-emailinterface.rst b/lib/lp/bugs/tests/bugs-emailinterface.rst | |||
447 | index 1a8a900..6555137 100644 | |||
448 | --- a/lib/lp/bugs/tests/bugs-emailinterface.rst | |||
449 | +++ b/lib/lp/bugs/tests/bugs-emailinterface.rst | |||
450 | @@ -2085,7 +2085,7 @@ An empty unsigned mail to new@malone: | |||
451 | 2085 | If we submit an email with no affects command, it is rejected. | 2085 | If we submit an email with no affects command, it is rejected. |
452 | 2086 | 2086 | ||
453 | 2087 | >>> from lp.bugs.model.bug import Bug | 2087 | >>> from lp.bugs.model.bug import Bug |
455 | 2088 | >>> before_count = Bug.select().count() | 2088 | >>> before_count = IStore(Bug).find(Bug).count() |
456 | 2089 | >>> submit_mail = b"""From: Foo Bar <foo.bar@canonical.com> | 2089 | >>> submit_mail = b"""From: Foo Bar <foo.bar@canonical.com> |
457 | 2090 | ... To: new@bugs.launchpad.ubuntu.com | 2090 | ... To: new@bugs.launchpad.ubuntu.com |
458 | 2091 | ... Date: Fri Jun 17 10:20:23 BST 2005 | 2091 | ... Date: Fri Jun 17 10:20:23 BST 2005 |
459 | @@ -2099,7 +2099,7 @@ If we submit an email with no affects command, it is rejected. | |||
460 | 2099 | ... """ | 2099 | ... """ |
461 | 2100 | 2100 | ||
462 | 2101 | >>> process_email(submit_mail) | 2101 | >>> process_email(submit_mail) |
464 | 2102 | >>> before_count == Bug.select().count() | 2102 | >>> before_count == IStore(Bug).find(Bug).count() |
465 | 2103 | True | 2103 | True |
466 | 2104 | 2104 | ||
467 | 2105 | >>> print_latest_email() | 2105 | >>> print_latest_email() |
468 | @@ -2118,7 +2118,7 @@ required. If it is missing, the message is also rejected. | |||
469 | 2118 | XXX: Gavin Panella 2009-07-24 bug=404010: The need for this test | 2118 | XXX: Gavin Panella 2009-07-24 bug=404010: The need for this test |
470 | 2119 | arises from the implementation of MaloneHandler. | 2119 | arises from the implementation of MaloneHandler. |
471 | 2120 | 2120 | ||
473 | 2121 | >>> before_count = Bug.select().count() | 2121 | >>> before_count = IStore(Bug).find(Bug).count() |
474 | 2122 | >>> submit_mail = b"""From: Foo Bar <foo.bar@canonical.com> | 2122 | >>> submit_mail = b"""From: Foo Bar <foo.bar@canonical.com> |
475 | 2123 | ... To: new@bugs.launchpad.ubuntu.com | 2123 | ... To: new@bugs.launchpad.ubuntu.com |
476 | 2124 | ... Date: Fri Jun 17 10:20:23 BST 2005 | 2124 | ... Date: Fri Jun 17 10:20:23 BST 2005 |
477 | @@ -2130,7 +2130,7 @@ arises from the implementation of MaloneHandler. | |||
478 | 2130 | ... """ | 2130 | ... """ |
479 | 2131 | 2131 | ||
480 | 2132 | >>> process_email(submit_mail) | 2132 | >>> process_email(submit_mail) |
482 | 2133 | >>> before_count == Bug.select().count() | 2133 | >>> before_count == IStore(Bug).find(Bug).count() |
483 | 2134 | True | 2134 | True |
484 | 2135 | 2135 | ||
485 | 2136 | >>> print_latest_email() | 2136 | >>> print_latest_email() |
486 | @@ -2151,7 +2151,7 @@ edit@bugs). | |||
487 | 2151 | XXX: Gavin Panella 2009-07-24 bug=404010: The need for this test | 2151 | XXX: Gavin Panella 2009-07-24 bug=404010: The need for this test |
488 | 2152 | arises from the implementation of MaloneHandler. | 2152 | arises from the implementation of MaloneHandler. |
489 | 2153 | 2153 | ||
491 | 2154 | >>> before_count = Bug.select().count() | 2154 | >>> before_count = IStore(Bug).find(Bug).count() |
492 | 2155 | >>> submit_mail = b"""\ | 2155 | >>> submit_mail = b"""\ |
493 | 2156 | ... From: Foo Bar <foo.bar@canonical.com> | 2156 | ... From: Foo Bar <foo.bar@canonical.com> |
494 | 2157 | ... To: new@bugs.launchpad.ubuntu.com | 2157 | ... To: new@bugs.launchpad.ubuntu.com |
495 | @@ -2162,7 +2162,7 @@ arises from the implementation of MaloneHandler. | |||
496 | 2162 | ... """ | 2162 | ... """ |
497 | 2163 | 2163 | ||
498 | 2164 | >>> process_email(submit_mail) | 2164 | >>> process_email(submit_mail) |
500 | 2165 | >>> before_count == Bug.select().count() | 2165 | >>> before_count == IStore(Bug).find(Bug).count() |
501 | 2166 | True | 2166 | True |
502 | 2167 | 2167 | ||
503 | 2168 | >>> print_latest_email() | 2168 | >>> print_latest_email() |
504 | @@ -2184,7 +2184,7 @@ bug-related commands do blow up before the check for a bugtask is | |||
505 | 2184 | reached. For example, unsubscribing oneself from a private bug then | 2184 | reached. For example, unsubscribing oneself from a private bug then |
506 | 2185 | linking a CVE. | 2185 | linking a CVE. |
507 | 2186 | 2186 | ||
509 | 2187 | >>> before_count = Bug.select().count() | 2187 | >>> before_count = IStore(Bug).find(Bug).count() |
510 | 2188 | >>> submit_mail = b"""\ | 2188 | >>> submit_mail = b"""\ |
511 | 2189 | ... From: Foo Bar <foo.bar@canonical.com> | 2189 | ... From: Foo Bar <foo.bar@canonical.com> |
512 | 2190 | ... To: new@bugs.launchpad.ubuntu.com | 2190 | ... To: new@bugs.launchpad.ubuntu.com |
513 | @@ -2197,7 +2197,7 @@ linking a CVE. | |||
514 | 2197 | ... """ | 2197 | ... """ |
515 | 2198 | 2198 | ||
516 | 2199 | >>> process_email(submit_mail) | 2199 | >>> process_email(submit_mail) |
518 | 2200 | >>> before_count == Bug.select().count() | 2200 | >>> before_count == IStore(Bug).find(Bug).count() |
519 | 2201 | True | 2201 | True |
520 | 2202 | 2202 | ||
521 | 2203 | >>> print_latest_email() | 2203 | >>> print_latest_email() |
522 | diff --git a/lib/lp/bugs/vocabularies.py b/lib/lp/bugs/vocabularies.py | |||
523 | index 9b337e0..5586ef3 100644 | |||
524 | --- a/lib/lp/bugs/vocabularies.py | |||
525 | +++ b/lib/lp/bugs/vocabularies.py | |||
526 | @@ -56,7 +56,6 @@ from lp.services.webapp.vocabulary import ( | |||
527 | 56 | CountableIterator, | 56 | CountableIterator, |
528 | 57 | IHugeVocabulary, | 57 | IHugeVocabulary, |
529 | 58 | NamedSQLObjectVocabulary, | 58 | NamedSQLObjectVocabulary, |
530 | 59 | SQLObjectVocabularyBase, | ||
531 | 60 | StormVocabularyBase, | 59 | StormVocabularyBase, |
532 | 61 | ) | 60 | ) |
533 | 62 | 61 | ||
534 | @@ -85,9 +84,9 @@ class UsesBugsDistributionVocabulary(DistributionVocabulary): | |||
535 | 85 | ) | 84 | ) |
536 | 86 | 85 | ||
537 | 87 | 86 | ||
539 | 88 | class BugVocabulary(SQLObjectVocabularyBase): | 87 | class BugVocabulary(StormVocabularyBase): |
540 | 89 | _table = Bug | 88 | _table = Bug |
542 | 90 | _orderBy = "id" | 89 | _order_by = "id" |
543 | 91 | 90 | ||
544 | 92 | 91 | ||
545 | 93 | @implementer(IHugeVocabulary) | 92 | @implementer(IHugeVocabulary) |
546 | diff --git a/lib/lp/registry/tests/test_person.py b/lib/lp/registry/tests/test_person.py | |||
547 | index e0f005c..3646ff7 100644 | |||
548 | --- a/lib/lp/registry/tests/test_person.py | |||
549 | +++ b/lib/lp/registry/tests/test_person.py | |||
550 | @@ -1016,7 +1016,7 @@ class TestPersonStates(TestCaseWithFactory): | |||
551 | 1016 | ) | 1016 | ) |
552 | 1017 | 1017 | ||
553 | 1018 | def test_Bug_person_validator(self): | 1018 | def test_Bug_person_validator(self): |
555 | 1019 | bug = Bug.select(limit=1)[0] | 1019 | bug = IStore(Bug).find(Bug).first() |
556 | 1020 | for attr_name in ["owner", "who_made_private"]: | 1020 | for attr_name in ["owner", "who_made_private"]: |
557 | 1021 | self.assertRaises( | 1021 | self.assertRaises( |
558 | 1022 | PrivatePersonLinkageError, setattr, bug, attr_name, self.myteam | 1022 | PrivatePersonLinkageError, setattr, bug, attr_name, self.myteam |
559 | diff --git a/lib/lp/scripts/harness.py b/lib/lp/scripts/harness.py | |||
560 | index 7be9884..a5a03ec 100644 | |||
561 | --- a/lib/lp/scripts/harness.py | |||
562 | +++ b/lib/lp/scripts/harness.py | |||
563 | @@ -77,8 +77,8 @@ def _get_locals(): | |||
564 | 77 | ds = DistroSeries.get(1) | 77 | ds = DistroSeries.get(1) |
565 | 78 | prod = Product.get(1) | 78 | prod = Product.get(1) |
566 | 79 | proj = ProjectGroup.get(1) | 79 | proj = ProjectGroup.get(1) |
569 | 80 | b2 = Bug.get(2) | 80 | b2 = store.get(Bug, 2) |
570 | 81 | b1 = Bug.get(1) | 81 | b1 = store.get(Bug, 1) |
571 | 82 | s = store.get(Specification, 1) | 82 | s = store.get(Specification, 1) |
572 | 83 | q = store.get(Question, 1) | 83 | q = store.get(Question, 1) |
573 | 84 | # Silence unused name warnings | 84 | # Silence unused name warnings |
574 | diff --git a/lib/lp/services/database/tests/test_bulk.py b/lib/lp/services/database/tests/test_bulk.py | |||
575 | index 6cdf569..b9f64c8 100644 | |||
576 | --- a/lib/lp/services/database/tests/test_bulk.py | |||
577 | +++ b/lib/lp/services/database/tests/test_bulk.py | |||
578 | @@ -252,7 +252,7 @@ class TestLoaders(TestCaseWithFactory): | |||
579 | 252 | expected = {bug.owner for bug in owning_objects} | 252 | expected = {bug.owner for bug in owning_objects} |
580 | 253 | self.assertEqual( | 253 | self.assertEqual( |
581 | 254 | expected, | 254 | expected, |
583 | 255 | set(bulk.load_related(Person, owning_objects, ["ownerID"])), | 255 | set(bulk.load_related(Person, owning_objects, ["owner_id"])), |
584 | 256 | ) | 256 | ) |
585 | 257 | 257 | ||
586 | 258 | def test_load_referencing(self): | 258 | def test_load_referencing(self): |
587 | diff --git a/lib/lp/services/statistics/model/statistics.py b/lib/lp/services/statistics/model/statistics.py | |||
588 | index 4b4ff60..f0a182e 100644 | |||
589 | --- a/lib/lp/services/statistics/model/statistics.py | |||
590 | +++ b/lib/lp/services/statistics/model/statistics.py | |||
591 | @@ -106,10 +106,10 @@ class LaunchpadStatisticSet: | |||
592 | 106 | getUtility(IPersonSet).updateStatistics() | 106 | getUtility(IPersonSet).updateStatistics() |
593 | 107 | 107 | ||
594 | 108 | def _updateMaloneStatistics(self, ztm): | 108 | def _updateMaloneStatistics(self, ztm): |
596 | 109 | self.update("bug_count", Bug.select().count()) | 109 | store = IStore(Bug) |
597 | 110 | self.update("bug_count", store.find(Bug).count()) | ||
598 | 110 | ztm.commit() | 111 | ztm.commit() |
599 | 111 | 112 | ||
600 | 112 | store = IStore(BugTask) | ||
601 | 113 | self.update("bugtask_count", store.find(BugTask).count()) | 113 | self.update("bugtask_count", store.find(BugTask).count()) |
602 | 114 | ztm.commit() | 114 | ztm.commit() |
603 | 115 | 115 |
LGTM 👍