Merge lp:~gary/launchpad/bug164196-3 into lp:launchpad/db-devel
- bug164196-3
- Merge into db-devel
Status: | Merged |
---|---|
Approved by: | Gary Poster |
Approved revision: | no longer in the source branch. |
Merged at revision: | 10215 |
Proposed branch: | lp:~gary/launchpad/bug164196-3 |
Merge into: | lp:launchpad/db-devel |
Prerequisite: | lp:~gary/launchpad/bug164196-2 |
Diff against target: |
519 lines (+140/-30) 13 files modified
cronscripts/send-bug-notifications.py (+8/-1) database/schema/comments.sql (+1/-0) database/schema/patch-2208-45-0.sql (+12/-0) lib/lp/bugs/doc/bugnotification-sending.txt (+61/-19) lib/lp/bugs/doc/bugnotification-threading.txt (+3/-3) lib/lp/bugs/enum.py (+27/-0) lib/lp/bugs/interfaces/bugnotification.py (+9/-0) lib/lp/bugs/mail/tests/test_bug_task_assignment.py (+2/-2) lib/lp/bugs/mail/tests/test_bug_task_modification.py (+1/-1) lib/lp/bugs/model/bugnotification.py (+6/-0) lib/lp/bugs/scripts/bugnotification.py (+4/-1) lib/lp/bugs/scripts/tests/test_bugnotification.py (+4/-2) lib/lp/bugs/tests/test_bugchanges.py (+2/-1) |
To merge this branch: | bzr merge lp:~gary/launchpad/bug164196-3 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Robert Collins (community) | Approve | ||
Stuart Bishop (community) | db | Approve | |
Aaron Bentley (community) | Approve | ||
Review via email: mp+49980@code.launchpad.net |
Commit message
[r=abentley,stub][bug=164196] makes sure that the omitted notification objects left over from duplicate actions are marked as processed so that they are not perpetually considered for subsequent notification cronscript runs.
Description of the change
This is the last of three branches that address bug 164196, and one of two that have a database patch. The other two are lp:~gary/launchpad/bug164196-1 and lp:~gary/launchpad/bug164196-3. My pre-implementation call for these changes was with Graham Binns.
This branch makes sure that the omitted notification objects left over from actions in the previous branch are marked as processed so that they are not perpetually considered for subsequent notification cronscript runs. It also marks these omitted notification objects with a flag in case we want to analyze them for debugging later, if we get a report of something gone wrong in this regard.
The database changes simply add the flag as described above.
The code in lib/lp/
lib/lp/
Thank you
Gary
Gary Poster (gary) wrote : | # |
I have followed both of Aaron's suggestions, because I agreed that they were nicer. If Stuart prefers a bool after all (presumably because of space reasons), I can revert that bit pretty easily.
Thank you
Stuart Bishop (stub) wrote : | # |
patch-2208-45-0.sql
Please add the following statement after the ALTER TABLE (all rows are going to be rewritten, so we want to repack the table):
CLUSTER BugNotification USING bugnotification
Bug notification is regularly trimmed, so there are not too many rows to deal with - should be fast enough for a db patch.
I prefer status rather than the boolean too.
Robert Collins (lifeless) : | # |
Preview Diff
1 | === modified file 'cronscripts/send-bug-notifications.py' | |||
2 | --- cronscripts/send-bug-notifications.py 2010-11-08 12:52:43 +0000 | |||
3 | +++ cronscripts/send-bug-notifications.py 2011-02-17 17:43:44 +0000 | |||
4 | @@ -20,6 +20,7 @@ | |||
5 | 20 | from canonical.config import config | 20 | from canonical.config import config |
6 | 21 | from canonical.database.constants import UTC_NOW | 21 | from canonical.database.constants import UTC_NOW |
7 | 22 | from canonical.launchpad.mail import sendmail | 22 | from canonical.launchpad.mail import sendmail |
8 | 23 | from lp.bugs.enum import BugNotificationStatus | ||
9 | 23 | from lp.bugs.interfaces.bugnotification import IBugNotificationSet | 24 | from lp.bugs.interfaces.bugnotification import IBugNotificationSet |
10 | 24 | from lp.bugs.scripts.bugnotification import get_email_notifications | 25 | from lp.bugs.scripts.bugnotification import get_email_notifications |
11 | 25 | from lp.services.scripts.base import LaunchpadCronScript | 26 | from lp.services.scripts.base import LaunchpadCronScript |
12 | @@ -30,7 +31,9 @@ | |||
13 | 30 | notifications_sent = False | 31 | notifications_sent = False |
14 | 31 | pending_notifications = get_email_notifications(getUtility( | 32 | pending_notifications = get_email_notifications(getUtility( |
15 | 32 | IBugNotificationSet).getNotificationsToSend()) | 33 | IBugNotificationSet).getNotificationsToSend()) |
17 | 33 | for bug_notifications, messages in pending_notifications: | 34 | for (bug_notifications, |
18 | 35 | omitted_notifications, | ||
19 | 36 | messages) in pending_notifications: | ||
20 | 34 | for message in messages: | 37 | for message in messages: |
21 | 35 | self.logger.info("Notifying %s about bug %d." % ( | 38 | self.logger.info("Notifying %s about bug %d." % ( |
22 | 36 | message['To'], bug_notifications[0].bug.id)) | 39 | message['To'], bug_notifications[0].bug.id)) |
23 | @@ -38,6 +41,10 @@ | |||
24 | 38 | self.logger.debug(message.as_string()) | 41 | self.logger.debug(message.as_string()) |
25 | 39 | for notification in bug_notifications: | 42 | for notification in bug_notifications: |
26 | 40 | notification.date_emailed = UTC_NOW | 43 | notification.date_emailed = UTC_NOW |
27 | 44 | notification.status = BugNotificationStatus.SENT | ||
28 | 45 | for notification in omitted_notifications: | ||
29 | 46 | notification.date_emailed = UTC_NOW | ||
30 | 47 | notification.status = BugNotificationStatus.OMITTED | ||
31 | 41 | notifications_sent = True | 48 | notifications_sent = True |
32 | 42 | # Commit after each batch of email sent, so that we won't | 49 | # Commit after each batch of email sent, so that we won't |
33 | 43 | # re-mail the notifications in case of something going wrong | 50 | # re-mail the notifications in case of something going wrong |
34 | 44 | 51 | ||
35 | === modified file 'database/schema/comments.sql' | |||
36 | --- database/schema/comments.sql 2011-02-17 17:43:43 +0000 | |||
37 | +++ database/schema/comments.sql 2011-02-17 17:43:44 +0000 | |||
38 | @@ -281,6 +281,7 @@ | |||
39 | 281 | COMMENT ON COLUMN BugNotification.is_comment IS 'Is the change a comment addition.'; | 281 | COMMENT ON COLUMN BugNotification.is_comment IS 'Is the change a comment addition.'; |
40 | 282 | COMMENT ON COLUMN BugNotification.date_emailed IS 'When this notification was emailed to the bug subscribers.'; | 282 | COMMENT ON COLUMN BugNotification.date_emailed IS 'When this notification was emailed to the bug subscribers.'; |
41 | 283 | COMMENT ON COLUMN BugNotification.activity IS 'The BugActivity record corresponding to this notification, if any.'; | 283 | COMMENT ON COLUMN BugNotification.activity IS 'The BugActivity record corresponding to this notification, if any.'; |
42 | 284 | COMMENT ON COLUMN BugNotification.status IS 'The status of this bug notification: pending, omitted, or sent.'; | ||
43 | 284 | 285 | ||
44 | 285 | 286 | ||
45 | 286 | -- BugNotificationAttachment | 287 | -- BugNotificationAttachment |
46 | 287 | 288 | ||
47 | === added file 'database/schema/patch-2208-45-0.sql' | |||
48 | --- database/schema/patch-2208-45-0.sql 1970-01-01 00:00:00 +0000 | |||
49 | +++ database/schema/patch-2208-45-0.sql 2011-02-17 17:43:44 +0000 | |||
50 | @@ -0,0 +1,12 @@ | |||
51 | 1 | -- Copyright 2011 Canonical Ltd. This software is licensed under the | ||
52 | 2 | -- GNU Affero General Public License version 3 (see the file LICENSE). | ||
53 | 3 | SET client_min_messages=ERROR; | ||
54 | 4 | |||
55 | 5 | -- The default value for status can be found in the | ||
56 | 6 | -- BugNotificationStatus DBEnum in lib/lp/bugs/enum.py. | ||
57 | 7 | ALTER TABLE BugNotification | ||
58 | 8 | ADD COLUMN status INTEGER NOT NULL DEFAULT 10; | ||
59 | 9 | |||
60 | 10 | CLUSTER BugNotification USING bugnotification__date_emailed__idx; | ||
61 | 11 | |||
62 | 12 | INSERT INTO LaunchpadDatabaseRevision VALUES (2208, 45, 0); | ||
63 | 0 | 13 | ||
64 | === modified file 'lib/lp/bugs/doc/bugnotification-sending.txt' | |||
65 | --- lib/lp/bugs/doc/bugnotification-sending.txt 2011-02-17 17:43:43 +0000 | |||
66 | +++ lib/lp/bugs/doc/bugnotification-sending.txt 2011-02-17 17:43:44 +0000 | |||
67 | @@ -63,7 +63,7 @@ | |||
68 | 63 | >>> from lp.bugs.scripts.bugnotification import ( | 63 | >>> from lp.bugs.scripts.bugnotification import ( |
69 | 64 | ... get_email_notifications) | 64 | ... get_email_notifications) |
70 | 65 | >>> email_notifications = get_email_notifications(notifications) | 65 | >>> email_notifications = get_email_notifications(notifications) |
72 | 66 | >>> for bug_notifications, messages in email_notifications: | 66 | >>> for bug_notifications, omitted, messages in email_notifications: |
73 | 67 | ... for message in messages: | 67 | ... for message in messages: |
74 | 68 | ... print_notification(message) | 68 | ... print_notification(message) |
75 | 69 | To: foo.bar@canonical.com | 69 | To: foo.bar@canonical.com |
76 | @@ -176,7 +176,7 @@ | |||
77 | 176 | >>> pending_notifications = getUtility( | 176 | >>> pending_notifications = getUtility( |
78 | 177 | ... IBugNotificationSet).getNotificationsToSend() | 177 | ... IBugNotificationSet).getNotificationsToSend() |
79 | 178 | >>> email_notifications = get_email_notifications(pending_notifications) | 178 | >>> email_notifications = get_email_notifications(pending_notifications) |
81 | 179 | >>> for bug_notifications, messages in email_notifications: | 179 | >>> for bug_notifications, omitted, messages in email_notifications: |
82 | 180 | ... for message in messages: | 180 | ... for message in messages: |
83 | 181 | ... print_notification(message) | 181 | ... print_notification(message) |
84 | 182 | To: foo.bar@canonical.com | 182 | To: foo.bar@canonical.com |
85 | @@ -222,7 +222,7 @@ | |||
86 | 222 | 2 | 222 | 2 |
87 | 223 | 223 | ||
88 | 224 | >>> email_notifications = get_email_notifications(pending_notifications) | 224 | >>> email_notifications = get_email_notifications(pending_notifications) |
90 | 225 | >>> for bug_notifications, messages in email_notifications: | 225 | >>> for bug_notifications, omitted, messages in email_notifications: |
91 | 226 | ... for message in messages: | 226 | ... for message in messages: |
92 | 227 | ... print_notification(message) | 227 | ... print_notification(message) |
93 | 228 | To: foo.bar@canonical.com | 228 | To: foo.bar@canonical.com |
94 | @@ -275,7 +275,7 @@ | |||
95 | 275 | in the order they were added: | 275 | in the order they were added: |
96 | 276 | 276 | ||
97 | 277 | >>> email_notifications = get_email_notifications(pending_notifications) | 277 | >>> email_notifications = get_email_notifications(pending_notifications) |
99 | 278 | >>> for bug_notifications, messages in email_notifications: | 278 | >>> for bug_notifications, omitted, messages in email_notifications: |
100 | 279 | ... for message in messages: | 279 | ... for message in messages: |
101 | 280 | ... print_notification(message) | 280 | ... print_notification(message) |
102 | 281 | To: foo.bar@canonical.com | 281 | To: foo.bar@canonical.com |
103 | @@ -371,7 +371,7 @@ | |||
104 | 371 | 1 | 371 | 1 |
105 | 372 | 372 | ||
106 | 373 | >>> email_notifications = get_email_notifications(pending_notifications) | 373 | >>> email_notifications = get_email_notifications(pending_notifications) |
108 | 374 | >>> for bug_notifications, messages in email_notifications: | 374 | >>> for bug_notifications, omitted, messages in email_notifications: |
109 | 375 | ... for message in messages: | 375 | ... for message in messages: |
110 | 376 | ... print message['To'] | 376 | ... print message['To'] |
111 | 377 | foo.bar@canonical.com | 377 | foo.bar@canonical.com |
112 | @@ -414,7 +414,7 @@ | |||
113 | 414 | >>> len(notifications) | 414 | >>> len(notifications) |
114 | 415 | 1 | 415 | 1 |
115 | 416 | 416 | ||
117 | 417 | >>> for bug_notifications, messages in ( | 417 | >>> for bug_notifications, omitted, messages in ( |
118 | 418 | ... get_email_notifications(notifications)): | 418 | ... get_email_notifications(notifications)): |
119 | 419 | ... for message in messages: | 419 | ... for message in messages: |
120 | 420 | ... print_notification(message) | 420 | ... print_notification(message) |
121 | @@ -485,7 +485,7 @@ | |||
122 | 485 | >>> notifications = ( | 485 | >>> notifications = ( |
123 | 486 | ... getUtility(IBugNotificationSet).getNotificationsToSend()) | 486 | ... getUtility(IBugNotificationSet).getNotificationsToSend()) |
124 | 487 | >>> email_notifications = get_email_notifications(notifications) | 487 | >>> email_notifications = get_email_notifications(notifications) |
126 | 488 | >>> for bug_notifications, messages in email_notifications: | 488 | >>> for bug_notifications, omitted, messages in email_notifications: |
127 | 489 | ... for message in messages: | 489 | ... for message in messages: |
128 | 490 | ... print_notification(message) | 490 | ... print_notification(message) |
129 | 491 | To: support@ubuntu.com | 491 | To: support@ubuntu.com |
130 | @@ -509,7 +509,7 @@ | |||
131 | 509 | >>> notifications = ( | 509 | >>> notifications = ( |
132 | 510 | ... getUtility(IBugNotificationSet).getNotificationsToSend()) | 510 | ... getUtility(IBugNotificationSet).getNotificationsToSend()) |
133 | 511 | >>> email_notifications = get_email_notifications(notifications) | 511 | >>> email_notifications = get_email_notifications(notifications) |
135 | 512 | >>> for bug_notifications, messages in email_notifications: | 512 | >>> for bug_notifications, omitted, messages in email_notifications: |
136 | 513 | ... for message in messages: | 513 | ... for message in messages: |
137 | 514 | ... print_notification(message) | 514 | ... print_notification(message) |
138 | 515 | To: support@ubuntu.com | 515 | To: support@ubuntu.com |
139 | @@ -556,11 +556,19 @@ | |||
140 | 556 | ... BugTitleChange( | 556 | ... BugTitleChange( |
141 | 557 | ... ten_minutes_ago, sample_person, "title", | 557 | ... ten_minutes_ago, sample_person, "title", |
142 | 558 | ... "Old summary", "New summary")) | 558 | ... "Old summary", "New summary")) |
143 | 559 | >>> bug_two.addChange( | ||
144 | 560 | ... BugVisibilityChange( | ||
145 | 561 | ... ten_minutes_ago, sample_person, "title", | ||
146 | 562 | ... False, True)) | ||
147 | 563 | >>> bug_two.addChange( | ||
148 | 564 | ... BugVisibilityChange( | ||
149 | 565 | ... ten_minutes_ago, sample_person, "title", | ||
150 | 566 | ... True, False)) | ||
151 | 559 | 567 | ||
152 | 560 | >>> notifications = getUtility( | 568 | >>> notifications = getUtility( |
153 | 561 | ... IBugNotificationSet).getNotificationsToSend() | 569 | ... IBugNotificationSet).getNotificationsToSend() |
154 | 562 | >>> len(notifications) | 570 | >>> len(notifications) |
156 | 563 | 6 | 571 | 8 |
157 | 564 | 572 | ||
158 | 565 | We need to commit the transaction so that the cronscript will see the | 573 | We need to commit the transaction so that the cronscript will see the |
159 | 566 | notifications. | 574 | notifications. |
160 | @@ -648,7 +656,41 @@ | |||
161 | 648 | INFO Notifying test@canonical.com about bug 1. | 656 | INFO Notifying test@canonical.com about bug 1. |
162 | 649 | ... | 657 | ... |
163 | 650 | 658 | ||
165 | 651 | >>> flush_notifications() | 659 | Note that the message omitted the undone visibility change. |
166 | 660 | |||
167 | 661 | The cronscript has to be sure to mark all notifications, omitted and | ||
168 | 662 | otherwise, as sent. It also marks the omitted notifications with a status, | ||
169 | 663 | so if there are any problems we can identify which notifications were omitted | ||
170 | 664 | during analysis. We'll commit a transaction to synchronize the database, | ||
171 | 665 | and then look at the notifications available. | ||
172 | 666 | |||
173 | 667 | >>> transaction.commit() | ||
174 | 668 | |||
175 | 669 | >>> notifications = getUtility( | ||
176 | 670 | ... IBugNotificationSet).getNotificationsToSend() | ||
177 | 671 | >>> len(notifications) | ||
178 | 672 | 0 | ||
179 | 673 | |||
180 | 674 | They have all been marked as sent, including the omitted ones. Let's look | ||
181 | 675 | more carefully at the notifications just to see that the status has | ||
182 | 676 | been set properly. | ||
183 | 677 | |||
184 | 678 | >>> from lp.bugs.model.bugnotification import BugNotification | ||
185 | 679 | >>> from lp.bugs.enum import BugNotificationStatus | ||
186 | 680 | >>> for notification in BugNotification.select(orderBy='id')[-8:]: | ||
187 | 681 | ... if notification.is_comment: | ||
188 | 682 | ... identifier = 'comment' | ||
189 | 683 | ... else: | ||
190 | 684 | ... identifier = notification.activity.whatchanged | ||
191 | 685 | ... print identifier, notification.status.title | ||
192 | 686 | comment Sent | ||
193 | 687 | summary Sent | ||
194 | 688 | comment Sent | ||
195 | 689 | summary Sent | ||
196 | 690 | comment Sent | ||
197 | 691 | summary Sent | ||
198 | 692 | visibility Omitted | ||
199 | 693 | visibility Omitted | ||
200 | 652 | 694 | ||
201 | 653 | 695 | ||
202 | 654 | The X-Launchpad-Bug header | 696 | The X-Launchpad-Bug header |
203 | @@ -673,7 +715,7 @@ | |||
204 | 673 | X-Launchpad-Bug headers were added: | 715 | X-Launchpad-Bug headers were added: |
205 | 674 | 716 | ||
206 | 675 | >>> email_notifications = get_email_notifications(notifications) | 717 | >>> email_notifications = get_email_notifications(notifications) |
208 | 676 | >>> for bug_notifications, messages in email_notifications: | 718 | >>> for bug_notifications, omitted, messages in email_notifications: |
209 | 677 | ... for message in messages: | 719 | ... for message in messages: |
210 | 678 | ... sorted(message.get_all('X-Launchpad-Bug')) | 720 | ... sorted(message.get_all('X-Launchpad-Bug')) |
211 | 679 | [u'distribution=debian; distroseries=sarge;... milestone=3.1;...', | 721 | [u'distribution=debian; distroseries=sarge;... milestone=3.1;...', |
212 | @@ -707,7 +749,7 @@ | |||
213 | 707 | 749 | ||
214 | 708 | >>> def get_email_messages(notifications): | 750 | >>> def get_email_messages(notifications): |
215 | 709 | ... messages = (message | 751 | ... messages = (message |
217 | 710 | ... for bug_notifications, messages in | 752 | ... for bug_notifications, omitted, messages in |
218 | 711 | ... get_email_notifications(notifications) | 753 | ... get_email_notifications(notifications) |
219 | 712 | ... for message in messages) | 754 | ... for message in messages) |
220 | 713 | ... return sorted(messages, key=lambda message: message['To']) | 755 | ... return sorted(messages, key=lambda message: message['To']) |
221 | @@ -963,7 +1005,7 @@ | |||
222 | 963 | 1005 | ||
223 | 964 | >>> from itertools import chain | 1006 | >>> from itertools import chain |
224 | 965 | >>> collated_messages = collate_messages_by_recipient( | 1007 | >>> collated_messages = collate_messages_by_recipient( |
226 | 966 | ... chain(*(messages for bug_notifications, messages in | 1008 | ... chain(*(messages for bug_notifications, omitted, messages in |
227 | 967 | ... get_email_notifications(notifications)))) | 1009 | ... get_email_notifications(notifications)))) |
228 | 968 | 1010 | ||
229 | 969 | We can see that Concise Person doesn't receive verbose notifications: | 1011 | We can see that Concise Person doesn't receive verbose notifications: |
230 | @@ -1151,7 +1193,7 @@ | |||
231 | 1151 | >>> pending_notifications = getUtility( | 1193 | >>> pending_notifications = getUtility( |
232 | 1152 | ... IBugNotificationSet).getNotificationsToSend() | 1194 | ... IBugNotificationSet).getNotificationsToSend() |
233 | 1153 | >>> email_notifications = get_email_notifications(pending_notifications) | 1195 | >>> email_notifications = get_email_notifications(pending_notifications) |
235 | 1154 | >>> for bug_notifications, messages in email_notifications: | 1196 | >>> for bug_notifications, omitted, messages in email_notifications: |
236 | 1155 | ... for message in messages: | 1197 | ... for message in messages: |
237 | 1156 | ... print_notification(message) | 1198 | ... print_notification(message) |
238 | 1157 | To: foo.bar@canonical.com | 1199 | To: foo.bar@canonical.com |
239 | @@ -1212,7 +1254,7 @@ | |||
240 | 1212 | >>> pending_notifications = getUtility( | 1254 | >>> pending_notifications = getUtility( |
241 | 1213 | ... IBugNotificationSet).getNotificationsToSend() | 1255 | ... IBugNotificationSet).getNotificationsToSend() |
242 | 1214 | >>> email_notifications = get_email_notifications(pending_notifications) | 1256 | >>> email_notifications = get_email_notifications(pending_notifications) |
244 | 1215 | >>> for bug_notifications, messages in email_notifications: | 1257 | >>> for bug_notifications, omitted, messages in email_notifications: |
245 | 1216 | ... for message in messages: | 1258 | ... for message in messages: |
246 | 1217 | ... print_notification(message) | 1259 | ... print_notification(message) |
247 | 1218 | To: foo.bar@canonical.com | 1260 | To: foo.bar@canonical.com |
248 | @@ -1271,7 +1313,7 @@ | |||
249 | 1271 | >>> pending_notifications = getUtility( | 1313 | >>> pending_notifications = getUtility( |
250 | 1272 | ... IBugNotificationSet).getNotificationsToSend() | 1314 | ... IBugNotificationSet).getNotificationsToSend() |
251 | 1273 | >>> email_notifications = get_email_notifications(pending_notifications) | 1315 | >>> email_notifications = get_email_notifications(pending_notifications) |
253 | 1274 | >>> for bug_notifications, messages in email_notifications: | 1316 | >>> for bug_notifications, omitted, messages in email_notifications: |
254 | 1275 | ... for message in messages: | 1317 | ... for message in messages: |
255 | 1276 | ... print_notification(message) | 1318 | ... print_notification(message) |
256 | 1277 | To: foo.bar@canonical.com | 1319 | To: foo.bar@canonical.com |
257 | @@ -1322,7 +1364,7 @@ | |||
258 | 1322 | >>> pending_notifications = getUtility( | 1364 | >>> pending_notifications = getUtility( |
259 | 1323 | ... IBugNotificationSet).getNotificationsToSend() | 1365 | ... IBugNotificationSet).getNotificationsToSend() |
260 | 1324 | >>> email_notifications = get_email_notifications(pending_notifications) | 1366 | >>> email_notifications = get_email_notifications(pending_notifications) |
262 | 1325 | >>> for bug_notifications, messages in email_notifications: | 1367 | >>> for bug_notifications, omitted, messages in email_notifications: |
263 | 1326 | ... for message in messages: | 1368 | ... for message in messages: |
264 | 1327 | ... print_notification(message) | 1369 | ... print_notification(message) |
265 | 1328 | To: foo.bar@canonical.com | 1370 | To: foo.bar@canonical.com |
266 | @@ -1386,7 +1428,7 @@ | |||
267 | 1386 | >>> pending_notifications = getUtility( | 1428 | >>> pending_notifications = getUtility( |
268 | 1387 | ... IBugNotificationSet).getNotificationsToSend() | 1429 | ... IBugNotificationSet).getNotificationsToSend() |
269 | 1388 | >>> email_notifications = get_email_notifications(pending_notifications) | 1430 | >>> email_notifications = get_email_notifications(pending_notifications) |
271 | 1389 | >>> for bug_notifications, messages in email_notifications: | 1431 | >>> for bug_notifications, omitted, messages in email_notifications: |
272 | 1390 | ... for message in messages: | 1432 | ... for message in messages: |
273 | 1391 | ... print_notification(message) | 1433 | ... print_notification(message) |
274 | 1392 | To: foo.bar@canonical.com | 1434 | To: foo.bar@canonical.com |
275 | @@ -1448,7 +1490,7 @@ | |||
276 | 1448 | >>> pending_notifications = getUtility( | 1490 | >>> pending_notifications = getUtility( |
277 | 1449 | ... IBugNotificationSet).getNotificationsToSend() | 1491 | ... IBugNotificationSet).getNotificationsToSend() |
278 | 1450 | >>> email_notifications = get_email_notifications(pending_notifications) | 1492 | >>> email_notifications = get_email_notifications(pending_notifications) |
280 | 1451 | >>> for bug_notifications, messages in email_notifications: | 1493 | >>> for bug_notifications, omitted, messages in email_notifications: |
281 | 1452 | ... for message in messages: | 1494 | ... for message in messages: |
282 | 1453 | ... print_notification(message) | 1495 | ... print_notification(message) |
283 | 1454 | To: foo.bar@canonical.com | 1496 | To: foo.bar@canonical.com |
284 | 1455 | 1497 | ||
285 | === modified file 'lib/lp/bugs/doc/bugnotification-threading.txt' | |||
286 | --- lib/lp/bugs/doc/bugnotification-threading.txt 2011-02-02 17:33:21 +0000 | |||
287 | +++ lib/lp/bugs/doc/bugnotification-threading.txt 2011-02-17 17:43:44 +0000 | |||
288 | @@ -35,7 +35,7 @@ | |||
289 | 35 | >>> notifications = getUtility( | 35 | >>> notifications = getUtility( |
290 | 36 | ... IBugNotificationSet).getNotificationsToSend() | 36 | ... IBugNotificationSet).getNotificationsToSend() |
291 | 37 | 37 | ||
293 | 38 | >>> messages = [emails for dummy, emails in | 38 | >>> messages = [emails for notifications, omitted, emails in |
294 | 39 | ... get_email_notifications(notifications)] | 39 | ... get_email_notifications(notifications)] |
295 | 40 | >>> len(messages) | 40 | >>> len(messages) |
296 | 41 | 1 | 41 | 1 |
297 | @@ -73,7 +73,7 @@ | |||
298 | 73 | ... True, False)) | 73 | ... True, False)) |
299 | 74 | >>> notifications = getUtility( | 74 | >>> notifications = getUtility( |
300 | 75 | ... IBugNotificationSet).getNotificationsToSend() | 75 | ... IBugNotificationSet).getNotificationsToSend() |
302 | 76 | >>> messages = [emails for dummy, emails in | 76 | >>> messages = [emails for notifications, omitted, emails in |
303 | 77 | ... get_email_notifications(notifications)] | 77 | ... get_email_notifications(notifications)] |
304 | 78 | >>> len(messages) | 78 | >>> len(messages) |
305 | 79 | 1 | 79 | 1 |
306 | @@ -106,7 +106,7 @@ | |||
307 | 106 | 106 | ||
308 | 107 | >>> notifications = getUtility( | 107 | >>> notifications = getUtility( |
309 | 108 | ... IBugNotificationSet).getNotificationsToSend() | 108 | ... IBugNotificationSet).getNotificationsToSend() |
311 | 109 | >>> messages = [emails for dummy, emails in | 109 | >>> messages = [emails for notifications, omitted, emails in |
312 | 110 | ... get_email_notifications(notifications)] | 110 | ... get_email_notifications(notifications)] |
313 | 111 | >>> len(messages) | 111 | >>> len(messages) |
314 | 112 | 1 | 112 | 1 |
315 | 113 | 113 | ||
316 | === modified file 'lib/lp/bugs/enum.py' | |||
317 | --- lib/lp/bugs/enum.py 2011-02-01 17:52:45 +0000 | |||
318 | +++ lib/lp/bugs/enum.py 2011-02-17 17:43:44 +0000 | |||
319 | @@ -6,6 +6,7 @@ | |||
320 | 6 | __metaclass__ = type | 6 | __metaclass__ = type |
321 | 7 | __all__ = [ | 7 | __all__ = [ |
322 | 8 | 'BugNotificationLevel', | 8 | 'BugNotificationLevel', |
323 | 9 | 'BugNotificationStatus', | ||
324 | 9 | ] | 10 | ] |
325 | 10 | 11 | ||
326 | 11 | from lazr.enum import ( | 12 | from lazr.enum import ( |
327 | @@ -47,3 +48,29 @@ | |||
328 | 47 | notifications about new events in the bugs's discussion, like new | 48 | notifications about new events in the bugs's discussion, like new |
329 | 48 | comments. | 49 | comments. |
330 | 49 | """) | 50 | """) |
331 | 51 | |||
332 | 52 | |||
333 | 53 | class BugNotificationStatus(DBEnumeratedType): | ||
334 | 54 | """The status of a bug notification. | ||
335 | 55 | |||
336 | 56 | A notification may be pending, sent, or omitted.""" | ||
337 | 57 | |||
338 | 58 | PENDING = DBItem(10, """ | ||
339 | 59 | Pending | ||
340 | 60 | |||
341 | 61 | The notification has not yet been sent. | ||
342 | 62 | """) | ||
343 | 63 | |||
344 | 64 | OMITTED = DBItem(20, """ | ||
345 | 65 | Omitted | ||
346 | 66 | |||
347 | 67 | The system considered sending the notification, but omitted it. | ||
348 | 68 | This is generally because the action reported by the notification | ||
349 | 69 | was immediately undone. | ||
350 | 70 | """) | ||
351 | 71 | |||
352 | 72 | SENT = DBItem(30, """ | ||
353 | 73 | Sent | ||
354 | 74 | |||
355 | 75 | The notification has been sent. | ||
356 | 76 | """) | ||
357 | 50 | 77 | ||
358 | === modified file 'lib/lp/bugs/interfaces/bugnotification.py' | |||
359 | --- lib/lp/bugs/interfaces/bugnotification.py 2011-02-17 17:43:43 +0000 | |||
360 | +++ lib/lp/bugs/interfaces/bugnotification.py 2011-02-17 17:43:44 +0000 | |||
361 | @@ -18,11 +18,13 @@ | |||
362 | 18 | ) | 18 | ) |
363 | 19 | from zope.schema import ( | 19 | from zope.schema import ( |
364 | 20 | Bool, | 20 | Bool, |
365 | 21 | Choice, | ||
366 | 21 | Datetime, | 22 | Datetime, |
367 | 22 | TextLine, | 23 | TextLine, |
368 | 23 | ) | 24 | ) |
369 | 24 | 25 | ||
370 | 25 | from canonical.launchpad import _ | 26 | from canonical.launchpad import _ |
371 | 27 | from lp.bugs.enum import BugNotificationStatus | ||
372 | 26 | from lp.registry.interfaces.role import IHasOwner | 28 | from lp.registry.interfaces.role import IHasOwner |
373 | 27 | from lp.services.fields import BugField | 29 | from lp.services.fields import BugField |
374 | 28 | 30 | ||
375 | @@ -51,6 +53,13 @@ | |||
376 | 51 | required=False) | 53 | required=False) |
377 | 52 | recipients = Attribute( | 54 | recipients = Attribute( |
378 | 53 | "The people to which this notification should be sent.") | 55 | "The people to which this notification should be sent.") |
379 | 56 | status = Choice( | ||
380 | 57 | title=_("Status"), required=True, | ||
381 | 58 | vocabulary=BugNotificationStatus, | ||
382 | 59 | default=BugNotificationStatus.PENDING, | ||
383 | 60 | description=_( | ||
384 | 61 | "The status of this bug notification."), | ||
385 | 62 | ) | ||
386 | 54 | 63 | ||
387 | 55 | 64 | ||
388 | 56 | class IBugNotificationSet(Interface): | 65 | class IBugNotificationSet(Interface): |
389 | 57 | 66 | ||
390 | === modified file 'lib/lp/bugs/mail/tests/test_bug_task_assignment.py' | |||
391 | --- lib/lp/bugs/mail/tests/test_bug_task_assignment.py 2010-12-21 19:11:49 +0000 | |||
392 | +++ lib/lp/bugs/mail/tests/test_bug_task_assignment.py 2011-02-17 17:43:44 +0000 | |||
393 | @@ -90,7 +90,7 @@ | |||
394 | 90 | self.bug_task, self.bug_task_before_modification, | 90 | self.bug_task, self.bug_task_before_modification, |
395 | 91 | ['assignee'], user=self.user)) | 91 | ['assignee'], user=self.user)) |
396 | 92 | latest_notification = BugNotification.selectFirst(orderBy='-id') | 92 | latest_notification = BugNotification.selectFirst(orderBy='-id') |
398 | 93 | notifications, messages = construct_email_notifications( | 93 | notifications, omitted, messages = construct_email_notifications( |
399 | 94 | [latest_notification]) | 94 | [latest_notification]) |
400 | 95 | self.assertEqual(len(notifications), 1, | 95 | self.assertEqual(len(notifications), 1, |
401 | 96 | 'email notication not created') | 96 | 'email notication not created') |
402 | @@ -107,7 +107,7 @@ | |||
403 | 107 | self.bug_task, self.bug_task_before_modification, | 107 | self.bug_task, self.bug_task_before_modification, |
404 | 108 | ['assignee'], user=self.user)) | 108 | ['assignee'], user=self.user)) |
405 | 109 | latest_notification = BugNotification.selectFirst(orderBy='-id') | 109 | latest_notification = BugNotification.selectFirst(orderBy='-id') |
407 | 110 | notifications, messages = construct_email_notifications( | 110 | notifications, omitted, messages = construct_email_notifications( |
408 | 111 | [latest_notification]) | 111 | [latest_notification]) |
409 | 112 | self.assertEqual(len(notifications), 1, | 112 | self.assertEqual(len(notifications), 1, |
410 | 113 | 'email notification not created') | 113 | 'email notification not created') |
411 | 114 | 114 | ||
412 | === modified file 'lib/lp/bugs/mail/tests/test_bug_task_modification.py' | |||
413 | --- lib/lp/bugs/mail/tests/test_bug_task_modification.py 2010-10-04 19:50:45 +0000 | |||
414 | +++ lib/lp/bugs/mail/tests/test_bug_task_modification.py 2011-02-17 17:43:44 +0000 | |||
415 | @@ -44,7 +44,7 @@ | |||
416 | 44 | ['status'], user=self.user)) | 44 | ['status'], user=self.user)) |
417 | 45 | transaction.commit() | 45 | transaction.commit() |
418 | 46 | latest_notification = BugNotification.selectFirst(orderBy='-id') | 46 | latest_notification = BugNotification.selectFirst(orderBy='-id') |
420 | 47 | notifications, messages = construct_email_notifications( | 47 | notifications, omitted, messages = construct_email_notifications( |
421 | 48 | [latest_notification]) | 48 | [latest_notification]) |
422 | 49 | self.assertEqual(len(notifications), 1, | 49 | self.assertEqual(len(notifications), 1, |
423 | 50 | 'email notification not created') | 50 | 'email notification not created') |
424 | 51 | 51 | ||
425 | === modified file 'lib/lp/bugs/model/bugnotification.py' | |||
426 | --- lib/lp/bugs/model/bugnotification.py 2011-02-17 17:43:43 +0000 | |||
427 | +++ lib/lp/bugs/model/bugnotification.py 2011-02-17 17:43:44 +0000 | |||
428 | @@ -28,10 +28,12 @@ | |||
429 | 28 | 28 | ||
430 | 29 | from canonical.config import config | 29 | from canonical.config import config |
431 | 30 | from canonical.database.datetimecol import UtcDateTimeCol | 30 | from canonical.database.datetimecol import UtcDateTimeCol |
432 | 31 | from canonical.database.enumcol import EnumCol | ||
433 | 31 | from canonical.database.sqlbase import ( | 32 | from canonical.database.sqlbase import ( |
434 | 32 | SQLBase, | 33 | SQLBase, |
435 | 33 | sqlvalues, | 34 | sqlvalues, |
436 | 34 | ) | 35 | ) |
437 | 36 | from lp.bugs.enum import BugNotificationStatus | ||
438 | 35 | from lp.bugs.interfaces.bugnotification import ( | 37 | from lp.bugs.interfaces.bugnotification import ( |
439 | 36 | IBugNotification, | 38 | IBugNotification, |
440 | 37 | IBugNotificationRecipient, | 39 | IBugNotificationRecipient, |
441 | @@ -49,6 +51,10 @@ | |||
442 | 49 | bug = ForeignKey(dbName='bug', notNull=True, foreignKey='Bug') | 51 | bug = ForeignKey(dbName='bug', notNull=True, foreignKey='Bug') |
443 | 50 | is_comment = BoolCol(notNull=True) | 52 | is_comment = BoolCol(notNull=True) |
444 | 51 | date_emailed = UtcDateTimeCol(notNull=False) | 53 | date_emailed = UtcDateTimeCol(notNull=False) |
445 | 54 | status = EnumCol( | ||
446 | 55 | dbName='status', | ||
447 | 56 | schema=BugNotificationStatus, default=BugNotificationStatus.PENDING, | ||
448 | 57 | notNull=True) | ||
449 | 52 | 58 | ||
450 | 53 | @property | 59 | @property |
451 | 54 | def recipients(self): | 60 | def recipients(self): |
452 | 55 | 61 | ||
453 | === modified file 'lib/lp/bugs/scripts/bugnotification.py' | |||
454 | --- lib/lp/bugs/scripts/bugnotification.py 2011-02-17 17:43:43 +0000 | |||
455 | +++ lib/lp/bugs/scripts/bugnotification.py 2011-02-17 17:43:44 +0000 | |||
456 | @@ -97,6 +97,7 @@ | |||
457 | 97 | 97 | ||
458 | 98 | recipients = {} | 98 | recipients = {} |
459 | 99 | filtered_notifications = [] | 99 | filtered_notifications = [] |
460 | 100 | omitted_notifications = [] | ||
461 | 100 | for notification in bug_notifications: | 101 | for notification in bug_notifications: |
462 | 101 | key = get_activity_key(notification) | 102 | key = get_activity_key(notification) |
463 | 102 | if (notification.is_comment or | 103 | if (notification.is_comment or |
464 | @@ -111,6 +112,8 @@ | |||
465 | 111 | email_people.remove(actor) | 112 | email_people.remove(actor) |
466 | 112 | for email_person in email_people: | 113 | for email_person in email_people: |
467 | 113 | recipients[email_person] = recipient | 114 | recipients[email_person] = recipient |
468 | 115 | else: | ||
469 | 116 | omitted_notifications.append(notification) | ||
470 | 114 | 117 | ||
471 | 115 | if bug.duplicateof is not None: | 118 | if bug.duplicateof is not None: |
472 | 116 | text_notifications.append( | 119 | text_notifications.append( |
473 | @@ -203,7 +206,7 @@ | |||
474 | 203 | rationale, references, msgid) | 206 | rationale, references, msgid) |
475 | 204 | messages.append(msg) | 207 | messages.append(msg) |
476 | 205 | 208 | ||
478 | 206 | return filtered_notifications, messages | 209 | return filtered_notifications, omitted_notifications, messages |
479 | 207 | 210 | ||
480 | 208 | 211 | ||
481 | 209 | def notification_comment_batches(notifications): | 212 | def notification_comment_batches(notifications): |
482 | 210 | 213 | ||
483 | === modified file 'lib/lp/bugs/scripts/tests/test_bugnotification.py' | |||
484 | --- lib/lp/bugs/scripts/tests/test_bugnotification.py 2011-02-17 17:43:43 +0000 | |||
485 | +++ lib/lp/bugs/scripts/tests/test_bugnotification.py 2011-02-17 17:43:44 +0000 | |||
486 | @@ -248,7 +248,7 @@ | |||
487 | 248 | email_notifications = get_email_notifications(notifications_to_send) | 248 | email_notifications = get_email_notifications(notifications_to_send) |
488 | 249 | to_addresses = set() | 249 | to_addresses = set() |
489 | 250 | sent_notifications = [] | 250 | sent_notifications = [] |
491 | 251 | for notifications, messages in email_notifications: | 251 | for notifications, omitted, messages in email_notifications: |
492 | 252 | for message in messages: | 252 | for message in messages: |
493 | 253 | to_addresses.add(message['to']) | 253 | to_addresses.add(message['to']) |
494 | 254 | recipients = {} | 254 | recipients = {} |
495 | @@ -572,7 +572,9 @@ | |||
496 | 572 | def get_messages(self): | 572 | def get_messages(self): |
497 | 573 | notifications = self.notification_set.getNotificationsToSend() | 573 | notifications = self.notification_set.getNotificationsToSend() |
498 | 574 | email_notifications = get_email_notifications(notifications) | 574 | email_notifications = get_email_notifications(notifications) |
500 | 575 | for bug_notifications, messages in email_notifications: | 575 | for (bug_notifications, |
501 | 576 | omitted_notifications, | ||
502 | 577 | messages) in email_notifications: | ||
503 | 576 | for message in messages: | 578 | for message in messages: |
504 | 577 | yield message, message.get_payload(decode=True) | 579 | yield message, message.get_payload(decode=True) |
505 | 578 | 580 | ||
506 | 579 | 581 | ||
507 | === modified file 'lib/lp/bugs/tests/test_bugchanges.py' | |||
508 | --- lib/lp/bugs/tests/test_bugchanges.py 2011-02-17 17:43:43 +0000 | |||
509 | +++ lib/lp/bugs/tests/test_bugchanges.py 2011-02-17 17:43:44 +0000 | |||
510 | @@ -183,7 +183,8 @@ | |||
511 | 183 | 183 | ||
512 | 184 | def assertRecipients(self, expected_recipients): | 184 | def assertRecipients(self, expected_recipients): |
513 | 185 | notifications = self.getNewNotifications() | 185 | notifications = self.getNewNotifications() |
515 | 186 | notifications, messages = construct_email_notifications(notifications) | 186 | notifications, omitted, messages = construct_email_notifications( |
516 | 187 | notifications) | ||
517 | 187 | recipients = set(message['to'] for message in messages) | 188 | recipients = set(message['to'] for message in messages) |
518 | 188 | 189 | ||
519 | 189 | self.assertEqual( | 190 | self.assertEqual( |
Per our IRC discussion, I recommend providing a status enum, rather than just is_omitted. The values could include PENDING, SENT, SKIPPED.
I believe ResultSets support slicing, so it might be clearer in the doctest to get a ResultSet and then slice it.
However, the branch as it stands is a worthwhile improvement, so I'll approve it.