Merge lp:~wgrant/launchpad/bug-736002 into lp:launchpad
- bug-736002
- Merge into devel
Proposed by
William Grant
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | William Grant | ||||
Approved revision: | no longer in the source branch. | ||||
Merged at revision: | 12808 | ||||
Proposed branch: | lp:~wgrant/launchpad/bug-736002 | ||||
Merge into: | lp:launchpad | ||||
Diff against target: |
430 lines (+113/-117) 11 files modified
lib/lp/bugs/browser/bugtarget.py (+28/-53) lib/lp/bugs/doc/bug-tags.txt (+20/-12) lib/lp/bugs/interfaces/bugtarget.py (+3/-1) lib/lp/bugs/model/bug.py (+43/-37) lib/lp/registry/model/distribution.py (+3/-2) lib/lp/registry/model/distributionsourcepackage.py (+2/-2) lib/lp/registry/model/distroseries.py (+3/-2) lib/lp/registry/model/product.py (+3/-2) lib/lp/registry/model/productseries.py (+3/-2) lib/lp/registry/model/projectgroup.py (+3/-2) lib/lp/registry/model/sourcepackage.py (+2/-2) |
||||
To merge this branch: | bzr merge lp:~wgrant/launchpad/bug-736002 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jeroen T. Vermeulen (community) | code | Approve | |
Review via email: mp+57278@code.launchpad.net |
Commit message
[r=jtv][bug=736002] Speed up +bugtarget-
Description of the change
This branch fixes tags_cloud_data to to issue only two queries, hopefully minimising timeouts on +bugtarget-
It used to display the official tags and the top 10 unofficial tags. This proves to be fairly expensive, so it will now display the official tags and the top 10 tags, some of which may duplicate the official tag set.
I've changed it to query the context's official tags, then perform a single UNION query to get the top 10 and official tag counts. Both sides of the union are done together in a single pass, and I've also avoided a slow join over Bug, letting the query complete in <4s for Ubuntu on staging.
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 | === modified file 'lib/lp/bugs/browser/bugtarget.py' | |||
2 | --- lib/lp/bugs/browser/bugtarget.py 2011-03-31 08:05:07 +0000 | |||
3 | +++ lib/lp/bugs/browser/bugtarget.py 2011-04-12 08:33:41 +0000 | |||
4 | @@ -1373,63 +1373,38 @@ | |||
5 | 1373 | # Use path_only here to reduce the size of the rendered page. | 1373 | # Use path_only here to reduce the size of the rendered page. |
6 | 1374 | return "+bugs?field.tag=%s" % urllib.quote(tag) | 1374 | return "+bugs?field.tag=%s" % urllib.quote(tag) |
7 | 1375 | 1375 | ||
36 | 1376 | def getUsedBugTagsWithURLs(self): | 1376 | def _calculateFactor(self, tag, count, max_count, official_tags): |
37 | 1377 | """Return the bug tags and their search URLs.""" | 1377 | bonus = 1.5 if tag in official_tags else 1 |
38 | 1378 | bug_tag_counts = self.context.getUsedBugTagsWithOpenCounts(self.user) | 1378 | return (count / max_count) + bonus |
11 | 1379 | return [ | ||
12 | 1380 | {'tag': tag, 'count': count, 'url': self._getSearchURL(tag)} | ||
13 | 1381 | for tag, count in bug_tag_counts] | ||
14 | 1382 | |||
15 | 1383 | @property | ||
16 | 1384 | def official_tags(self): | ||
17 | 1385 | """Get the official tags to diplay.""" | ||
18 | 1386 | official_tags = set(self.context.official_bug_tags) | ||
19 | 1387 | tags = [tag for tag in self.getUsedBugTagsWithURLs() | ||
20 | 1388 | if tag['tag'] in official_tags] | ||
21 | 1389 | used_tags = set(tag['tag'] for tag in tags) | ||
22 | 1390 | tags.sort(key=itemgetter('count'), reverse=True) | ||
23 | 1391 | for tag in sorted(official_tags - used_tags): | ||
24 | 1392 | tags.append( | ||
25 | 1393 | {'tag': tag, 'count': 0, 'url': self._getSearchURL(tag)}) | ||
26 | 1394 | return tags | ||
27 | 1395 | |||
28 | 1396 | @property | ||
29 | 1397 | def other_tags(self): | ||
30 | 1398 | """Get the unofficial tags to diplay.""" | ||
31 | 1399 | official_tags = set(self.context.official_bug_tags) | ||
32 | 1400 | tags = [tag for tag in self.getUsedBugTagsWithURLs() | ||
33 | 1401 | if tag['tag'] not in official_tags] | ||
34 | 1402 | tags.sort(key=itemgetter('count'), reverse=True) | ||
35 | 1403 | return tags[:10] | ||
39 | 1404 | 1379 | ||
40 | 1405 | @property | 1380 | @property |
41 | 1406 | def tags_cloud_data(self): | 1381 | def tags_cloud_data(self): |
42 | 1407 | """The data for rendering a tags cloud""" | 1382 | """The data for rendering a tags cloud""" |
68 | 1408 | official_tags = list(self.context.official_bug_tags) | 1383 | official_tags = self.context.official_bug_tags |
69 | 1409 | tags = self.getUsedBugTagsWithURLs() | 1384 | |
70 | 1410 | other_tags = [tag for tag in tags if tag['tag'] not in official_tags] | 1385 | # Construct a dict of official and top 10 tags. |
71 | 1411 | popular_tags = [tag['tag'] for tag in sorted( | 1386 | # getUsedBugTagsWithOpenCounts is expensive, so do the union in |
72 | 1412 | other_tags, key=itemgetter('count'), reverse=True)[:10]] | 1387 | # SQL. Also preseed with 0 for all the official tags, as gUBTWOC |
73 | 1413 | tags = [ | 1388 | # won't return unused ones. |
74 | 1414 | tag for tag in tags | 1389 | top_ten = removeSecurityProxy( |
75 | 1415 | if tag['tag'] in official_tags + popular_tags] | 1390 | self.context.getUsedBugTagsWithOpenCounts(self.user)[:10]) |
76 | 1416 | all_tag_dicts = [tag['tag'] for tag in tags] | 1391 | official = removeSecurityProxy( |
77 | 1417 | for official_tag in official_tags: | 1392 | self.context.getUsedBugTagsWithOpenCounts( |
78 | 1418 | if official_tag not in all_tag_dicts: | 1393 | self.user, official_tags)) |
79 | 1419 | tags.append({ | 1394 | tags = dict((tag, 0) for tag in official_tags) |
80 | 1420 | 'tag': official_tag, | 1395 | tags.update(dict(top_ten.union(official))) |
81 | 1421 | 'count': 0, | 1396 | |
82 | 1422 | 'url': "+bugs?field.tag=%s" % urllib.quote(official_tag)}) | 1397 | max_count = float(max([1] + tags.values())) |
83 | 1423 | max_count = float(max([1] + [tag['count'] for tag in tags])) | 1398 | |
84 | 1424 | for tag in tags: | 1399 | return sorted( |
85 | 1425 | if tag['tag'] in official_tags: | 1400 | [dict( |
86 | 1426 | if tag['count'] == 0: | 1401 | tag=tag, |
87 | 1427 | tag['factor'] = 1.5 | 1402 | factor=self._calculateFactor( |
88 | 1428 | else: | 1403 | tag, count, max_count, official_tags), |
89 | 1429 | tag['factor'] = 1.5 + (tag['count'] / max_count) | 1404 | url=self._getSearchURL(tag), |
90 | 1430 | else: | 1405 | ) |
91 | 1431 | tag['factor'] = 1 + (tag['count'] / max_count) | 1406 | for (tag, count) in tags.iteritems()], |
92 | 1432 | return sorted(tags, key=itemgetter('tag')) | 1407 | key=itemgetter('tag')) |
93 | 1433 | 1408 | ||
94 | 1434 | @property | 1409 | @property |
95 | 1435 | def show_manage_tags_link(self): | 1410 | def show_manage_tags_link(self): |
96 | 1436 | 1411 | ||
97 | === modified file 'lib/lp/bugs/doc/bug-tags.txt' | |||
98 | --- lib/lp/bugs/doc/bug-tags.txt 2011-01-11 09:40:05 +0000 | |||
99 | +++ lib/lp/bugs/doc/bug-tags.txt 2011-04-12 08:33:41 +0000 | |||
100 | @@ -332,27 +332,35 @@ | |||
101 | 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 |
102 | 333 | bugs each tag has. Only tags having open bugs are returned. | 333 | bugs each tag has. Only tags having open bugs are returned. |
103 | 334 | 334 | ||
111 | 335 | >>> firefox.getUsedBugTagsWithOpenCounts(None) | 335 | >>> list(firefox.getUsedBugTagsWithOpenCounts(None)) |
112 | 336 | [(u'doc', 1L), (u'sco', 1L), (u'svg', 1L)] | 336 | [(u'doc', 1L), (u'sco', 1L), (u'svg', 1L)] |
113 | 337 | 337 | ||
114 | 338 | >>> mozilla.getUsedBugTagsWithOpenCounts(None) | 338 | >>> list(mozilla.getUsedBugTagsWithOpenCounts(None)) |
115 | 339 | [(u'doc', 1L), (u'sco', 1L), (u'svg', 1L)] | 339 | [(u'doc', 1L), (u'sco', 1L), (u'svg', 1L)] |
116 | 340 | 340 | ||
117 | 341 | >>> ubuntu.getUsedBugTagsWithOpenCounts(None) | 341 | >>> list(ubuntu.getUsedBugTagsWithOpenCounts(None)) |
118 | 342 | [(u'crash', 2L), (u'dataloss', 1L), (u'pebcak', 1L), | 342 | [(u'crash', 2L), (u'dataloss', 1L), (u'pebcak', 1L), |
119 | 343 | (u'sco', 1L), (u'svg', 1L)] | 343 | (u'sco', 1L), (u'svg', 1L)] |
120 | 344 | 344 | ||
121 | 345 | We can even ask for the counts for a particular set of tags. | ||
122 | 346 | |||
123 | 347 | >>> list(ubuntu.getUsedBugTagsWithOpenCounts( | ||
124 | 348 | ... None, wanted_tags=['pebcak', 'svg', 'fake'])) | ||
125 | 349 | [(u'pebcak', 1L), (u'svg', 1L)] | ||
126 | 350 | >>> list(ubuntu.getUsedBugTagsWithOpenCounts(None, wanted_tags=[])) | ||
127 | 351 | [] | ||
128 | 352 | |||
129 | 345 | Source packages are a bit special, they return all the tags that are | 353 | Source packages are a bit special, they return all the tags that are |
130 | 346 | used in the whole distribution, while the bug count includes only bugs | 354 | used in the whole distribution, while the bug count includes only bugs |
131 | 347 | in the specific package. | 355 | in the specific package. |
132 | 348 | 356 | ||
134 | 349 | >>> ubuntu_thunderbird.getUsedBugTagsWithOpenCounts(None) | 357 | >>> list(ubuntu_thunderbird.getUsedBugTagsWithOpenCounts(None)) |
135 | 350 | [(u'crash', 1L)] | 358 | [(u'crash', 1L)] |
136 | 351 | 359 | ||
138 | 352 | >>> debian_woody.getUsedBugTagsWithOpenCounts(None) | 360 | >>> list(debian_woody.getUsedBugTagsWithOpenCounts(None)) |
139 | 353 | [(u'dataloss', 1L), (u'layout-test', 1L), (u'pebcak', 1L)] | 361 | [(u'dataloss', 1L), (u'layout-test', 1L), (u'pebcak', 1L)] |
140 | 354 | 362 | ||
142 | 355 | >>> debian_woody_firefox.getUsedBugTagsWithOpenCounts(None) | 363 | >>> list(debian_woody_firefox.getUsedBugTagsWithOpenCounts(None)) |
143 | 356 | [(u'dataloss', 1L), (u'layout-test', 1L), (u'pebcak', 1L)] | 364 | [(u'dataloss', 1L), (u'layout-test', 1L), (u'pebcak', 1L)] |
144 | 357 | 365 | ||
145 | 358 | Only bugs that the supplied user has access to will be counted: | 366 | Only bugs that the supplied user has access to will be counted: |
146 | @@ -362,13 +370,13 @@ | |||
147 | 362 | True | 370 | True |
148 | 363 | >>> flush_database_updates() | 371 | >>> flush_database_updates() |
149 | 364 | 372 | ||
151 | 365 | >>> ubuntu_thunderbird.getUsedBugTagsWithOpenCounts(None) | 373 | >>> list(ubuntu_thunderbird.getUsedBugTagsWithOpenCounts(None)) |
152 | 366 | [] | 374 | [] |
153 | 367 | 375 | ||
154 | 368 | >>> sample_person = getUtility(ILaunchBag).user | 376 | >>> sample_person = getUtility(ILaunchBag).user |
155 | 369 | >>> bug_nine.isSubscribed(sample_person) | 377 | >>> bug_nine.isSubscribed(sample_person) |
156 | 370 | True | 378 | True |
158 | 371 | >>> ubuntu_thunderbird.getUsedBugTagsWithOpenCounts(sample_person) | 379 | >>> list(ubuntu_thunderbird.getUsedBugTagsWithOpenCounts(sample_person)) |
159 | 372 | [(u'crash', 1L)] | 380 | [(u'crash', 1L)] |
160 | 373 | 381 | ||
161 | 374 | When context doesn't have any tags getUsedBugTags() returns a empty list. | 382 | When context doesn't have any tags getUsedBugTags() returns a empty list. |
162 | 375 | 383 | ||
163 | === modified file 'lib/lp/bugs/interfaces/bugtarget.py' | |||
164 | --- lib/lp/bugs/interfaces/bugtarget.py 2011-03-28 20:54:50 +0000 | |||
165 | +++ lib/lp/bugs/interfaces/bugtarget.py 2011-04-12 08:33:41 +0000 | |||
166 | @@ -403,13 +403,15 @@ | |||
167 | 403 | def getUsedBugTags(): | 403 | def getUsedBugTags(): |
168 | 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.""" |
169 | 405 | 405 | ||
171 | 406 | def getUsedBugTagsWithOpenCounts(user): | 406 | def getUsedBugTagsWithOpenCounts(user, wanted_tags=None): |
172 | 407 | """Return name and bug count of tags having open bugs. | 407 | """Return name and bug count of tags having open bugs. |
173 | 408 | 408 | ||
174 | 409 | It returns a list of tuples contining the tag name, and the | 409 | It returns a list of tuples contining the tag name, and the |
175 | 410 | number of open bugs having that tag. Only the bugs that the user | 410 | number of open bugs having that tag. Only the bugs that the user |
176 | 411 | has permission to see are counted, and only tags having open | 411 | has permission to see are counted, and only tags having open |
177 | 412 | bugs will be returned. | 412 | bugs will be returned. |
178 | 413 | |||
179 | 414 | If wanted_tags is specified, only those tags will be returned. | ||
180 | 413 | """ | 415 | """ |
181 | 414 | 416 | ||
182 | 415 | def _getOfficialTagClause(): | 417 | def _getOfficialTagClause(): |
183 | 416 | 418 | ||
184 | === modified file 'lib/lp/bugs/model/bug.py' | |||
185 | --- lib/lp/bugs/model/bug.py 2011-04-07 16:23:24 +0000 | |||
186 | +++ lib/lp/bugs/model/bug.py 2011-04-12 08:33:41 +0000 | |||
187 | @@ -50,6 +50,9 @@ | |||
188 | 50 | from storm.expr import ( | 50 | from storm.expr import ( |
189 | 51 | And, | 51 | And, |
190 | 52 | Count, | 52 | Count, |
191 | 53 | Desc, | ||
192 | 54 | Exists, | ||
193 | 55 | Join, | ||
194 | 53 | LeftJoin, | 56 | LeftJoin, |
195 | 54 | Max, | 57 | Max, |
196 | 55 | Not, | 58 | Not, |
197 | @@ -211,6 +214,29 @@ | |||
198 | 211 | %(condition)s GROUP BY BugTag.tag ORDER BY BugTag.tag""" | 214 | %(condition)s GROUP BY BugTag.tag ORDER BY BugTag.tag""" |
199 | 212 | 215 | ||
200 | 213 | 216 | ||
201 | 217 | def snapshot_bug_params(bug_params): | ||
202 | 218 | """Return a snapshot of a `CreateBugParams` object.""" | ||
203 | 219 | return Snapshot( | ||
204 | 220 | bug_params, names=[ | ||
205 | 221 | "owner", "title", "comment", "description", "msg", | ||
206 | 222 | "datecreated", "security_related", "private", | ||
207 | 223 | "distribution", "sourcepackagename", "binarypackagename", | ||
208 | 224 | "product", "status", "subscribers", "tags", | ||
209 | 225 | "subscribe_owner", "filed_by"]) | ||
210 | 226 | |||
211 | 227 | |||
212 | 228 | class BugTag(SQLBase): | ||
213 | 229 | """A tag belonging to a bug.""" | ||
214 | 230 | |||
215 | 231 | bug = ForeignKey(dbName='bug', foreignKey='Bug', notNull=True) | ||
216 | 232 | tag = StringCol(notNull=True) | ||
217 | 233 | |||
218 | 234 | |||
219 | 235 | # We need to always use the same Count instance or the | ||
220 | 236 | # get_bug_tags_open_count is not UNIONable. | ||
221 | 237 | tag_count_columns = (BugTag.tag, Count()) | ||
222 | 238 | |||
223 | 239 | |||
224 | 214 | def get_bug_tags(context_clause): | 240 | def get_bug_tags(context_clause): |
225 | 215 | """Return all the bug tags as a list of strings. | 241 | """Return all the bug tags as a list of strings. |
226 | 216 | 242 | ||
227 | @@ -230,59 +256,39 @@ | |||
228 | 230 | return shortlist([row[0] for row in cur.fetchall()]) | 256 | return shortlist([row[0] for row in cur.fetchall()]) |
229 | 231 | 257 | ||
230 | 232 | 258 | ||
232 | 233 | def get_bug_tags_open_count(context_condition, user): | 259 | def get_bug_tags_open_count(context_condition, user, wanted_tags=None): |
233 | 234 | """Return all the used bug tags with their open bug count. | 260 | """Return all the used bug tags with their open bug count. |
234 | 235 | 261 | ||
235 | 236 | :param context_condition: A Storm SQL expression, limiting the | 262 | :param context_condition: A Storm SQL expression, limiting the |
236 | 237 | used tags to a specific context. Only the BugTask table may be | 263 | used tags to a specific context. Only the BugTask table may be |
237 | 238 | used to choose the context. | 264 | used to choose the context. |
238 | 239 | :param user: The user performing the search. | 265 | :param user: The user performing the search. |
239 | 266 | :param wanted_tags: A set of tags within which to restrict the search. | ||
240 | 240 | 267 | ||
241 | 241 | :return: A list of tuples, (tag name, open bug count). | 268 | :return: A list of tuples, (tag name, open bug count). |
242 | 242 | """ | 269 | """ |
250 | 243 | open_statuses_condition = BugTask.status.is_in( | 270 | tables = ( |
244 | 244 | UNRESOLVED_BUGTASK_STATUSES) | ||
245 | 245 | columns = [ | ||
246 | 246 | BugTag.tag, | ||
247 | 247 | Count(), | ||
248 | 248 | ] | ||
249 | 249 | tables = [ | ||
251 | 250 | BugTag, | 271 | BugTag, |
257 | 251 | LeftJoin(Bug, Bug.id == BugTag.bugID), | 272 | Join(BugTask, BugTask.bugID == BugTag.bugID), |
258 | 252 | LeftJoin( | 273 | ) |
254 | 253 | BugTask, | ||
255 | 254 | And(BugTask.bugID == Bug.id, open_statuses_condition)), | ||
256 | 255 | ] | ||
259 | 256 | where_conditions = [ | 274 | where_conditions = [ |
261 | 257 | open_statuses_condition, | 275 | BugTask.status.is_in(UNRESOLVED_BUGTASK_STATUSES), |
262 | 258 | context_condition, | 276 | context_condition, |
263 | 259 | ] | 277 | ] |
264 | 278 | if wanted_tags is not None: | ||
265 | 279 | where_conditions.append(BugTag.tag.is_in(wanted_tags)) | ||
266 | 260 | privacy_filter = get_bug_privacy_filter(user) | 280 | privacy_filter = get_bug_privacy_filter(user) |
267 | 261 | if privacy_filter: | 281 | if privacy_filter: |
269 | 262 | where_conditions.append(SQLRaw(privacy_filter)) | 282 | # The EXISTS sub-select avoids a join against Bug, improving |
270 | 283 | # performance significantly. | ||
271 | 284 | where_conditions.append( | ||
272 | 285 | Exists(Select( | ||
273 | 286 | columns=[True], tables=[Bug], | ||
274 | 287 | where=And(Bug.id == BugTag.bugID, SQLRaw(privacy_filter))))) | ||
275 | 263 | store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR) | 288 | store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR) |
298 | 264 | result = store.execute(Select( | 289 | return store.using(*tables).find( |
299 | 265 | columns=columns, where=And(*where_conditions), tables=tables, | 290 | tag_count_columns, *where_conditions).group_by(BugTag.tag).order_by( |
300 | 266 | group_by=BugTag.tag, order_by=BugTag.tag)) | 291 | Desc(Count()), BugTag.tag) |
279 | 267 | return shortlist([(row[0], row[1]) for row in result.get_all()]) | ||
280 | 268 | |||
281 | 269 | |||
282 | 270 | def snapshot_bug_params(bug_params): | ||
283 | 271 | """Return a snapshot of a `CreateBugParams` object.""" | ||
284 | 272 | return Snapshot( | ||
285 | 273 | bug_params, names=[ | ||
286 | 274 | "owner", "title", "comment", "description", "msg", | ||
287 | 275 | "datecreated", "security_related", "private", | ||
288 | 276 | "distribution", "sourcepackagename", "binarypackagename", | ||
289 | 277 | "product", "status", "subscribers", "tags", | ||
290 | 278 | "subscribe_owner", "filed_by"]) | ||
291 | 279 | |||
292 | 280 | |||
293 | 281 | class BugTag(SQLBase): | ||
294 | 282 | """A tag belonging to a bug.""" | ||
295 | 283 | |||
296 | 284 | bug = ForeignKey(dbName='bug', foreignKey='Bug', notNull=True) | ||
297 | 285 | tag = StringCol(notNull=True) | ||
301 | 286 | 292 | ||
302 | 287 | 293 | ||
303 | 288 | class BugBecameQuestionEvent: | 294 | class BugBecameQuestionEvent: |
304 | 289 | 295 | ||
305 | === modified file 'lib/lp/registry/model/distribution.py' | |||
306 | --- lib/lp/registry/model/distribution.py 2011-04-08 15:44:02 +0000 | |||
307 | +++ lib/lp/registry/model/distribution.py 2011-04-12 08:33:41 +0000 | |||
308 | @@ -649,9 +649,10 @@ | |||
309 | 649 | """See `IBugTarget`.""" | 649 | """See `IBugTarget`.""" |
310 | 650 | return get_bug_tags("BugTask.distribution = %s" % sqlvalues(self)) | 650 | return get_bug_tags("BugTask.distribution = %s" % sqlvalues(self)) |
311 | 651 | 651 | ||
313 | 652 | def getUsedBugTagsWithOpenCounts(self, user): | 652 | def getUsedBugTagsWithOpenCounts(self, user, wanted_tags=None): |
314 | 653 | """See `IBugTarget`.""" | 653 | """See `IBugTarget`.""" |
316 | 654 | return get_bug_tags_open_count(BugTask.distribution == self, user) | 654 | return get_bug_tags_open_count( |
317 | 655 | BugTask.distribution == self, user, wanted_tags=wanted_tags) | ||
318 | 655 | 656 | ||
319 | 656 | def getMirrorByName(self, name): | 657 | def getMirrorByName(self, name): |
320 | 657 | """See `IDistribution`.""" | 658 | """See `IDistribution`.""" |
321 | 658 | 659 | ||
322 | === modified file 'lib/lp/registry/model/distributionsourcepackage.py' | |||
323 | --- lib/lp/registry/model/distributionsourcepackage.py 2011-03-23 16:28:51 +0000 | |||
324 | +++ lib/lp/registry/model/distributionsourcepackage.py 2011-04-12 08:33:41 +0000 | |||
325 | @@ -488,12 +488,12 @@ | |||
326 | 488 | """See `IBugTarget`.""" | 488 | """See `IBugTarget`.""" |
327 | 489 | return self.distribution.getUsedBugTags() | 489 | return self.distribution.getUsedBugTags() |
328 | 490 | 490 | ||
330 | 491 | def getUsedBugTagsWithOpenCounts(self, user): | 491 | def getUsedBugTagsWithOpenCounts(self, user, wanted_tags=None): |
331 | 492 | """See `IBugTarget`.""" | 492 | """See `IBugTarget`.""" |
332 | 493 | return get_bug_tags_open_count( | 493 | return get_bug_tags_open_count( |
333 | 494 | And(BugTask.distribution == self.distribution, | 494 | And(BugTask.distribution == self.distribution, |
334 | 495 | BugTask.sourcepackagename == self.sourcepackagename), | 495 | BugTask.sourcepackagename == self.sourcepackagename), |
336 | 496 | user) | 496 | user, wanted_tags=wanted_tags) |
337 | 497 | 497 | ||
338 | 498 | def _getOfficialTagClause(self): | 498 | def _getOfficialTagClause(self): |
339 | 499 | return self.distribution._getOfficialTagClause() | 499 | return self.distribution._getOfficialTagClause() |
340 | 500 | 500 | ||
341 | === modified file 'lib/lp/registry/model/distroseries.py' | |||
342 | --- lib/lp/registry/model/distroseries.py 2011-04-08 15:44:02 +0000 | |||
343 | +++ lib/lp/registry/model/distroseries.py 2011-04-12 08:33:41 +0000 | |||
344 | @@ -833,9 +833,10 @@ | |||
345 | 833 | """See `IHasBugs`.""" | 833 | """See `IHasBugs`.""" |
346 | 834 | return get_bug_tags("BugTask.distroseries = %s" % sqlvalues(self)) | 834 | return get_bug_tags("BugTask.distroseries = %s" % sqlvalues(self)) |
347 | 835 | 835 | ||
349 | 836 | def getUsedBugTagsWithOpenCounts(self, user): | 836 | def getUsedBugTagsWithOpenCounts(self, user, wanted_tags=None): |
350 | 837 | """See `IHasBugs`.""" | 837 | """See `IHasBugs`.""" |
352 | 838 | return get_bug_tags_open_count(BugTask.distroseries == self, user) | 838 | return get_bug_tags_open_count( |
353 | 839 | BugTask.distroseries == self, user, wanted_tags=wanted_tags) | ||
354 | 839 | 840 | ||
355 | 840 | @property | 841 | @property |
356 | 841 | def has_any_specifications(self): | 842 | def has_any_specifications(self): |
357 | 842 | 843 | ||
358 | === modified file 'lib/lp/registry/model/product.py' | |||
359 | --- lib/lp/registry/model/product.py 2011-03-28 20:54:50 +0000 | |||
360 | +++ lib/lp/registry/model/product.py 2011-04-12 08:33:41 +0000 | |||
361 | @@ -800,9 +800,10 @@ | |||
362 | 800 | """See `IBugTarget`.""" | 800 | """See `IBugTarget`.""" |
363 | 801 | return get_bug_tags("BugTask.product = %s" % sqlvalues(self)) | 801 | return get_bug_tags("BugTask.product = %s" % sqlvalues(self)) |
364 | 802 | 802 | ||
366 | 803 | def getUsedBugTagsWithOpenCounts(self, user): | 803 | def getUsedBugTagsWithOpenCounts(self, user, wanted_tags=None): |
367 | 804 | """See `IBugTarget`.""" | 804 | """See `IBugTarget`.""" |
369 | 805 | return get_bug_tags_open_count(BugTask.product == self, user) | 805 | return get_bug_tags_open_count( |
370 | 806 | BugTask.product == self, user, wanted_tags=wanted_tags) | ||
371 | 806 | 807 | ||
372 | 807 | series = SQLMultipleJoin('ProductSeries', joinColumn='product', | 808 | series = SQLMultipleJoin('ProductSeries', joinColumn='product', |
373 | 808 | orderBy='name') | 809 | orderBy='name') |
374 | 809 | 810 | ||
375 | === modified file 'lib/lp/registry/model/productseries.py' | |||
376 | --- lib/lp/registry/model/productseries.py 2011-03-28 20:54:50 +0000 | |||
377 | +++ lib/lp/registry/model/productseries.py 2011-04-12 08:33:41 +0000 | |||
378 | @@ -433,9 +433,10 @@ | |||
379 | 433 | """See IBugTarget.""" | 433 | """See IBugTarget.""" |
380 | 434 | return get_bug_tags("BugTask.productseries = %s" % sqlvalues(self)) | 434 | return get_bug_tags("BugTask.productseries = %s" % sqlvalues(self)) |
381 | 435 | 435 | ||
383 | 436 | def getUsedBugTagsWithOpenCounts(self, user): | 436 | def getUsedBugTagsWithOpenCounts(self, user, wanted_tags=None): |
384 | 437 | """See IBugTarget.""" | 437 | """See IBugTarget.""" |
386 | 438 | return get_bug_tags_open_count(BugTask.productseries == self, user) | 438 | return get_bug_tags_open_count( |
387 | 439 | BugTask.productseries == self, user, wanted_tags=wanted_tags) | ||
388 | 439 | 440 | ||
389 | 440 | def createBug(self, bug_params): | 441 | def createBug(self, bug_params): |
390 | 441 | """See IBugTarget.""" | 442 | """See IBugTarget.""" |
391 | 442 | 443 | ||
392 | === modified file 'lib/lp/registry/model/projectgroup.py' | |||
393 | --- lib/lp/registry/model/projectgroup.py 2011-01-21 08:30:55 +0000 | |||
394 | +++ lib/lp/registry/model/projectgroup.py 2011-04-12 08:33:41 +0000 | |||
395 | @@ -343,13 +343,14 @@ | |||
396 | 343 | return get_bug_tags( | 343 | return get_bug_tags( |
397 | 344 | "BugTask.product IN (%s)" % ",".join(product_ids)) | 344 | "BugTask.product IN (%s)" % ",".join(product_ids)) |
398 | 345 | 345 | ||
400 | 346 | def getUsedBugTagsWithOpenCounts(self, user): | 346 | def getUsedBugTagsWithOpenCounts(self, user, wanted_tags=None): |
401 | 347 | """See `IHasBugs`.""" | 347 | """See `IHasBugs`.""" |
402 | 348 | if not self.products: | 348 | if not self.products: |
403 | 349 | return [] | 349 | return [] |
404 | 350 | product_ids = [product.id for product in self.products] | 350 | product_ids = [product.id for product in self.products] |
405 | 351 | return get_bug_tags_open_count( | 351 | return get_bug_tags_open_count( |
407 | 352 | BugTask.productID.is_in(product_ids), user) | 352 | BugTask.productID.is_in(product_ids), user, |
408 | 353 | wanted_tags=wanted_tags) | ||
409 | 353 | 354 | ||
410 | 354 | def _getBugTaskContextClause(self): | 355 | def _getBugTaskContextClause(self): |
411 | 355 | """See `HasBugsBase`.""" | 356 | """See `HasBugsBase`.""" |
412 | 356 | 357 | ||
413 | === modified file 'lib/lp/registry/model/sourcepackage.py' | |||
414 | --- lib/lp/registry/model/sourcepackage.py 2011-04-07 19:08:45 +0000 | |||
415 | +++ lib/lp/registry/model/sourcepackage.py 2011-04-12 08:33:41 +0000 | |||
416 | @@ -495,12 +495,12 @@ | |||
417 | 495 | """See `IBugTarget`.""" | 495 | """See `IBugTarget`.""" |
418 | 496 | return self.distroseries.getUsedBugTags() | 496 | return self.distroseries.getUsedBugTags() |
419 | 497 | 497 | ||
421 | 498 | def getUsedBugTagsWithOpenCounts(self, user): | 498 | def getUsedBugTagsWithOpenCounts(self, user, wanted_tags=None): |
422 | 499 | """See `IBugTarget`.""" | 499 | """See `IBugTarget`.""" |
423 | 500 | return get_bug_tags_open_count( | 500 | return get_bug_tags_open_count( |
424 | 501 | And(BugTask.distroseries == self.distroseries, | 501 | And(BugTask.distroseries == self.distroseries, |
425 | 502 | BugTask.sourcepackagename == self.sourcepackagename), | 502 | BugTask.sourcepackagename == self.sourcepackagename), |
427 | 503 | user) | 503 | user, wanted_tags=wanted_tags) |
428 | 504 | 504 | ||
429 | 505 | @property | 505 | @property |
430 | 506 | def max_bug_heat(self): | 506 | def max_bug_heat(self): |
Looks good. On IRC you said that we don't expect anyone to have a problem with the functional change, or if they do, we can solve it in other ways later.
Another, very small thing, in lib/lp/ bugs/browser/ bugtask. py:
80 + tags = [] iteritems( ): _getSearchURL( tag), tag_dict)
81 + for tag, count in raw_tags.
82 + tag_dict = dict(
83 + tag=tag, count=count, url=self.
84 + factor=1 + (count / max_count))
85 + if tag in official_tags:
86 + tag_dict['factor'] += 0.5
87 + tags.append(
The "factor=1 + (count / maxcount)" is a bit misleading in terms of associativity. If you isolated the factor computation (including the "+= 0.5") in a function, this whole loop could be replaced with a list comprehension.
Jeroen