Merge lp:~wgrant/launchpad/bugsummary-v2-apg-app into lp:launchpad
- bugsummary-v2-apg-app
- Merge into devel
Status: | Merged |
---|---|
Approved by: | Curtis Hovey |
Approved revision: | no longer in the source branch. |
Merged at revision: | 15691 |
Proposed branch: | lp:~wgrant/launchpad/bugsummary-v2-apg-app |
Merge into: | lp:launchpad |
Diff against target: |
572 lines (+202/-138) 5 files modified
lib/lp/bugs/doc/bugsummary.txt (+107/-78) lib/lp/bugs/model/bug.py (+13/-21) lib/lp/bugs/model/bugsummary.py (+61/-3) lib/lp/bugs/model/bugtask.py (+11/-21) lib/lp/bugs/model/tests/test_bugsummary.py (+10/-15) |
To merge this branch: | bzr merge lp:~wgrant/launchpad/bugsummary-v2-apg-app |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Curtis Hovey (community) | code | Approve | |
Review via email:
|
Commit message
Teach model and test code about BugSummary.
Description of the change
BugSummary privacy is a little special. Public bugs show up in a single row with viewed_by=None, private bugs in several rows with viewed_
This branch teaches the application code about the new column, removing the last blocker for lp:~wgrant/launchpad/bugsummary-v2-apg-db, which alters the triggers to start setting it. The two production queries and one test query that filter on viewed_by have been adjusted to use a factored out filter method, which knows about access policies. I've verified that they still all perform excellently.
Tests are a bit of an issue, as it can't really be tested until the DB changes are deployed as well. But the query is factored out and well tested.
I also reworked bugsummary.txt a bit. The new column makes the printed tables a bit wide, so I made privacy display optional.
Preview Diff
1 | === modified file 'lib/lp/bugs/doc/bugsummary.txt' |
2 | --- lib/lp/bugs/doc/bugsummary.txt 2012-07-11 22:31:52 +0000 |
3 | +++ lib/lp/bugs/doc/bugsummary.txt 2012-07-25 08:05:24 +0000 |
4 | @@ -19,9 +19,9 @@ |
5 | First we should setup some helpers to use in the examples. These will |
6 | let us dump the BugSummary table in a readable format. |
7 | |
8 | - --------------------------------------------------------------- |
9 | - prod ps dist ds spn tag mile status import pa vis # |
10 | - --------------------------------------------------------------- |
11 | + ---------------------------------------------------------- |
12 | + prod ps dist ds spn tag mile status import pa # |
13 | + ---------------------------------------------------------- |
14 | |
15 | The columns are product, productseries, distribution, distroseries, |
16 | sourcepackagename, tag, milestone, status, importance, has_patch, |
17 | @@ -40,7 +40,18 @@ |
18 | ... return 'x' |
19 | ... return object_or_none.name |
20 | |
21 | - >>> def print_result(bugsummary_resultset): |
22 | + >>> def ap_desc(policy_or_none): |
23 | + ... if policy_or_none is None: |
24 | + ... return 'x' |
25 | + ... type_names = { |
26 | + ... InformationType.PRIVATESECURITY: 'se', |
27 | + ... InformationType.USERDATA: 'pr', |
28 | + ... InformationType.PROPRIETARY: 'pp', |
29 | + ... } |
30 | + ... return '%-4s/%-2s' % ( |
31 | + ... policy_or_none.pillar.name, type_names[policy_or_none.type]) |
32 | + |
33 | + >>> def print_result(bugsummary_resultset, include_privacy=False): |
34 | ... # First, flush and invalidate the cache so we see the effects |
35 | ... # of the underlying database triggers. Normally you don't want |
36 | ... # to bother with this as you are only interested in counts of |
37 | @@ -59,18 +70,28 @@ |
38 | ... BugSummary.sourcepackagename_id, BugSummary.tag, |
39 | ... BugSummary.milestone_id, BugSummary.status, |
40 | ... BugSummary.importance, BugSummary.has_patch, |
41 | - ... BugSummary.viewed_by_id, BugSummary.id) |
42 | + ... BugSummary.viewed_by_id, BugSummary.access_policy_id, |
43 | + ... BugSummary.id) |
44 | ... fmt = ( |
45 | ... "%-4s %-4s %-4s %-4s %-5s %-3s %-4s " |
46 | - ... "%-6s %-6s %-2s %-4s %3s") |
47 | - ... header = fmt % ( |
48 | + ... "%-6s %-6s %-2s") |
49 | + ... titles = ( |
50 | ... 'prod', 'ps', 'dist', 'ds', 'spn', 'tag', 'mile', |
51 | - ... 'status', 'import', 'pa', 'vis', '#') |
52 | + ... 'status', 'import', 'pa') |
53 | + ... if include_privacy: |
54 | + ... fmt += ' %-4s %-7s' |
55 | + ... titles += ('gra', 'pol') |
56 | + ... fmt += ' %3s' |
57 | + ... titles += ('#',) |
58 | + ... header = fmt % titles |
59 | ... print "-" * len(header) |
60 | ... print header |
61 | ... print "-" * len(header) |
62 | ... for bugsummary in ordered_results: |
63 | - ... print fmt % ( |
64 | + ... if not include_privacy: |
65 | + ... assert bugsummary.viewed_by is None |
66 | + ... assert bugsummary.access_policy is None |
67 | + ... data = ( |
68 | ... name(bugsummary.product), |
69 | ... name(bugsummary.productseries), |
70 | ... name(bugsummary.distribution), |
71 | @@ -80,9 +101,13 @@ |
72 | ... name(bugsummary.milestone), |
73 | ... str(bugsummary.status)[:6], |
74 | ... str(bugsummary.importance)[:6], |
75 | - ... str(bugsummary.has_patch)[:1], |
76 | - ... name(bugsummary.viewed_by), |
77 | - ... bugsummary.count) |
78 | + ... str(bugsummary.has_patch)[:1]) |
79 | + ... if include_privacy: |
80 | + ... data += ( |
81 | + ... name(bugsummary.viewed_by), |
82 | + ... ap_desc(bugsummary.access_policy), |
83 | + ... ) |
84 | + ... print fmt % (data + (bugsummary.count,)) |
85 | ... print " " * (len(header) - 4), |
86 | ... print "===" |
87 | ... sum = bugsummary_resultset.sum(BugSummary.count) |
88 | @@ -90,8 +115,9 @@ |
89 | ... print "%3s" % sum |
90 | |
91 | >>> def print_find(*bs_query_args, **bs_query_kw): |
92 | + ... include_privacy = bs_query_kw.pop('include_privacy', False) |
93 | ... resultset = store.find(BugSummary, *bs_query_args, **bs_query_kw) |
94 | - ... print_result(resultset) |
95 | + ... print_result(resultset, include_privacy=include_privacy) |
96 | |
97 | |
98 | /!\ A Note About Privacy in These Examples |
99 | @@ -119,12 +145,12 @@ |
100 | ... BugSummary.tag == None) |
101 | |
102 | >>> print_result(bug_summaries) |
103 | - ------------------------------------------------------------ |
104 | - prod ps dist ds spn tag mile status import pa vis # |
105 | - ------------------------------------------------------------ |
106 | - pr-a x x x x x x New Undeci F x 1 |
107 | - === |
108 | - 1 |
109 | + ------------------------------------------------------- |
110 | + prod ps dist ds spn tag mile status import pa # |
111 | + ------------------------------------------------------- |
112 | + pr-a x x x x x x New Undeci F 1 |
113 | + === |
114 | + 1 |
115 | |
116 | There is one row per tag per combination of product, status and milestone. |
117 | If we are interested in all bugs targeted to a product regardless of how |
118 | @@ -145,13 +171,13 @@ |
119 | ... BugSummary.product == prod_a, |
120 | ... BugSummary.tag == None, |
121 | ... BugSummary.viewed_by == None) |
122 | - ------------------------------------------------------------ |
123 | - prod ps dist ds spn tag mile status import pa vis # |
124 | - ------------------------------------------------------------ |
125 | - pr-a x x x x x x New Undeci F x 2 |
126 | - pr-a x x x x x x Confir Undeci F x 2 |
127 | - === |
128 | - 4 |
129 | + ------------------------------------------------------- |
130 | + prod ps dist ds spn tag mile status import pa # |
131 | + ------------------------------------------------------- |
132 | + pr-a x x x x x x New Undeci F 2 |
133 | + pr-a x x x x x x Confir Undeci F 2 |
134 | + === |
135 | + 4 |
136 | |
137 | Here are the rows associated with the 't-a' tag. There is 1 Confirmed |
138 | bug task targetted to the pr-a product who's bug is tagged 't-a'.: |
139 | @@ -160,12 +186,12 @@ |
140 | ... BugSummary.product == prod_a, |
141 | ... BugSummary.tag == u't-a', |
142 | ... BugSummary.viewed_by == None) |
143 | - ------------------------------------------------------------ |
144 | - prod ps dist ds spn tag mile status import pa vis # |
145 | - ------------------------------------------------------------ |
146 | - pr-a x x x x t-a x Confir Undeci F x 1 |
147 | - === |
148 | - 1 |
149 | + ------------------------------------------------------- |
150 | + prod ps dist ds spn tag mile status import pa # |
151 | + ------------------------------------------------------- |
152 | + pr-a x x x x t-a x Confir Undeci F 1 |
153 | + === |
154 | + 1 |
155 | |
156 | You will normally want to get the total count counted in the database |
157 | rather than waste transmission time to calculate the rows client side. |
158 | @@ -205,17 +231,17 @@ |
159 | >>> print_find( |
160 | ... BugSummary.product == prod_a, |
161 | ... BugSummary.viewed_by == None) |
162 | - ------------------------------------------------------------ |
163 | - prod ps dist ds spn tag mile status import pa vis # |
164 | - ------------------------------------------------------------ |
165 | - pr-a x x x x t-a x Confir Undeci F x 1 |
166 | - pr-a x x x x t-b ms-a New Undeci F x 1 |
167 | - pr-a x x x x t-c ms-a New Undeci F x 1 |
168 | - pr-a x x x x x ms-a New Undeci F x 1 |
169 | - pr-a x x x x x x New Undeci F x 2 |
170 | - pr-a x x x x x x Confir Undeci F x 2 |
171 | - === |
172 | - 8 |
173 | + ------------------------------------------------------- |
174 | + prod ps dist ds spn tag mile status import pa # |
175 | + ------------------------------------------------------- |
176 | + pr-a x x x x t-a x Confir Undeci F 1 |
177 | + pr-a x x x x t-b ms-a New Undeci F 1 |
178 | + pr-a x x x x t-c ms-a New Undeci F 1 |
179 | + pr-a x x x x x ms-a New Undeci F 1 |
180 | + pr-a x x x x x x New Undeci F 2 |
181 | + pr-a x x x x x x Confir Undeci F 2 |
182 | + === |
183 | + 8 |
184 | |
185 | Number of New bugs not targeted to a milestone. Note the difference |
186 | between selecting records where tag is None, and where milestone is None: |
187 | @@ -269,13 +295,13 @@ |
188 | ... BugSummary.productseries == productseries_b, |
189 | ... BugSummary.product == prod_b), |
190 | ... BugSummary.viewed_by == None) |
191 | - ------------------------------------------------------------ |
192 | - prod ps dist ds spn tag mile status import pa vis # |
193 | - ------------------------------------------------------------ |
194 | - pr-b x x x x x x New Undeci F x 1 |
195 | - x ps-b x x x x x New Undeci F x 1 |
196 | - === |
197 | - 2 |
198 | + ------------------------------------------------------- |
199 | + prod ps dist ds spn tag mile status import pa # |
200 | + ------------------------------------------------------- |
201 | + pr-b x x x x x x New Undeci F 1 |
202 | + x ps-b x x x x x New Undeci F 1 |
203 | + === |
204 | + 2 |
205 | |
206 | Distribution Bug Counts |
207 | ----------------------- |
208 | @@ -297,14 +323,14 @@ |
209 | >>> print_find( |
210 | ... BugSummary.distribution == distribution, |
211 | ... BugSummary.viewed_by == None) |
212 | - ------------------------------------------------------------ |
213 | - prod ps dist ds spn tag mile status import pa vis # |
214 | - ------------------------------------------------------------ |
215 | - x x di-a x sp-a x x New Undeci F x 1 |
216 | - x x di-a x x x x New Undeci F x 1 |
217 | - x x di-a x x x x Confir Undeci F x 1 |
218 | - === |
219 | - 3 |
220 | + ------------------------------------------------------- |
221 | + prod ps dist ds spn tag mile status import pa # |
222 | + ------------------------------------------------------- |
223 | + x x di-a x sp-a x x New Undeci F 1 |
224 | + x x di-a x x x x New Undeci F 1 |
225 | + x x di-a x x x x Confir Undeci F 1 |
226 | + === |
227 | + 3 |
228 | |
229 | How many bugs targeted to a distribution? |
230 | |
231 | @@ -369,12 +395,12 @@ |
232 | >>> print_find( |
233 | ... BugSummary.distroseries == series_c, |
234 | ... BugSummary.viewed_by == None) |
235 | - ------------------------------------------------------------ |
236 | - prod ps dist ds spn tag mile status import pa vis # |
237 | - ------------------------------------------------------------ |
238 | - x x x ds-c x x x New Undeci F x 1 |
239 | - === |
240 | - 1 |
241 | + ------------------------------------------------------- |
242 | + prod ps dist ds spn tag mile status import pa # |
243 | + ------------------------------------------------------- |
244 | + x x x ds-c x x x New Undeci F 1 |
245 | + === |
246 | + 1 |
247 | |
248 | |
249 | Privacy |
250 | @@ -440,19 +466,19 @@ |
251 | >>> distro_or_series = Or( |
252 | ... BugSummary.distribution == distro_p, |
253 | ... BugSummary.distroseries == series_p) |
254 | - >>> print_find(distro_or_series) |
255 | - ------------------------------------------------------------ |
256 | - prod ps dist ds spn tag mile status import pa vis # |
257 | - ------------------------------------------------------------ |
258 | - x x di-p x x x x New Undeci F p-b 1 |
259 | - x x di-p x x x x New Undeci F own 3 |
260 | - x x di-p x x x x New Undeci F t-a 1 |
261 | - x x di-p x x x x New Undeci F t-c 1 |
262 | - x x di-p x x x x New Undeci F x 1 |
263 | - x x x ds-p x x x New Undeci F own 1 |
264 | - x x x ds-p x x x New Undeci F t-c 1 |
265 | - === |
266 | - 9 |
267 | + >>> print_find(distro_or_series, include_privacy=True) |
268 | + -------------------------------------------------------------------- |
269 | + prod ps dist ds spn tag mile status import pa gra pol # |
270 | + -------------------------------------------------------------------- |
271 | + x x di-p x x x x New Undeci F p-b x 1 |
272 | + x x di-p x x x x New Undeci F own x 3 |
273 | + x x di-p x x x x New Undeci F t-a x 1 |
274 | + x x di-p x x x x New Undeci F t-c x 1 |
275 | + x x di-p x x x x New Undeci F x x 1 |
276 | + x x x ds-p x x x New Undeci F own x 1 |
277 | + x x x ds-p x x x New Undeci F t-c x 1 |
278 | + === |
279 | + 9 |
280 | |
281 | So how many public bugs are there on the distro? |
282 | |
283 | @@ -460,12 +486,14 @@ |
284 | ... BugSummary, |
285 | ... BugSummary.distribution == distro_p, |
286 | ... BugSummary.viewed_by == None, # Public bugs only |
287 | + ... BugSummary.access_policy == None, # Public bugs only |
288 | ... BugSummary.sourcepackagename == None, |
289 | ... BugSummary.tag == None).sum(BugSummary.count) or 0 |
290 | 1 |
291 | |
292 | But how many can the owner see? |
293 | |
294 | + >>> from storm.expr import And |
295 | >>> join = LeftJoin( |
296 | ... BugSummary, TeamParticipation, |
297 | ... BugSummary.viewed_by_id == TeamParticipation.teamID) |
298 | @@ -473,7 +501,8 @@ |
299 | ... BugSummary, |
300 | ... BugSummary.distribution == distro_p, |
301 | ... Or( |
302 | - ... BugSummary.viewed_by == None, |
303 | + ... And(BugSummary.viewed_by == None, |
304 | + ... BugSummary.access_policy == None), |
305 | ... TeamParticipation.person == owner), |
306 | ... BugSummary.sourcepackagename == None, |
307 | ... BugSummary.tag == None).sum(BugSummary.count) or 0 |
308 | |
309 | === modified file 'lib/lp/bugs/model/bug.py' |
310 | --- lib/lp/bugs/model/bug.py 2012-07-19 04:40:03 +0000 |
311 | +++ lib/lp/bugs/model/bug.py 2012-07-25 08:05:24 +0000 |
312 | @@ -227,12 +227,7 @@ |
313 | get_property_cache, |
314 | ) |
315 | from lp.services.webapp.authorization import check_permission |
316 | -from lp.services.webapp.interfaces import ( |
317 | - DEFAULT_FLAVOR, |
318 | - ILaunchBag, |
319 | - IStoreSelector, |
320 | - MAIN_STORE, |
321 | - ) |
322 | +from lp.services.webapp.interfaces import ILaunchBag |
323 | |
324 | |
325 | _bug_tag_query_template = """ |
326 | @@ -291,29 +286,26 @@ |
327 | (and {} returned). |
328 | """ |
329 | # Circular fail. |
330 | - from lp.bugs.model.bugsummary import BugSummary |
331 | + from lp.bugs.model.bugsummary import ( |
332 | + BugSummary, |
333 | + get_bugsummary_filter_for_user, |
334 | + ) |
335 | tags = {} |
336 | if include_tags: |
337 | tags = dict((tag, 0) for tag in include_tags) |
338 | - store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR) |
339 | - admin_team = getUtility(ILaunchpadCelebrities).admin |
340 | - if user is not None and not user.inTeam(admin_team): |
341 | - store = store.with_(SQL( |
342 | - "teams AS (" |
343 | - "SELECT team from TeamParticipation WHERE person=?)", (user.id,))) |
344 | where_conditions = [ |
345 | BugSummary.status.is_in(UNRESOLVED_BUGTASK_STATUSES), |
346 | BugSummary.tag != None, |
347 | context_condition, |
348 | ] |
349 | - if user is None: |
350 | - where_conditions.append(BugSummary.viewed_by_id == None) |
351 | - elif not user.inTeam(admin_team): |
352 | - where_conditions.append( |
353 | - Or( |
354 | - BugSummary.viewed_by_id == None, |
355 | - BugSummary.viewed_by_id.is_in(SQL("SELECT team FROM teams")) |
356 | - )) |
357 | + |
358 | + # Apply the privacy filter. |
359 | + store = IStore(BugSummary) |
360 | + user_with, user_where = get_bugsummary_filter_for_user(user) |
361 | + if user_with: |
362 | + store = store.with_(user_with) |
363 | + where_conditions.extend(user_where) |
364 | + |
365 | sum_count = Sum(BugSummary.count) |
366 | tag_count_columns = (BugSummary.tag, sum_count) |
367 | |
368 | |
369 | === modified file 'lib/lp/bugs/model/bugsummary.py' |
370 | --- lib/lp/bugs/model/bugsummary.py 2012-06-25 09:45:56 +0000 |
371 | +++ lib/lp/bugs/model/bugsummary.py 2012-07-25 08:05:24 +0000 |
372 | @@ -7,16 +7,23 @@ |
373 | __all__ = [ |
374 | 'BugSummary', |
375 | 'CombineBugSummaryConstraint', |
376 | + 'get_bugsummary_filter_for_user', |
377 | ] |
378 | |
379 | -from storm.locals import ( |
380 | +from storm.base import Storm |
381 | +from storm.expr import ( |
382 | And, |
383 | + Or, |
384 | + Select, |
385 | + SQL, |
386 | + With, |
387 | + ) |
388 | +from storm.properties import ( |
389 | Bool, |
390 | Int, |
391 | - Reference, |
392 | - Storm, |
393 | Unicode, |
394 | ) |
395 | +from storm.references import Reference |
396 | from zope.interface import implements |
397 | from zope.security.proxy import removeSecurityProxy |
398 | |
399 | @@ -29,6 +36,11 @@ |
400 | BugTaskStatus, |
401 | BugTaskStatusSearch, |
402 | ) |
403 | +from lp.registry.interfaces.role import IPersonRoles |
404 | +from lp.registry.model.accesspolicy import ( |
405 | + AccessPolicy, |
406 | + AccessPolicyGrant, |
407 | + ) |
408 | from lp.registry.model.distribution import Distribution |
409 | from lp.registry.model.distroseries import DistroSeries |
410 | from lp.registry.model.milestone import Milestone |
411 | @@ -36,6 +48,7 @@ |
412 | from lp.registry.model.product import Product |
413 | from lp.registry.model.productseries import ProductSeries |
414 | from lp.registry.model.sourcepackagename import SourcePackageName |
415 | +from lp.registry.model.teammembership import TeamParticipation |
416 | from lp.services.database.enumcol import EnumCol |
417 | |
418 | |
419 | @@ -76,6 +89,8 @@ |
420 | |
421 | viewed_by_id = Int(name='viewed_by') |
422 | viewed_by = Reference(viewed_by_id, Person.id) |
423 | + access_policy_id = Int(name='access_policy') |
424 | + access_policy = Reference(access_policy_id, AccessPolicy.id) |
425 | |
426 | has_patch = Bool() |
427 | |
428 | @@ -99,3 +114,46 @@ |
429 | def getBugSummaryContextWhereClause(self): |
430 | """See `IBugSummaryDimension`.""" |
431 | return And(*self.dimensions) |
432 | + |
433 | + |
434 | +def get_bugsummary_filter_for_user(user): |
435 | + """Build a Storm expression to filter BugSummary by visibility. |
436 | + |
437 | + :param user: The user for which visible rows should be calculated. |
438 | + :return: (with_clauses, where_clauses) |
439 | + """ |
440 | + # Admins get to see every bug, everyone else only sees bugs |
441 | + # viewable by them-or-their-teams. |
442 | + # Note that because admins can see every bug regardless of |
443 | + # subscription they will see rather inflated counts. Admins get to |
444 | + # deal. |
445 | + public_filter = And( |
446 | + BugSummary.viewed_by_id == None, |
447 | + BugSummary.access_policy_id == None) |
448 | + if user is None: |
449 | + return [], [public_filter] |
450 | + elif IPersonRoles(user).in_admin: |
451 | + return [], [] |
452 | + else: |
453 | + with_clauses = [ |
454 | + With( |
455 | + 'teams', |
456 | + Select( |
457 | + TeamParticipation.teamID, tables=[TeamParticipation], |
458 | + where=(TeamParticipation.personID == user.id))), |
459 | + With( |
460 | + 'policies', |
461 | + Select( |
462 | + AccessPolicyGrant.policy_id, |
463 | + tables=[AccessPolicyGrant], |
464 | + where=( |
465 | + AccessPolicyGrant.grantee_id.is_in( |
466 | + SQL("SELECT team FROM teams"))))), |
467 | + ] |
468 | + where_clauses = [Or( |
469 | + public_filter, |
470 | + BugSummary.viewed_by_id.is_in( |
471 | + SQL("SELECT team FROM teams")), |
472 | + BugSummary.access_policy_id.is_in( |
473 | + SQL("SELECT policy FROM policies")))] |
474 | + return with_clauses, where_clauses |
475 | |
476 | === modified file 'lib/lp/bugs/model/bugtask.py' |
477 | --- lib/lp/bugs/model/bugtask.py 2012-07-24 10:03:32 +0000 |
478 | +++ lib/lp/bugs/model/bugtask.py 2012-07-25 08:05:24 +0000 |
479 | @@ -1549,7 +1549,10 @@ |
480 | def countBugs(self, user, contexts, group_on): |
481 | """See `IBugTaskSet`.""" |
482 | # Circular fail. |
483 | - from lp.bugs.model.bugsummary import BugSummary |
484 | + from lp.bugs.model.bugsummary import ( |
485 | + BugSummary, |
486 | + get_bugsummary_filter_for_user, |
487 | + ) |
488 | conditions = [] |
489 | # Open bug statuses |
490 | conditions.append( |
491 | @@ -1577,27 +1580,14 @@ |
492 | conditions.append(BugSummary.tag == None) |
493 | else: |
494 | conditions.append(BugSummary.tag != None) |
495 | + |
496 | + # Apply the privacy filter. |
497 | store = IStore(BugSummary) |
498 | - admin_team = getUtility(ILaunchpadCelebrities).admin |
499 | - if user is not None and not user.inTeam(admin_team): |
500 | - # admins get to see every bug, everyone else only sees bugs |
501 | - # viewable by them-or-their-teams. |
502 | - store = store.with_(SQL( |
503 | - "teams AS (" |
504 | - "SELECT team from TeamParticipation WHERE person=?)", |
505 | - (user.id,))) |
506 | - # Note that because admins can see every bug regardless of |
507 | - # subscription they will see rather inflated counts. Admins get to |
508 | - # deal. |
509 | - if user is None: |
510 | - conditions.append(BugSummary.viewed_by_id == None) |
511 | - elif not user.inTeam(admin_team): |
512 | - conditions.append( |
513 | - Or( |
514 | - BugSummary.viewed_by_id == None, |
515 | - BugSummary.viewed_by_id.is_in( |
516 | - SQL("SELECT team FROM teams")) |
517 | - )) |
518 | + user_with, user_where = get_bugsummary_filter_for_user(user) |
519 | + if user_with: |
520 | + store = store.with_(user_with) |
521 | + conditions.extend(user_where) |
522 | + |
523 | sum_count = Sum(BugSummary.count) |
524 | resultset = store.find(group_on + (sum_count,), *conditions) |
525 | resultset.group_by(*group_on) |
526 | |
527 | === modified file 'lib/lp/bugs/model/tests/test_bugsummary.py' |
528 | --- lib/lp/bugs/model/tests/test_bugsummary.py 2012-05-24 22:37:33 +0000 |
529 | +++ lib/lp/bugs/model/tests/test_bugsummary.py 2012-07-25 08:05:24 +0000 |
530 | @@ -16,10 +16,12 @@ |
531 | BugTaskStatus, |
532 | ) |
533 | from lp.bugs.model.bug import BugTag |
534 | -from lp.bugs.model.bugsummary import BugSummary |
535 | +from lp.bugs.model.bugsummary import ( |
536 | + BugSummary, |
537 | + get_bugsummary_filter_for_user, |
538 | + ) |
539 | from lp.bugs.model.bugtask import BugTask |
540 | from lp.registry.enums import InformationType |
541 | -from lp.registry.model.teammembership import TeamParticipation |
542 | from lp.services.database.lpstorm import IMasterStore |
543 | from lp.testing import TestCaseWithFactory |
544 | from lp.testing.dbuser import switch_dbuser |
545 | @@ -41,21 +43,14 @@ |
546 | |
547 | def getCount(self, person, **kw_find_expr): |
548 | self._maybe_rollup() |
549 | - |
550 | - public_summaries = self.store.find( |
551 | - BugSummary, |
552 | - BugSummary.viewed_by == None, |
553 | - **kw_find_expr) |
554 | - private_summaries = self.store.find( |
555 | - BugSummary, |
556 | - BugSummary.viewed_by_id == TeamParticipation.teamID, |
557 | - TeamParticipation.person == person, |
558 | - **kw_find_expr) |
559 | - all_summaries = public_summaries.union(private_summaries, all=True) |
560 | - |
561 | + store = self.store |
562 | + user_with, user_where = get_bugsummary_filter_for_user(person) |
563 | + if user_with: |
564 | + store = store.with_(user_with) |
565 | + summaries = store.find(BugSummary, *user_where, **kw_find_expr) |
566 | # Note that if there a 0 records found, sum() returns None, but |
567 | # we prefer to return 0 here. |
568 | - return all_summaries.sum(BugSummary.count) or 0 |
569 | + return summaries.sum(BugSummary.count) or 0 |
570 | |
571 | def assertCount(self, count, user=None, **kw_find_expr): |
572 | self.assertEqual(count, self.getCount(user, **kw_find_expr)) |
Thank you.