22
23
>>> from lp.bugs.adapters.bugchange import get_bug_changes
23
24
>>> from lp.bugs.mail.newbug import generate_bug_add_email
25
Let's demonstrate what the bugmails will look like, by going through
26
the various events that can happen that would cause a notification to
27
be sent. We'll start by importing some things we'll need for the
26
Let's demonstrate what the bugmails will look like, by going through the
27
various events that can happen that would cause a notification to be
28
sent. We'll start by importing some things we'll need for the examples
30
31
>>> from zope.component import getUtility
31
>>> from canonical.launchpad.interfaces.emailaddress import IEmailAddressSet
32
>>> from canonical.launchpad.interfaces.emailaddress import (
32
34
>>> from lp.bugs.adapters.bugdelta import BugDelta
33
35
>>> from lp.bugs.interfaces.bug import (
37
39
>>> from lp.registry.interfaces.person import IPersonSet
41
45
generate_bug_add_email accepts one argument: the IBug that was just
42
46
added. With that, it generates an appropriately-formatted notification
88
94
New security related bugs are sent with a prominent warning:
90
>>> changed = bug_four.setSecurityRelated(True, getUtility(ILaunchBag).user)
96
>>> changed = bug_four.setSecurityRelated(
97
... True, getUtility(ILaunchBag).user)
92
99
>>> subject, body = generate_bug_add_email(bug_four)
94
101
u'[Bug 4] [NEW] Reflow problems with complex page layouts'
96
104
*** This bug is a security vulnerability ***
125
134
>>> edited_bug.title = "the new title"
126
135
>>> old_description = edited_bug.description
127
136
>>> edited_bug.description = (
128
... "The Trash folder seems to have significant problems! At the"
129
... " moment, dragging an item to the Trash results in immediate"
130
... " deletion. The item does not appear in the Trash, it is just"
131
... " deleted from my hard disk. There is no undo or ability to"
132
... " recover the deleted file. Help!")
137
... "The Trash folder seems to have significant problems! At the"
138
... " moment, dragging an item to the Trash results in immediate"
139
... " deletion. The item does not appear in the Trash, it is just"
140
... " deleted from my hard disk. There is no undo or ability to"
141
... " recover the deleted file. Help!")
134
143
>>> bug_delta = BugDelta(
136
... bugurl="http://www.example.com/bugs/2",
137
... user=sample_person,
138
... title={'new': edited_bug.title, 'old': old_title},
139
... description={'new': edited_bug.description,
140
... 'old': old_description})
145
... bugurl="http://www.example.com/bugs/2",
146
... user=sample_person,
147
... title={'new': edited_bug.title, 'old': old_title},
148
... description={'new': edited_bug.description,
149
... 'old': old_description})
141
150
>>> IBugDelta.providedBy(bug_delta)
144
153
>>> from lp.bugs.interfaces.bugchange import IBugChange
145
154
>>> changes = get_bug_changes(bug_delta)
146
155
>>> for change in changes:
147
... IBugChange.providedBy(change)
156
... IBugChange.providedBy(change)
151
160
>>> for change in get_bug_changes(bug_delta):
152
... notification = change.getBugNotification()
153
... print notification['text'] #doctest: -NORMALIZE_WHITESPACE
154
... print "-----------------------------"
161
... notification = change.getBugNotification()
162
... print notification['text'] #doctest: -NORMALIZE_WHITESPACE
163
... print "-----------------------------"
155
164
** Summary changed:
157
166
- Blackhole Trash folder
170
179
is wrapped properly:
172
181
>>> old_description = edited_bug.description
173
>>> edited_bug.description = """\
174
... a new description that is quite long. but the nice thing is that the edit notification email generator knows how to indent and wrap descriptions, so this will appear quite nice in the actual email that gets sent.\n\nit's also smart enough to preserve whitespace, finally!"""
182
>>> edited_bug.description = ''.join([
183
... "A new description that is quite long. ",
184
... "But the nice thing is that the edit notification email ",
185
... "generator knows how to indent and wrap descriptions, so this ",
186
... "will appear quite nice in the actual email that gets sent.",
189
... "It's also smart enough to preserve whitespace, finally!",
176
192
>>> bug_delta = BugDelta(
177
193
... bug=edited_bug,
178
194
... bugurl="http://www.example.com/bugs/2",
179
195
... user=sample_person,
180
... description={'new': edited_bug.description,
181
... 'old': old_description})
197
... 'new': edited_bug.description,
198
... 'old': old_description,
182
200
>>> for change in get_bug_changes(bug_delta):
183
201
... notification = change.getBugNotification()
184
202
... print notification['text'] #doctest: -NORMALIZE_WHITESPACE
189
207
- dragging an item to the Trash results in immediate deletion. The item
190
208
- does not appear in the Trash, it is just deleted from my hard disk.
191
209
- There is no undo or ability to recover the deleted file. Help!
192
+ a new description that is quite long. but the nice thing is that the
210
+ A new description that is quite long. But the nice thing is that the
193
211
+ edit notification email generator knows how to indent and wrap
194
212
+ descriptions, so this will appear quite nice in the actual email that
197
+ it's also smart enough to preserve whitespace, finally!
215
+ It's also smart enough to preserve whitespace, finally!
198
216
-----------------------------
200
218
Let's make the bug security-related, and private (we need to switch
205
223
>>> edited_bug.setPrivate(True, getUtility(ILaunchBag).user)
207
>>> changed = edited_bug.setSecurityRelated(True, getUtility(ILaunchBag).user)
226
>>> changed = edited_bug.setSecurityRelated(
227
... True, getUtility(ILaunchBag).user)
208
228
>>> bug_delta = BugDelta(
209
229
... bug=edited_bug,
210
230
... bugurl="http://www.example.com/bugs/2",
211
231
... user=sample_person,
212
232
... private={'old': False, 'new': edited_bug.private},
213
... security_related={'old': False, 'new': edited_bug.security_related})
233
... security_related={
235
... 'new': edited_bug.security_related,
215
238
>>> for change in get_bug_changes(bug_delta):
216
239
... notification = change.getBugNotification()
228
251
>>> edited_bug.setPrivate(False, getUtility(ILaunchBag).user)
230
>>> changed = edited_bug.setSecurityRelated(False, getUtility(ILaunchBag).user)
254
>>> changed = edited_bug.setSecurityRelated(
255
... False, getUtility(ILaunchBag).user)
231
256
>>> bug_delta = BugDelta(
232
257
... bug=edited_bug,
233
258
... bugurl="http://www.example.com/bugs/2",
234
259
... user=sample_person,
235
260
... private={'old': True, 'new': edited_bug.private},
236
... security_related={'old': True, 'new': edited_bug.security_related})
261
... security_related={
263
... 'new': edited_bug.security_related,
237
265
>>> for change in get_bug_changes(bug_delta):
238
266
... notification = change.getBugNotification()
239
267
... print notification['text'] #doctest: -NORMALIZE_WHITESPACE
248
276
>>> old_tags = []
249
277
>>> edited_bug.tags = [u'foo', u'bar']
250
278
>>> bug_delta = BugDelta(
252
... bugurl="http://www.example.com/bugs/2",
253
... user=sample_person,
254
... tags={'old': old_tags, 'new': edited_bug.tags})
280
... bugurl="http://www.example.com/bugs/2",
281
... user=sample_person,
282
... tags={'old': old_tags, 'new': edited_bug.tags})
255
283
>>> for change in get_bug_changes(bug_delta):
256
... notification = change.getBugNotification()
257
... print notification['text'] #doctest: -NORMALIZE_WHITESPACE
258
... print "-----------------------------"
284
... notification = change.getBugNotification()
285
... print notification['text'] #doctest: -NORMALIZE_WHITESPACE
286
... print "-----------------------------"
259
287
** Tags added: bar foo
260
288
-----------------------------
264
292
>>> old_tags = edited_bug.tags
265
293
>>> edited_bug.tags = [u'foo', u'baz']
266
294
>>> bug_delta = BugDelta(
268
... bugurl="http://www.example.com/bugs/2",
269
... user=sample_person,
270
... tags={'old': old_tags, 'new': edited_bug.tags})
296
... bugurl="http://www.example.com/bugs/2",
297
... user=sample_person,
298
... tags={'old': old_tags, 'new': edited_bug.tags})
271
299
>>> for change in get_bug_changes(bug_delta):
272
... notification = change.getBugNotification()
273
... print notification['text'] #doctest: -NORMALIZE_WHITESPACE
274
... print "-----------------------------"
300
... notification = change.getBugNotification()
301
... print notification['text'] #doctest: -NORMALIZE_WHITESPACE
302
... print "-----------------------------"
275
303
** Tags removed: bar
276
304
** Tags added: baz
277
305
-----------------------------
280
= Editing a bug task =
282
As you might expect, get_bug_changes handles generating the
283
text representations of the changes when a bug task is edited.
311
As you might expect, get_bug_changes handles generating the text
312
representations of the changes when a bug task is edited.
285
314
We use a BugTaskDelta to represent changes to a BugTask.
287
316
>>> from canonical.launchpad.webapp.testing import verifyObject
288
317
>>> from lp.bugs.interfaces.bugtask import (
293
322
>>> from lp.bugs.model.bugtask import BugTaskDelta
294
323
>>> example_bug_task = factory.makeBugTask()
295
324
>>> example_delta = BugTaskDelta(example_bug_task)
299
328
>>> edited_bugtask = getUtility(IBugTaskSet).get(3)
300
329
>>> edited_bugtask.transitionToStatus(
301
... BugTaskStatus.CONFIRMED, getUtility(ILaunchBag).user)
330
... BugTaskStatus.CONFIRMED, getUtility(ILaunchBag).user)
302
331
>>> edited_bugtask.transitionToAssignee(sample_person)
303
332
>>> bugtask_delta = BugTaskDelta(
304
333
... bugtask=edited_bugtask,
326
355
>>> debian_bugtask = getUtility(IBugTaskSet).get(5)
327
356
>>> print debian_bugtask.bugtargetname
328
357
mozilla-firefox (Debian)
329
359
>>> debian_bugtask.transitionToAssignee(None)
330
360
>>> bugtask_delta = BugTaskDelta(
331
361
... bugtask=debian_bugtask,
414
445
-----------------------------
417
= Generation of From: and Reply-To: addresses =
448
Generation of From: and Reply-To: addresses
449
===========================================
419
451
The Reply-To: and From: addresses used to send email are generated in a
420
452
pair of handy functions defined in mailnotification.py:
433
465
>>> stub = getUtility(IPersonSet).getByName("stub")
434
466
>>> [(email.email, email.status.name) for email
435
... in getUtility(IEmailAddressSet).getByPerson(stub)]
467
... in getUtility(IEmailAddressSet).getByPerson(stub)]
436
468
[(u'stuart.bishop@canonical.com', 'PREFERRED'),
437
469
(u'stuart@stuartbishop.net', 'VALIDATED'),
438
470
(u'stub@fastmail.fm', 'NEW'),
479
511
'Ford Prefect <4@bugs.launchpad.net>'
482
== Construction of bug notification emails ==
514
Construction of bug notification emails
515
---------------------------------------
484
517
mailnotification.py contains a class, BugNotificationBuilder, which is
485
518
used to construct bug notification emails.
487
520
>>> from lp.bugs.mail.bugnotificationbuilder import BugNotificationBuilder
489
When instantiatiated it derives a list of common unchanging headers
490
from the bug so that they are not calculated for every recipient.
522
When instantiatiated it derives a list of common unchanging headers from
523
the bug so that they are not calculated for every recipient.
492
525
>>> bug_four_notification_builder = BugNotificationBuilder(bug_four,
493
526
... private_person)
504
537
X-Launchpad-Bug-Modifier: Ford Prefect (person-name...)
506
539
The build() method of a builder accepts a number of parameters and
507
returns an instance of email.MIMEText. The most basic invocation of
508
this method requires a from address, a to address, a body, a subject
509
and a sending date for the mail.
540
returns an instance of email.MIMEText. The most basic invocation of this
541
method requires a from address, a to address, a body, a subject and a
542
sending date for the mail.
511
544
>>> from datetime import datetime
519
552
... from_address, 'foo.bar@canonical.com',
520
553
... "A test body.", "A test subject.", sending_date)
522
The fields of the generated notification email will be set according
523
to the parameters that were used to instantiate BugNotificationBuilder
524
and passed to <builder>.build().
555
The fields of the generated notification email will be set according to
556
the parameters that were used to instantiate BugNotificationBuilder and
557
passed to <builder>.build().
526
559
>>> print notification_email['From']
527
560
Launchpad Bug Tracker <4@bugs.launchpad.net>