Merge lp:~gary/launchpad/refactoractivitylog into lp:launchpad

Proposed by Gary Poster
Status: Merged
Approved by: Gary Poster
Approved revision: no longer in the source branch.
Merged at revision: 12367
Proposed branch: lp:~gary/launchpad/refactoractivitylog
Merge into: lp:launchpad
Diff against target: 341 lines (+104/-94)
4 files modified
lib/lp/bugs/browser/bugtask.py (+0/-38)
lib/lp/bugs/doc/bugactivity.txt (+52/-56)
lib/lp/bugs/interfaces/bugactivity.py (+14/-0)
lib/lp/bugs/model/bugactivity.py (+38/-0)
To merge this branch: bzr merge lp:~gary/launchpad/refactoractivitylog
Reviewer Review Type Date Requested Status
Brad Crittenden (community) code Approve
Benji York (community) code* Approve
Review via email: mp+49444@code.launchpad.net

Commit message

[r=bac,benji][no-qa] move some code, which lets you look at bug activity log entires more precisely, from browser code to model code. This is a step along the path to addressing bug 164196.

Description of the change

This branch simply moves some code that lets you look at bug activity log entires more precisely from browser code to model code. It is a step along the path to addressing bug 164196.

pre-imp with gmb.

Lint is happy.

This is an incremental branch of internal code-organization changes, and no QA is necessary.

Thank you.

To post a comment you must log in.
Revision history for this message
Benji York (benji) wrote :

This branch looks good. It's a mostly mechanical translation with a
nice addition to documentation/testing. Approved* (*Brad will have to
review my review.)

One small thing I noticed: in lib/lp/bugs/interfaces/bugactivity.py the
description arguments of the two moved attributes (target and attribute)
use different quotes (single vs. double) and indentation (just 4 spaces
for target and fancy indentation for attribute).

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

Thank you.

I adjusted the spacing.

For single and double quotes, I generally prefer single except when I need to use double, and single are generally used in this file. I needed to use double for "attribute" because there is a single quote in the string acting as an apostrophe. I'll leave that as it is.

Revision history for this message
Brad Crittenden (bac) wrote :

Good branch and review.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/bugs/browser/bugtask.py'
2--- lib/lp/bugs/browser/bugtask.py 2011-02-03 16:35:23 +0000
3+++ lib/lp/bugs/browser/bugtask.py 2011-02-11 19:21:49 +0000
4@@ -3801,48 +3801,10 @@
5 """A decorated BugActivity."""
6 delegates(IBugActivity, 'activity')
7
8- # The regular expression we use for matching bug task changes.
9- bugtask_change_re = re.compile(
10- '(?P<target>[a-z0-9][a-z0-9\+\.\-]+( \([A-Za-z0-9\s]+\))?): '
11- '(?P<attribute>assignee|importance|milestone|status)')
12-
13 def __init__(self, activity):
14 self.activity = activity
15
16 @property
17- def target(self):
18- """Return the target of this BugActivityItem.
19-
20- `target` is determined based on the `whatchanged` string of the
21- original BugAcitivity.
22-
23- :return: The target name of the item if `whatchanged` is of the
24- form <target_name>: <attribute>. Otherwise, return None.
25- """
26- match = self.bugtask_change_re.match(self.whatchanged)
27- if match is None:
28- return None
29- else:
30- return match.groupdict()['target']
31-
32- @property
33- def attribute(self):
34- """Return the attribute changed in this BugActivityItem.
35-
36- `attribute` is determined based on the `whatchanged` string of the
37- original BugAcitivity.
38-
39- :return: The attribute name of the item if `whatchanged` is of
40- the form <target_name>: <attribute>. Otherwise, return the
41- original `whatchanged` string.
42- """
43- match = self.bugtask_change_re.match(self.whatchanged)
44- if match is None:
45- return self.whatchanged
46- else:
47- return match.groupdict()['attribute']
48-
49- @property
50 def change_summary(self):
51 """Return a formatted summary of the change."""
52 return self.attribute
53
54=== modified file 'lib/lp/bugs/doc/bugactivity.txt'
55--- lib/lp/bugs/doc/bugactivity.txt 2010-10-19 18:44:31 +0000
56+++ lib/lp/bugs/doc/bugactivity.txt 2011-02-11 19:21:49 +0000
57@@ -1,8 +1,10 @@
58-= Bug Activity =
59+Bug Activity
60+~~~~~~~~~~~~
61
62-Bugs are problems that occur in software. In Malone, various things can be
63-added to, edited and removed from a bug over the course of a bug's lifetime. We
64-call this stuff bug activity. This document is about bug activity.
65+Bugs are problems that occur in software. In Malone, various things can
66+be added to, edited and removed from a bug over the course of a bug's
67+lifetime. We call this stuff bug activity. This document is about bug
68+activity.
69
70 Each activity can happen more than once to a bug over the course of its
71 lifetime. For auditing reasons, it's useful to track when these happen, why
72@@ -20,8 +22,7 @@
73 are simple little functions.
74
75 >>> from zope.event import notify
76- >>> from lazr.lifecycle.event import (
77- ... ObjectCreatedEvent, ObjectModifiedEvent)
78+ >>> from lazr.lifecycle.event import ObjectModifiedEvent
79 >>> from lazr.lifecycle.snapshot import Snapshot
80 >>> from lp.bugs.interfaces.bugtask import (
81 ... IBugTaskSet,
82@@ -32,7 +33,8 @@
83 >>> user = getUtility(ILaunchBag).user
84
85
86-== User files a bug ==
87+User files a bug
88+================
89
90 >>> from lp.bugs.interfaces.bug import CreateBugParams
91 >>> firefox = getUtility(IProductSet)['firefox']
92@@ -50,7 +52,8 @@
93 u'added bug'
94
95
96-== Bug title edited ==
97+Bug title edited
98+================
99
100 >>> from lp.bugs.interfaces.bug import IBug
101 >>> old_state = Snapshot(bug, providing=IBug)
102@@ -67,12 +70,14 @@
103 u'new bug title'
104
105
106-== Source package assignment edited ==
107+Source package assignment edited
108+================================
109
110 >>> from lazr.lifecycle.snapshot import Snapshot
111 >>> from lp.bugs.interfaces.bugtask import BugTaskStatus
112 >>> from lp.registry.interfaces.distribution import IDistributionSet
113- >>> from lp.registry.interfaces.sourcepackagename import ISourcePackageNameSet
114+ >>> from lp.registry.interfaces.sourcepackagename import (
115+ ... ISourcePackageNameSet)
116 >>> mozilla_firefox = getUtility(ISourcePackageNameSet)['mozilla-firefox']
117 >>> ubuntu = getUtility(IDistributionSet).getByName('ubuntu')
118 >>> source_package_assignment = getUtility(IBugTaskSet).createTask(
119@@ -97,8 +102,27 @@
120 >>> latest_activity.newvalue == BugTaskStatus.CONFIRMED.title
121 True
122
123-
124-== Upstream product assignment edited ==
125+You will notice that the `whatchanged` attribute in this case specifies
126+a project and an attribute. This happens when the change is to a bugtask.
127+The activity object object provides a couple of simple attributes to separate
128+out these values: `target` and `attribute`.
129+
130+ >>> latest_activity.target
131+ u'mozilla-firefox (Ubuntu)'
132+ >>> latest_activity.attribute
133+ u'status'
134+
135+If the activity is not for a bug task, `target` is None, and `attribute` is
136+the same as `whatchanged`. For instance, look at the attributes on the
137+previous activity.
138+
139+ >>> print bug.activity[-2].target
140+ None
141+ >>> bug.activity[-2].attribute
142+ u'summary'
143+
144+Upstream product assignment edited
145+==================================
146
147 >>> product_assignment = getUtility(IBugTaskSet).createTask(
148 ... bug=bug, product=getUtility(IProductSet)['thunderbird'], owner=user)
149@@ -115,13 +139,18 @@
150 >>> latest_activity = bug.activity[-1]
151 >>> latest_activity.whatchanged
152 u'thunderbird: status'
153+ >>> latest_activity.target
154+ u'thunderbird'
155+ >>> latest_activity.attribute
156+ u'status'
157 >>> latest_activity.oldvalue == BugTaskStatus.NEW.title
158 True
159 >>> latest_activity.newvalue == BugTaskStatus.INVALID.title
160 True
161
162
163-== Bug report is marked as a duplicate of another bug report ==
164+Bug report is marked as a duplicate of another bug report
165+=========================================================
166
167 >>> edit_fields = [
168 ... "id", "title", "description", "name",
169@@ -140,7 +169,8 @@
170 True
171
172
173-== Bug report has its duplicate marker changed to another bug report ==
174+Bug report has its duplicate marker changed to another bug report
175+=================================================================
176
177 >>> edit_fields = [
178 ... "id", "title", "description", "name", "private", "duplicateof",
179@@ -159,7 +189,8 @@
180 True
181
182
183-== The bug report is un-duplicated ==
184+The bug report is un-duplicated
185+===============================
186
187 >>> edit_fields = [
188 ... "id", "title", "description", "name", "private", "duplicateof",
189@@ -177,7 +208,8 @@
190 True
191
192
193-== A bug with multiple duplicates ==
194+A bug with multiple duplicates
195+==============================
196
197 When a bug has multiple duplicates and is itself marked a duplicate,
198 the duplicates are automatically duped to the same master bug. These changes
199@@ -217,7 +249,8 @@
200 True
201
202
203-== BugActivityItem ==
204+BugActivityItem
205+===============
206
207 BugActivityItem implements the stuff that BugActivity doesn't need to
208 know about.
209@@ -299,8 +332,8 @@
210 added: tag3<br />removed: tag2
211
212 For changes to bug tasks, BugActivityItem returns the name of the attribute
213-that was changed (see "Determining the target and attribute of a
214-change", below).
215+that was changed (using the `attribute` property on the bug activity
216+discussed above).
217
218 >>> activity = getUtility(IBugActivitySet).new(
219 ... bug=bug_one, whatchanged='malone: status', oldvalue='New',
220@@ -344,40 +377,3 @@
221 >>> print "%s: %s" % (
222 ... activity_item.change_summary, activity_item.change_details)
223 description: updated
224-
225-
226-=== Determining the target and attribute of a change ===
227-
228-Some changes - specifically changes to a BugTask - are listed in
229-BugActivity as targeted changes, i.e. their `whatchanged` string will be
230-of the form "<target>: <attribute>". BugActivityItem offers two
231-properties by which to extract these.
232-
233-BugActivityItem.target will return the <target> part of the whatchanged
234-string or None if the BugActivity is not targeted to a BugTask.
235-
236- >>> activity = getUtility(IBugActivitySet).new(
237- ... bug=bug_one, whatchanged='malone: assignee',
238- ... oldvalue='nobody', newvalue='New Assignee',
239- ... person=user, datechanged=nowish)
240- >>> targeted_activity_item = BugActivityItem(activity)
241- >>> print targeted_activity_item.target
242- malone
243-
244- >>> activity = getUtility(IBugActivitySet).new(
245- ... bug=bug_one, whatchanged='description',
246- ... oldvalue='Old description', newvalue='New description',
247- ... person=user, datechanged=nowish)
248- >>> untargeted_activity_item = BugActivityItem(activity)
249- >>> print untargeted_activity_item.target
250- None
251-
252-BugActivityItem.attribute will return the <attribute> part of the
253-whatchanged string if the activity is targeted to a BugTask. Otherwise
254-it will simply return the whole whatchanged string.
255-
256- >>> print targeted_activity_item.attribute
257- assignee
258-
259- >>> print untargeted_activity_item.attribute
260- description
261
262=== modified file 'lib/lp/bugs/interfaces/bugactivity.py'
263--- lib/lp/bugs/interfaces/bugactivity.py 2010-09-20 19:23:08 +0000
264+++ lib/lp/bugs/interfaces/bugactivity.py 2011-02-11 19:21:49 +0000
265@@ -54,6 +54,20 @@
266 description=_("The property of the bug that changed."),
267 readonly=True))
268
269+ target = TextLine(
270+ title=_('Change Target'), required=False, readonly=True,
271+ description=_(
272+ 'The target of what changed, if the change occurred on a '
273+ 'bugtask.'))
274+
275+ attribute = TextLine(
276+ title=_('Changed Attribute'), required=True, readonly=True,
277+ description=_(
278+ "The attribute that changed. If the change occurred on a "
279+ "bugtask, this will be the bugtask's attribute; otherwise "
280+ "it will be the bug attribute, and the same as 'what "
281+ "changed'."))
282+
283 oldvalue = exported(
284 TextLine(title=_('Old Value'),
285 description=_("The value before the change."),
286
287=== modified file 'lib/lp/bugs/model/bugactivity.py'
288--- lib/lp/bugs/model/bugactivity.py 2010-08-20 20:31:18 +0000
289+++ lib/lp/bugs/model/bugactivity.py 2011-02-11 19:21:49 +0000
290@@ -6,6 +6,8 @@
291 __metaclass__ = type
292 __all__ = ['BugActivity', 'BugActivitySet']
293
294+import re
295+
296 from sqlobject import (
297 ForeignKey,
298 StringCol,
299@@ -39,6 +41,42 @@
300 newvalue = StringCol(default=None)
301 message = StringCol(default=None)
302
303+ # The regular expression we use for matching bug task changes.
304+ bugtask_change_re = re.compile(
305+ '(?P<target>[a-z0-9][a-z0-9\+\.\-]+( \([A-Za-z0-9\s]+\))?): '
306+ '(?P<attribute>assignee|importance|milestone|status)')
307+
308+ @property
309+ def target(self):
310+ """Return the target of this BugActivityItem.
311+
312+ `target` is determined based on the `whatchanged` string.
313+
314+ :return: The target name of the item if `whatchanged` is of the
315+ form <target_name>: <attribute>. Otherwise, return None.
316+ """
317+ match = self.bugtask_change_re.match(self.whatchanged)
318+ if match is None:
319+ return None
320+ else:
321+ return match.groupdict()['target']
322+
323+ @property
324+ def attribute(self):
325+ """Return the attribute changed in this BugActivityItem.
326+
327+ `attribute` is determined based on the `whatchanged` string.
328+
329+ :return: The attribute name of the item if `whatchanged` is of
330+ the form <target_name>: <attribute>. Otherwise, return the
331+ original `whatchanged` string.
332+ """
333+ match = self.bugtask_change_re.match(self.whatchanged)
334+ if match is None:
335+ return self.whatchanged
336+ else:
337+ return match.groupdict()['attribute']
338+
339
340 class BugActivitySet:
341 """See IBugActivitySet."""