Merge lp:~gary/launchpad/bug164196-1 into lp:launchpad/db-devel

Proposed by Gary Poster
Status: Merged
Approved by: Gary Poster
Approved revision: no longer in the source branch.
Merged at revision: 10215
Proposed branch: lp:~gary/launchpad/bug164196-1
Merge into: lp:launchpad/db-devel
Diff against target: 293 lines (+50/-52)
11 files modified
database/schema/comments.sql (+1/-0)
database/schema/patch-2208-46-0.sql (+8/-0)
lib/lp/bugs/configure.zcml (+0/-6)
lib/lp/bugs/doc/bugnotifications.txt (+16/-0)
lib/lp/bugs/interfaces/bug.py (+5/-2)
lib/lp/bugs/interfaces/bugnotification.py (+6/-1)
lib/lp/bugs/model/bug.py (+6/-4)
lib/lp/bugs/model/bugnotification.py (+4/-2)
lib/lp/bugs/subscribers/bug.py (+0/-10)
lib/lp/bugs/subscribers/bugactivity.py (+2/-26)
lib/lp/bugs/tests/test_bugnotification.py (+2/-1)
To merge this branch: bzr merge lp:~gary/launchpad/bug164196-1
Reviewer Review Type Date Requested Status
Robert Collins (community) Approve
Stuart Bishop (community) db Approve
Данило Шеган (community) code Approve
Review via email: mp+49973@code.launchpad.net

Commit message

add a link from bug notifications (which are about sending emails) to bug activities (which are about keeping an activity log for each bug, and are more normalized in their description of what happened) as part of work towards bug 164196.

Description of the change

This is the first of three branches that address bug 164196, and one of two that have a database patch. The other two are lp:~gary/launchpad/bug164196-2 and lp:~gary/launchpad/bug164196-3. My pre-implementation call for these changes was with Graham Binns.

The primary job of this branch is to add a link from bug notifications (which are about sending emails) to bug activities (which are about keeping an activity log for each bug, and are more normalized in their description of what happened). This allows me in branch 2 to use the bug activity records to determine what was done and undone.

To do this, I added an activity column in the database, and made the code that generates bug notifications and bug activities synchronize the two. The first of those is straightforward, but the second has some wrinkles.

I'll highlight what I did for the second part of the task in bullet form.

 * In the set of bug notifications, I added an activity object to the signature of addNotification.
 * Bug target's addCommentNotification and addNotification include activities when they get them (from the bug change object in addNotification, and explicitly in addCommentNotification). Note for this bullet item and later ones that comment notifications do not need activities for my needs for this bug, but I looked at them for completeness.
 * I combined lp.bugs.subscribers.bug.notify_bug_added and lp.bugs.subscribers.bugactivity.record_bug_added into the latter, so that the notification could have a link to the activity.

In the course of doing this, I discovered that lp.bugs.subscribers.bugactivity.record_bug_edited duplicated a small part (only the 'name' attribute, as found in BUG_INTERESTING_FIELDS) of the functionality in lp.bugs.subscribers.bug.notify_bug_modified. They were both registered for the same event and had the same result (for the one attribute). Therefore, I deleted it from the file, deleted BUG_INTERESTING_FIELDS since it was the only user, and removed the zcml registration, and ran tests to confirm: tests passed.

Thank you

To post a comment you must log in.
Revision history for this message
Данило Шеган (danilo) wrote :

<danilos> gary_poster, re -1, in lib/lp/bugs/model/bug.py you modify addCommentNotification to add activity parameter, but you don't modify addChange which I believe does a similar thing for non-comment changes — is that intentional or not?
<gary_poster> danilos, intentional. addChange uses change objects, which handle activities themselves

review: Approve (code)
Revision history for this message
Stuart Bishop (stub) wrote :

Will be be looking up BugNotification records by BugActivity? If so, we will need an index:
   CREATE INDEX bugnotification__activity__idx ON BugNotification(activity);

I don't think the BugActivity information has been useful in the past. Perhaps this will give it some life?

patch-2208-46-0.sql

review: Approve (db)
Revision history for this message
Gary Poster (gary) wrote :

On Feb 17, 2011, at 7:17 AM, Stuart Bishop wrote:

> Will be be looking up BugNotification records by BugActivity? If so, we will need an index:
> CREATE INDEX bugnotification__activity__idx ON BugNotification(activity);
...

My branches are not doing that, and I don't know of a reason to do so, so I'll leave it out for now.

Thank you.

Gary

Revision history for this message
Данило Шеган (danilo) wrote :

Also, I just remembered one thing: we might need to add the column on BugNotificationArchive as well: not sure how important that is, but that table contains all notifications older than one month.

Revision history for this message
Gary Poster (gary) wrote :

On Feb 17, 2011, at 7:49 AM, Данило Шеган wrote:

> Also, I just remembered one thing: we might need to add the column on BugNotificationArchive as well: not sure how important that is, but that table contains all notifications older than one month.

Good thought. However, we have no use case for that at the moment, so I'm inclined to leave that alone until we do.

Revision history for this message
Robert Collins (lifeless) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'database/schema/comments.sql'
--- database/schema/comments.sql 2011-02-02 17:52:54 +0000
+++ database/schema/comments.sql 2011-02-17 17:41:21 +0000
@@ -280,6 +280,7 @@
280COMMENT ON COLUMN BugNotification.message IS 'The message the contains the textual representation of the change.';280COMMENT ON COLUMN BugNotification.message IS 'The message the contains the textual representation of the change.';
281COMMENT ON COLUMN BugNotification.is_comment IS 'Is the change a comment addition.';281COMMENT ON COLUMN BugNotification.is_comment IS 'Is the change a comment addition.';
282COMMENT ON COLUMN BugNotification.date_emailed IS 'When this notification was emailed to the bug subscribers.';282COMMENT ON COLUMN BugNotification.date_emailed IS 'When this notification was emailed to the bug subscribers.';
283COMMENT ON COLUMN BugNotification.activity IS 'The BugActivity record corresponding to this notification, if any.';
283284
284285
285-- BugNotificationAttachment286-- BugNotificationAttachment
286287
=== added file 'database/schema/patch-2208-46-0.sql'
--- database/schema/patch-2208-46-0.sql 1970-01-01 00:00:00 +0000
+++ database/schema/patch-2208-46-0.sql 2011-02-17 17:41:21 +0000
@@ -0,0 +1,8 @@
1-- Copyright 2011 Canonical Ltd. This software is licensed under the
2-- GNU Affero General Public License version 3 (see the file LICENSE).
3SET client_min_messages=ERROR;
4
5ALTER TABLE BugNotification
6 ADD COLUMN activity INTEGER REFERENCES BugActivity;
7
8INSERT INTO LaunchpadDatabaseRevision VALUES (2208, 46, 0);
09
=== modified file 'lib/lp/bugs/configure.zcml'
--- lib/lp/bugs/configure.zcml 2011-02-17 01:03:34 +0000
+++ lib/lp/bugs/configure.zcml 2011-02-17 17:41:21 +0000
@@ -58,9 +58,6 @@
58 handler="lp.bugs.subscribers.bugcreation.at_least_one_task"/>58 handler="lp.bugs.subscribers.bugcreation.at_least_one_task"/>
59 <subscriber59 <subscriber
60 for="lp.bugs.interfaces.bug.IBug lazr.lifecycle.interfaces.IObjectCreatedEvent"60 for="lp.bugs.interfaces.bug.IBug lazr.lifecycle.interfaces.IObjectCreatedEvent"
61 handler="lp.bugs.subscribers.bug.notify_bug_added"/>
62 <subscriber
63 for="lp.bugs.interfaces.bug.IBug lazr.lifecycle.interfaces.IObjectCreatedEvent"
64 handler="canonical.launchpad.subscribers.karma.bug_created"/>61 handler="canonical.launchpad.subscribers.karma.bug_created"/>
65 <subscriber62 <subscriber
66 for="lp.bugs.interfaces.bug.IBug lazr.lifecycle.interfaces.IObjectModifiedEvent"63 for="lp.bugs.interfaces.bug.IBug lazr.lifecycle.interfaces.IObjectModifiedEvent"
@@ -915,9 +912,6 @@
915 for="lp.bugs.interfaces.bug.IBug zope.lifecycleevent.interfaces.IObjectCreatedEvent"912 for="lp.bugs.interfaces.bug.IBug zope.lifecycleevent.interfaces.IObjectCreatedEvent"
916 handler="lp.bugs.subscribers.bugactivity.record_bug_added"/>913 handler="lp.bugs.subscribers.bugactivity.record_bug_added"/>
917 <subscriber914 <subscriber
918 for="lp.bugs.interfaces.bug.IBug lazr.lifecycle.interfaces.IObjectModifiedEvent"
919 handler="lp.bugs.subscribers.bugactivity.record_bug_edited"/>
920 <subscriber
921 for="lp.bugs.interfaces.bugcve.IBugCve lazr.lifecycle.interfaces.IObjectCreatedEvent"915 for="lp.bugs.interfaces.bugcve.IBugCve lazr.lifecycle.interfaces.IObjectCreatedEvent"
922 handler="lp.bugs.subscribers.bugactivity.record_cve_linked_to_bug"/>916 handler="lp.bugs.subscribers.bugactivity.record_cve_linked_to_bug"/>
923 <subscriber917 <subscriber
924918
=== modified file 'lib/lp/bugs/doc/bugnotifications.txt'
--- lib/lp/bugs/doc/bugnotifications.txt 2010-10-19 18:44:31 +0000
+++ lib/lp/bugs/doc/bugnotifications.txt 2011-02-17 17:41:21 +0000
@@ -51,6 +51,16 @@
51 >>> print latest_notification.message.text_contents51 >>> print latest_notification.message.text_contents
52 this is a comment52 this is a comment
5353
54Notifications usually have references to a corresponding bug activity. These
55can get you details on precisely what changed from a more programmatic
56perspective. You can get details on what the activity provides in
57bugactivity.txt, but for now here is a small demo.
58
59 >>> print latest_notification.activity.whatchanged
60 bug
61 >>> print latest_notification.activity.person.displayname
62 Sample Person
63
5464
55=== Editing a bug report ===65=== Editing a bug report ===
5666
@@ -72,6 +82,12 @@
72 >>> print latest_notification.message.text_contents82 >>> print latest_notification.message.text_contents
73 ** Description changed:83 ** Description changed:
74 ...84 ...
85 >>> print latest_notification.activity.attribute
86 description
87 >>> print latest_notification.activity.oldvalue
88 this is a comment
89 >>> print latest_notification.activity.newvalue
90 a new description
7591
7692
77=== Filing a new task on an existing bug ===93=== Filing a new task on an existing bug ===
7894
=== modified file 'lib/lp/bugs/interfaces/bug.py'
--- lib/lp/bugs/interfaces/bug.py 2011-02-17 01:03:34 +0000
+++ lib/lp/bugs/interfaces/bug.py 2011-02-17 17:41:21 +0000
@@ -546,8 +546,11 @@
546 True to include the master bug's subscribers as recipients.546 True to include the master bug's subscribers as recipients.
547 """547 """
548548
549 def addCommentNotification(message, recipients=None):549 def addCommentNotification(message, recipients=None, activity=None):
550 """Add a bug comment notification."""550 """Add a bug comment notification.
551
552 If a BugActivity instance is provided as an `activity`, it is linked
553 to the notification."""
551554
552 def addChange(change, recipients=None):555 def addChange(change, recipients=None):
553 """Record a change to the bug.556 """Record a change to the bug.
554557
=== modified file 'lib/lp/bugs/interfaces/bugnotification.py'
--- lib/lp/bugs/interfaces/bugnotification.py 2010-08-20 20:31:18 +0000
+++ lib/lp/bugs/interfaces/bugnotification.py 2011-02-17 17:41:21 +0000
@@ -34,6 +34,11 @@
34 message = Attribute(34 message = Attribute(
35 "The message containing the text representation of the changes"35 "The message containing the text representation of the changes"
36 " to the bug.")36 " to the bug.")
37 activity = Attribute(
38 "The bug activity object corresponding to this notification. Will "
39 "be None for older notification objects, and will be None if the "
40 "bugchange object that provides the data for the change returns None "
41 "for getBugActivity.")
37 bug = BugField(title=u"The bug this notification is for.",42 bug = BugField(title=u"The bug this notification is for.",
38 required=True)43 required=True)
39 is_comment = Bool(44 is_comment = Bool(
@@ -54,7 +59,7 @@
54 def getNotificationsToSend():59 def getNotificationsToSend():
55 """Returns the notifications pending to be sent."""60 """Returns the notifications pending to be sent."""
5661
57 def addNotification(self, bug, is_comment, message, recipients):62 def addNotification(self, bug, is_comment, message, recipients, activity):
58 """Create a new `BugNotification`.63 """Create a new `BugNotification`.
5964
60 Create a new `BugNotification` object and the corresponding65 Create a new `BugNotification` object and the corresponding
6166
=== modified file 'lib/lp/bugs/model/bug.py'
--- lib/lp/bugs/model/bug.py 2011-02-17 01:03:34 +0000
+++ lib/lp/bugs/model/bug.py 2011-02-17 17:41:21 +0000
@@ -1019,14 +1019,14 @@
1019 # original `Bug`.1019 # original `Bug`.
1020 return recipients1020 return recipients
10211021
1022 def addCommentNotification(self, message, recipients=None):1022 def addCommentNotification(self, message, recipients=None, activity=None):
1023 """See `IBug`."""1023 """See `IBug`."""
1024 if recipients is None:1024 if recipients is None:
1025 recipients = self.getBugNotificationRecipients(1025 recipients = self.getBugNotificationRecipients(
1026 level=BugNotificationLevel.COMMENTS)1026 level=BugNotificationLevel.COMMENTS)
1027 getUtility(IBugNotificationSet).addNotification(1027 getUtility(IBugNotificationSet).addNotification(
1028 bug=self, is_comment=True,1028 bug=self, is_comment=True,
1029 message=message, recipients=recipients)1029 message=message, recipients=recipients, activity=activity)
10301030
1031 def addChange(self, change, recipients=None):1031 def addChange(self, change, recipients=None):
1032 """See `IBug`."""1032 """See `IBug`."""
@@ -1036,12 +1036,14 @@
10361036
1037 activity_data = change.getBugActivity()1037 activity_data = change.getBugActivity()
1038 if activity_data is not None:1038 if activity_data is not None:
1039 getUtility(IBugActivitySet).new(1039 activity = getUtility(IBugActivitySet).new(
1040 self, when, change.person,1040 self, when, change.person,
1041 activity_data['whatchanged'],1041 activity_data['whatchanged'],
1042 activity_data.get('oldvalue'),1042 activity_data.get('oldvalue'),
1043 activity_data.get('newvalue'),1043 activity_data.get('newvalue'),
1044 activity_data.get('message'))1044 activity_data.get('message'))
1045 else:
1046 activity = None
10451047
1046 notification_data = change.getBugNotification()1048 notification_data = change.getBugNotification()
1047 if notification_data is not None:1049 if notification_data is not None:
@@ -1055,7 +1057,7 @@
1055 level=BugNotificationLevel.METADATA)1057 level=BugNotificationLevel.METADATA)
1056 getUtility(IBugNotificationSet).addNotification(1058 getUtility(IBugNotificationSet).addNotification(
1057 bug=self, is_comment=False, message=message,1059 bug=self, is_comment=False, message=message,
1058 recipients=recipients)1060 recipients=recipients, activity=activity)
10591061
1060 self.updateHeat()1062 self.updateHeat()
10611063
10621064
=== modified file 'lib/lp/bugs/model/bugnotification.py'
--- lib/lp/bugs/model/bugnotification.py 2010-08-20 20:31:18 +0000
+++ lib/lp/bugs/model/bugnotification.py 2011-02-17 17:41:21 +0000
@@ -44,6 +44,8 @@
44 implements(IBugNotification)44 implements(IBugNotification)
4545
46 message = ForeignKey(dbName='message', notNull=True, foreignKey='Message')46 message = ForeignKey(dbName='message', notNull=True, foreignKey='Message')
47 activity = ForeignKey(
48 dbName='activity', notNull=False, foreignKey='BugActivity')
47 bug = ForeignKey(dbName='bug', notNull=True, foreignKey='Bug')49 bug = ForeignKey(dbName='bug', notNull=True, foreignKey='Bug')
48 is_comment = BoolCol(notNull=True)50 is_comment = BoolCol(notNull=True)
49 date_emailed = UtcDateTimeCol(notNull=False)51 date_emailed = UtcDateTimeCol(notNull=False)
@@ -94,13 +96,13 @@
94 pending_notifications.reverse()96 pending_notifications.reverse()
95 return pending_notifications97 return pending_notifications
9698
97 def addNotification(self, bug, is_comment, message, recipients):99 def addNotification(self, bug, is_comment, message, recipients, activity):
98 """See `IBugNotificationSet`."""100 """See `IBugNotificationSet`."""
99 if not recipients:101 if not recipients:
100 return102 return
101 bug_notification = BugNotification(103 bug_notification = BugNotification(
102 bug=bug, is_comment=is_comment,104 bug=bug, is_comment=is_comment,
103 message=message, date_emailed=None)105 message=message, date_emailed=None, activity=activity)
104 store = Store.of(bug_notification)106 store = Store.of(bug_notification)
105 # XXX jamesh 2008-05-21: these flushes are to fix ordering107 # XXX jamesh 2008-05-21: these flushes are to fix ordering
106 # problems in the bugnotification-sending.txt tests.108 # problems in the bugnotification-sending.txt tests.
107109
=== modified file 'lib/lp/bugs/subscribers/bug.py'
--- lib/lp/bugs/subscribers/bug.py 2011-02-07 09:39:54 +0000
+++ lib/lp/bugs/subscribers/bug.py 2011-02-17 17:41:21 +0000
@@ -6,7 +6,6 @@
6 'add_bug_change_notifications',6 'add_bug_change_notifications',
7 'get_bug_delta',7 'get_bug_delta',
8 'get_bugtask_indirect_subscribers',8 'get_bugtask_indirect_subscribers',
9 'notify_bug_added',
10 'notify_bug_attachment_added',9 'notify_bug_attachment_added',
11 'notify_bug_attachment_removed',10 'notify_bug_attachment_removed',
12 'notify_bug_comment_added',11 'notify_bug_comment_added',
@@ -44,15 +43,6 @@
4443
4544
46@block_implicit_flushes45@block_implicit_flushes
47def notify_bug_added(bug, event):
48 """Send an email notification that a bug was added.
49
50 Event must be an IObjectCreatedEvent.
51 """
52 bug.addCommentNotification(bug.initial_message)
53
54
55@block_implicit_flushes
56def notify_bug_modified(bug, event):46def notify_bug_modified(bug, event):
57 """Handle bug change events.47 """Handle bug change events.
5848
5949
=== modified file 'lib/lp/bugs/subscribers/bugactivity.py'
--- lib/lp/bugs/subscribers/bugactivity.py 2011-01-28 10:03:12 +0000
+++ lib/lp/bugs/subscribers/bugactivity.py 2011-02-17 17:41:21 +0000
@@ -33,11 +33,6 @@
33vocabulary_registry = getVocabularyRegistry()33vocabulary_registry = getVocabularyRegistry()
3434
3535
36BUG_INTERESTING_FIELDS = [
37 'name',
38 ]
39
40
41def get_string_representation(obj):36def get_string_representation(obj):
42 """Returns a string representation of an object.37 """Returns a string representation of an object.
4338
@@ -100,32 +95,13 @@
10095
101@block_implicit_flushes96@block_implicit_flushes
102def record_bug_added(bug, object_created_event):97def record_bug_added(bug, object_created_event):
103 getUtility(IBugActivitySet).new(98 activity = getUtility(IBugActivitySet).new(
104 bug = bug.id,99 bug = bug.id,
105 datechanged = UTC_NOW,100 datechanged = UTC_NOW,
106 person = IPerson(object_created_event.user),101 person = IPerson(object_created_event.user),
107 whatchanged = "bug",102 whatchanged = "bug",
108 message = "added bug")103 message = "added bug")
109104 bug.addCommentNotification(bug.initial_message, activity=activity)
110
111@block_implicit_flushes
112def record_bug_edited(bug_edited, sqlobject_modified_event):
113 # If the event was triggered by a web service named operation, its
114 # edited_fields will be empty. We'll need to check all interesting
115 # fields to see which were actually changed.
116 sqlobject_modified_event.edited_fields = BUG_INTERESTING_FIELDS
117
118 changes = what_changed(sqlobject_modified_event)
119 for changed_field in changes:
120 oldvalue, newvalue = changes[changed_field]
121 getUtility(IBugActivitySet).new(
122 bug=bug_edited.id,
123 datechanged=UTC_NOW,
124 person=IPerson(sqlobject_modified_event.user),
125 whatchanged=changed_field,
126 oldvalue=oldvalue,
127 newvalue=newvalue,
128 message="")
129105
130106
131@block_implicit_flushes107@block_implicit_flushes
132108
=== modified file 'lib/lp/bugs/tests/test_bugnotification.py'
--- lib/lp/bugs/tests/test_bugnotification.py 2010-12-24 15:15:21 +0000
+++ lib/lp/bugs/tests/test_bugnotification.py 2011-02-17 17:41:21 +0000
@@ -157,7 +157,8 @@
157 message = MessageSet().fromText(157 message = MessageSet().fromText(
158 subject='subject', content='content')158 subject='subject', content='content')
159 BugNotificationSet().addNotification(159 BugNotificationSet().addNotification(
160 bug=bug, is_comment=False, message=message, recipients=[])160 bug=bug, is_comment=False, message=message, recipients=[],
161 activity=None)
161162
162163
163class TestNotificationsForDuplicates(TestCaseWithFactory):164class TestNotificationsForDuplicates(TestCaseWithFactory):

Subscribers

People subscribed via source and target branches

to status/vote changes: