3
Package bug subscriptions allow zero, one, or more people or teams that get
4
explicitly Cc'd to all public bugs filed on a package.
4
Package bug subscriptions allow zero, one, or more people or teams that
5
get explicitly Cc'd to all public bugs filed on a package.
6
7
The package bug subscriptions are obtained from looking at the
7
8
StructuralSubscription table.
22
23
Adding a package subscription is done with the
23
IDistributionSourcePackage.addBugSubscription method. You have to
24
be logged in to call this method:
24
IDistributionSourcePackage.addBugSubscription method. You have to be
25
logged in to call this method:
26
27
>>> from lp.registry.interfaces.person import IPersonSet
27
28
>>> personset = getUtility(IPersonSet)
54
56
>>> debian_firefox.addBugSubscription(ubuntu_team, ubuntu_team)
55
57
<...StructuralSubscription object at ...>
57
>>> sorted([sub.subscriber.name for sub in debian_firefox.bug_subscriptions])
60
... sub.subscriber.name for sub in debian_firefox.bug_subscriptions])
58
61
[u'name12', u'ubuntu-team']
60
63
To remove a subscription, use
61
64
IStructuralSubscriptionTarget.removeBugSubscription:
63
66
>>> debian_firefox.removeBugSubscription(sample_person, sample_person)
64
>>> sorted([sub.subscriber.id for sub in debian_firefox.bug_subscriptions])
68
... sub.subscriber.id for sub in debian_firefox.bug_subscriptions])
67
Trying to remove a subscription that doesn't exist on a source package raises a
68
DeleteSubscriptionError.
71
Trying to remove a subscription that doesn't exist on a source package
72
raises a DeleteSubscriptionError.
70
74
>>> foobar = personset.getByName("name16")
71
75
>>> debian_firefox.removeBugSubscription(foobar, foobar)
74
78
DeleteSubscriptionError: ...
77
== Package Subscriptions and Bug Tasks ==
81
Package Subscriptions and Bug Tasks
82
-----------------------------------
79
84
Often a bug gets reported on package foo, when it should have been
80
85
reported on bar. When a user, likely a bug triager or developer, changes
81
the source package, the subscribers for the new package get
82
subscribed. The subscribers of the previous package also remain
86
the source package, the subscribers for the new package get subscribed.
87
The subscribers of the previous package also remain subscribed.
85
To demonstrate, let's change the source package for bug #1 in
86
mozilla-firefox in Ubuntu to be pmount in Ubuntu, and see how the
87
subscribers list changes.
89
To demonstrate, let's change the source package for bug #1 in mozilla-
90
firefox in Ubuntu to be pmount in Ubuntu, and see how the subscribers
89
93
>>> from lp.bugs.interfaces.bugtask import IBugTaskSet
103
107
... subscribers = chain(
104
108
... bug.getDirectSubscribers(),
105
109
... bug.getIndirectSubscribers())
106
... return sorted(subscriber.displayname for subscriber in subscribers)
111
... subscriber.displayname for subscriber in subscribers)
108
>>> subscriber_names(bug_one_in_ubuntu_firefox.bug)
109
[u'Foo Bar', u'Mark Shuttleworth', u'Sample Person', u'Steve Alexander',
113
>>> names = subscriber_names(bug_one_in_ubuntu_firefox.bug)
114
>>> for name in names:
112
122
Changing the package for bug_one_in_ubuntu_firefox to pmount will
113
123
implicitly subscribe the new package's subscribers to the bug. In
114
demonstrating this, we'll also make Sample Person a subscriber to
115
ubuntu pmount, to show that the subscription changes behave correctly
116
when a subscriber to the new package is already subscribed to the bug:
124
demonstrating this, we'll also make Sample Person a subscriber to ubuntu
125
pmount, to show that the subscription changes behave correctly when a
126
subscriber to the new package is already subscribed to the bug:
118
128
>>> from zope.event import notify
126
136
>>> daf = personset.getByName("daf")
127
137
>>> ubuntu_pmount.addBugSubscription(daf, daf)
128
138
<...StructuralSubscription object at ...>
129
140
>>> ubuntu_pmount.addBugSubscription(sample_person, sample_person)
130
141
<...StructuralSubscription object at ...>
132
143
>>> old_state = Snapshot(
133
... bug_one_in_ubuntu_firefox, providing=IBugTask)
144
... bug_one_in_ubuntu_firefox, providing=IBugTask)
135
146
>>> bug_one_in_ubuntu_firefox.transitionToTarget(ubuntu_pmount)
137
148
>>> source_package_changed = ObjectModifiedEvent(
138
... bug_one_in_ubuntu_firefox, old_state,
139
... ["id", "title", "sourcepackagename"])
149
... bug_one_in_ubuntu_firefox, old_state,
150
... ["id", "title", "sourcepackagename"])
141
152
>>> notify(source_package_changed)
142
153
>>> transaction.commit()
150
161
daf is sent an email giving him complete information about the bug that
151
162
has just been retargeted, including the title, description, status,
152
importance, etc. The References header of the email contains the
153
msgid of the initial bug report (as if daf was a original recipient of
154
the bug notification). The email has the X-Launchpad-Message-Rationale
155
header to track why daf received the email. The rational is repeated
156
in the footer of the email with the bug title and URL.
163
importance, etc. The References header of the email contains the msgid
164
of the initial bug report (as if daf was a original recipient of the bug
165
notification). The email has the X-Launchpad-Message-Rationale header to
166
track why daf received the email. The rational is repeated in the footer
167
of the email with the bug title and URL.
160
171
>>> def by_to_addrs(a, b):
161
... return cmp(a[1], b[1])
172
... return cmp(a[1], b[1])
163
174
>>> test_emails = list(stub.test_emails)
164
175
>>> test_emails.sort(by_to_addrs)
166
177
>>> len(test_emails)
168
180
>>> from_addr, to_addr, raw_message = test_emails.pop()
169
181
>>> print from_addr
170
182
bounces@canonical.com
171
184
>>> print to_addr
172
185
['daf@canonical.com']
174
187
>>> msg = email.message_from_string(raw_message)
175
188
>>> msg['References'] == (
176
... bug_one_in_ubuntu_firefox.bug.initial_message.rfc822msgid)
189
... bug_one_in_ubuntu_firefox.bug.initial_message.rfc822msgid)
178
192
>>> msg['X-Launchpad-Message-Rationale']
179
193
'Subscriber (pmount in Ubuntu)'
180
195
>>> msg['Subject']
181
196
'[Bug 1] [NEW] Firefox does not support SVG'
182
198
>>> print msg.get_payload(decode=True)
183
199
You have been subscribed to a public bug:
219
235
>>> stub.test_emails = []
221
Let's see that nothing unexpected happens when we set the source
237
Let's see that nothing unexpected happens when we set the source package
224
240
>>> old_state = Snapshot(
225
... bug_one_in_ubuntu_firefox, providing=IBugTask)
241
... bug_one_in_ubuntu_firefox, providing=IBugTask)
227
243
>>> bug_one_in_ubuntu_firefox.transitionToTarget(ubuntu)
229
245
>>> source_package_changed = ObjectModifiedEvent(
230
... bug_one_in_ubuntu_firefox, old_state, ["sourcepackagename"])
246
... bug_one_in_ubuntu_firefox, old_state, ["sourcepackagename"])
232
248
>>> notify(source_package_changed)
233
249
>>> transaction.commit()
236
252
The package subscribers, Daf and Foo Bar, are implicitly unsubscribed:
238
>>> subscriber_names(bug_one_in_ubuntu_firefox.bug)
239
[u'Mark Shuttleworth', u'Sample Person', u'Steve Alexander', u'Ubuntu Team']
254
>>> names = subscriber_names(bug_one_in_ubuntu_firefox.bug)
255
>>> for name in names:
241
Subscriptions are not limited to persons; teams are also allowed to subscribe.
242
Teams are a bit different, since they might not have a contact address. Let's
243
add such a team as a subscriber.
262
Subscriptions are not limited to persons; teams are also allowed to
263
subscribe. Teams are a bit different, since they might not have a
264
contact address. Let's add such a team as a subscriber.
245
266
>>> ubuntu_gnome = personset.getByName("name18")
246
267
>>> ubuntu_gnome.preferredemail is None
248
270
>>> ubuntu_pmount.addBugSubscription(ubuntu_gnome, ubuntu_gnome)
249
271
<...StructuralSubscription object at ...>
251
273
>>> old_state = Snapshot(
252
... bug_one_in_ubuntu_firefox, providing=IBugTask)
274
... bug_one_in_ubuntu_firefox, providing=IBugTask)
254
276
>>> bug_one_in_ubuntu_firefox.transitionToTarget(ubuntu_pmount)
256
278
>>> source_package_changed = ObjectModifiedEvent(
257
... bug_one_in_ubuntu_firefox, old_state, ["sourcepackagename"])
279
... bug_one_in_ubuntu_firefox, old_state, ["sourcepackagename"])
259
281
>>> notify(source_package_changed)
260
282
>>> transaction.commit()
270
292
bug is reassigned to a different package.
272
294
>>> bug_one_in_ubuntu_firefox.bug.setPrivate(
273
... True, getUtility(ILaunchBag).user)
295
... True, getUtility(ILaunchBag).user)
276
298
So, if Martin Pitt subscribes to ubuntu mozilla-firefox:
282
304
and then the bug gets reassigned to mozilla firefox:
284
306
>>> old_state = Snapshot(
285
... bug_one_in_ubuntu_firefox, providing=IBugTask)
307
... bug_one_in_ubuntu_firefox, providing=IBugTask)
287
309
>>> bug_one_in_ubuntu_firefox.transitionToTarget(ubuntu_firefox)
289
311
>>> source_package_changed = ObjectModifiedEvent(
290
... bug_one_in_ubuntu_firefox, old_state,
291
... ["id", "title", "sourcepackagename"])
312
... bug_one_in_ubuntu_firefox, old_state,
313
... ["id", "title", "sourcepackagename"])
293
315
>>> notify(source_package_changed)
294
316
>>> transaction.commit()
295
317
>>> stub.test_emails = []
297
the unallowed subscribers are removed:
319
the subscriptions remain unchanged:
299
321
>>> subscriber_names(bug_one_in_ubuntu_firefox.bug)
300
[u'Foo Bar', u'Mark Shuttleworth', u'Sample Person', u'Ubuntu Team']
303
== Product Bug Supervisors and Bug Tasks ==
322
[u'Dafydd Harries', u'Foo Bar', u'Mark Shuttleworth',
323
u'Sample Person', u'Steve Alexander', u'Ubuntu Gnome Team',
327
Product Bug Supervisors and Bug Tasks
328
-------------------------------------
305
330
Like reassigning a bug task to another package, reassigning a bug task
306
331
to another product will subscribe any new product bug supervisors to the
320
345
>>> bug_two_in_ubuntu = getUtility(IBugTaskSet).get(3)
321
346
>>> print bug_two_in_ubuntu.bug.id
323
349
>>> print bug_two_in_ubuntu.product.name
327
... [subscription.person.displayname for subscription in
328
... bug_two_in_ubuntu.bug.subscriptions])
353
... [subscription.person.displayname for subscription in
354
... bug_two_in_ubuntu.bug.subscriptions])
329
355
[u'Steve Alexander']
331
357
>>> old_state = Snapshot(bug_two_in_ubuntu, providing=IBugTask)
333
359
>>> bug_two_in_ubuntu.transitionToTarget(mozilla_firefox)
335
361
>>> product_changed = ObjectModifiedEvent(
336
... bug_two_in_ubuntu, old_state, ["id", "title", "product"])
362
... bug_two_in_ubuntu, old_state, ["id", "title", "product"])
338
364
>>> notify(product_changed)
339
365
>>> transaction.commit()
356
382
>>> len(test_emails)
358
385
>>> from_addr, to_addr, raw_message = test_emails.pop()
359
386
>>> print from_addr
360
387
bounces@canonical.com
361
389
>>> print to_addr
362
390
['foo.bar@canonical.com']
364
392
>>> msg = email.message_from_string(raw_message)
365
393
>>> msg['Subject']
366
394
'[Bug 2] [NEW] Blackhole Trash folder'
367
396
>>> print msg.get_payload(decode=True)
368
397
You have been subscribed to a public bug:
376
405
are subscribed to Mozilla Firefox.
379
== Teams as bug supervisors ==
408
Teams as bug supervisors
409
------------------------
381
The list of teams that a user may add to a package as a bug supervisor will
382
only contain those teams of which the user is an administrator.
411
The list of teams that a user may add to a package as a bug supervisor
412
will only contain those teams of which the user is an administrator.
384
414
>>> from zope.component import getMultiAdapter
385
415
>>> from canonical.launchpad.webapp.servers import LaunchpadTestRequest
397
427
>>> sample_person = view.user
398
428
>>> ["%s: %s" % (membership.team.displayname, membership.status.name)
399
... for membership in sample_person.team_memberships]
429
... for membership in sample_person.team_memberships]
400
430
[u'HWDB Team: APPROVED',
401
431
u'Landscape Developers: ADMIN',
402
432
u'Launchpad Users: ADMIN',
403
433
u'Warty Security Team: APPROVED']
405
But is only an administrator of Landscape Developers, so that is the only
406
team that will be listed when the user is changing a package bug supervisor:
435
But is only an administrator of Landscape Developers, so that is the
436
only team that will be listed when the user is changing a package bug
408
439
>>> for team in view.user.getAdministratedTeams():
409
... print team.displayname
440
... print team.displayname
410
441
Landscape Developers