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
=== modified file 'database/schema/security.cfg'
--- database/schema/security.cfg 2010-10-07 22:46:08 +0000
+++ database/schema/security.cfg 2010-10-15 16:17:07 +0000
@@ -549,6 +549,7 @@
549public.bugsubscriptionfilterstatus = SELECT549public.bugsubscriptionfilterstatus = SELECT
550public.bugsubscriptionfilterimportance = SELECT550public.bugsubscriptionfilterimportance = SELECT
551public.bugsubscriptionfiltertag = SELECT551public.bugsubscriptionfiltertag = SELECT
552public.bugtag = SELECT
552public.bugtask = SELECT, INSERT, UPDATE553public.bugtask = SELECT, INSERT, UPDATE
553public.bugtracker = SELECT, INSERT554public.bugtracker = SELECT, INSERT
554public.bugtrackercomponent = SELECT, INSERT, UPDATE, DELETE555public.bugtrackercomponent = SELECT, INSERT, UPDATE, DELETE
@@ -638,6 +639,7 @@
638public.bugsubscriptionfiltertag = SELECT639public.bugsubscriptionfiltertag = SELECT
639public.bugnotification = SELECT, INSERT640public.bugnotification = SELECT, INSERT
640public.bugnotificationrecipient = SELECT, INSERT641public.bugnotificationrecipient = SELECT, INSERT
642public.bugtag = SELECT
641public.structuralsubscription = SELECT643public.structuralsubscription = SELECT
642public.message = SELECT, INSERT644public.message = SELECT, INSERT
643public.messagechunk = SELECT, INSERT645public.messagechunk = SELECT, INSERT
@@ -833,6 +835,7 @@
833public.bugnotification = SELECT, INSERT835public.bugnotification = SELECT, INSERT
834public.bugnotificationrecipient = SELECT, INSERT836public.bugnotificationrecipient = SELECT, INSERT
835public.bugnomination = SELECT837public.bugnomination = SELECT
838public.bugtag = SELECT
836public.bugtask = SELECT, UPDATE839public.bugtask = SELECT, UPDATE
837public.product = SELECT840public.product = SELECT
838public.project = SELECT841public.project = SELECT
@@ -1194,6 +1197,7 @@
1194public.bugnotification = SELECT, INSERT1197public.bugnotification = SELECT, INSERT
1195public.bugnotificationrecipient = SELECT, INSERT1198public.bugnotificationrecipient = SELECT, INSERT
1196public.bugnomination = SELECT1199public.bugnomination = SELECT
1200public.bugtag = SELECT
1197public.bugtask = SELECT, UPDATE1201public.bugtask = SELECT, UPDATE
1198public.product = SELECT, UPDATE1202public.product = SELECT, UPDATE
1199public.project = SELECT, UPDATE1203public.project = SELECT, UPDATE
@@ -1300,6 +1304,7 @@
1300public.bugnotification = SELECT, INSERT1304public.bugnotification = SELECT, INSERT
1301public.bugnotificationrecipient = SELECT, INSERT1305public.bugnotificationrecipient = SELECT, INSERT
1302public.bugnomination = SELECT1306public.bugnomination = SELECT
1307public.bugtag = SELECT
1303public.bugtask = SELECT, UPDATE1308public.bugtask = SELECT, UPDATE
1304public.product = SELECT, UPDATE1309public.product = SELECT, UPDATE
1305public.project = SELECT, UPDATE1310public.project = SELECT, UPDATE
@@ -1855,6 +1860,7 @@
1855public.bug = SELECT, INSERT, UPDATE1860public.bug = SELECT, INSERT, UPDATE
1856public.bugjob = SELECT, INSERT1861public.bugjob = SELECT, INSERT
1857public.bugaffectsperson = SELECT, INSERT, UPDATE, DELETE1862public.bugaffectsperson = SELECT, INSERT, UPDATE, DELETE
1863public.bugtag = SELECT
1858public.bugtask = SELECT, INSERT, UPDATE1864public.bugtask = SELECT, INSERT, UPDATE
1859public.accountpassword = SELECT, INSERT1865public.accountpassword = SELECT, INSERT
1860public.teamparticipation = SELECT, INSERT1866public.teamparticipation = SELECT, INSERT
@@ -1917,6 +1923,7 @@
1917public.bugaffectsperson = SELECT1923public.bugaffectsperson = SELECT
1918public.bugnotification = SELECT, DELETE1924public.bugnotification = SELECT, DELETE
1919public.bugnotificationrecipientarchive = SELECT1925public.bugnotificationrecipientarchive = SELECT
1926public.bugtag = SELECT
1920public.bugwatch = SELECT, UPDATE1927public.bugwatch = SELECT, UPDATE
1921public.bugwatchactivity = SELECT, DELETE1928public.bugwatchactivity = SELECT, DELETE
1922public.codeimportresult = SELECT, DELETE1929public.codeimportresult = SELECT, DELETE
19231930
=== modified file 'lib/lp/bugs/configure.zcml'
--- lib/lp/bugs/configure.zcml 2010-10-06 18:53:53 +0000
+++ lib/lp/bugs/configure.zcml 2010-10-15 16:17:07 +0000
@@ -706,6 +706,7 @@
706 getSubscribersForPerson706 getSubscribersForPerson
707 indexed_messages707 indexed_messages
708 getAlsoNotifiedSubscribers708 getAlsoNotifiedSubscribers
709 getStructuralSubscribers
709 getBugWatch710 getBugWatch
710 canBeNominatedFor711 canBeNominatedFor
711 getNominationFor712 getNominationFor
712713
=== modified file 'lib/lp/bugs/interfaces/bug.py'
--- lib/lp/bugs/interfaces/bug.py 2010-10-12 14:56:31 +0000
+++ lib/lp/bugs/interfaces/bug.py 2010-10-15 16:17:07 +0000
@@ -493,6 +493,12 @@
493 from duplicates.493 from duplicates.
494 """494 """
495495
496 def getStructuralSubscribers(recipients=None, level=None):
497 """Return `IPerson`s subscribed to this bug's targets.
498
499 This takes into account bug subscription filters.
500 """
501
496 def getSubscriptionsFromDuplicates():502 def getSubscriptionsFromDuplicates():
497 """Return IBugSubscriptions subscribed from dupes of this bug."""503 """Return IBugSubscriptions subscribed from dupes of this bug."""
498504
@@ -501,9 +507,9 @@
501507
502 def getSubscribersForPerson(person):508 def getSubscribersForPerson(person):
503 """Find the persons or teams by which person is subscribed.509 """Find the persons or teams by which person is subscribed.
504 510
505 This call should be quite cheap to make and performs a single query.511 This call should be quite cheap to make and performs a single query.
506 512
507 :return: An IResultSet.513 :return: An IResultSet.
508 """514 """
509515
510516
=== modified file 'lib/lp/bugs/model/bug.py'
--- lib/lp/bugs/model/bug.py 2010-10-14 11:05:05 +0000
+++ lib/lp/bugs/model/bug.py 2010-10-15 16:17:07 +0000
@@ -51,7 +51,6 @@
51 In,51 In,
52 LeftJoin,52 LeftJoin,
53 Max,53 Max,
54 Min,
55 Not,54 Not,
56 Or,55 Or,
57 Select,56 Select,
@@ -156,14 +155,13 @@
156from lp.bugs.model.bugnomination import BugNomination155from lp.bugs.model.bugnomination import BugNomination
157from lp.bugs.model.bugnotification import BugNotification156from lp.bugs.model.bugnotification import BugNotification
158from lp.bugs.model.bugsubscription import BugSubscription157from lp.bugs.model.bugsubscription import BugSubscription
158from lp.bugs.model.bugtarget import OfficialBugTag
159from lp.bugs.model.bugtask import (159from lp.bugs.model.bugtask import (
160 BugTask,160 BugTask,
161 bugtask_sort_key,161 bugtask_sort_key,
162 BugTaskSet,
163 get_bug_privacy_filter,162 get_bug_privacy_filter,
164 NullBugTask,163 NullBugTask,
165 )164 )
166from lp.bugs.model.bugtarget import OfficialBugTag
167from lp.bugs.model.bugwatch import BugWatch165from lp.bugs.model.bugwatch import BugWatch
168from lp.hardwaredb.interfaces.hwdb import IHWSubmissionBugSet166from lp.hardwaredb.interfaces.hwdb import IHWSubmissionBugSet
169from lp.registry.enum import BugNotificationLevel167from lp.registry.enum import BugNotificationLevel
@@ -172,10 +170,7 @@
172 IDistributionSourcePackage,170 IDistributionSourcePackage,
173 )171 )
174from lp.registry.interfaces.distroseries import IDistroSeries172from lp.registry.interfaces.distroseries import IDistroSeries
175from lp.registry.interfaces.person import (173from lp.registry.interfaces.person import validate_public_person
176 IPersonSet,
177 validate_public_person,
178 )
179from lp.registry.interfaces.product import IProduct174from lp.registry.interfaces.product import IProduct
180from lp.registry.interfaces.productseries import IProductSeries175from lp.registry.interfaces.productseries import IProductSeries
181from lp.registry.interfaces.series import SeriesStatus176from lp.registry.interfaces.series import SeriesStatus
@@ -458,11 +453,9 @@
458 store = Store.of(self)453 store = Store.of(self)
459 message_by_id = {}454 message_by_id = {}
460 if include_parents:455 if include_parents:
461 def to_messages(rows):456 to_messages = lambda rows: [row[0] for row in rows]
462 return [row[0] for row in rows]
463 else:457 else:
464 def to_messages(rows):458 to_messages = lambda rows: rows
465 return rows
466 def eager_load_owners(messages):459 def eager_load_owners(messages):
467 # Because we may have multiple owners, we spend less time in storm460 # Because we may have multiple owners, we spend less time in storm
468 # with very large bugs by not joining and instead querying a second461 # with very large bugs by not joining and instead querying a second
@@ -976,31 +969,12 @@
976969
977 also_notified_subscribers = set()970 also_notified_subscribers = set()
978971
979 structural_subscription_targets = set()
980
981 for bugtask in self.bugtasks:972 for bugtask in self.bugtasks:
982 if bugtask.assignee:973 if bugtask.assignee:
983 also_notified_subscribers.add(bugtask.assignee)974 also_notified_subscribers.add(bugtask.assignee)
984 if recipients is not None:975 if recipients is not None:
985 recipients.addAssignee(bugtask.assignee)976 recipients.addAssignee(bugtask.assignee)
986977
987 if IStructuralSubscriptionTarget.providedBy(bugtask.target):
988 structural_subscription_targets.add(bugtask.target)
989 if bugtask.target.parent_subscription_target is not None:
990 structural_subscription_targets.add(
991 bugtask.target.parent_subscription_target)
992
993 if ISourcePackage.providedBy(bugtask.target):
994 # Distribution series bug tasks with a package have the
995 # source package set as their target, so we add the
996 # distroseries explicitly to the set of subscription
997 # targets.
998 structural_subscription_targets.add(
999 bugtask.distroseries)
1000
1001 if bugtask.milestone is not None:
1002 structural_subscription_targets.add(bugtask.milestone)
1003
1004 # If the target's bug supervisor isn't set,978 # If the target's bug supervisor isn't set,
1005 # we add the owner as a subscriber.979 # we add the owner as a subscriber.
1006 pillar = bugtask.pillar980 pillar = bugtask.pillar
@@ -1009,22 +983,73 @@
1009 if recipients is not None:983 if recipients is not None:
1010 recipients.addRegistrant(pillar.owner, pillar)984 recipients.addRegistrant(pillar.owner, pillar)
1011985
1012 person_set = getUtility(IPersonSet)986 # Structural subscribers.
1013 target_subscribers = person_set.getSubscribersForTargets(987 also_notified_subscribers.update(
1014 structural_subscription_targets, recipients=recipients,988 self.getStructuralSubscribers(
1015 level=level)989 recipients=recipients, level=level))
1016
1017 also_notified_subscribers.update(target_subscribers)
1018990
1019 # Direct subscriptions always take precedence over indirect991 # Direct subscriptions always take precedence over indirect
1020 # subscriptions.992 # subscriptions.
1021 direct_subscribers = set(self.getDirectSubscribers())993 direct_subscribers = set(self.getDirectSubscribers())
994
1022 # Remove security proxy for the sort key, but return995 # Remove security proxy for the sort key, but return
1023 # the regular proxied object.996 # the regular proxied object.
1024 return sorted(997 return sorted(
1025 (also_notified_subscribers - direct_subscribers),998 (also_notified_subscribers - direct_subscribers),
1026 key=lambda x: removeSecurityProxy(x).displayname)999 key=lambda x: removeSecurityProxy(x).displayname)
10271000
1001 def getStructuralSubscribers(self, recipients=None, level=None):
1002 """See `IBug`. """
1003 query_arguments = []
1004 for bugtask in self.bugtasks:
1005 if IStructuralSubscriptionTarget.providedBy(bugtask.target):
1006 query_arguments.append((bugtask.target, bugtask))
1007 if bugtask.target.parent_subscription_target is not None:
1008 query_arguments.append(
1009 (bugtask.target.parent_subscription_target, bugtask))
1010 if ISourcePackage.providedBy(bugtask.target):
1011 # Distribution series bug tasks with a package have the source
1012 # package set as their target, so we add the distroseries
1013 # explicitly to the set of subscription targets.
1014 query_arguments.append((bugtask.distroseries, bugtask))
1015 if bugtask.milestone is not None:
1016 query_arguments.append((bugtask.milestone, bugtask))
1017
1018 if len(query_arguments) == 0:
1019 return EmptyResultSet()
1020
1021 if level is None:
1022 # If level is not specified, default to NOTHING so that all
1023 # subscriptions are found. XXX: Perhaps this should go in
1024 # getSubscriptionsForBugTask()?
1025 level = BugNotificationLevel.NOTHING
1026
1027 # Build the query.
1028 union = lambda left, right: left.union(right)
1029 queries = (
1030 target.getSubscriptionsForBugTask(bugtask, level)
1031 for target, bugtask in query_arguments)
1032 subscriptions = reduce(union, queries)
1033
1034 # Pull all the subscriptions in.
1035 subscriptions = list(subscriptions)
1036
1037 # Prepare a query for the subscribers.
1038 subscribers = Store.of(self).find(
1039 Person, Person.id.is_in(
1040 subscription.subscriberID
1041 for subscription in subscriptions))
1042
1043 if recipients is not None:
1044 # We need to process subscriptions, so pull all the subscribes
1045 # into the cache, then update recipients with the subscriptions.
1046 subscribers = list(subscribers)
1047 for subscription in subscriptions:
1048 recipients.addStructuralSubscriber(
1049 subscription.subscriber, subscription.target)
1050
1051 return subscribers
1052
1028 def getBugNotificationRecipients(self, duplicateof=None, old_bug=None,1053 def getBugNotificationRecipients(self, duplicateof=None, old_bug=None,
1029 level=None,1054 level=None,
1030 include_master_dupe_subscribers=False):1055 include_master_dupe_subscribers=False):
10311056
=== renamed file 'lib/lp/bugs/tests/test_bug.py' => 'lib/lp/bugs/model/tests/test_bug.py'
--- lib/lp/bugs/tests/test_bug.py 2010-10-14 13:47:47 +0000
+++ lib/lp/bugs/model/tests/test_bug.py 2010-10-15 16:17:07 +0000
@@ -5,7 +5,10 @@
55
6__metaclass__ = type6__metaclass__ = type
77
8from storm.store import ResultSet
9
8from canonical.testing.layers import DatabaseFunctionalLayer10from canonical.testing.layers import DatabaseFunctionalLayer
11from lp.bugs.mail.bugnotificationrecipients import BugNotificationRecipients
9from lp.registry.enum import BugNotificationLevel12from lp.registry.enum import BugNotificationLevel
10from lp.registry.interfaces.person import PersonVisibility13from lp.registry.interfaces.person import PersonVisibility
11from lp.registry.model.structuralsubscription import StructuralSubscription14from lp.registry.model.structuralsubscription import StructuralSubscription
@@ -14,6 +17,7 @@
14 person_logged_in,17 person_logged_in,
15 TestCaseWithFactory,18 TestCaseWithFactory,
16 )19 )
20from lp.testing.matchers import StartsWith
1721
1822
19class TestBug(TestCaseWithFactory):23class TestBug(TestCaseWithFactory):
@@ -36,7 +40,7 @@
36 bug = self.factory.makeBug()40 bug = self.factory.makeBug()
37 person = self.factory.makePerson()41 person = self.factory.makePerson()
38 team1 = self.factory.makeTeam(members=[person])42 team1 = self.factory.makeTeam(members=[person])
39 team2 = self.factory.makeTeam(members=[person])43 self.factory.makeTeam(members=[person])
40 with person_logged_in(person):44 with person_logged_in(person):
41 bug.subscribe(team1, person)45 bug.subscribe(team1, person)
42 self.assertEqual([team1], list(bug.getSubscribersForPerson(person)))46 self.assertEqual([team1], list(bug.getSubscribersForPerson(person)))
@@ -157,8 +161,7 @@
157 subscriber = self.factory.makePerson()161 subscriber = self.factory.makePerson()
158 subscribers.append(subscriber)162 subscribers.append(subscriber)
159 with person_logged_in(subscriber):163 with person_logged_in(subscriber):
160 subscription = bug.subscribe(164 bug.subscribe(subscriber, subscriber, level=level)
161 subscriber, subscriber, level=level)
162 direct_subscribers = bug.getDirectSubscribers(level=level)165 direct_subscribers = bug.getDirectSubscribers(level=level)
163166
164 # All the previous subscribers will be included because167 # All the previous subscribers will be included because
@@ -181,8 +184,7 @@
181 subscriber = self.factory.makePerson()184 subscriber = self.factory.makePerson()
182 subscribers.append(subscriber)185 subscribers.append(subscriber)
183 with person_logged_in(subscriber):186 with person_logged_in(subscriber):
184 subscription = bug.subscribe(187 bug.subscribe(subscriber, subscriber, level=level)
185 subscriber, subscriber, level=level)
186188
187 # All the subscribers should be returned by189 # All the subscribers should be returned by
188 # getDirectSubscribers() because it defaults to returning190 # getDirectSubscribers() because it defaults to returning
@@ -211,8 +213,7 @@
211 subscriber = self.factory.makePerson()213 subscriber = self.factory.makePerson()
212 subscribers.append(subscriber)214 subscribers.append(subscriber)
213 with person_logged_in(subscriber):215 with person_logged_in(subscriber):
214 subscription = duplicate_bug.subscribe(216 duplicate_bug.subscribe(subscriber, subscriber, level=level)
215 subscriber, subscriber, level=level)
216 duplicate_subscribers = (217 duplicate_subscribers = (
217 bug.getSubscribersFromDuplicates(level=level))218 bug.getSubscribersFromDuplicates(level=level))
218 # All the previous subscribers will be included because219 # All the previous subscribers will be included because
@@ -237,11 +238,92 @@
237 duplicate_bug.owner, duplicate_bug.owner)238 duplicate_bug.owner, duplicate_bug.owner)
238 subscriber = self.factory.makePerson()239 subscriber = self.factory.makePerson()
239 with person_logged_in(subscriber):240 with person_logged_in(subscriber):
240 direct_subscription = bug.subscribe(241 bug.subscribe(
241 subscriber, subscriber, level=BugNotificationLevel.NOTHING)242 subscriber, subscriber, level=BugNotificationLevel.NOTHING)
242 dupe_subscription = duplicate_bug.subscribe(243 duplicate_bug.subscribe(
243 subscriber, subscriber, level=BugNotificationLevel.METADATA)244 subscriber, subscriber, level=BugNotificationLevel.METADATA)
244 duplicate_subscribers = bug.getSubscribersFromDuplicates()245 duplicate_subscribers = bug.getSubscribersFromDuplicates()
245 self.assertTrue(246 self.assertTrue(
246 subscriber not in duplicate_subscribers,247 subscriber not in duplicate_subscribers,
247 "Subscriber should not be in duplicate_subscribers.")248 "Subscriber should not be in duplicate_subscribers.")
249
250
251class TestBugStructuralSubscribers(TestCaseWithFactory):
252
253 layer = DatabaseFunctionalLayer
254
255 def test_getStructuralSubscribers_no_subscribers(self):
256 # If there are no subscribers for any of the bug's targets then no
257 # subscribers will be returned by getStructuralSubscribers().
258 product = self.factory.makeProduct()
259 bug = self.factory.makeBug(product=product)
260 subscribers = bug.getStructuralSubscribers()
261 self.assertIsInstance(subscribers, ResultSet)
262 self.assertEqual([], list(subscribers))
263
264 def test_getStructuralSubscribers_single_target(self):
265 # Subscribers for any of the bug's targets are returned.
266 subscriber = self.factory.makePerson()
267 login_person(subscriber)
268 product = self.factory.makeProduct()
269 product.addBugSubscription(subscriber, subscriber)
270 bug = self.factory.makeBug(product=product)
271 self.assertEqual([subscriber], list(bug.getStructuralSubscribers()))
272
273 def test_getStructuralSubscribers_multiple_targets(self):
274 # Subscribers for any of the bug's targets are returned.
275 actor = self.factory.makePerson()
276 login_person(actor)
277
278 subscriber1 = self.factory.makePerson()
279 subscriber2 = self.factory.makePerson()
280
281 product1 = self.factory.makeProduct(owner=actor)
282 product1.addBugSubscription(subscriber1, subscriber1)
283 product2 = self.factory.makeProduct(owner=actor)
284 product2.addBugSubscription(subscriber2, subscriber2)
285
286 bug = self.factory.makeBug(product=product1)
287 bug.addTask(actor, product2)
288
289 subscribers = bug.getStructuralSubscribers()
290 self.assertIsInstance(subscribers, ResultSet)
291 self.assertEqual(set([subscriber1, subscriber2]), set(subscribers))
292
293 def test_getStructuralSubscribers_recipients(self):
294 # If provided, getStructuralSubscribers() calls the appropriate
295 # methods on a BugNotificationRecipients object.
296 subscriber = self.factory.makePerson()
297 login_person(subscriber)
298 product = self.factory.makeProduct()
299 product.addBugSubscription(subscriber, subscriber)
300 bug = self.factory.makeBug(product=product)
301 recipients = BugNotificationRecipients()
302 subscribers = bug.getStructuralSubscribers(recipients=recipients)
303 # The return value is a list only when populating recipients.
304 self.assertIsInstance(subscribers, list)
305 self.assertEqual([subscriber], recipients.getRecipients())
306 reason, header = recipients.getReason(subscriber)
307 self.assertThat(
308 reason, StartsWith(
309 u"You received this bug notification because "
310 u"you are subscribed to "))
311 self.assertThat(header, StartsWith(u"Subscriber "))
312
313 def test_getStructuralSubscribers_level(self):
314 # getStructuralSubscribers() respects the given level.
315 subscriber = self.factory.makePerson()
316 login_person(subscriber)
317 product = self.factory.makeProduct()
318 subscription = product.addBugSubscription(subscriber, subscriber)
319 subscription.bug_notification_level = BugNotificationLevel.METADATA
320 bug = self.factory.makeBug(product=product)
321 self.assertEqual(
322 [subscriber], list(
323 bug.getStructuralSubscribers(
324 level=BugNotificationLevel.METADATA)))
325 subscription.bug_notification_level = BugNotificationLevel.METADATA
326 self.assertEqual(
327 [], list(
328 bug.getStructuralSubscribers(
329 level=BugNotificationLevel.COMMENTS)))
248330
=== modified file 'lib/lp/registry/doc/structural-subscriptions.txt'
--- lib/lp/registry/doc/structural-subscriptions.txt 2010-10-06 18:53:53 +0000
+++ lib/lp/registry/doc/structural-subscriptions.txt 2010-10-15 16:17:07 +0000
@@ -198,61 +198,6 @@
198 name16198 name16
199199
200200
201Retrieving structural subscribers of targets
202============================================
203
204Subscribers of one or more targets are retrieved by
205PersonSet.getSubscribersForTargets.
206
207XXX Abel Deuring 2008-07-11 We must remove the security proxy, because
208PersonSet.getSubscribersForTargets() accesses the private attribute
209firefox._targets_args. This attribute should be renamed. Bug #247525.
210
211 >>> from zope.security.proxy import removeSecurityProxy
212 >>> firefox_no_proxy = removeSecurityProxy(firefox)
213 >>> from lp.registry.interfaces.person import IPersonSet
214 >>> person_set = getUtility(IPersonSet)
215 >>> firefox_subscribers = person_set.getSubscribersForTargets(
216 ... [firefox_no_proxy])
217 >>> print_bug_subscribers(firefox_subscribers)
218 name12
219
220If PersonSet.getSubscribersForTargets() is passed a
221BugNotificationLevel in its `level` parameter, only structural
222subscribers with that notification level or higher will be returned.
223The subscription level of ff_sub, the only structural subscription
224to firefox, is NOTHING, hence we get an empty list if we pass any
225larger bug notification level.
226
227 >>> firefox_subscribers = person_set.getSubscribersForTargets(
228 ... [firefox_no_proxy], level=BugNotificationLevel.LIFECYCLE)
229 >>> print len(firefox_subscribers)
230 0
231
232If the bug notification level of ff_sub is increased, the subscription
233is included in the result of PersonSet.getSubscribersForTarget().
234
235 >>> ff_sub.level = BugNotificationLevel.LIFECYCLE
236 >>> firefox_subscribers = person_set.getSubscribersForTargets(
237 ... [firefox_no_proxy])
238 >>> print_bug_subscribers(firefox_subscribers)
239 name12
240 >>> ff_sub.level = BugNotificationLevel.METADATA
241 >>> firefox_subscribers = person_set.getSubscribersForTargets(
242 ... [firefox_no_proxy])
243 >>> print_bug_subscribers(firefox_subscribers)
244 name12
245
246More than one target can be passed to PersonSet.getSubscribersForTargets().
247
248 >>> ubuntu_no_proxy = removeSecurityProxy(ubuntu)
249 >>> ubuntu_or_firefox_subscribers = person_set.getSubscribersForTargets(
250 ... [firefox_no_proxy, ubuntu_no_proxy])
251 >>> print_bug_subscribers(ubuntu_or_firefox_subscribers)
252 name12
253 name16
254
255
256Target type display201Target type display
257===================202===================
258203
259204
=== modified file 'lib/lp/registry/interfaces/person.py'
--- lib/lp/registry/interfaces/person.py 2010-09-29 20:05:17 +0000
+++ lib/lp/registry/interfaces/person.py 2010-10-15 16:17:07 +0000
@@ -2006,17 +2006,6 @@
2006 specified product are returned.2006 specified product are returned.
2007 """2007 """
20082008
2009 def getSubscribersForTargets(targets, recipients=None):
2010 """Return the set of subscribers for `targets`.
2011
2012 :param targets: The sequence of targets for which to get the
2013 subscribers.
2014 :param recipients: An optional instance of
2015 `BugNotificationRecipients`.
2016 If present, all found subscribers will be
2017 added to it.
2018 """
2019
2020 def updatePersonalStandings():2009 def updatePersonalStandings():
2021 """Update the personal standings of some people.2010 """Update the personal standings of some people.
20222011
20232012
=== modified file 'lib/lp/registry/interfaces/structuralsubscription.py'
--- lib/lp/registry/interfaces/structuralsubscription.py 2010-10-05 09:23:22 +0000
+++ lib/lp/registry/interfaces/structuralsubscription.py 2010-10-15 16:17:07 +0000
@@ -200,8 +200,8 @@
200 def userHasBugSubscriptions(user):200 def userHasBugSubscriptions(user):
201 """Is `user` subscribed, directly or via a team, to bug mail?"""201 """Is `user` subscribed, directly or via a team, to bug mail?"""
202202
203 def getSubscriptionsForBug(bug, level):203 def getSubscriptionsForBugTask(bug, level):
204 """Return subscriptions for a given `IBug` at `level`."""204 """Return subscriptions for a given `IBugTask` at `level`."""
205205
206206
207class IStructuralSubscriptionTargetWrite(Interface):207class IStructuralSubscriptionTargetWrite(Interface):
208208
=== modified file 'lib/lp/registry/model/person.py'
--- lib/lp/registry/model/person.py 2010-09-29 14:38:30 +0000
+++ lib/lp/registry/model/person.py 2010-10-15 16:17:07 +0000
@@ -4060,59 +4060,6 @@
4060 Person.id in (%s)4060 Person.id in (%s)
4061 ''' % branch_clause)4061 ''' % branch_clause)
40624062
4063 def getSubscribersForTargets(self, targets, recipients=None, level=None):
4064 """See `IPersonSet`. """
4065 if len(targets) == 0:
4066 return set()
4067 target_criteria = []
4068 for target in targets:
4069 # target_args is a mapping from query arguments
4070 # to query values.
4071 target_args = target._target_args
4072 target_criteria_clauses = []
4073 for key, value in target_args.items():
4074 if value is not None:
4075 target_criteria_clauses.append(
4076 '%s = %s' % (key, quote(value)))
4077 else:
4078 target_criteria_clauses.append(
4079 '%s IS NULL' % key)
4080 if level is not None:
4081 target_criteria_clauses.append('bug_notification_level >= %s'
4082 % quote(level.value))
4083
4084 target_criteria.append(
4085 '(%s)' % ' AND '.join(target_criteria_clauses))
4086
4087 # Build a UNION query, since using OR slows down the query a lot.
4088 subscriptions = StructuralSubscription.select(target_criteria[0])
4089 for target_criterion in target_criteria[1:]:
4090 subscriptions = subscriptions.union(
4091 StructuralSubscription.select(target_criterion))
4092
4093 # Listify the result, since we want to loop over it multiple times.
4094 subscriptions = list(subscriptions)
4095
4096 # We can't use prejoins in UNION queries, so populate the cache
4097 # by getting all the subscribers.
4098 subscriber_ids = [
4099 subscription.subscriberID for subscription in subscriptions]
4100 if len(subscriber_ids) > 0:
4101 # Pull in ValidPersonCache records in addition to Person
4102 # records to warm up the cache.
4103 list(IStore(Person).find(
4104 (Person, ValidPersonCache),
4105 In(Person.id, subscriber_ids),
4106 ValidPersonCache.id == Person.id))
4107
4108 subscribers = set()
4109 for subscription in subscriptions:
4110 subscribers.add(subscription.subscriber)
4111 if recipients is not None:
4112 recipients.addStructuralSubscriber(
4113 subscription.subscriber, subscription.target)
4114 return subscribers
4115
4116 def updatePersonalStandings(self):4063 def updatePersonalStandings(self):
4117 """See `IPersonSet`."""4064 """See `IPersonSet`."""
4118 cur = cursor()4065 cur = cursor()
41194066
=== modified file 'lib/lp/registry/model/structuralsubscription.py'
--- lib/lp/registry/model/structuralsubscription.py 2010-10-05 16:52:02 +0000
+++ lib/lp/registry/model/structuralsubscription.py 2010-10-15 16:17:07 +0000
@@ -484,21 +484,8 @@
484 return True484 return True
485 return False485 return False
486486
487 def getSubscriptionsForBug(self, bug, level):487 def getSubscriptionsForBugTask(self, bugtask, level):
488 """See `IStructuralSubscriptionTarget`."""488 """See `IStructuralSubscriptionTarget`."""
489 if IProjectGroup.providedBy(self):
490 targets = set(self.products)
491 elif IMilestone.providedBy(self):
492 targets = [self.series_target]
493 else:
494 targets = [self]
495
496 bugtasks = [
497 bugtask for bugtask in bug.bugtasks
498 if bugtask.target in targets]
499
500 assert len(bugtasks) != 0, repr(self)
501
502 origin = [489 origin = [
503 StructuralSubscription,490 StructuralSubscription,
504 LeftJoin(491 LeftJoin(
@@ -515,7 +502,7 @@
515 BugSubscriptionFilter.id)),502 BugSubscriptionFilter.id)),
516 ]503 ]
517504
518 if len(bug.tags) == 0:505 if len(bugtask.bug.tags) == 0:
519 tag_conditions = [506 tag_conditions = [
520 BugSubscriptionFilter.include_any_tags == False,507 BugSubscriptionFilter.include_any_tags == False,
521 ]508 ]
@@ -534,13 +521,12 @@
534 # There's no status filter, or there is a status filter521 # There's no status filter, or there is a status filter
535 # and and it matches.522 # and and it matches.
536 Or(BugSubscriptionFilterStatus.id == None,523 Or(BugSubscriptionFilterStatus.id == None,
537 BugSubscriptionFilterStatus.status.is_in(524 BugSubscriptionFilterStatus.status == bugtask.status),
538 bugtask.status for bugtask in bugtasks)),
539 # There's no importance filter, or there is an importance525 # There's no importance filter, or there is an importance
540 # filter and it matches.526 # filter and it matches.
541 Or(BugSubscriptionFilterImportance.id == None,527 Or(BugSubscriptionFilterImportance.id == None,
542 BugSubscriptionFilterImportance.importance.is_in(528 BugSubscriptionFilterImportance.importance == (
543 bugtask.importance for bugtask in bugtasks)),529 bugtask.importance)),
544 # Any number of conditions relating to tags.530 # Any number of conditions relating to tags.
545 *tag_conditions)),531 *tag_conditions)),
546 ]532 ]
547533
=== modified file 'lib/lp/registry/tests/test_structuralsubscriptiontarget.py'
--- lib/lp/registry/tests/test_structuralsubscriptiontarget.py 2010-10-06 18:53:53 +0000
+++ lib/lp/registry/tests/test_structuralsubscriptiontarget.py 2010-10-15 16:17:07 +0000
@@ -173,19 +173,20 @@
173 def makeBugTask(self):173 def makeBugTask(self):
174 return self.factory.makeBugTask(target=self.target)174 return self.factory.makeBugTask(target=self.target)
175175
176 def test_getSubscriptionsForBug(self):176 def test_getSubscriptionsForBugTask(self):
177 # If no one has a filtered subscription for the given bug, the result177 # If no one has a filtered subscription for the given bug, the result
178 # of getSubscriptionsForBug() is the same as for getSubscriptions().178 # of getSubscriptionsForBugTask() is the same as for
179 # getSubscriptions().
179 bugtask = self.makeBugTask()180 bugtask = self.makeBugTask()
180 subscriptions = self.target.getSubscriptions(181 subscriptions = self.target.getSubscriptions(
181 min_bug_notification_level=BugNotificationLevel.NOTHING)182 min_bug_notification_level=BugNotificationLevel.NOTHING)
182 subscriptions_for_bug = self.target.getSubscriptionsForBug(183 subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask(
183 bugtask.bug, BugNotificationLevel.NOTHING)184 bugtask, BugNotificationLevel.NOTHING)
184 self.assertEqual(list(subscriptions), list(subscriptions_for_bug))185 self.assertEqual(list(subscriptions), list(subscriptions_for_bugtask))
185186
186 def test_getSubscriptionsForBug_with_filter_on_status(self):187 def test_getSubscriptionsForBugTask_with_filter_on_status(self):
187 # If a status filter exists for a subscription, the result of188 # If a status filter exists for a subscription, the result of
188 # getSubscriptionsForBug() may be a subset of getSubscriptions().189 # getSubscriptionsForBugTask() may be a subset of getSubscriptions().
189 bugtask = self.makeBugTask()190 bugtask = self.makeBugTask()
190191
191 # Create a new subscription on self.target.192 # Create a new subscription on self.target.
@@ -195,9 +196,9 @@
195 subscription.bug_notification_level = BugNotificationLevel.COMMENTS196 subscription.bug_notification_level = BugNotificationLevel.COMMENTS
196197
197 # Without any filters the subscription is found.198 # Without any filters the subscription is found.
198 subscriptions_for_bug = self.target.getSubscriptionsForBug(199 subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask(
199 bugtask.bug, BugNotificationLevel.NOTHING)200 bugtask, BugNotificationLevel.NOTHING)
200 self.assertEqual([subscription], list(subscriptions_for_bug))201 self.assertEqual([subscription], list(subscriptions_for_bugtask))
201202
202 # Filter the subscription to bugs in the CONFIRMED state.203 # Filter the subscription to bugs in the CONFIRMED state.
203 subscription_filter = BugSubscriptionFilter()204 subscription_filter = BugSubscriptionFilter()
@@ -205,19 +206,19 @@
205 subscription_filter.statuses = [BugTaskStatus.CONFIRMED]206 subscription_filter.statuses = [BugTaskStatus.CONFIRMED]
206207
207 # With the filter the subscription is not found.208 # With the filter the subscription is not found.
208 subscriptions_for_bug = self.target.getSubscriptionsForBug(209 subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask(
209 bugtask.bug, BugNotificationLevel.NOTHING)210 bugtask, BugNotificationLevel.NOTHING)
210 self.assertEqual([], list(subscriptions_for_bug))211 self.assertEqual([], list(subscriptions_for_bugtask))
211212
212 # If the filter is adjusted, the subscription is found again.213 # If the filter is adjusted, the subscription is found again.
213 subscription_filter.statuses = [bugtask.status]214 subscription_filter.statuses = [bugtask.status]
214 subscriptions_for_bug = self.target.getSubscriptionsForBug(215 subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask(
215 bugtask.bug, BugNotificationLevel.NOTHING)216 bugtask, BugNotificationLevel.NOTHING)
216 self.assertEqual([subscription], list(subscriptions_for_bug))217 self.assertEqual([subscription], list(subscriptions_for_bugtask))
217218
218 def test_getSubscriptionsForBug_with_filter_on_importance(self):219 def test_getSubscriptionsForBugTask_with_filter_on_importance(self):
219 # If an importance filter exists for a subscription, the result of220 # If an importance filter exists for a subscription, the result of
220 # getSubscriptionsForBug() may be a subset of getSubscriptions().221 # getSubscriptionsForBugTask() may be a subset of getSubscriptions().
221 bugtask = self.makeBugTask()222 bugtask = self.makeBugTask()
222223
223 # Create a new subscription on self.target.224 # Create a new subscription on self.target.
@@ -227,9 +228,9 @@
227 subscription.bug_notification_level = BugNotificationLevel.COMMENTS228 subscription.bug_notification_level = BugNotificationLevel.COMMENTS
228229
229 # Without any filters the subscription is found.230 # Without any filters the subscription is found.
230 subscriptions_for_bug = self.target.getSubscriptionsForBug(231 subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask(
231 bugtask.bug, BugNotificationLevel.NOTHING)232 bugtask, BugNotificationLevel.NOTHING)
232 self.assertEqual([subscription], list(subscriptions_for_bug))233 self.assertEqual([subscription], list(subscriptions_for_bugtask))
233234
234 # Filter the subscription to bugs in the CRITICAL state.235 # Filter the subscription to bugs in the CRITICAL state.
235 subscription_filter = BugSubscriptionFilter()236 subscription_filter = BugSubscriptionFilter()
@@ -237,19 +238,19 @@
237 subscription_filter.importances = [BugTaskImportance.CRITICAL]238 subscription_filter.importances = [BugTaskImportance.CRITICAL]
238239
239 # With the filter the subscription is not found.240 # With the filter the subscription is not found.
240 subscriptions_for_bug = self.target.getSubscriptionsForBug(241 subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask(
241 bugtask.bug, BugNotificationLevel.NOTHING)242 bugtask, BugNotificationLevel.NOTHING)
242 self.assertEqual([], list(subscriptions_for_bug))243 self.assertEqual([], list(subscriptions_for_bugtask))
243244
244 # If the filter is adjusted, the subscription is found again.245 # If the filter is adjusted, the subscription is found again.
245 subscription_filter.importances = [bugtask.importance]246 subscription_filter.importances = [bugtask.importance]
246 subscriptions_for_bug = self.target.getSubscriptionsForBug(247 subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask(
247 bugtask.bug, BugNotificationLevel.NOTHING)248 bugtask, BugNotificationLevel.NOTHING)
248 self.assertEqual([subscription], list(subscriptions_for_bug))249 self.assertEqual([subscription], list(subscriptions_for_bugtask))
249250
250 def test_getSubscriptionsForBug_with_filter_on_level(self):251 def test_getSubscriptionsForBugTask_with_filter_on_level(self):
251 # All structural subscriptions have a level for bug notifications252 # All structural subscriptions have a level for bug notifications
252 # which getSubscriptionsForBug() observes.253 # which getSubscriptionsForBugTask() observes.
253 bugtask = self.makeBugTask()254 bugtask = self.makeBugTask()
254255
255 # Create a new METADATA level subscription on self.target.256 # Create a new METADATA level subscription on self.target.
@@ -259,19 +260,19 @@
259 subscription.bug_notification_level = BugNotificationLevel.METADATA260 subscription.bug_notification_level = BugNotificationLevel.METADATA
260261
261 # The subscription is found when looking for NOTHING or above.262 # The subscription is found when looking for NOTHING or above.
262 subscriptions_for_bug = self.target.getSubscriptionsForBug(263 subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask(
263 bugtask.bug, BugNotificationLevel.NOTHING)264 bugtask, BugNotificationLevel.NOTHING)
264 self.assertEqual([subscription], list(subscriptions_for_bug))265 self.assertEqual([subscription], list(subscriptions_for_bugtask))
265 # The subscription is found when looking for METADATA or above.266 # The subscription is found when looking for METADATA or above.
266 subscriptions_for_bug = self.target.getSubscriptionsForBug(267 subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask(
267 bugtask.bug, BugNotificationLevel.METADATA)268 bugtask, BugNotificationLevel.METADATA)
268 self.assertEqual([subscription], list(subscriptions_for_bug))269 self.assertEqual([subscription], list(subscriptions_for_bugtask))
269 # The subscription is not found when looking for COMMENTS or above.270 # The subscription is not found when looking for COMMENTS or above.
270 subscriptions_for_bug = self.target.getSubscriptionsForBug(271 subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask(
271 bugtask.bug, BugNotificationLevel.COMMENTS)272 bugtask, BugNotificationLevel.COMMENTS)
272 self.assertEqual([], list(subscriptions_for_bug))273 self.assertEqual([], list(subscriptions_for_bugtask))
273274
274 def test_getSubscriptionsForBug_with_filter_include_any_tags(self):275 def test_getSubscriptionsForBugTask_with_filter_include_any_tags(self):
275 # If a subscription filter has include_any_tags, a bug with one or276 # If a subscription filter has include_any_tags, a bug with one or
276 # more tags is matched.277 # more tags is matched.
277 bugtask = self.makeBugTask()278 bugtask = self.makeBugTask()
@@ -285,17 +286,17 @@
285 subscription_filter.include_any_tags = True286 subscription_filter.include_any_tags = True
286287
287 # Without any tags the subscription is not found.288 # Without any tags the subscription is not found.
288 subscriptions_for_bug = self.target.getSubscriptionsForBug(289 subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask(
289 bugtask.bug, BugNotificationLevel.NOTHING)290 bugtask, BugNotificationLevel.NOTHING)
290 self.assertEqual([], list(subscriptions_for_bug))291 self.assertEqual([], list(subscriptions_for_bugtask))
291292
292 # With any tag the subscription is found.293 # With any tag the subscription is found.
293 bugtask.bug.tags = ["foo"]294 bugtask.bug.tags = ["foo"]
294 subscriptions_for_bug = self.target.getSubscriptionsForBug(295 subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask(
295 bugtask.bug, BugNotificationLevel.NOTHING)296 bugtask, BugNotificationLevel.NOTHING)
296 self.assertEqual([subscription], list(subscriptions_for_bug))297 self.assertEqual([subscription], list(subscriptions_for_bugtask))
297298
298 def test_getSubscriptionsForBug_with_filter_exclude_any_tags(self):299 def test_getSubscriptionsForBugTask_with_filter_exclude_any_tags(self):
299 # If a subscription filter has exclude_any_tags, only bugs with no300 # If a subscription filter has exclude_any_tags, only bugs with no
300 # tags are matched.301 # tags are matched.
301 bugtask = self.makeBugTask()302 bugtask = self.makeBugTask()
@@ -309,17 +310,17 @@
309 subscription_filter.exclude_any_tags = True310 subscription_filter.exclude_any_tags = True
310311
311 # Without any tags the subscription is found.312 # Without any tags the subscription is found.
312 subscriptions_for_bug = self.target.getSubscriptionsForBug(313 subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask(
313 bugtask.bug, BugNotificationLevel.NOTHING)314 bugtask, BugNotificationLevel.NOTHING)
314 self.assertEqual([subscription], list(subscriptions_for_bug))315 self.assertEqual([subscription], list(subscriptions_for_bugtask))
315316
316 # With any tag the subscription is not found.317 # With any tag the subscription is not found.
317 bugtask.bug.tags = ["foo"]318 bugtask.bug.tags = ["foo"]
318 subscriptions_for_bug = self.target.getSubscriptionsForBug(319 subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask(
319 bugtask.bug, BugNotificationLevel.NOTHING)320 bugtask, BugNotificationLevel.NOTHING)
320 self.assertEqual([], list(subscriptions_for_bug))321 self.assertEqual([], list(subscriptions_for_bugtask))
321322
322 def test_getSubscriptionsForBug_with_multiple_filters(self):323 def test_getSubscriptionsForBugTask_with_multiple_filters(self):
323 # If multiple filters exist for a subscription, all filters must324 # If multiple filters exist for a subscription, all filters must
324 # match.325 # match.
325 bugtask = self.makeBugTask()326 bugtask = self.makeBugTask()
@@ -337,23 +338,23 @@
337 subscription_filter.importances = [BugTaskImportance.CRITICAL]338 subscription_filter.importances = [BugTaskImportance.CRITICAL]
338339
339 # With the filter the subscription is not found.340 # With the filter the subscription is not found.
340 subscriptions_for_bug = self.target.getSubscriptionsForBug(341 subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask(
341 bugtask.bug, BugNotificationLevel.NOTHING)342 bugtask, BugNotificationLevel.NOTHING)
342 self.assertEqual([], list(subscriptions_for_bug))343 self.assertEqual([], list(subscriptions_for_bugtask))
343344
344 # If the filter is adjusted to match status but not importance, the345 # If the filter is adjusted to match status but not importance, the
345 # subscription is still not found.346 # subscription is still not found.
346 subscription_filter.statuses = [bugtask.status]347 subscription_filter.statuses = [bugtask.status]
347 subscriptions_for_bug = self.target.getSubscriptionsForBug(348 subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask(
348 bugtask.bug, BugNotificationLevel.NOTHING)349 bugtask, BugNotificationLevel.NOTHING)
349 self.assertEqual([], list(subscriptions_for_bug))350 self.assertEqual([], list(subscriptions_for_bugtask))
350351
351 # If the filter is adjusted to also match importance, the subscription352 # If the filter is adjusted to also match importance, the subscription
352 # is found again.353 # is found again.
353 subscription_filter.importances = [bugtask.importance]354 subscription_filter.importances = [bugtask.importance]
354 subscriptions_for_bug = self.target.getSubscriptionsForBug(355 subscriptions_for_bugtask = self.target.getSubscriptionsForBugTask(
355 bugtask.bug, BugNotificationLevel.NOTHING)356 bugtask, BugNotificationLevel.NOTHING)
356 self.assertEqual([subscription], list(subscriptions_for_bug))357 self.assertEqual([subscription], list(subscriptions_for_bugtask))
357358
358359
359class TestStructuralSubscriptionForDistro(360class TestStructuralSubscriptionForDistro(
360361
=== modified file 'utilities/format-new-and-modified-imports'
--- utilities/format-new-and-modified-imports 2010-09-28 09:50:49 +0000
+++ utilities/format-new-and-modified-imports 2010-10-15 16:17:07 +0000
@@ -9,5 +9,5 @@
9#9#
1010
11bzr status --short "$@" | \11bzr status --short "$@" | \
12 awk '/^.[MN] .*[.]py$/ { print $2 }' | \12 awk '/^.[MN] .*[.]py$/ { print $NF }' | \
13 xargs -r "$(dirname "$0")/format-imports"13 xargs -r "$(dirname "$0")/format-imports"