Merge lp:~allenap/launchpad/wire-up-filter-subs-bug-655567-devel into lp:launchpad
- wire-up-filter-subs-bug-655567-devel
- Merge into devel
Proposed by
Gavin Panella
Status: | Merged |
---|---|
Approved by: | Gavin Panella |
Approved revision: | no longer in the source branch. |
Merged at revision: | 11721 |
Proposed branch: | lp:~allenap/launchpad/wire-up-filter-subs-bug-655567-devel |
Merge into: | lp:launchpad |
Diff against target: |
881 lines (+239/-250) 12 files modified
database/schema/security.cfg (+7/-0) lib/lp/bugs/configure.zcml (+1/-0) lib/lp/bugs/interfaces/bug.py (+8/-2) lib/lp/bugs/model/bug.py (+61/-36) lib/lp/bugs/model/tests/test_bug.py (+91/-9) lib/lp/registry/doc/structural-subscriptions.txt (+0/-55) lib/lp/registry/interfaces/person.py (+0/-11) lib/lp/registry/interfaces/structuralsubscription.py (+2/-2) lib/lp/registry/model/person.py (+0/-53) lib/lp/registry/model/structuralsubscription.py (+5/-19) lib/lp/registry/tests/test_structuralsubscriptiontarget.py (+63/-62) utilities/format-new-and-modified-imports (+1/-1) |
To merge this branch: | bzr merge lp:~allenap/launchpad/wire-up-filter-subs-bug-655567-devel |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Abel Deuring (community) | code | Approve | |
Review via email: mp+38562@code.launchpad.net |
Commit message
Merge wire-up-
Description of the change
Merge lp:~allenap/launchpad/wire-up-filter-subs-bug-655567 into devel. It's already in db-devel.
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 'database/schema/security.cfg' |
2 | --- database/schema/security.cfg 2010-10-07 22:46:08 +0000 |
3 | +++ database/schema/security.cfg 2010-10-15 16:17:07 +0000 |
4 | @@ -549,6 +549,7 @@ |
5 | public.bugsubscriptionfilterstatus = SELECT |
6 | public.bugsubscriptionfilterimportance = SELECT |
7 | public.bugsubscriptionfiltertag = SELECT |
8 | +public.bugtag = SELECT |
9 | public.bugtask = SELECT, INSERT, UPDATE |
10 | public.bugtracker = SELECT, INSERT |
11 | public.bugtrackercomponent = SELECT, INSERT, UPDATE, DELETE |
12 | @@ -638,6 +639,7 @@ |
13 | public.bugsubscriptionfiltertag = SELECT |
14 | public.bugnotification = SELECT, INSERT |
15 | public.bugnotificationrecipient = SELECT, INSERT |
16 | +public.bugtag = SELECT |
17 | public.structuralsubscription = SELECT |
18 | public.message = SELECT, INSERT |
19 | public.messagechunk = SELECT, INSERT |
20 | @@ -833,6 +835,7 @@ |
21 | public.bugnotification = SELECT, INSERT |
22 | public.bugnotificationrecipient = SELECT, INSERT |
23 | public.bugnomination = SELECT |
24 | +public.bugtag = SELECT |
25 | public.bugtask = SELECT, UPDATE |
26 | public.product = SELECT |
27 | public.project = SELECT |
28 | @@ -1194,6 +1197,7 @@ |
29 | public.bugnotification = SELECT, INSERT |
30 | public.bugnotificationrecipient = SELECT, INSERT |
31 | public.bugnomination = SELECT |
32 | +public.bugtag = SELECT |
33 | public.bugtask = SELECT, UPDATE |
34 | public.product = SELECT, UPDATE |
35 | public.project = SELECT, UPDATE |
36 | @@ -1300,6 +1304,7 @@ |
37 | public.bugnotification = SELECT, INSERT |
38 | public.bugnotificationrecipient = SELECT, INSERT |
39 | public.bugnomination = SELECT |
40 | +public.bugtag = SELECT |
41 | public.bugtask = SELECT, UPDATE |
42 | public.product = SELECT, UPDATE |
43 | public.project = SELECT, UPDATE |
44 | @@ -1855,6 +1860,7 @@ |
45 | public.bug = SELECT, INSERT, UPDATE |
46 | public.bugjob = SELECT, INSERT |
47 | public.bugaffectsperson = SELECT, INSERT, UPDATE, DELETE |
48 | +public.bugtag = SELECT |
49 | public.bugtask = SELECT, INSERT, UPDATE |
50 | public.accountpassword = SELECT, INSERT |
51 | public.teamparticipation = SELECT, INSERT |
52 | @@ -1917,6 +1923,7 @@ |
53 | public.bugaffectsperson = SELECT |
54 | public.bugnotification = SELECT, DELETE |
55 | public.bugnotificationrecipientarchive = SELECT |
56 | +public.bugtag = SELECT |
57 | public.bugwatch = SELECT, UPDATE |
58 | public.bugwatchactivity = SELECT, DELETE |
59 | public.codeimportresult = SELECT, DELETE |
60 | |
61 | === modified file 'lib/lp/bugs/configure.zcml' |
62 | --- lib/lp/bugs/configure.zcml 2010-10-06 18:53:53 +0000 |
63 | +++ lib/lp/bugs/configure.zcml 2010-10-15 16:17:07 +0000 |
64 | @@ -706,6 +706,7 @@ |
65 | getSubscribersForPerson |
66 | indexed_messages |
67 | getAlsoNotifiedSubscribers |
68 | + getStructuralSubscribers |
69 | getBugWatch |
70 | canBeNominatedFor |
71 | getNominationFor |
72 | |
73 | === modified file 'lib/lp/bugs/interfaces/bug.py' |
74 | --- lib/lp/bugs/interfaces/bug.py 2010-10-12 14:56:31 +0000 |
75 | +++ lib/lp/bugs/interfaces/bug.py 2010-10-15 16:17:07 +0000 |
76 | @@ -493,6 +493,12 @@ |
77 | from duplicates. |
78 | """ |
79 | |
80 | + def getStructuralSubscribers(recipients=None, level=None): |
81 | + """Return `IPerson`s subscribed to this bug's targets. |
82 | + |
83 | + This takes into account bug subscription filters. |
84 | + """ |
85 | + |
86 | def getSubscriptionsFromDuplicates(): |
87 | """Return IBugSubscriptions subscribed from dupes of this bug.""" |
88 | |
89 | @@ -501,9 +507,9 @@ |
90 | |
91 | def getSubscribersForPerson(person): |
92 | """Find the persons or teams by which person is subscribed. |
93 | - |
94 | + |
95 | This call should be quite cheap to make and performs a single query. |
96 | - |
97 | + |
98 | :return: An IResultSet. |
99 | """ |
100 | |
101 | |
102 | === modified file 'lib/lp/bugs/model/bug.py' |
103 | --- lib/lp/bugs/model/bug.py 2010-10-14 11:05:05 +0000 |
104 | +++ lib/lp/bugs/model/bug.py 2010-10-15 16:17:07 +0000 |
105 | @@ -51,7 +51,6 @@ |
106 | In, |
107 | LeftJoin, |
108 | Max, |
109 | - Min, |
110 | Not, |
111 | Or, |
112 | Select, |
113 | @@ -156,14 +155,13 @@ |
114 | from lp.bugs.model.bugnomination import BugNomination |
115 | from lp.bugs.model.bugnotification import BugNotification |
116 | from lp.bugs.model.bugsubscription import BugSubscription |
117 | +from lp.bugs.model.bugtarget import OfficialBugTag |
118 | from lp.bugs.model.bugtask import ( |
119 | BugTask, |
120 | bugtask_sort_key, |
121 | - BugTaskSet, |
122 | get_bug_privacy_filter, |
123 | NullBugTask, |
124 | ) |
125 | -from lp.bugs.model.bugtarget import OfficialBugTag |
126 | from lp.bugs.model.bugwatch import BugWatch |
127 | from lp.hardwaredb.interfaces.hwdb import IHWSubmissionBugSet |
128 | from lp.registry.enum import BugNotificationLevel |
129 | @@ -172,10 +170,7 @@ |
130 | IDistributionSourcePackage, |
131 | ) |
132 | from lp.registry.interfaces.distroseries import IDistroSeries |
133 | -from lp.registry.interfaces.person import ( |
134 | - IPersonSet, |
135 | - validate_public_person, |
136 | - ) |
137 | +from lp.registry.interfaces.person import validate_public_person |
138 | from lp.registry.interfaces.product import IProduct |
139 | from lp.registry.interfaces.productseries import IProductSeries |
140 | from lp.registry.interfaces.series import SeriesStatus |
141 | @@ -458,11 +453,9 @@ |
142 | store = Store.of(self) |
143 | message_by_id = {} |
144 | if include_parents: |
145 | - def to_messages(rows): |
146 | - return [row[0] for row in rows] |
147 | + to_messages = lambda rows: [row[0] for row in rows] |
148 | else: |
149 | - def to_messages(rows): |
150 | - return rows |
151 | + to_messages = lambda rows: rows |
152 | def eager_load_owners(messages): |
153 | # Because we may have multiple owners, we spend less time in storm |
154 | # with very large bugs by not joining and instead querying a second |
155 | @@ -976,31 +969,12 @@ |
156 | |
157 | also_notified_subscribers = set() |
158 | |
159 | - structural_subscription_targets = set() |
160 | - |
161 | for bugtask in self.bugtasks: |
162 | if bugtask.assignee: |
163 | also_notified_subscribers.add(bugtask.assignee) |
164 | if recipients is not None: |
165 | recipients.addAssignee(bugtask.assignee) |
166 | |
167 | - if IStructuralSubscriptionTarget.providedBy(bugtask.target): |
168 | - structural_subscription_targets.add(bugtask.target) |
169 | - if bugtask.target.parent_subscription_target is not None: |
170 | - structural_subscription_targets.add( |
171 | - bugtask.target.parent_subscription_target) |
172 | - |
173 | - if ISourcePackage.providedBy(bugtask.target): |
174 | - # Distribution series bug tasks with a package have the |
175 | - # source package set as their target, so we add the |
176 | - # distroseries explicitly to the set of subscription |
177 | - # targets. |
178 | - structural_subscription_targets.add( |
179 | - bugtask.distroseries) |
180 | - |
181 | - if bugtask.milestone is not None: |
182 | - structural_subscription_targets.add(bugtask.milestone) |
183 | - |
184 | # If the target's bug supervisor isn't set, |
185 | # we add the owner as a subscriber. |
186 | pillar = bugtask.pillar |
187 | @@ -1009,22 +983,73 @@ |
188 | if recipients is not None: |
189 | recipients.addRegistrant(pillar.owner, pillar) |
190 | |
191 | - person_set = getUtility(IPersonSet) |
192 | - target_subscribers = person_set.getSubscribersForTargets( |
193 | - structural_subscription_targets, recipients=recipients, |
194 | - level=level) |
195 | - |
196 | - also_notified_subscribers.update(target_subscribers) |
197 | + # Structural subscribers. |
198 | + also_notified_subscribers.update( |
199 | + self.getStructuralSubscribers( |
200 | + recipients=recipients, level=level)) |
201 | |
202 | # Direct subscriptions always take precedence over indirect |
203 | # subscriptions. |
204 | direct_subscribers = set(self.getDirectSubscribers()) |
205 | + |
206 | # Remove security proxy for the sort key, but return |
207 | # the regular proxied object. |
208 | return sorted( |
209 | (also_notified_subscribers - direct_subscribers), |
210 | key=lambda x: removeSecurityProxy(x).displayname) |
211 | |
212 | + def getStructuralSubscribers(self, recipients=None, level=None): |
213 | + """See `IBug`. """ |
214 | + query_arguments = [] |
215 | + for bugtask in self.bugtasks: |
216 | + if IStructuralSubscriptionTarget.providedBy(bugtask.target): |
217 | + query_arguments.append((bugtask.target, bugtask)) |
218 | + if bugtask.target.parent_subscription_target is not None: |
219 | + query_arguments.append( |
220 | + (bugtask.target.parent_subscription_target, bugtask)) |
221 | + if ISourcePackage.providedBy(bugtask.target): |
222 | + # Distribution series bug tasks with a package have the source |
223 | + # package set as their target, so we add the distroseries |
224 | + # explicitly to the set of subscription targets. |
225 | + query_arguments.append((bugtask.distroseries, bugtask)) |
226 | + if bugtask.milestone is not None: |
227 | + query_arguments.append((bugtask.milestone, bugtask)) |
228 | + |
229 | + if len(query_arguments) == 0: |
230 | + return EmptyResultSet() |
231 | + |
232 | + if level is None: |
233 | + # If level is not specified, default to NOTHING so that all |
234 | + # subscriptions are found. XXX: Perhaps this should go in |
235 | + # getSubscriptionsForBugTask()? |
236 | + level = BugNotificationLevel.NOTHING |
237 | + |
238 | + # Build the query. |
239 | + union = lambda left, right: left.union(right) |
240 | + queries = ( |
241 | + target.getSubscriptionsForBugTask(bugtask, level) |
242 | + for target, bugtask in query_arguments) |
243 | + subscriptions = reduce(union, queries) |
244 | + |
245 | + # Pull all the subscriptions in. |
246 | + subscriptions = list(subscriptions) |
247 | + |
248 | + # Prepare a query for the subscribers. |
249 | + subscribers = Store.of(self).find( |
250 | + Person, Person.id.is_in( |
251 | + subscription.subscriberID |
252 | + for subscription in subscriptions)) |
253 | + |
254 | + if recipients is not None: |
255 | + # We need to process subscriptions, so pull all the subscribes |
256 | + # into the cache, then update recipients with the subscriptions. |
257 | + subscribers = list(subscribers) |
258 | + for subscription in subscriptions: |
259 | + recipients.addStructuralSubscriber( |
260 | + subscription.subscriber, subscription.target) |
261 | + |
262 | + return subscribers |
263 | + |
264 | def getBugNotificationRecipients(self, duplicateof=None, old_bug=None, |
265 | level=None, |
266 | include_master_dupe_subscribers=False): |
267 | |
268 | === renamed file 'lib/lp/bugs/tests/test_bug.py' => 'lib/lp/bugs/model/tests/test_bug.py' |
269 | --- lib/lp/bugs/tests/test_bug.py 2010-10-14 13:47:47 +0000 |
270 | +++ lib/lp/bugs/model/tests/test_bug.py 2010-10-15 16:17:07 +0000 |
271 | @@ -5,7 +5,10 @@ |
272 | |
273 | __metaclass__ = type |
274 | |
275 | +from storm.store import ResultSet |
276 | + |
277 | from canonical.testing.layers import DatabaseFunctionalLayer |
278 | +from lp.bugs.mail.bugnotificationrecipients import BugNotificationRecipients |
279 | from lp.registry.enum import BugNotificationLevel |
280 | from lp.registry.interfaces.person import PersonVisibility |
281 | from lp.registry.model.structuralsubscription import StructuralSubscription |
282 | @@ -14,6 +17,7 @@ |
283 | person_logged_in, |
284 | TestCaseWithFactory, |
285 | ) |
286 | +from lp.testing.matchers import StartsWith |
287 | |
288 | |
289 | class TestBug(TestCaseWithFactory): |
290 | @@ -36,7 +40,7 @@ |
291 | bug = self.factory.makeBug() |
292 | person = self.factory.makePerson() |
293 | team1 = self.factory.makeTeam(members=[person]) |
294 | - team2 = self.factory.makeTeam(members=[person]) |
295 | + self.factory.makeTeam(members=[person]) |
296 | with person_logged_in(person): |
297 | bug.subscribe(team1, person) |
298 | self.assertEqual([team1], list(bug.getSubscribersForPerson(person))) |
299 | @@ -157,8 +161,7 @@ |
300 | subscriber = self.factory.makePerson() |
301 | subscribers.append(subscriber) |
302 | with person_logged_in(subscriber): |
303 | - subscription = bug.subscribe( |
304 | - subscriber, subscriber, level=level) |
305 | + bug.subscribe(subscriber, subscriber, level=level) |
306 | direct_subscribers = bug.getDirectSubscribers(level=level) |
307 | |
308 | # All the previous subscribers will be included because |
309 | @@ -181,8 +184,7 @@ |
310 | subscriber = self.factory.makePerson() |
311 | subscribers.append(subscriber) |
312 | with person_logged_in(subscriber): |
313 | - subscription = bug.subscribe( |
314 | - subscriber, subscriber, level=level) |
315 | + bug.subscribe(subscriber, subscriber, level=level) |
316 | |
317 | # All the subscribers should be returned by |
318 | # getDirectSubscribers() because it defaults to returning |
319 | @@ -211,8 +213,7 @@ |
320 | subscriber = self.factory.makePerson() |
321 | subscribers.append(subscriber) |
322 | with person_logged_in(subscriber): |
323 | - subscription = duplicate_bug.subscribe( |
324 | - subscriber, subscriber, level=level) |
325 | + duplicate_bug.subscribe(subscriber, subscriber, level=level) |
326 | duplicate_subscribers = ( |
327 | bug.getSubscribersFromDuplicates(level=level)) |
328 | # All the previous subscribers will be included because |
329 | @@ -237,11 +238,92 @@ |
330 | duplicate_bug.owner, duplicate_bug.owner) |
331 | subscriber = self.factory.makePerson() |
332 | with person_logged_in(subscriber): |
333 | - direct_subscription = bug.subscribe( |
334 | + bug.subscribe( |
335 | subscriber, subscriber, level=BugNotificationLevel.NOTHING) |
336 | - dupe_subscription = duplicate_bug.subscribe( |
337 | + duplicate_bug.subscribe( |
338 | subscriber, subscriber, level=BugNotificationLevel.METADATA) |
339 | duplicate_subscribers = bug.getSubscribersFromDuplicates() |
340 | self.assertTrue( |
341 | subscriber not in duplicate_subscribers, |
342 | "Subscriber should not be in duplicate_subscribers.") |
343 | + |
344 | + |
345 | +class TestBugStructuralSubscribers(TestCaseWithFactory): |
346 | + |
347 | + layer = DatabaseFunctionalLayer |
348 | + |
349 | + def test_getStructuralSubscribers_no_subscribers(self): |
350 | + # If there are no subscribers for any of the bug's targets then no |
351 | + # subscribers will be returned by getStructuralSubscribers(). |
352 | + product = self.factory.makeProduct() |
353 | + bug = self.factory.makeBug(product=product) |
354 | + subscribers = bug.getStructuralSubscribers() |
355 | + self.assertIsInstance(subscribers, ResultSet) |
356 | + self.assertEqual([], list(subscribers)) |
357 | + |
358 | + def test_getStructuralSubscribers_single_target(self): |
359 | + # Subscribers for any of the bug's targets are returned. |
360 | + subscriber = self.factory.makePerson() |
361 | + login_person(subscriber) |
362 | + product = self.factory.makeProduct() |
363 | + product.addBugSubscription(subscriber, subscriber) |
364 | + bug = self.factory.makeBug(product=product) |
365 | + self.assertEqual([subscriber], list(bug.getStructuralSubscribers())) |
366 | + |
367 | + def test_getStructuralSubscribers_multiple_targets(self): |
368 | + # Subscribers for any of the bug's targets are returned. |
369 | + actor = self.factory.makePerson() |
370 | + login_person(actor) |
371 | + |
372 | + subscriber1 = self.factory.makePerson() |
373 | + subscriber2 = self.factory.makePerson() |
374 | + |
375 | + product1 = self.factory.makeProduct(owner=actor) |
376 | + product1.addBugSubscription(subscriber1, subscriber1) |
377 | + product2 = self.factory.makeProduct(owner=actor) |
378 | + product2.addBugSubscription(subscriber2, subscriber2) |
379 | + |
380 | + bug = self.factory.makeBug(product=product1) |
381 | + bug.addTask(actor, product2) |
382 | + |
383 | + subscribers = bug.getStructuralSubscribers() |
384 | + self.assertIsInstance(subscribers, ResultSet) |
385 | + self.assertEqual(set([subscriber1, subscriber2]), set(subscribers)) |
386 | + |
387 | + def test_getStructuralSubscribers_recipients(self): |
388 | + # If provided, getStructuralSubscribers() calls the appropriate |
389 | + # methods on a BugNotificationRecipients object. |
390 | + subscriber = self.factory.makePerson() |
391 | + login_person(subscriber) |
392 | + product = self.factory.makeProduct() |
393 | + product.addBugSubscription(subscriber, subscriber) |
394 | + bug = self.factory.makeBug(product=product) |
395 | + recipients = BugNotificationRecipients() |
396 | + subscribers = bug.getStructuralSubscribers(recipients=recipients) |
397 | + # The return value is a list only when populating recipients. |
398 | + self.assertIsInstance(subscribers, list) |
399 | + self.assertEqual([subscriber], recipients.getRecipients()) |
400 | + reason, header = recipients.getReason(subscriber) |
401 | + self.assertThat( |
402 | + reason, StartsWith( |
403 | + u"You received this bug notification because " |
404 | + u"you are subscribed to ")) |
405 | + self.assertThat(header, StartsWith(u"Subscriber ")) |
406 | + |
407 | + def test_getStructuralSubscribers_level(self): |
408 | + # getStructuralSubscribers() respects the given level. |
409 | + subscriber = self.factory.makePerson() |
410 | + login_person(subscriber) |
411 | + product = self.factory.makeProduct() |
412 | + subscription = product.addBugSubscription(subscriber, subscriber) |
413 | + subscription.bug_notification_level = BugNotificationLevel.METADATA |
414 | + bug = self.factory.makeBug(product=product) |
415 | + self.assertEqual( |
416 | + [subscriber], list( |
417 | + bug.getStructuralSubscribers( |
418 | + level=BugNotificationLevel.METADATA))) |
419 | + subscription.bug_notification_level = BugNotificationLevel.METADATA |
420 | + self.assertEqual( |
421 | + [], list( |
422 | + bug.getStructuralSubscribers( |
423 | + level=BugNotificationLevel.COMMENTS))) |
424 | |
425 | === modified file 'lib/lp/registry/doc/structural-subscriptions.txt' |
426 | --- lib/lp/registry/doc/structural-subscriptions.txt 2010-10-06 18:53:53 +0000 |
427 | +++ lib/lp/registry/doc/structural-subscriptions.txt 2010-10-15 16:17:07 +0000 |
428 | @@ -198,61 +198,6 @@ |
429 | name16 |
430 | |
431 | |
432 | -Retrieving structural subscribers of targets |
433 | -============================================ |
434 | - |
435 | -Subscribers of one or more targets are retrieved by |
436 | -PersonSet.getSubscribersForTargets. |
437 | - |
438 | -XXX Abel Deuring 2008-07-11 We must remove the security proxy, because |
439 | -PersonSet.getSubscribersForTargets() accesses the private attribute |
440 | -firefox._targets_args. This attribute should be renamed. Bug #247525. |
441 | - |
442 | - >>> from zope.security.proxy import removeSecurityProxy |
443 | - >>> firefox_no_proxy = removeSecurityProxy(firefox) |
444 | - >>> from lp.registry.interfaces.person import IPersonSet |
445 | - >>> person_set = getUtility(IPersonSet) |
446 | - >>> firefox_subscribers = person_set.getSubscribersForTargets( |
447 | - ... [firefox_no_proxy]) |
448 | - >>> print_bug_subscribers(firefox_subscribers) |
449 | - name12 |
450 | - |
451 | -If PersonSet.getSubscribersForTargets() is passed a |
452 | -BugNotificationLevel in its `level` parameter, only structural |
453 | -subscribers with that notification level or higher will be returned. |
454 | -The subscription level of ff_sub, the only structural subscription |
455 | -to firefox, is NOTHING, hence we get an empty list if we pass any |
456 | -larger bug notification level. |
457 | - |
458 | - >>> firefox_subscribers = person_set.getSubscribersForTargets( |
459 | - ... [firefox_no_proxy], level=BugNotificationLevel.LIFECYCLE) |
460 | - >>> print len(firefox_subscribers) |
461 | - 0 |
462 | - |
463 | -If the bug notification level of ff_sub is increased, the subscription |
464 | -is included in the result of PersonSet.getSubscribersForTarget(). |
465 | - |
466 | - >>> ff_sub.level = BugNotificationLevel.LIFECYCLE |
467 | - >>> firefox_subscribers = person_set.getSubscribersForTargets( |
468 | - ... [firefox_no_proxy]) |
469 | - >>> print_bug_subscribers(firefox_subscribers) |
470 | - name12 |
471 | - >>> ff_sub.level = BugNotificationLevel.METADATA |
472 | - >>> firefox_subscribers = person_set.getSubscribersForTargets( |
473 | - ... [firefox_no_proxy]) |
474 | - >>> print_bug_subscribers(firefox_subscribers) |
475 | - name12 |
476 | - |
477 | -More than one target can be passed to PersonSet.getSubscribersForTargets(). |
478 | - |
479 | - >>> ubuntu_no_proxy = removeSecurityProxy(ubuntu) |
480 | - >>> ubuntu_or_firefox_subscribers = person_set.getSubscribersForTargets( |
481 | - ... [firefox_no_proxy, ubuntu_no_proxy]) |
482 | - >>> print_bug_subscribers(ubuntu_or_firefox_subscribers) |
483 | - name12 |
484 | - name16 |
485 | - |
486 | - |
487 | Target type display |
488 | =================== |
489 | |
490 | |
491 | === modified file 'lib/lp/registry/interfaces/person.py' |
492 | --- lib/lp/registry/interfaces/person.py 2010-09-29 20:05:17 +0000 |
493 | +++ lib/lp/registry/interfaces/person.py 2010-10-15 16:17:07 +0000 |
494 | @@ -2006,17 +2006,6 @@ |
495 | specified product are returned. |
496 | """ |
497 | |
498 | - def getSubscribersForTargets(targets, recipients=None): |
499 | - """Return the set of subscribers for `targets`. |
500 | - |
501 | - :param targets: The sequence of targets for which to get the |
502 | - subscribers. |
503 | - :param recipients: An optional instance of |
504 | - `BugNotificationRecipients`. |
505 | - If present, all found subscribers will be |
506 | - added to it. |
507 | - """ |
508 | - |
509 | def updatePersonalStandings(): |
510 | """Update the personal standings of some people. |
511 | |
512 | |
513 | === modified file 'lib/lp/registry/interfaces/structuralsubscription.py' |
514 | --- lib/lp/registry/interfaces/structuralsubscription.py 2010-10-05 09:23:22 +0000 |
515 | +++ lib/lp/registry/interfaces/structuralsubscription.py 2010-10-15 16:17:07 +0000 |
516 | @@ -200,8 +200,8 @@ |
517 | def userHasBugSubscriptions(user): |
518 | """Is `user` subscribed, directly or via a team, to bug mail?""" |
519 | |
520 | - def getSubscriptionsForBug(bug, level): |
521 | - """Return subscriptions for a given `IBug` at `level`.""" |
522 | + def getSubscriptionsForBugTask(bug, level): |
523 | + """Return subscriptions for a given `IBugTask` at `level`.""" |
524 | |
525 | |
526 | class IStructuralSubscriptionTargetWrite(Interface): |
527 | |
528 | === modified file 'lib/lp/registry/model/person.py' |
529 | --- lib/lp/registry/model/person.py 2010-09-29 14:38:30 +0000 |
530 | +++ lib/lp/registry/model/person.py 2010-10-15 16:17:07 +0000 |
531 | @@ -4060,59 +4060,6 @@ |
532 | Person.id in (%s) |
533 | ''' % branch_clause) |
534 | |
535 | - def getSubscribersForTargets(self, targets, recipients=None, level=None): |
536 | - """See `IPersonSet`. """ |
537 | - if len(targets) == 0: |
538 | - return set() |
539 | - target_criteria = [] |
540 | - for target in targets: |
541 | - # target_args is a mapping from query arguments |
542 | - # to query values. |
543 | - target_args = target._target_args |
544 | - target_criteria_clauses = [] |
545 | - for key, value in target_args.items(): |
546 | - if value is not None: |
547 | - target_criteria_clauses.append( |
548 | - '%s = %s' % (key, quote(value))) |
549 | - else: |
550 | - target_criteria_clauses.append( |
551 | - '%s IS NULL' % key) |
552 | - if level is not None: |
553 | - target_criteria_clauses.append('bug_notification_level >= %s' |
554 | - % quote(level.value)) |
555 | - |
556 | - target_criteria.append( |
557 | - '(%s)' % ' AND '.join(target_criteria_clauses)) |
558 | - |
559 | - # Build a UNION query, since using OR slows down the query a lot. |
560 | - subscriptions = StructuralSubscription.select(target_criteria[0]) |
561 | - for target_criterion in target_criteria[1:]: |
562 | - subscriptions = subscriptions.union( |
563 | - StructuralSubscription.select(target_criterion)) |
564 | - |
565 | - # Listify the result, since we want to loop over it multiple times. |
566 | - subscriptions = list(subscriptions) |
567 | - |
568 | - # We can't use prejoins in UNION queries, so populate the cache |
569 | - # by getting all the subscribers. |
570 | - subscriber_ids = [ |
571 | - subscription.subscriberID for subscription in subscriptions] |
572 | - if len(subscriber_ids) > 0: |
573 | - # Pull in ValidPersonCache records in addition to Person |
574 | - # records to warm up the cache. |
575 | - list(IStore(Person).find( |
576 | - (Person, ValidPersonCache), |
577 | - In(Person.id, subscriber_ids), |
578 | - ValidPersonCache.id == Person.id)) |
579 | - |
580 | - subscribers = set() |
581 | - for subscription in subscriptions: |
582 | - subscribers.add(subscription.subscriber) |
583 | - if recipients is not None: |
584 | - recipients.addStructuralSubscriber( |
585 | - subscription.subscriber, subscription.target) |
586 | - return subscribers |
587 | - |
588 | def updatePersonalStandings(self): |
589 | """See `IPersonSet`.""" |
590 | cur = cursor() |
591 | |
592 | === modified file 'lib/lp/registry/model/structuralsubscription.py' |
593 | --- lib/lp/registry/model/structuralsubscription.py 2010-10-05 16:52:02 +0000 |
594 | +++ lib/lp/registry/model/structuralsubscription.py 2010-10-15 16:17:07 +0000 |
595 | @@ -484,21 +484,8 @@ |
596 | return True |
597 | return False |
598 | |
599 | - def getSubscriptionsForBug(self, bug, level): |
600 | + def getSubscriptionsForBugTask(self, bugtask, level): |
601 | """See `IStructuralSubscriptionTarget`.""" |
602 | - if IProjectGroup.providedBy(self): |
603 | - targets = set(self.products) |
604 | - elif IMilestone.providedBy(self): |
605 | - targets = [self.series_target] |
606 | - else: |
607 | - targets = [self] |
608 | - |
609 | - bugtasks = [ |
610 | - bugtask for bugtask in bug.bugtasks |
611 | - if bugtask.target in targets] |
612 | - |
613 | - assert len(bugtasks) != 0, repr(self) |
614 | - |
615 | origin = [ |
616 | StructuralSubscription, |
617 | LeftJoin( |
618 | @@ -515,7 +502,7 @@ |
619 | BugSubscriptionFilter.id)), |
620 | ] |
621 | |
622 | - if len(bug.tags) == 0: |
623 | + if len(bugtask.bug.tags) == 0: |
624 | tag_conditions = [ |
625 | BugSubscriptionFilter.include_any_tags == False, |
626 | ] |
627 | @@ -534,13 +521,12 @@ |
628 | # There's no status filter, or there is a status filter |
629 | # and and it matches. |
630 | Or(BugSubscriptionFilterStatus.id == None, |
631 | - BugSubscriptionFilterStatus.status.is_in( |
632 | - bugtask.status for bugtask in bugtasks)), |
633 | + BugSubscriptionFilterStatus.status == bugtask.status), |
634 | # There's no importance filter, or there is an importance |
635 | # filter and it matches. |
636 | Or(BugSubscriptionFilterImportance.id == None, |
637 | - BugSubscriptionFilterImportance.importance.is_in( |
638 | - bugtask.importance for bugtask in bugtasks)), |
639 | + BugSubscriptionFilterImportance.importance == ( |
640 | + bugtask.importance)), |
641 | # Any number of conditions relating to tags. |
642 | *tag_conditions)), |
643 | ] |
644 | |
645 | === modified file 'lib/lp/registry/tests/test_structuralsubscriptiontarget.py' |
646 | --- lib/lp/registry/tests/test_structuralsubscriptiontarget.py 2010-10-06 18:53:53 +0000 |
647 | +++ lib/lp/registry/tests/test_structuralsubscriptiontarget.py 2010-10-15 16:17:07 +0000 |
648 | @@ -173,19 +173,20 @@ |
649 | def makeBugTask(self): |
650 | return self.factory.makeBugTask(target=self.target) |
651 | |
652 | - def test_getSubscriptionsForBug(self): |
653 | + def test_getSubscriptionsForBugTask(self): |
654 | # If no one has a filtered subscription for the given bug, the result |
655 | - # of getSubscriptionsForBug() is the same as for getSubscriptions(). |
656 | + # of getSubscriptionsForBugTask() is the same as for |
657 | + # getSubscriptions(). |
658 | bugtask = self.makeBugTask() |
659 | subscriptions = self.target.getSubscriptions( |
660 | min_bug_notification_level=BugNotificationLevel.NOTHING) |
661 | - subscriptions_for_bug = self.target.getSubscriptionsForBug( |
662 | - bugtask.bug, BugNotificationLevel.NOTHING) |
663 | - self.assertEqual(list(subscriptions), list(subscriptions_for_bug)) |
664 | + subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask( |
665 | + bugtask, BugNotificationLevel.NOTHING) |
666 | + self.assertEqual(list(subscriptions), list(subscriptions_for_bugtask)) |
667 | |
668 | - def test_getSubscriptionsForBug_with_filter_on_status(self): |
669 | + def test_getSubscriptionsForBugTask_with_filter_on_status(self): |
670 | # If a status filter exists for a subscription, the result of |
671 | - # getSubscriptionsForBug() may be a subset of getSubscriptions(). |
672 | + # getSubscriptionsForBugTask() may be a subset of getSubscriptions(). |
673 | bugtask = self.makeBugTask() |
674 | |
675 | # Create a new subscription on self.target. |
676 | @@ -195,9 +196,9 @@ |
677 | subscription.bug_notification_level = BugNotificationLevel.COMMENTS |
678 | |
679 | # Without any filters the subscription is found. |
680 | - subscriptions_for_bug = self.target.getSubscriptionsForBug( |
681 | - bugtask.bug, BugNotificationLevel.NOTHING) |
682 | - self.assertEqual([subscription], list(subscriptions_for_bug)) |
683 | + subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask( |
684 | + bugtask, BugNotificationLevel.NOTHING) |
685 | + self.assertEqual([subscription], list(subscriptions_for_bugtask)) |
686 | |
687 | # Filter the subscription to bugs in the CONFIRMED state. |
688 | subscription_filter = BugSubscriptionFilter() |
689 | @@ -205,19 +206,19 @@ |
690 | subscription_filter.statuses = [BugTaskStatus.CONFIRMED] |
691 | |
692 | # With the filter the subscription is not found. |
693 | - subscriptions_for_bug = self.target.getSubscriptionsForBug( |
694 | - bugtask.bug, BugNotificationLevel.NOTHING) |
695 | - self.assertEqual([], list(subscriptions_for_bug)) |
696 | + subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask( |
697 | + bugtask, BugNotificationLevel.NOTHING) |
698 | + self.assertEqual([], list(subscriptions_for_bugtask)) |
699 | |
700 | # If the filter is adjusted, the subscription is found again. |
701 | subscription_filter.statuses = [bugtask.status] |
702 | - subscriptions_for_bug = self.target.getSubscriptionsForBug( |
703 | - bugtask.bug, BugNotificationLevel.NOTHING) |
704 | - self.assertEqual([subscription], list(subscriptions_for_bug)) |
705 | + subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask( |
706 | + bugtask, BugNotificationLevel.NOTHING) |
707 | + self.assertEqual([subscription], list(subscriptions_for_bugtask)) |
708 | |
709 | - def test_getSubscriptionsForBug_with_filter_on_importance(self): |
710 | + def test_getSubscriptionsForBugTask_with_filter_on_importance(self): |
711 | # If an importance filter exists for a subscription, the result of |
712 | - # getSubscriptionsForBug() may be a subset of getSubscriptions(). |
713 | + # getSubscriptionsForBugTask() may be a subset of getSubscriptions(). |
714 | bugtask = self.makeBugTask() |
715 | |
716 | # Create a new subscription on self.target. |
717 | @@ -227,9 +228,9 @@ |
718 | subscription.bug_notification_level = BugNotificationLevel.COMMENTS |
719 | |
720 | # Without any filters the subscription is found. |
721 | - subscriptions_for_bug = self.target.getSubscriptionsForBug( |
722 | - bugtask.bug, BugNotificationLevel.NOTHING) |
723 | - self.assertEqual([subscription], list(subscriptions_for_bug)) |
724 | + subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask( |
725 | + bugtask, BugNotificationLevel.NOTHING) |
726 | + self.assertEqual([subscription], list(subscriptions_for_bugtask)) |
727 | |
728 | # Filter the subscription to bugs in the CRITICAL state. |
729 | subscription_filter = BugSubscriptionFilter() |
730 | @@ -237,19 +238,19 @@ |
731 | subscription_filter.importances = [BugTaskImportance.CRITICAL] |
732 | |
733 | # With the filter the subscription is not found. |
734 | - subscriptions_for_bug = self.target.getSubscriptionsForBug( |
735 | - bugtask.bug, BugNotificationLevel.NOTHING) |
736 | - self.assertEqual([], list(subscriptions_for_bug)) |
737 | + subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask( |
738 | + bugtask, BugNotificationLevel.NOTHING) |
739 | + self.assertEqual([], list(subscriptions_for_bugtask)) |
740 | |
741 | # If the filter is adjusted, the subscription is found again. |
742 | subscription_filter.importances = [bugtask.importance] |
743 | - subscriptions_for_bug = self.target.getSubscriptionsForBug( |
744 | - bugtask.bug, BugNotificationLevel.NOTHING) |
745 | - self.assertEqual([subscription], list(subscriptions_for_bug)) |
746 | + subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask( |
747 | + bugtask, BugNotificationLevel.NOTHING) |
748 | + self.assertEqual([subscription], list(subscriptions_for_bugtask)) |
749 | |
750 | - def test_getSubscriptionsForBug_with_filter_on_level(self): |
751 | + def test_getSubscriptionsForBugTask_with_filter_on_level(self): |
752 | # All structural subscriptions have a level for bug notifications |
753 | - # which getSubscriptionsForBug() observes. |
754 | + # which getSubscriptionsForBugTask() observes. |
755 | bugtask = self.makeBugTask() |
756 | |
757 | # Create a new METADATA level subscription on self.target. |
758 | @@ -259,19 +260,19 @@ |
759 | subscription.bug_notification_level = BugNotificationLevel.METADATA |
760 | |
761 | # The subscription is found when looking for NOTHING or above. |
762 | - subscriptions_for_bug = self.target.getSubscriptionsForBug( |
763 | - bugtask.bug, BugNotificationLevel.NOTHING) |
764 | - self.assertEqual([subscription], list(subscriptions_for_bug)) |
765 | + subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask( |
766 | + bugtask, BugNotificationLevel.NOTHING) |
767 | + self.assertEqual([subscription], list(subscriptions_for_bugtask)) |
768 | # The subscription is found when looking for METADATA or above. |
769 | - subscriptions_for_bug = self.target.getSubscriptionsForBug( |
770 | - bugtask.bug, BugNotificationLevel.METADATA) |
771 | - self.assertEqual([subscription], list(subscriptions_for_bug)) |
772 | + subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask( |
773 | + bugtask, BugNotificationLevel.METADATA) |
774 | + self.assertEqual([subscription], list(subscriptions_for_bugtask)) |
775 | # The subscription is not found when looking for COMMENTS or above. |
776 | - subscriptions_for_bug = self.target.getSubscriptionsForBug( |
777 | - bugtask.bug, BugNotificationLevel.COMMENTS) |
778 | - self.assertEqual([], list(subscriptions_for_bug)) |
779 | + subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask( |
780 | + bugtask, BugNotificationLevel.COMMENTS) |
781 | + self.assertEqual([], list(subscriptions_for_bugtask)) |
782 | |
783 | - def test_getSubscriptionsForBug_with_filter_include_any_tags(self): |
784 | + def test_getSubscriptionsForBugTask_with_filter_include_any_tags(self): |
785 | # If a subscription filter has include_any_tags, a bug with one or |
786 | # more tags is matched. |
787 | bugtask = self.makeBugTask() |
788 | @@ -285,17 +286,17 @@ |
789 | subscription_filter.include_any_tags = True |
790 | |
791 | # Without any tags the subscription is not found. |
792 | - subscriptions_for_bug = self.target.getSubscriptionsForBug( |
793 | - bugtask.bug, BugNotificationLevel.NOTHING) |
794 | - self.assertEqual([], list(subscriptions_for_bug)) |
795 | + subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask( |
796 | + bugtask, BugNotificationLevel.NOTHING) |
797 | + self.assertEqual([], list(subscriptions_for_bugtask)) |
798 | |
799 | # With any tag the subscription is found. |
800 | bugtask.bug.tags = ["foo"] |
801 | - subscriptions_for_bug = self.target.getSubscriptionsForBug( |
802 | - bugtask.bug, BugNotificationLevel.NOTHING) |
803 | - self.assertEqual([subscription], list(subscriptions_for_bug)) |
804 | + subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask( |
805 | + bugtask, BugNotificationLevel.NOTHING) |
806 | + self.assertEqual([subscription], list(subscriptions_for_bugtask)) |
807 | |
808 | - def test_getSubscriptionsForBug_with_filter_exclude_any_tags(self): |
809 | + def test_getSubscriptionsForBugTask_with_filter_exclude_any_tags(self): |
810 | # If a subscription filter has exclude_any_tags, only bugs with no |
811 | # tags are matched. |
812 | bugtask = self.makeBugTask() |
813 | @@ -309,17 +310,17 @@ |
814 | subscription_filter.exclude_any_tags = True |
815 | |
816 | # Without any tags the subscription is found. |
817 | - subscriptions_for_bug = self.target.getSubscriptionsForBug( |
818 | - bugtask.bug, BugNotificationLevel.NOTHING) |
819 | - self.assertEqual([subscription], list(subscriptions_for_bug)) |
820 | + subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask( |
821 | + bugtask, BugNotificationLevel.NOTHING) |
822 | + self.assertEqual([subscription], list(subscriptions_for_bugtask)) |
823 | |
824 | # With any tag the subscription is not found. |
825 | bugtask.bug.tags = ["foo"] |
826 | - subscriptions_for_bug = self.target.getSubscriptionsForBug( |
827 | - bugtask.bug, BugNotificationLevel.NOTHING) |
828 | - self.assertEqual([], list(subscriptions_for_bug)) |
829 | + subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask( |
830 | + bugtask, BugNotificationLevel.NOTHING) |
831 | + self.assertEqual([], list(subscriptions_for_bugtask)) |
832 | |
833 | - def test_getSubscriptionsForBug_with_multiple_filters(self): |
834 | + def test_getSubscriptionsForBugTask_with_multiple_filters(self): |
835 | # If multiple filters exist for a subscription, all filters must |
836 | # match. |
837 | bugtask = self.makeBugTask() |
838 | @@ -337,23 +338,23 @@ |
839 | subscription_filter.importances = [BugTaskImportance.CRITICAL] |
840 | |
841 | # With the filter the subscription is not found. |
842 | - subscriptions_for_bug = self.target.getSubscriptionsForBug( |
843 | - bugtask.bug, BugNotificationLevel.NOTHING) |
844 | - self.assertEqual([], list(subscriptions_for_bug)) |
845 | + subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask( |
846 | + bugtask, BugNotificationLevel.NOTHING) |
847 | + self.assertEqual([], list(subscriptions_for_bugtask)) |
848 | |
849 | # If the filter is adjusted to match status but not importance, the |
850 | # subscription is still not found. |
851 | subscription_filter.statuses = [bugtask.status] |
852 | - subscriptions_for_bug = self.target.getSubscriptionsForBug( |
853 | - bugtask.bug, BugNotificationLevel.NOTHING) |
854 | - self.assertEqual([], list(subscriptions_for_bug)) |
855 | + subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask( |
856 | + bugtask, BugNotificationLevel.NOTHING) |
857 | + self.assertEqual([], list(subscriptions_for_bugtask)) |
858 | |
859 | # If the filter is adjusted to also match importance, the subscription |
860 | # is found again. |
861 | subscription_filter.importances = [bugtask.importance] |
862 | - subscriptions_for_bug = self.target.getSubscriptionsForBug( |
863 | - bugtask.bug, BugNotificationLevel.NOTHING) |
864 | - self.assertEqual([subscription], list(subscriptions_for_bug)) |
865 | + subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask( |
866 | + bugtask, BugNotificationLevel.NOTHING) |
867 | + self.assertEqual([subscription], list(subscriptions_for_bugtask)) |
868 | |
869 | |
870 | class TestStructuralSubscriptionForDistro( |
871 | |
872 | === modified file 'utilities/format-new-and-modified-imports' |
873 | --- utilities/format-new-and-modified-imports 2010-09-28 09:50:49 +0000 |
874 | +++ utilities/format-new-and-modified-imports 2010-10-15 16:17:07 +0000 |
875 | @@ -9,5 +9,5 @@ |
876 | # |
877 | |
878 | bzr status --short "$@" | \ |
879 | - awk '/^.[MN] .*[.]py$/ { print $2 }' | \ |
880 | + awk '/^.[MN] .*[.]py$/ { print $NF }' | \ |
881 | xargs -r "$(dirname "$0")/format-imports" |
(18:18:58) allenap: adeuring: It's already reviewed and landed in db-devel, so I just need a +1 to land on devel.
(18:20:00) adeuring: allenap: so, rs=me
(18:20:10) allenap: adeuring: Thanks.