Merge lp:~gmb/launchpad/refactor-bugnotification-recipient-rationales-bug-594208 into lp:launchpad
- refactor-bugnotification-recipient-rationales-bug-594208
- Merge into devel
Status: | Merged |
---|---|
Approved by: | Graham Binns |
Approved revision: | no longer in the source branch. |
Merged at revision: | 11040 |
Proposed branch: | lp:~gmb/launchpad/refactor-bugnotification-recipient-rationales-bug-594208 |
Merge into: | lp:launchpad |
Diff against target: |
418 lines (+173/-150) 8 files modified
lib/canonical/launchpad/mailnotification.py (+9/-144) lib/lp/bugs/configure.zcml (+1/-1) lib/lp/bugs/doc/bug-change.txt (+1/-1) lib/lp/bugs/doc/bugnotificationrecipients.txt (+1/-1) lib/lp/bugs/mail/bugnotificationrecipients.py (+158/-0) lib/lp/bugs/model/bug.py (+1/-1) lib/lp/bugs/scripts/tests/test_bugnotification.py (+1/-1) lib/lp/registry/doc/structural-subscriptions.txt (+1/-1) |
To merge this branch: | bzr merge lp:~gmb/launchpad/refactor-bugnotification-recipient-rationales-bug-594208 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jeroen T. Vermeulen (community) | code | Approve | |
Review via email: mp+27687@code.launchpad.net |
Commit message
BugNotification
Description of the change
This branch moves BugNotification
This turned out to be a much easier branch than I anticipated. I thought that some refactoring would be needed to make BugNotification
I've moved the class out of mailnotification.py and updated all the import sites I could find. I've added an XXX to mailnotification.py for the import of NotificationRec
Preview Diff
1 | === modified file 'lib/canonical/launchpad/mailnotification.py' | |||
2 | --- lib/canonical/launchpad/mailnotification.py 2010-06-10 10:54:51 +0000 | |||
3 | +++ lib/canonical/launchpad/mailnotification.py 2010-06-16 08:38:25 +0000 | |||
4 | @@ -32,9 +32,8 @@ | |||
5 | 32 | get_contact_email_addresses, get_email_template, shortlist) | 32 | get_contact_email_addresses, get_email_template, shortlist) |
6 | 33 | from canonical.launchpad.interfaces import ( | 33 | from canonical.launchpad.interfaces import ( |
7 | 34 | IEmailAddressSet, IHeldMessageDetails, ILaunchpadCelebrities, | 34 | IEmailAddressSet, IHeldMessageDetails, ILaunchpadCelebrities, |
11 | 35 | INotificationRecipientSet, IPerson, IPersonSet, ISpecification, | 35 | IPerson, IPersonSet, ISpecification, IStructuralSubscriptionTarget, |
12 | 36 | IStructuralSubscriptionTarget, ITeamMembershipSet, IUpstreamBugTask, | 36 | ITeamMembershipSet, IUpstreamBugTask, TeamMembershipStatus) |
10 | 37 | TeamMembershipStatus) | ||
13 | 38 | from lp.bugs.interfaces.bugchange import IBugChange | 37 | from lp.bugs.interfaces.bugchange import IBugChange |
14 | 39 | from canonical.launchpad.interfaces.launchpad import ILaunchpadRoot | 38 | from canonical.launchpad.interfaces.launchpad import ILaunchpadRoot |
15 | 40 | from canonical.launchpad.interfaces.message import ( | 39 | from canonical.launchpad.interfaces.message import ( |
16 | @@ -44,154 +43,20 @@ | |||
17 | 44 | from canonical.launchpad.mail import ( | 43 | from canonical.launchpad.mail import ( |
18 | 45 | sendmail, simple_sendmail, simple_sendmail_from_person, format_address) | 44 | sendmail, simple_sendmail, simple_sendmail_from_person, format_address) |
19 | 46 | from lp.services.mail.mailwrapper import MailWrapper | 45 | from lp.services.mail.mailwrapper import MailWrapper |
20 | 46 | from canonical.launchpad.webapp.publisher import canonical_url | ||
21 | 47 | from canonical.launchpad.webapp.url import urlappend | ||
22 | 48 | |||
23 | 49 | # XXX 2010-06-16 gmb bug=594985 | ||
24 | 50 | # This shouldn't be here, but if we take it out lots of things cry, | ||
25 | 51 | # which is sad. | ||
26 | 47 | from lp.services.mail.notificationrecipientset import ( | 52 | from lp.services.mail.notificationrecipientset import ( |
27 | 48 | NotificationRecipientSet) | 53 | NotificationRecipientSet) |
28 | 49 | from canonical.launchpad.webapp.publisher import canonical_url | ||
29 | 50 | from canonical.launchpad.webapp.url import urlappend | ||
30 | 51 | 54 | ||
31 | 55 | from lp.bugs.mail.bugnotificationrecipients import BugNotificationRecipients | ||
32 | 52 | 56 | ||
33 | 53 | CC = "CC" | 57 | CC = "CC" |
34 | 54 | 58 | ||
35 | 55 | 59 | ||
36 | 56 | class BugNotificationRecipients(NotificationRecipientSet): | ||
37 | 57 | """A set of emails and rationales notified for a bug change. | ||
38 | 58 | |||
39 | 59 | Each email address registered in a BugNotificationRecipients is | ||
40 | 60 | associated to a string and a header that explain why the address is | ||
41 | 61 | being emailed. For instance, if the email address is that of a | ||
42 | 62 | distribution bug supervisor for a bug, the string and header will make | ||
43 | 63 | that fact clear. | ||
44 | 64 | |||
45 | 65 | The string is meant to be rendered in the email footer. The header | ||
46 | 66 | is meant to be used in an X-Launchpad-Message-Rationale header. | ||
47 | 67 | |||
48 | 68 | The first rationale registered for an email address is the one | ||
49 | 69 | which will be used, regardless of other rationales being added | ||
50 | 70 | for it later. This gives us a predictable policy of preserving | ||
51 | 71 | the first reason added to the registry; the callsite should | ||
52 | 72 | ensure that the manipulation of the BugNotificationRecipients | ||
53 | 73 | instance is done in preferential order. | ||
54 | 74 | |||
55 | 75 | Instances of this class are meant to be returned by | ||
56 | 76 | IBug.getBugNotificationRecipients(). | ||
57 | 77 | """ | ||
58 | 78 | implements(INotificationRecipientSet) | ||
59 | 79 | |||
60 | 80 | def __init__(self, duplicateof=None): | ||
61 | 81 | """Constructs a new BugNotificationRecipients instance. | ||
62 | 82 | |||
63 | 83 | If this bug is a duplicate, duplicateof should be used to | ||
64 | 84 | specify which bug ID it is a duplicate of. | ||
65 | 85 | |||
66 | 86 | Note that there are two duplicate situations that are | ||
67 | 87 | important: | ||
68 | 88 | - One is when this bug is a duplicate of another bug: | ||
69 | 89 | the subscribers to the main bug get notified of our | ||
70 | 90 | changes. | ||
71 | 91 | - Another is when the bug we are changing has | ||
72 | 92 | duplicates; in that case, direct subscribers of | ||
73 | 93 | duplicate bugs get notified of our changes. | ||
74 | 94 | These two situations are catered respectively by the | ||
75 | 95 | duplicateof parameter above and the addDupeSubscriber method. | ||
76 | 96 | Don't confuse them! | ||
77 | 97 | """ | ||
78 | 98 | NotificationRecipientSet.__init__(self) | ||
79 | 99 | self.duplicateof = duplicateof | ||
80 | 100 | |||
81 | 101 | def _addReason(self, person, reason, header): | ||
82 | 102 | """Adds a reason (text and header) for a person. | ||
83 | 103 | |||
84 | 104 | It takes care of modifying the message when the person is notified | ||
85 | 105 | via a duplicate. | ||
86 | 106 | """ | ||
87 | 107 | if self.duplicateof is not None: | ||
88 | 108 | reason = reason + " (via bug %s)" % self.duplicateof.id | ||
89 | 109 | header = header + " via Bug %s" % self.duplicateof.id | ||
90 | 110 | reason = "You received this bug notification because you %s." % reason | ||
91 | 111 | self.add(person, reason, header) | ||
92 | 112 | |||
93 | 113 | def addDupeSubscriber(self, person): | ||
94 | 114 | """Registers a subscriber of a duplicate of this bug.""" | ||
95 | 115 | reason = "Subscriber of Duplicate" | ||
96 | 116 | if person.isTeam(): | ||
97 | 117 | text = ("are a member of %s, which is a subscriber " | ||
98 | 118 | "of a duplicate bug" % person.displayname) | ||
99 | 119 | reason += " @%s" % person.name | ||
100 | 120 | else: | ||
101 | 121 | text = "are a direct subscriber of a duplicate bug" | ||
102 | 122 | self._addReason(person, text, reason) | ||
103 | 123 | |||
104 | 124 | def addDirectSubscriber(self, person): | ||
105 | 125 | """Registers a direct subscriber of this bug.""" | ||
106 | 126 | reason = "Subscriber" | ||
107 | 127 | if person.isTeam(): | ||
108 | 128 | text = ("are a member of %s, which is a direct subscriber" | ||
109 | 129 | % person.displayname) | ||
110 | 130 | reason += " @%s" % person.name | ||
111 | 131 | else: | ||
112 | 132 | text = "are a direct subscriber of the bug" | ||
113 | 133 | self._addReason(person, text, reason) | ||
114 | 134 | |||
115 | 135 | def addAssignee(self, person): | ||
116 | 136 | """Registers an assignee of a bugtask of this bug.""" | ||
117 | 137 | reason = "Assignee" | ||
118 | 138 | if person.isTeam(): | ||
119 | 139 | text = ("are a member of %s, which is a bug assignee" | ||
120 | 140 | % person.displayname) | ||
121 | 141 | reason += " @%s" % person.name | ||
122 | 142 | else: | ||
123 | 143 | text = "are a bug assignee" | ||
124 | 144 | self._addReason(person, text, reason) | ||
125 | 145 | |||
126 | 146 | def addDistroBugSupervisor(self, person, distro): | ||
127 | 147 | """Registers a distribution bug supervisor for this bug.""" | ||
128 | 148 | reason = "Bug Supervisor (%s)" % distro.displayname | ||
129 | 149 | # All displaynames in these reasons should be changed to bugtargetname | ||
130 | 150 | # (as part of bug 113262) once bugtargetname is finalized for packages | ||
131 | 151 | # (bug 113258). Changing it before then would be excessively | ||
132 | 152 | # disruptive. | ||
133 | 153 | if person.isTeam(): | ||
134 | 154 | text = ("are a member of %s, which is the bug supervisor for %s" % | ||
135 | 155 | (person.displayname, distro.displayname)) | ||
136 | 156 | reason += " @%s" % person.name | ||
137 | 157 | else: | ||
138 | 158 | text = "are the bug supervisor for %s" % distro.displayname | ||
139 | 159 | self._addReason(person, text, reason) | ||
140 | 160 | |||
141 | 161 | def addStructuralSubscriber(self, person, target): | ||
142 | 162 | """Registers a structural subscriber to this bug's target.""" | ||
143 | 163 | reason = "Subscriber (%s)" % target.displayname | ||
144 | 164 | if person.isTeam(): | ||
145 | 165 | text = ("are a member of %s, which is subscribed to %s" % | ||
146 | 166 | (person.displayname, target.displayname)) | ||
147 | 167 | reason += " @%s" % person.name | ||
148 | 168 | else: | ||
149 | 169 | text = "are subscribed to %s" % target.displayname | ||
150 | 170 | self._addReason(person, text, reason) | ||
151 | 171 | |||
152 | 172 | def addUpstreamBugSupervisor(self, person, upstream): | ||
153 | 173 | """Registers an upstream bug supervisor for this bug.""" | ||
154 | 174 | reason = "Bug Supervisor (%s)" % upstream.displayname | ||
155 | 175 | if person.isTeam(): | ||
156 | 176 | text = ("are a member of %s, which is the bug supervisor for %s" % | ||
157 | 177 | (person.displayname, upstream.displayname)) | ||
158 | 178 | reason += " @%s" % person.name | ||
159 | 179 | else: | ||
160 | 180 | text = "are the bug supervisor for %s" % upstream.displayname | ||
161 | 181 | self._addReason(person, text, reason) | ||
162 | 182 | |||
163 | 183 | def addRegistrant(self, person, upstream): | ||
164 | 184 | """Registers an upstream product registrant for this bug.""" | ||
165 | 185 | reason = "Registrant (%s)" % upstream.displayname | ||
166 | 186 | if person.isTeam(): | ||
167 | 187 | text = ("are a member of %s, which is the registrant for %s" % | ||
168 | 188 | (person.displayname, upstream.displayname)) | ||
169 | 189 | reason += " @%s" % person.name | ||
170 | 190 | else: | ||
171 | 191 | text = "are the registrant for %s" % upstream.displayname | ||
172 | 192 | self._addReason(person, text, reason) | ||
173 | 193 | |||
174 | 194 | |||
175 | 195 | def format_rfc2822_date(date): | 60 | def format_rfc2822_date(date): |
176 | 196 | """Formats a date according to RFC2822's desires.""" | 61 | """Formats a date according to RFC2822's desires.""" |
177 | 197 | return formatdate(rfc822.mktime_tz(date.utctimetuple() + (0, ))) | 62 | return formatdate(rfc822.mktime_tz(date.utctimetuple() + (0, ))) |
178 | 198 | 63 | ||
179 | === modified file 'lib/lp/bugs/configure.zcml' | |||
180 | --- lib/lp/bugs/configure.zcml 2010-06-04 09:31:21 +0000 | |||
181 | +++ lib/lp/bugs/configure.zcml 2010-06-16 08:38:25 +0000 | |||
182 | @@ -952,7 +952,7 @@ | |||
183 | 952 | interface="lp.bugs.interfaces.bugnotification.IBugNotificationRecipient"/> | 952 | interface="lp.bugs.interfaces.bugnotification.IBugNotificationRecipient"/> |
184 | 953 | </class> | 953 | </class> |
185 | 954 | <class | 954 | <class |
187 | 955 | class="canonical.launchpad.mailnotification.BugNotificationRecipients"> | 955 | class="lp.bugs.mail.bugnotificationrecipients.BugNotificationRecipients"> |
188 | 956 | <allow | 956 | <allow |
189 | 957 | interface="canonical.launchpad.interfaces.INotificationRecipientSet"/> | 957 | interface="canonical.launchpad.interfaces.INotificationRecipientSet"/> |
190 | 958 | </class> | 958 | </class> |
191 | 959 | 959 | ||
192 | === modified file 'lib/lp/bugs/doc/bug-change.txt' | |||
193 | --- lib/lp/bugs/doc/bug-change.txt 2009-06-12 16:36:02 +0000 | |||
194 | +++ lib/lp/bugs/doc/bug-change.txt 2010-06-16 08:38:25 +0000 | |||
195 | @@ -54,7 +54,7 @@ | |||
196 | 54 | 54 | ||
197 | 55 | We'll create a test class that actually implements the methods we need. | 55 | We'll create a test class that actually implements the methods we need. |
198 | 56 | 56 | ||
200 | 57 | >>> from canonical.launchpad.mailnotification import ( | 57 | >>> from lp.bugs.mail.bugnotificationrecipients import ( |
201 | 58 | ... BugNotificationRecipients) | 58 | ... BugNotificationRecipients) |
202 | 59 | 59 | ||
203 | 60 | >>> example_message = factory.makeMessage(content="Hello, world") | 60 | >>> example_message = factory.makeMessage(content="Hello, world") |
204 | 61 | 61 | ||
205 | === modified file 'lib/lp/bugs/doc/bugnotificationrecipients.txt' | |||
206 | --- lib/lp/bugs/doc/bugnotificationrecipients.txt 2009-08-13 19:03:36 +0000 | |||
207 | +++ lib/lp/bugs/doc/bugnotificationrecipients.txt 2010-06-16 08:38:25 +0000 | |||
208 | @@ -63,7 +63,7 @@ | |||
209 | 63 | Let's up some data for our test: | 63 | Let's up some data for our test: |
210 | 64 | 64 | ||
211 | 65 | >>> from canonical.launchpad.interfaces import IPersonSet | 65 | >>> from canonical.launchpad.interfaces import IPersonSet |
213 | 66 | >>> from canonical.launchpad.mailnotification import ( | 66 | >>> from lp.bugs.mail.bugnotificationrecipients import ( |
214 | 67 | ... BugNotificationRecipients) | 67 | ... BugNotificationRecipients) |
215 | 68 | >>> debian = Distribution.selectOneBy(name="debian") | 68 | >>> debian = Distribution.selectOneBy(name="debian") |
216 | 69 | >>> pmount = debian.getSourcePackage("pmount") | 69 | >>> pmount = debian.getSourcePackage("pmount") |
217 | 70 | 70 | ||
218 | === added file 'lib/lp/bugs/mail/bugnotificationrecipients.py' | |||
219 | --- lib/lp/bugs/mail/bugnotificationrecipients.py 1970-01-01 00:00:00 +0000 | |||
220 | +++ lib/lp/bugs/mail/bugnotificationrecipients.py 2010-06-16 08:38:25 +0000 | |||
221 | @@ -0,0 +1,158 @@ | |||
222 | 1 | # Copyright 2010 Canonical Ltd. This software is licensed under the | ||
223 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | ||
224 | 3 | |||
225 | 4 | """Code for handling bug notification recipients in bug mail.""" | ||
226 | 5 | |||
227 | 6 | __metaclass__ = type | ||
228 | 7 | __all__ = [ | ||
229 | 8 | 'BugNotificationRecipients', | ||
230 | 9 | ] | ||
231 | 10 | |||
232 | 11 | from zope.interface import implements | ||
233 | 12 | |||
234 | 13 | from canonical.launchpad.interfaces import INotificationRecipientSet | ||
235 | 14 | |||
236 | 15 | from lp.services.mail.notificationrecipientset import ( | ||
237 | 16 | NotificationRecipientSet) | ||
238 | 17 | |||
239 | 18 | |||
240 | 19 | class BugNotificationRecipients(NotificationRecipientSet): | ||
241 | 20 | """A set of emails and rationales notified for a bug change. | ||
242 | 21 | |||
243 | 22 | Each email address registered in a BugNotificationRecipients is | ||
244 | 23 | associated to a string and a header that explain why the address is | ||
245 | 24 | being emailed. For instance, if the email address is that of a | ||
246 | 25 | distribution bug supervisor for a bug, the string and header will make | ||
247 | 26 | that fact clear. | ||
248 | 27 | |||
249 | 28 | The string is meant to be rendered in the email footer. The header | ||
250 | 29 | is meant to be used in an X-Launchpad-Message-Rationale header. | ||
251 | 30 | |||
252 | 31 | The first rationale registered for an email address is the one | ||
253 | 32 | which will be used, regardless of other rationales being added | ||
254 | 33 | for it later. This gives us a predictable policy of preserving | ||
255 | 34 | the first reason added to the registry; the callsite should | ||
256 | 35 | ensure that the manipulation of the BugNotificationRecipients | ||
257 | 36 | instance is done in preferential order. | ||
258 | 37 | |||
259 | 38 | Instances of this class are meant to be returned by | ||
260 | 39 | IBug.getBugNotificationRecipients(). | ||
261 | 40 | """ | ||
262 | 41 | implements(INotificationRecipientSet) | ||
263 | 42 | |||
264 | 43 | def __init__(self, duplicateof=None): | ||
265 | 44 | """Constructs a new BugNotificationRecipients instance. | ||
266 | 45 | |||
267 | 46 | If this bug is a duplicate, duplicateof should be used to | ||
268 | 47 | specify which bug ID it is a duplicate of. | ||
269 | 48 | |||
270 | 49 | Note that there are two duplicate situations that are | ||
271 | 50 | important: | ||
272 | 51 | - One is when this bug is a duplicate of another bug: | ||
273 | 52 | the subscribers to the main bug get notified of our | ||
274 | 53 | changes. | ||
275 | 54 | - Another is when the bug we are changing has | ||
276 | 55 | duplicates; in that case, direct subscribers of | ||
277 | 56 | duplicate bugs get notified of our changes. | ||
278 | 57 | These two situations are catered respectively by the | ||
279 | 58 | duplicateof parameter above and the addDupeSubscriber method. | ||
280 | 59 | Don't confuse them! | ||
281 | 60 | """ | ||
282 | 61 | NotificationRecipientSet.__init__(self) | ||
283 | 62 | self.duplicateof = duplicateof | ||
284 | 63 | |||
285 | 64 | def _addReason(self, person, reason, header): | ||
286 | 65 | """Adds a reason (text and header) for a person. | ||
287 | 66 | |||
288 | 67 | It takes care of modifying the message when the person is notified | ||
289 | 68 | via a duplicate. | ||
290 | 69 | """ | ||
291 | 70 | if self.duplicateof is not None: | ||
292 | 71 | reason = reason + " (via bug %s)" % self.duplicateof.id | ||
293 | 72 | header = header + " via Bug %s" % self.duplicateof.id | ||
294 | 73 | reason = "You received this bug notification because you %s." % reason | ||
295 | 74 | self.add(person, reason, header) | ||
296 | 75 | |||
297 | 76 | def addDupeSubscriber(self, person): | ||
298 | 77 | """Registers a subscriber of a duplicate of this bug.""" | ||
299 | 78 | reason = "Subscriber of Duplicate" | ||
300 | 79 | if person.isTeam(): | ||
301 | 80 | text = ("are a member of %s, which is a subscriber " | ||
302 | 81 | "of a duplicate bug" % person.displayname) | ||
303 | 82 | reason += " @%s" % person.name | ||
304 | 83 | else: | ||
305 | 84 | text = "are a direct subscriber of a duplicate bug" | ||
306 | 85 | self._addReason(person, text, reason) | ||
307 | 86 | |||
308 | 87 | def addDirectSubscriber(self, person): | ||
309 | 88 | """Registers a direct subscriber of this bug.""" | ||
310 | 89 | reason = "Subscriber" | ||
311 | 90 | if person.isTeam(): | ||
312 | 91 | text = ("are a member of %s, which is a direct subscriber" | ||
313 | 92 | % person.displayname) | ||
314 | 93 | reason += " @%s" % person.name | ||
315 | 94 | else: | ||
316 | 95 | text = "are a direct subscriber of the bug" | ||
317 | 96 | self._addReason(person, text, reason) | ||
318 | 97 | |||
319 | 98 | def addAssignee(self, person): | ||
320 | 99 | """Registers an assignee of a bugtask of this bug.""" | ||
321 | 100 | reason = "Assignee" | ||
322 | 101 | if person.isTeam(): | ||
323 | 102 | text = ("are a member of %s, which is a bug assignee" | ||
324 | 103 | % person.displayname) | ||
325 | 104 | reason += " @%s" % person.name | ||
326 | 105 | else: | ||
327 | 106 | text = "are a bug assignee" | ||
328 | 107 | self._addReason(person, text, reason) | ||
329 | 108 | |||
330 | 109 | def addDistroBugSupervisor(self, person, distro): | ||
331 | 110 | """Registers a distribution bug supervisor for this bug.""" | ||
332 | 111 | reason = "Bug Supervisor (%s)" % distro.displayname | ||
333 | 112 | # All displaynames in these reasons should be changed to bugtargetname | ||
334 | 113 | # (as part of bug 113262) once bugtargetname is finalized for packages | ||
335 | 114 | # (bug 113258). Changing it before then would be excessively | ||
336 | 115 | # disruptive. | ||
337 | 116 | if person.isTeam(): | ||
338 | 117 | text = ("are a member of %s, which is the bug supervisor for %s" % | ||
339 | 118 | (person.displayname, distro.displayname)) | ||
340 | 119 | reason += " @%s" % person.name | ||
341 | 120 | else: | ||
342 | 121 | text = "are the bug supervisor for %s" % distro.displayname | ||
343 | 122 | self._addReason(person, text, reason) | ||
344 | 123 | |||
345 | 124 | def addStructuralSubscriber(self, person, target): | ||
346 | 125 | """Registers a structural subscriber to this bug's target.""" | ||
347 | 126 | reason = "Subscriber (%s)" % target.displayname | ||
348 | 127 | if person.isTeam(): | ||
349 | 128 | text = ("are a member of %s, which is subscribed to %s" % | ||
350 | 129 | (person.displayname, target.displayname)) | ||
351 | 130 | reason += " @%s" % person.name | ||
352 | 131 | else: | ||
353 | 132 | text = "are subscribed to %s" % target.displayname | ||
354 | 133 | self._addReason(person, text, reason) | ||
355 | 134 | |||
356 | 135 | def addUpstreamBugSupervisor(self, person, upstream): | ||
357 | 136 | """Registers an upstream bug supervisor for this bug.""" | ||
358 | 137 | reason = "Bug Supervisor (%s)" % upstream.displayname | ||
359 | 138 | if person.isTeam(): | ||
360 | 139 | text = ("are a member of %s, which is the bug supervisor for %s" % | ||
361 | 140 | (person.displayname, upstream.displayname)) | ||
362 | 141 | reason += " @%s" % person.name | ||
363 | 142 | else: | ||
364 | 143 | text = "are the bug supervisor for %s" % upstream.displayname | ||
365 | 144 | self._addReason(person, text, reason) | ||
366 | 145 | |||
367 | 146 | def addRegistrant(self, person, upstream): | ||
368 | 147 | """Registers an upstream product registrant for this bug.""" | ||
369 | 148 | reason = "Registrant (%s)" % upstream.displayname | ||
370 | 149 | if person.isTeam(): | ||
371 | 150 | text = ("are a member of %s, which is the registrant for %s" % | ||
372 | 151 | (person.displayname, upstream.displayname)) | ||
373 | 152 | reason += " @%s" % person.name | ||
374 | 153 | else: | ||
375 | 154 | text = "are the registrant for %s" % upstream.displayname | ||
376 | 155 | self._addReason(person, text, reason) | ||
377 | 156 | |||
378 | 157 | |||
379 | 158 | |||
380 | 0 | 159 | ||
381 | === modified file 'lib/lp/bugs/model/bug.py' | |||
382 | --- lib/lp/bugs/model/bug.py 2010-06-10 18:55:22 +0000 | |||
383 | +++ lib/lp/bugs/model/bug.py 2010-06-16 08:38:25 +0000 | |||
384 | @@ -58,7 +58,7 @@ | |||
385 | 58 | IMessage, IndexedMessage) | 58 | IMessage, IndexedMessage) |
386 | 59 | from lp.registry.interfaces.structuralsubscription import ( | 59 | from lp.registry.interfaces.structuralsubscription import ( |
387 | 60 | BugNotificationLevel, IStructuralSubscriptionTarget) | 60 | BugNotificationLevel, IStructuralSubscriptionTarget) |
389 | 61 | from canonical.launchpad.mailnotification import BugNotificationRecipients | 61 | from lp.bugs.mail.bugnotificationrecipients import BugNotificationRecipients |
390 | 62 | from canonical.launchpad.validators import LaunchpadValidationError | 62 | from canonical.launchpad.validators import LaunchpadValidationError |
391 | 63 | from canonical.launchpad.webapp.interfaces import ( | 63 | from canonical.launchpad.webapp.interfaces import ( |
392 | 64 | IStoreSelector, DEFAULT_FLAVOR, MAIN_STORE, NotFoundError) | 64 | IStoreSelector, DEFAULT_FLAVOR, MAIN_STORE, NotFoundError) |
393 | 65 | 65 | ||
394 | === modified file 'lib/lp/bugs/scripts/tests/test_bugnotification.py' | |||
395 | --- lib/lp/bugs/scripts/tests/test_bugnotification.py 2010-01-12 21:12:12 +0000 | |||
396 | +++ lib/lp/bugs/scripts/tests/test_bugnotification.py 2010-06-16 08:38:25 +0000 | |||
397 | @@ -21,7 +21,7 @@ | |||
398 | 21 | from lp.bugs.interfaces.bug import IBug, IBugSet | 21 | from lp.bugs.interfaces.bug import IBug, IBugSet |
399 | 22 | from lp.registry.interfaces.person import IPersonSet | 22 | from lp.registry.interfaces.person import IPersonSet |
400 | 23 | from lp.registry.interfaces.product import IProductSet | 23 | from lp.registry.interfaces.product import IProductSet |
402 | 24 | from canonical.launchpad.mailnotification import BugNotificationRecipients | 24 | from lp.bugs.mail.bugnotificationrecipients import BugNotificationRecipients |
403 | 25 | from lp.bugs.scripts.bugnotification import ( | 25 | from lp.bugs.scripts.bugnotification import ( |
404 | 26 | get_email_notifications) | 26 | get_email_notifications) |
405 | 27 | from canonical.testing import LaunchpadZopelessLayer | 27 | from canonical.testing import LaunchpadZopelessLayer |
406 | 28 | 28 | ||
407 | === modified file 'lib/lp/registry/doc/structural-subscriptions.txt' | |||
408 | --- lib/lp/registry/doc/structural-subscriptions.txt 2010-02-20 14:16:37 +0000 | |||
409 | +++ lib/lp/registry/doc/structural-subscriptions.txt 2010-06-16 08:38:25 +0000 | |||
410 | @@ -86,7 +86,7 @@ | |||
411 | 86 | >>> from canonical.launchpad.ftests import syncUpdate | 86 | >>> from canonical.launchpad.ftests import syncUpdate |
412 | 87 | >>> from canonical.launchpad.interfaces import ( | 87 | >>> from canonical.launchpad.interfaces import ( |
413 | 88 | ... BlueprintNotificationLevel, BugNotificationLevel) | 88 | ... BlueprintNotificationLevel, BugNotificationLevel) |
415 | 89 | >>> from canonical.launchpad.mailnotification import ( | 89 | >>> from lp.bugs.mail.bugnotificationrecipients import ( |
416 | 90 | ... BugNotificationRecipients) | 90 | ... BugNotificationRecipients) |
417 | 91 | 91 | ||
418 | 92 | We define some utility functions for printing out bug subscriptions and | 92 | We define some utility functions for printing out bug subscriptions and |
Fine with me. As discussed on IRC, there's plenty more work to be done on the code that's moving (dealing with supervisors of duplicate bugs, factoring out the repetition in text composition, replacing reason = reason + x with reason += x) but as you point out that's best left to later branches after first moving the code out of conflicts' way.