Merge lp:~allenap/launchpad/wire-up-filter-subs-bug-655567-devel into lp:launchpad

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
Reviewer Review Type Date Requested Status
Abel Deuring (community) code Approve
Review via email: mp+38562@code.launchpad.net

Commit message

Merge wire-up-filter-subs-bug-655567 into devel.

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.
Revision history for this message
Abel Deuring (adeuring) wrote :

(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.

review: Approve (code)

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"