Merge lp:~lifeless/launchpad/bug-793809 into lp:launchpad
- bug-793809
- Merge into devel
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | William Grant | ||||
Approved revision: | no longer in the source branch. | ||||
Merged at revision: | 13171 | ||||
Proposed branch: | lp:~lifeless/launchpad/bug-793809 | ||||
Merge into: | lp:launchpad | ||||
Diff against target: |
416 lines (+126/-100) 11 files modified
lib/lp/bugs/browser/bugtarget.py (+4/-15) lib/lp/bugs/doc/bug-tags.txt (+20/-21) lib/lp/bugs/interfaces/bugtarget.py (+9/-7) lib/lp/bugs/model/bug.py (+43/-30) lib/lp/registry/model/distribution.py (+7/-3) lib/lp/registry/model/distributionsourcepackage.py (+7/-5) lib/lp/registry/model/distroseries.py (+10/-3) lib/lp/registry/model/product.py (+6/-3) lib/lp/registry/model/productseries.py (+5/-2) lib/lp/registry/model/projectgroup.py (+8/-6) lib/lp/registry/model/sourcepackage.py (+7/-5) |
||||
To merge this branch: | bzr merge lp:~lifeless/launchpad/bug-793809 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
William Grant | code | Approve | |
Review via email: mp+63635@code.launchpad.net |
Commit message
[r=wgrant][bug=793809] Use BugSummary to do tag portlet calculations
Description of the change
Per the linked bug generating bug tag portlets is extraordinarily slow. This is meant to be addressed by querying bugsummary, which this branch implements.
I have changed the interface to match our needs allowing a faster query (the with clause on teams for visibility).
Other than that I can see a path to reduce the duplicate code that we use here, but its unrelated to fixing this bug, so I've focused on getting the bug fixed ;)
Robert Collins (lifeless) wrote : | # |
> 21 + tags = self.context.
> 10,
> 22 + official_tags)
>
> Nice cleanup. But bad indentation.
How so ?
> 33 + official_tags = self.context.
>
> Sure this doesn't need to be materialised?
The core function returns a list.
> 131 + :param tag_limit: The number of tags to return (excludes
> those found by
> 132 + matching include_tags). If 0 then all tags are returned.
> If
> 133 + non-zero then the most frequently used tags are returned.
>
> 0? Why not None?
Shrug; can change it to that, but 0 would be noddy. Using 0 as the flag avoids bad behaviour if a limit of 0 is passed in.
> 172 +def get_bug_
> 173 + include_tags=None):
>
> Bad indentation.
How so ?
> 200 + store = store.with_(SQL(
> 201 + "teams AS ("
> 202 + "SELECT team from TeamParticipation WHERE person=%s)" %
> user.id))
>
> String formatting in SQL: Australia says no.
>
> Also, calling that "store" is a bit too much of a lie for my tastes.
It meets the core contract and is better understood than 'resultset_
> 228 + BugSummary.
> teams"))
>
> What's the benefit of the WITH clause here? I doubt there is any performance
> gain, and including the full thing here is shorter and clearer.
Its faster. 150ms difference when I was testing.
Preview Diff
1 | === modified file 'lib/lp/bugs/browser/bugtarget.py' | |||
2 | --- lib/lp/bugs/browser/bugtarget.py 2011-06-06 06:44:23 +0000 | |||
3 | +++ lib/lp/bugs/browser/bugtarget.py 2011-06-07 05:39:33 +0000 | |||
4 | @@ -1405,19 +1405,8 @@ | |||
5 | 1405 | def tags_cloud_data(self): | 1405 | def tags_cloud_data(self): |
6 | 1406 | """The data for rendering a tags cloud""" | 1406 | """The data for rendering a tags cloud""" |
7 | 1407 | official_tags = self.context.official_bug_tags | 1407 | official_tags = self.context.official_bug_tags |
21 | 1408 | 1408 | tags = self.context.getUsedBugTagsWithOpenCounts( | |
22 | 1409 | # Construct a dict of official and top 10 tags. | 1409 | self.user, 10, official_tags) |
10 | 1410 | # getUsedBugTagsWithOpenCounts is expensive, so do the union in | ||
11 | 1411 | # SQL. Also preseed with 0 for all the official tags, as gUBTWOC | ||
12 | 1412 | # won't return unused ones. | ||
13 | 1413 | top_ten = removeSecurityProxy( | ||
14 | 1414 | self.context.getUsedBugTagsWithOpenCounts(self.user)[:10]) | ||
15 | 1415 | official = removeSecurityProxy( | ||
16 | 1416 | self.context.getUsedBugTagsWithOpenCounts( | ||
17 | 1417 | self.user, official_tags)) | ||
18 | 1418 | tags = dict((tag, 0) for tag in official_tags) | ||
19 | 1419 | tags.update(dict(top_ten.union(official))) | ||
20 | 1420 | |||
23 | 1421 | max_count = float(max([1] + tags.values())) | 1410 | max_count = float(max([1] + tags.values())) |
24 | 1422 | 1411 | ||
25 | 1423 | return sorted( | 1412 | return sorted( |
26 | @@ -1462,8 +1451,8 @@ | |||
27 | 1462 | @property | 1451 | @property |
28 | 1463 | def tags_js_data(self): | 1452 | def tags_js_data(self): |
29 | 1464 | """Return the JSON representation of the bug tags.""" | 1453 | """Return the JSON representation of the bug tags.""" |
32 | 1465 | used_tags = dict(self.context.getUsedBugTagsWithOpenCounts(self.user)) | 1454 | used_tags = self.context.getUsedBugTagsWithOpenCounts(self.user) |
33 | 1466 | official_tags = list(self.context.official_bug_tags) | 1455 | official_tags = self.context.official_bug_tags |
34 | 1467 | return """<script type="text/javascript"> | 1456 | return """<script type="text/javascript"> |
35 | 1468 | var used_bug_tags = %s; | 1457 | var used_bug_tags = %s; |
36 | 1469 | var official_bug_tags = %s; | 1458 | var official_bug_tags = %s; |
37 | 1470 | 1459 | ||
38 | === modified file 'lib/lp/bugs/doc/bug-tags.txt' | |||
39 | --- lib/lp/bugs/doc/bug-tags.txt 2011-04-12 06:40:51 +0000 | |||
40 | +++ lib/lp/bugs/doc/bug-tags.txt 2011-06-07 05:39:33 +0000 | |||
41 | @@ -332,35 +332,34 @@ | |||
42 | 332 | We can also get all the used tags, together with the number of open | 332 | We can also get all the used tags, together with the number of open |
43 | 333 | bugs each tag has. Only tags having open bugs are returned. | 333 | bugs each tag has. Only tags having open bugs are returned. |
44 | 334 | 334 | ||
52 | 335 | >>> list(firefox.getUsedBugTagsWithOpenCounts(None)) | 335 | >>> sorted(firefox.getUsedBugTagsWithOpenCounts(None).items()) |
53 | 336 | [(u'doc', 1L), (u'sco', 1L), (u'svg', 1L)] | 336 | [(u'doc', 1L), (u'sco', 1L), (u'svg', 1L)] |
54 | 337 | 337 | ||
55 | 338 | >>> list(mozilla.getUsedBugTagsWithOpenCounts(None)) | 338 | >>> sorted(mozilla.getUsedBugTagsWithOpenCounts(None).items()) |
56 | 339 | [(u'doc', 1L), (u'sco', 1L), (u'svg', 1L)] | 339 | [(u'doc', 1L), (u'sco', 1L), (u'svg', 1L)] |
57 | 340 | 340 | ||
58 | 341 | >>> list(ubuntu.getUsedBugTagsWithOpenCounts(None)) | 341 | >>> sorted(ubuntu.getUsedBugTagsWithOpenCounts(None).items()) |
59 | 342 | [(u'crash', 2L), (u'dataloss', 1L), (u'pebcak', 1L), | 342 | [(u'crash', 2L), (u'dataloss', 1L), (u'pebcak', 1L), |
60 | 343 | (u'sco', 1L), (u'svg', 1L)] | 343 | (u'sco', 1L), (u'svg', 1L)] |
61 | 344 | 344 | ||
63 | 345 | We can even ask for the counts for a particular set of tags. | 345 | We can require that some tags be included in the output even when limiting the |
64 | 346 | results. | ||
65 | 346 | 347 | ||
71 | 347 | >>> list(ubuntu.getUsedBugTagsWithOpenCounts( | 348 | >>> sorted(ubuntu.getUsedBugTagsWithOpenCounts(None, |
72 | 348 | ... None, wanted_tags=['pebcak', 'svg', 'fake'])) | 349 | ... tag_limit=1, include_tags=[u'pebcak', u'svg', u'fake']).items()) |
73 | 349 | [(u'pebcak', 1L), (u'svg', 1L)] | 350 | [(u'crash', 2L), (u'fake', 0), (u'pebcak', 1L), (u'svg', 1L)] |
69 | 350 | >>> list(ubuntu.getUsedBugTagsWithOpenCounts(None, wanted_tags=[])) | ||
70 | 351 | [] | ||
74 | 352 | 351 | ||
75 | 353 | Source packages are a bit special, they return all the tags that are | 352 | Source packages are a bit special, they return all the tags that are |
76 | 354 | used in the whole distribution, while the bug count includes only bugs | 353 | used in the whole distribution, while the bug count includes only bugs |
77 | 355 | in the specific package. | 354 | in the specific package. |
78 | 356 | 355 | ||
81 | 357 | >>> list(ubuntu_thunderbird.getUsedBugTagsWithOpenCounts(None)) | 356 | >>> ubuntu_thunderbird.getUsedBugTagsWithOpenCounts(None) |
82 | 358 | [(u'crash', 1L)] | 357 | {u'crash': 1L} |
83 | 359 | 358 | ||
85 | 360 | >>> list(debian_woody.getUsedBugTagsWithOpenCounts(None)) | 359 | >>> sorted(debian_woody.getUsedBugTagsWithOpenCounts(None).items()) |
86 | 361 | [(u'dataloss', 1L), (u'layout-test', 1L), (u'pebcak', 1L)] | 360 | [(u'dataloss', 1L), (u'layout-test', 1L), (u'pebcak', 1L)] |
87 | 362 | 361 | ||
89 | 363 | >>> list(debian_woody_firefox.getUsedBugTagsWithOpenCounts(None)) | 362 | >>> sorted(debian_woody_firefox.getUsedBugTagsWithOpenCounts(None).items()) |
90 | 364 | [(u'dataloss', 1L), (u'layout-test', 1L), (u'pebcak', 1L)] | 363 | [(u'dataloss', 1L), (u'layout-test', 1L), (u'pebcak', 1L)] |
91 | 365 | 364 | ||
92 | 366 | Only bugs that the supplied user has access to will be counted: | 365 | Only bugs that the supplied user has access to will be counted: |
93 | @@ -370,14 +369,14 @@ | |||
94 | 370 | True | 369 | True |
95 | 371 | >>> flush_database_updates() | 370 | >>> flush_database_updates() |
96 | 372 | 371 | ||
99 | 373 | >>> list(ubuntu_thunderbird.getUsedBugTagsWithOpenCounts(None)) | 372 | >>> ubuntu_thunderbird.getUsedBugTagsWithOpenCounts(None) |
100 | 374 | [] | 373 | {} |
101 | 375 | 374 | ||
102 | 376 | >>> sample_person = getUtility(ILaunchBag).user | 375 | >>> sample_person = getUtility(ILaunchBag).user |
103 | 377 | >>> bug_nine.isSubscribed(sample_person) | 376 | >>> bug_nine.isSubscribed(sample_person) |
104 | 378 | True | 377 | True |
107 | 379 | >>> list(ubuntu_thunderbird.getUsedBugTagsWithOpenCounts(sample_person)) | 378 | >>> ubuntu_thunderbird.getUsedBugTagsWithOpenCounts(sample_person) |
108 | 380 | [(u'crash', 1L)] | 379 | {u'crash': 1L} |
109 | 381 | 380 | ||
110 | 382 | When context doesn't have any tags getUsedBugTags() returns a empty list. | 381 | When context doesn't have any tags getUsedBugTags() returns a empty list. |
111 | 383 | 382 | ||
112 | 384 | 383 | ||
113 | === modified file 'lib/lp/bugs/interfaces/bugtarget.py' | |||
114 | --- lib/lp/bugs/interfaces/bugtarget.py 2011-04-12 06:21:39 +0000 | |||
115 | +++ lib/lp/bugs/interfaces/bugtarget.py 2011-06-07 05:39:33 +0000 | |||
116 | @@ -403,15 +403,17 @@ | |||
117 | 403 | def getUsedBugTags(): | 403 | def getUsedBugTags(): |
118 | 404 | """Return the tags used by the context as a sorted list of strings.""" | 404 | """Return the tags used by the context as a sorted list of strings.""" |
119 | 405 | 405 | ||
121 | 406 | def getUsedBugTagsWithOpenCounts(user, wanted_tags=None): | 406 | def getUsedBugTagsWithOpenCounts(user, tag_limit=0, include_tags=None): |
122 | 407 | """Return name and bug count of tags having open bugs. | 407 | """Return name and bug count of tags having open bugs. |
123 | 408 | 408 | ||
130 | 409 | It returns a list of tuples contining the tag name, and the | 409 | :param user: The user who wants the report. |
131 | 410 | number of open bugs having that tag. Only the bugs that the user | 410 | :param tag_limit: The number of tags to return (excludes those found by |
132 | 411 | has permission to see are counted, and only tags having open | 411 | matching include_tags). If 0 then all tags are returned. If |
133 | 412 | bugs will be returned. | 412 | non-zero then the most frequently used tags are returned. |
134 | 413 | 413 | :param include_tags: A list of string tags to return irrespective of | |
135 | 414 | If wanted_tags is specified, only those tags will be returned. | 414 | usage. Tags in this list that have no open bugs are returned with a |
136 | 415 | count of 0. May be None if there are tags to require inclusion of. | ||
137 | 416 | :return: A dict from tag -> count. | ||
138 | 415 | """ | 417 | """ |
139 | 416 | 418 | ||
140 | 417 | def _getOfficialTagClause(): | 419 | def _getOfficialTagClause(): |
141 | 418 | 420 | ||
142 | === modified file 'lib/lp/bugs/model/bug.py' | |||
143 | --- lib/lp/bugs/model/bug.py 2011-06-03 10:38:25 +0000 | |||
144 | +++ lib/lp/bugs/model/bug.py 2011-06-07 05:39:33 +0000 | |||
145 | @@ -63,6 +63,7 @@ | |||
146 | 63 | Select, | 63 | Select, |
147 | 64 | SQL, | 64 | SQL, |
148 | 65 | SQLRaw, | 65 | SQLRaw, |
149 | 66 | Sum, | ||
150 | 66 | Union, | 67 | Union, |
151 | 67 | ) | 68 | ) |
152 | 68 | from storm.info import ClassAlias | 69 | from storm.info import ClassAlias |
153 | @@ -242,11 +243,6 @@ | |||
154 | 242 | tag = StringCol(notNull=True) | 243 | tag = StringCol(notNull=True) |
155 | 243 | 244 | ||
156 | 244 | 245 | ||
157 | 245 | # We need to always use the same Count instance or the | ||
158 | 246 | # get_bug_tags_open_count is not UNIONable. | ||
159 | 247 | tag_count_columns = (BugTag.tag, Count()) | ||
160 | 248 | |||
161 | 249 | |||
162 | 250 | def get_bug_tags(context_clause): | 246 | def get_bug_tags(context_clause): |
163 | 251 | """Return all the bug tags as a list of strings. | 247 | """Return all the bug tags as a list of strings. |
164 | 252 | 248 | ||
165 | @@ -266,39 +262,56 @@ | |||
166 | 266 | return shortlist([row[0] for row in cur.fetchall()]) | 262 | return shortlist([row[0] for row in cur.fetchall()]) |
167 | 267 | 263 | ||
168 | 268 | 264 | ||
172 | 269 | def get_bug_tags_open_count(context_condition, user, wanted_tags=None): | 265 | def get_bug_tags_open_count(context_condition, user, tag_limit=0, |
173 | 270 | """Return all the used bug tags with their open bug count. | 266 | include_tags=None): |
174 | 271 | 267 | """Worker for IBugTarget.getUsedBugTagsWithOpenCounts. | |
175 | 268 | |||
176 | 269 | See `IBugTarget` for details. | ||
177 | 270 | |||
178 | 271 | The only change is that this function takes a SQL expression for limiting | ||
179 | 272 | the found tags. | ||
180 | 272 | :param context_condition: A Storm SQL expression, limiting the | 273 | :param context_condition: A Storm SQL expression, limiting the |
181 | 273 | used tags to a specific context. Only the BugTask table may be | 274 | used tags to a specific context. Only the BugTask table may be |
182 | 274 | used to choose the context. | 275 | used to choose the context. |
183 | 275 | :param user: The user performing the search. | ||
184 | 276 | :param wanted_tags: A set of tags within which to restrict the search. | ||
185 | 277 | |||
186 | 278 | :return: A list of tuples, (tag name, open bug count). | ||
187 | 279 | """ | 276 | """ |
192 | 280 | tables = ( | 277 | # Circular fail. |
193 | 281 | BugTag, | 278 | from lp.bugs.model.bugsummary import BugSummary |
194 | 282 | Join(BugTask, BugTask.bugID == BugTag.bugID), | 279 | tags = {} |
195 | 283 | ) | 280 | if include_tags: |
196 | 281 | tags = dict((tag, 0) for tag in include_tags) | ||
197 | 282 | store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR) | ||
198 | 283 | admin_team = getUtility(ILaunchpadCelebrities).admin | ||
199 | 284 | if user is not None and not user.inTeam(admin_team): | ||
200 | 285 | store = store.with_(SQL( | ||
201 | 286 | "teams AS (" | ||
202 | 287 | "SELECT team from TeamParticipation WHERE person=?)", (user.id,))) | ||
203 | 284 | where_conditions = [ | 288 | where_conditions = [ |
205 | 285 | BugTask.status.is_in(UNRESOLVED_BUGTASK_STATUSES), | 289 | BugSummary.status.is_in(UNRESOLVED_BUGTASK_STATUSES), |
206 | 290 | BugSummary.tag != None, | ||
207 | 286 | context_condition, | 291 | context_condition, |
208 | 287 | ] | 292 | ] |
215 | 288 | if wanted_tags is not None: | 293 | if user is None: |
216 | 289 | where_conditions.append(BugTag.tag.is_in(wanted_tags)) | 294 | where_conditions.append(BugSummary.viewed_by_id == None) |
217 | 290 | privacy_filter = get_bug_privacy_filter(user) | 295 | elif not user.inTeam(admin_team): |
212 | 291 | if privacy_filter: | ||
213 | 292 | # The EXISTS sub-select avoids a join against Bug, improving | ||
214 | 293 | # performance significantly. | ||
218 | 294 | where_conditions.append( | 296 | where_conditions.append( |
226 | 295 | Exists(Select( | 297 | Or( |
227 | 296 | columns=[True], tables=[Bug], | 298 | BugSummary.viewed_by_id == None, |
228 | 297 | where=And(Bug.id == BugTag.bugID, SQLRaw(privacy_filter))))) | 299 | BugSummary.viewed_by_id.is_in(SQL("SELECT team FROM teams")) |
229 | 298 | store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR) | 300 | )) |
230 | 299 | return store.using(*tables).find( | 301 | tag_count_columns = (BugSummary.tag, Sum(BugSummary.count)) |
231 | 300 | tag_count_columns, *where_conditions).group_by(BugTag.tag).order_by( | 302 | # Always query for used |
232 | 301 | Desc(Count()), BugTag.tag) | 303 | def _query(*args): |
233 | 304 | return store.find(tag_count_columns, *(where_conditions + list(args)) | ||
234 | 305 | ).group_by(BugSummary.tag).order_by( | ||
235 | 306 | Desc(Sum(BugSummary.count)), BugSummary.tag) | ||
236 | 307 | used = _query() | ||
237 | 308 | if tag_limit: | ||
238 | 309 | used = used[:tag_limit] | ||
239 | 310 | if include_tags: | ||
240 | 311 | # Union in a query for just include_tags. | ||
241 | 312 | used = used.union(_query(BugSummary.tag.is_in(include_tags))) | ||
242 | 313 | tags.update(dict(used)) | ||
243 | 314 | return tags | ||
244 | 302 | 315 | ||
245 | 303 | 316 | ||
246 | 304 | class BugBecameQuestionEvent: | 317 | class BugBecameQuestionEvent: |
247 | 305 | 318 | ||
248 | === modified file 'lib/lp/registry/model/distribution.py' | |||
249 | --- lib/lp/registry/model/distribution.py 2011-05-27 21:12:25 +0000 | |||
250 | +++ lib/lp/registry/model/distribution.py 2011-06-07 05:39:33 +0000 | |||
251 | @@ -646,10 +646,14 @@ | |||
252 | 646 | """See `IBugTarget`.""" | 646 | """See `IBugTarget`.""" |
253 | 647 | return get_bug_tags("BugTask.distribution = %s" % sqlvalues(self)) | 647 | return get_bug_tags("BugTask.distribution = %s" % sqlvalues(self)) |
254 | 648 | 648 | ||
257 | 649 | def getUsedBugTagsWithOpenCounts(self, user, wanted_tags=None): | 649 | def getUsedBugTagsWithOpenCounts(self, user, tag_limit=0, include_tags=None): |
258 | 650 | """See `IBugTarget`.""" | 650 | """See IBugTarget.""" |
259 | 651 | # Circular fail. | ||
260 | 652 | from lp.bugs.model.bugsummary import BugSummary | ||
261 | 651 | return get_bug_tags_open_count( | 653 | return get_bug_tags_open_count( |
263 | 652 | BugTask.distribution == self, user, wanted_tags=wanted_tags) | 654 | And(BugSummary.distribution_id == self.id, |
264 | 655 | BugSummary.sourcepackagename_id == None), | ||
265 | 656 | user, tag_limit=tag_limit, include_tags=include_tags) | ||
266 | 653 | 657 | ||
267 | 654 | def getMirrorByName(self, name): | 658 | def getMirrorByName(self, name): |
268 | 655 | """See `IDistribution`.""" | 659 | """See `IDistribution`.""" |
269 | 656 | 660 | ||
270 | === modified file 'lib/lp/registry/model/distributionsourcepackage.py' | |||
271 | --- lib/lp/registry/model/distributionsourcepackage.py 2011-05-12 04:18:32 +0000 | |||
272 | +++ lib/lp/registry/model/distributionsourcepackage.py 2011-06-07 05:39:33 +0000 | |||
273 | @@ -486,12 +486,14 @@ | |||
274 | 486 | """See `IBugTarget`.""" | 486 | """See `IBugTarget`.""" |
275 | 487 | return self.distribution.getUsedBugTags() | 487 | return self.distribution.getUsedBugTags() |
276 | 488 | 488 | ||
279 | 489 | def getUsedBugTagsWithOpenCounts(self, user, wanted_tags=None): | 489 | def getUsedBugTagsWithOpenCounts(self, user, tag_limit=0, include_tags=None): |
280 | 490 | """See `IBugTarget`.""" | 490 | """See IBugTarget.""" |
281 | 491 | # Circular fail. | ||
282 | 492 | from lp.bugs.model.bugsummary import BugSummary | ||
283 | 491 | return get_bug_tags_open_count( | 493 | return get_bug_tags_open_count( |
287 | 492 | And(BugTask.distribution == self.distribution, | 494 | And(BugSummary.distribution == self.distribution, |
288 | 493 | BugTask.sourcepackagename == self.sourcepackagename), | 495 | BugSummary.sourcepackagename == self.sourcepackagename), |
289 | 494 | user, wanted_tags=wanted_tags) | 496 | user, tag_limit=tag_limit, include_tags=include_tags) |
290 | 495 | 497 | ||
291 | 496 | def _getOfficialTagClause(self): | 498 | def _getOfficialTagClause(self): |
292 | 497 | return self.distribution._getOfficialTagClause() | 499 | return self.distribution._getOfficialTagClause() |
293 | 498 | 500 | ||
294 | === modified file 'lib/lp/registry/model/distroseries.py' | |||
295 | --- lib/lp/registry/model/distroseries.py 2011-05-31 15:45:19 +0000 | |||
296 | +++ lib/lp/registry/model/distroseries.py 2011-06-07 05:39:33 +0000 | |||
297 | @@ -28,6 +28,7 @@ | |||
298 | 28 | StringCol, | 28 | StringCol, |
299 | 29 | ) | 29 | ) |
300 | 30 | from storm.locals import ( | 30 | from storm.locals import ( |
301 | 31 | And, | ||
302 | 31 | Desc, | 32 | Desc, |
303 | 32 | Join, | 33 | Join, |
304 | 33 | SQL, | 34 | SQL, |
305 | @@ -853,10 +854,16 @@ | |||
306 | 853 | """See `IHasBugs`.""" | 854 | """See `IHasBugs`.""" |
307 | 854 | return get_bug_tags("BugTask.distroseries = %s" % sqlvalues(self)) | 855 | return get_bug_tags("BugTask.distroseries = %s" % sqlvalues(self)) |
308 | 855 | 856 | ||
311 | 856 | def getUsedBugTagsWithOpenCounts(self, user, wanted_tags=None): | 857 | def getUsedBugTagsWithOpenCounts(self, user, tag_limit=0, include_tags=None): |
312 | 857 | """See `IHasBugs`.""" | 858 | """See IBugTarget.""" |
313 | 859 | # Circular fail. | ||
314 | 860 | from lp.bugs.model.bugsummary import BugSummary | ||
315 | 858 | return get_bug_tags_open_count( | 861 | return get_bug_tags_open_count( |
317 | 859 | BugTask.distroseries == self, user, wanted_tags=wanted_tags) | 862 | And( |
318 | 863 | BugSummary.distroseries_id == self.id, | ||
319 | 864 | BugSummary.sourcepackagename_id == None | ||
320 | 865 | ), | ||
321 | 866 | user, tag_limit=tag_limit, include_tags=include_tags) | ||
322 | 860 | 867 | ||
323 | 861 | @property | 868 | @property |
324 | 862 | def has_any_specifications(self): | 869 | def has_any_specifications(self): |
325 | 863 | 870 | ||
326 | === modified file 'lib/lp/registry/model/product.py' | |||
327 | --- lib/lp/registry/model/product.py 2011-05-27 21:12:25 +0000 | |||
328 | +++ lib/lp/registry/model/product.py 2011-06-07 05:39:33 +0000 | |||
329 | @@ -799,10 +799,13 @@ | |||
330 | 799 | """See `IBugTarget`.""" | 799 | """See `IBugTarget`.""" |
331 | 800 | return get_bug_tags("BugTask.product = %s" % sqlvalues(self)) | 800 | return get_bug_tags("BugTask.product = %s" % sqlvalues(self)) |
332 | 801 | 801 | ||
335 | 802 | def getUsedBugTagsWithOpenCounts(self, user, wanted_tags=None): | 802 | def getUsedBugTagsWithOpenCounts(self, user, tag_limit=0, include_tags=None): |
336 | 803 | """See `IBugTarget`.""" | 803 | """See IBugTarget.""" |
337 | 804 | # Circular fail. | ||
338 | 805 | from lp.bugs.model.bugsummary import BugSummary | ||
339 | 804 | return get_bug_tags_open_count( | 806 | return get_bug_tags_open_count( |
341 | 805 | BugTask.product == self, user, wanted_tags=wanted_tags) | 807 | BugSummary.product_id == self.id, |
342 | 808 | user, tag_limit=tag_limit, include_tags=include_tags) | ||
343 | 806 | 809 | ||
344 | 807 | series = SQLMultipleJoin('ProductSeries', joinColumn='product', | 810 | series = SQLMultipleJoin('ProductSeries', joinColumn='product', |
345 | 808 | orderBy='name') | 811 | orderBy='name') |
346 | 809 | 812 | ||
347 | === modified file 'lib/lp/registry/model/productseries.py' | |||
348 | --- lib/lp/registry/model/productseries.py 2011-05-27 21:12:25 +0000 | |||
349 | +++ lib/lp/registry/model/productseries.py 2011-06-07 05:39:33 +0000 | |||
350 | @@ -446,10 +446,13 @@ | |||
351 | 446 | """See IBugTarget.""" | 446 | """See IBugTarget.""" |
352 | 447 | return get_bug_tags("BugTask.productseries = %s" % sqlvalues(self)) | 447 | return get_bug_tags("BugTask.productseries = %s" % sqlvalues(self)) |
353 | 448 | 448 | ||
355 | 449 | def getUsedBugTagsWithOpenCounts(self, user, wanted_tags=None): | 449 | def getUsedBugTagsWithOpenCounts(self, user, tag_limit=0, include_tags=None): |
356 | 450 | """See IBugTarget.""" | 450 | """See IBugTarget.""" |
357 | 451 | # Circular fail. | ||
358 | 452 | from lp.bugs.model.bugsummary import BugSummary | ||
359 | 451 | return get_bug_tags_open_count( | 453 | return get_bug_tags_open_count( |
361 | 452 | BugTask.productseries == self, user, wanted_tags=wanted_tags) | 454 | BugSummary.productseries_id == self.id, user, tag_limit=tag_limit, |
362 | 455 | include_tags=include_tags) | ||
363 | 453 | 456 | ||
364 | 454 | def createBug(self, bug_params): | 457 | def createBug(self, bug_params): |
365 | 455 | """See IBugTarget.""" | 458 | """See IBugTarget.""" |
366 | 456 | 459 | ||
367 | === modified file 'lib/lp/registry/model/projectgroup.py' | |||
368 | --- lib/lp/registry/model/projectgroup.py 2011-04-26 16:22:11 +0000 | |||
369 | +++ lib/lp/registry/model/projectgroup.py 2011-06-07 05:39:33 +0000 | |||
370 | @@ -343,14 +343,16 @@ | |||
371 | 343 | return get_bug_tags( | 343 | return get_bug_tags( |
372 | 344 | "BugTask.product IN (%s)" % ",".join(product_ids)) | 344 | "BugTask.product IN (%s)" % ",".join(product_ids)) |
373 | 345 | 345 | ||
378 | 346 | def getUsedBugTagsWithOpenCounts(self, user, wanted_tags=None): | 346 | def getUsedBugTagsWithOpenCounts(self, user, tag_limit=0, include_tags=None): |
379 | 347 | """See `IHasBugs`.""" | 347 | """See IBugTarget.""" |
380 | 348 | if not self.products: | 348 | # Circular fail. |
381 | 349 | return [] | 349 | from lp.bugs.model.bugsummary import BugSummary |
382 | 350 | product_ids = [product.id for product in self.products] | 350 | product_ids = [product.id for product in self.products] |
383 | 351 | if not product_ids: | ||
384 | 352 | return {} | ||
385 | 351 | return get_bug_tags_open_count( | 353 | return get_bug_tags_open_count( |
388 | 352 | BugTask.productID.is_in(product_ids), user, | 354 | BugSummary.product_id.is_in(product_ids), |
389 | 353 | wanted_tags=wanted_tags) | 355 | user, tag_limit=tag_limit, include_tags=include_tags) |
390 | 354 | 356 | ||
391 | 355 | def _getBugTaskContextClause(self): | 357 | def _getBugTaskContextClause(self): |
392 | 356 | """See `HasBugsBase`.""" | 358 | """See `HasBugsBase`.""" |
393 | 357 | 359 | ||
394 | === modified file 'lib/lp/registry/model/sourcepackage.py' | |||
395 | --- lib/lp/registry/model/sourcepackage.py 2011-05-14 15:03:04 +0000 | |||
396 | +++ lib/lp/registry/model/sourcepackage.py 2011-06-07 05:39:33 +0000 | |||
397 | @@ -494,12 +494,14 @@ | |||
398 | 494 | """See `IBugTarget`.""" | 494 | """See `IBugTarget`.""" |
399 | 495 | return self.distroseries.getUsedBugTags() | 495 | return self.distroseries.getUsedBugTags() |
400 | 496 | 496 | ||
403 | 497 | def getUsedBugTagsWithOpenCounts(self, user, wanted_tags=None): | 497 | def getUsedBugTagsWithOpenCounts(self, user, tag_limit=0, include_tags=None): |
404 | 498 | """See `IBugTarget`.""" | 498 | """See IBugTarget.""" |
405 | 499 | # Circular fail. | ||
406 | 500 | from lp.bugs.model.bugsummary import BugSummary | ||
407 | 499 | return get_bug_tags_open_count( | 501 | return get_bug_tags_open_count( |
411 | 500 | And(BugTask.distroseries == self.distroseries, | 502 | And(BugSummary.distroseries == self.distroseries, |
412 | 501 | BugTask.sourcepackagename == self.sourcepackagename), | 503 | BugSummary.sourcepackagename == self.sourcepackagename), |
413 | 502 | user, wanted_tags=wanted_tags) | 504 | user, tag_limit=tag_limit, include_tags=include_tags) |
414 | 503 | 505 | ||
415 | 504 | @property | 506 | @property |
416 | 505 | def max_bug_heat(self): | 507 | def max_bug_heat(self): |
21 + tags = self.context. getUsedBugTagsW ithOpenCounts( self.user, 10,
22 + official_tags)
Nice cleanup. But bad indentation.
33 + official_tags = self.context. official_ bug_tags
Sure this doesn't need to be materialised?
131 + :param tag_limit: The number of tags to return (excludes those found by
132 + matching include_tags). If 0 then all tags are returned. If
133 + non-zero then the most frequently used tags are returned.
0? Why not None?
172 +def get_bug_ tags_open_ count(context_ condition, user, tag_limit=0,
173 + include_tags=None):
Bad indentation.
200 + store = store.with_(SQL(
201 + "teams AS ("
202 + "SELECT team from TeamParticipation WHERE person=%s)" % user.id))
String formatting in SQL: Australia says no.
Also, calling that "store" is a bit too much of a lie for my tastes.
228 + BugSummary. viewed_ by_id.is_ in(SQL( "SELECT team FROM teams"))
What's the benefit of the WITH clause here? I doubt there is any performance gain, and including the full thing here is shorter and clearer.