Merge lp:~thumper/launchpad/revision-karma-fix into lp:launchpad
- revision-karma-fix
- Merge into devel
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Tim Penhey | ||||
Approved revision: | not available | ||||
Merged at revision: | 10105 | ||||
Proposed branch: | lp:~thumper/launchpad/revision-karma-fix | ||||
Merge into: | lp:launchpad | ||||
Diff against target: |
279 lines (+78/-31) 8 files modified
lib/lp/code/interfaces/branchtarget.py (+1/-1) lib/lp/code/model/branchtarget.py (+9/-6) lib/lp/code/model/revision.py (+9/-9) lib/lp/code/model/tests/test_revision.py (+44/-9) lib/lp/code/scripts/revisionkarma.py (+3/-2) lib/lp/registry/interfaces/person.py (+4/-1) lib/lp/registry/model/person.py (+5/-2) lib/lp/testing/factory.py (+3/-1) |
||||
To merge this branch: | bzr merge lp:~thumper/launchpad/revision-karma-fix | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jonathan Lange (community) | Needs Fixing | ||
Michael Hudson-Doyle | Approve | ||
Review via email: mp+16825@code.launchpad.net |
Commit message
Fix the allocation of karma for revisions
Description of the change
Tim Penhey (thumper) wrote : | # |
Michael Hudson-Doyle (mwhudson) wrote : | # |
Generally, I like the changes.
> === modified file 'lib/lp/
> --- lib/lp/
> +++ lib/lp/
> @@ -86,10 +86,8 @@
> """See `IRevision`."""
> # If we know who the revision author is, give them karma.
> author = self.revision_
> - if (author is not None and branch.product is not None):
> - # No karma for junk branches as we need a product to link
> - # against.
> - karma = author.
> + if (author is not None):
These parens can go now.
> + karma = branch.
> # Backdate the karma to the time the revision was created. If the
> # revision_date on the revision is in future (for whatever weird
> # reason) we will use the date_created from the revision (which
> @@ -98,8 +96,12 @@
> # is lying), and a problem with the way the Launchpad code
> # currently does its karma degradation over time.
> if karma is not None:
> - karma.datecreated = min(self.
> + removeSecurityP
> + self.revision_date, self.date_created)
> self.karma_
> + return karma
> + else:
> + return None
>
> def getBranch(self, allow_private=
> """See `IRevision`."""
> @@ -385,9 +387,6 @@
> from lp.registry.
>
> store = getUtility(
> -
> - # XXX: Tim Penhey 2008-08-12, bug 244768
> - # Using Not(column == None) rather than column != None.
> return store.find(
> Revision,
> Revision.
> @@ -397,7 +396,8 @@
> Select(True,
> And(BranchRevis
> BranchRevision.
> - Not(Branch.product == None)),
> + Or(Branch.product != None,
> + Branch.distroseries != None)),
> (Branch, BranchRevision))))
>
> @staticmethod
> === modified file 'lib/lp/
> --- lib/lp/
> +++ lib/lp/
> @@ -39,8 +39,9 @@
> count = 0
> revision_set = getUtility(
> # Break into bits.
> - revisions = list(
> - revision_
> + result_set = revision_
> + self.logger.
> + revisions = list(result_
> while len(revisions) > 0:
> for revision in revisions:
> # Find t...
Jonathan Lange (jml) wrote : | # |
> === modified file 'lib/lp/
> --- lib/lp/
> +++ lib/lp/
...
> @@ -98,8 +96,12 @@
> # is lying), and a problem with the way the Launchpad code
> # currently does its karma degradation over time.
> if karma is not None:
> - karma.datecreated = min(self.
> + removeSecurityP
> + self.revision_date, self.date_created)
> self.karma_
> + return karma
> + else:
> + return None
>
Like Michael, I generally like the changes. However, I'm pretty sure we should avoid using removeSecurityProxy in model code. If we have no other choice, then we should at least have a comment explaining why.
Preview Diff
1 | === modified file 'lib/lp/code/interfaces/branchtarget.py' |
2 | --- lib/lp/code/interfaces/branchtarget.py 2009-08-04 00:41:49 +0000 |
3 | +++ lib/lp/code/interfaces/branchtarget.py 2010-01-05 08:21:19 +0000 |
4 | @@ -108,7 +108,7 @@ |
5 | |
6 | collection = Attribute("An IBranchCollection for this target.") |
7 | |
8 | - def assignKarma(person, action_name): |
9 | + def assignKarma(person, action_name, date_created=None): |
10 | """Assign karma to the person on the appropriate target.""" |
11 | |
12 | def getBugTask(bug): |
13 | |
14 | === modified file 'lib/lp/code/model/branchtarget.py' |
15 | --- lib/lp/code/model/branchtarget.py 2009-11-16 22:53:42 +0000 |
16 | +++ lib/lp/code/model/branchtarget.py 2010-01-05 08:21:19 +0000 |
17 | @@ -115,12 +115,13 @@ |
18 | else: |
19 | return False |
20 | |
21 | - def assignKarma(self, person, action_name): |
22 | + def assignKarma(self, person, action_name, date_created=None): |
23 | """See `IBranchTarget`.""" |
24 | - person.assignKarma( |
25 | + return person.assignKarma( |
26 | action_name, |
27 | distribution=self.context.distribution, |
28 | - sourcepackagename=self.context.sourcepackagename) |
29 | + sourcepackagename=self.context.sourcepackagename, |
30 | + datecreated=date_created) |
31 | |
32 | def getBugTask(self, bug): |
33 | """See `IBranchTarget`.""" |
34 | @@ -185,9 +186,10 @@ |
35 | """See `IBranchTarget`.""" |
36 | return False |
37 | |
38 | - def assignKarma(self, person, action_name): |
39 | + def assignKarma(self, person, action_name, date_created=None): |
40 | """See `IBranchTarget`.""" |
41 | # Does nothing. No karma for +junk. |
42 | + return None |
43 | |
44 | def getBugTask(self, bug): |
45 | """See `IBranchTarget`.""" |
46 | @@ -274,9 +276,10 @@ |
47 | else: |
48 | return False |
49 | |
50 | - def assignKarma(self, person, action_name): |
51 | + def assignKarma(self, person, action_name, date_created=None): |
52 | """See `IBranchTarget`.""" |
53 | - person.assignKarma(action_name, product=self.product) |
54 | + return person.assignKarma( |
55 | + action_name, product=self.product, datecreated=date_created) |
56 | |
57 | def getBugTask(self, bug): |
58 | """See `IBranchTarget`.""" |
59 | |
60 | === modified file 'lib/lp/code/model/revision.py' |
61 | --- lib/lp/code/model/revision.py 2009-12-08 04:03:30 +0000 |
62 | +++ lib/lp/code/model/revision.py 2010-01-05 08:21:19 +0000 |
63 | @@ -86,10 +86,7 @@ |
64 | """See `IRevision`.""" |
65 | # If we know who the revision author is, give them karma. |
66 | author = self.revision_author.person |
67 | - if (author is not None and branch.product is not None): |
68 | - # No karma for junk branches as we need a product to link |
69 | - # against. |
70 | - karma = author.assignKarma('revisionadded', branch.product) |
71 | + if author is not None: |
72 | # Backdate the karma to the time the revision was created. If the |
73 | # revision_date on the revision is in future (for whatever weird |
74 | # reason) we will use the date_created from the revision (which |
75 | @@ -97,9 +94,14 @@ |
76 | # events is both wrong, as the revision has been created (and it |
77 | # is lying), and a problem with the way the Launchpad code |
78 | # currently does its karma degradation over time. |
79 | + karma_date = min(self.revision_date, self.date_created) |
80 | + karma = branch.target.assignKarma( |
81 | + author, 'revisionadded', karma_date) |
82 | if karma is not None: |
83 | - karma.datecreated = min(self.revision_date, self.date_created) |
84 | self.karma_allocated = True |
85 | + return karma |
86 | + else: |
87 | + return None |
88 | |
89 | def getBranch(self, allow_private=False, allow_junk=True): |
90 | """See `IRevision`.""" |
91 | @@ -385,9 +387,6 @@ |
92 | from lp.registry.model.person import ValidPersonCache |
93 | |
94 | store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR) |
95 | - |
96 | - # XXX: Tim Penhey 2008-08-12, bug 244768 |
97 | - # Using Not(column == None) rather than column != None. |
98 | return store.find( |
99 | Revision, |
100 | Revision.revision_author == RevisionAuthor.id, |
101 | @@ -397,7 +396,8 @@ |
102 | Select(True, |
103 | And(BranchRevision.revision == Revision.id, |
104 | BranchRevision.branch == Branch.id, |
105 | - Not(Branch.product == None)), |
106 | + Or(Branch.product != None, |
107 | + Branch.distroseries != None)), |
108 | (Branch, BranchRevision)))) |
109 | |
110 | @staticmethod |
111 | |
112 | === modified file 'lib/lp/code/model/tests/test_revision.py' |
113 | --- lib/lp/code/model/tests/test_revision.py 2009-11-17 17:28:10 +0000 |
114 | +++ lib/lp/code/model/tests/test_revision.py 2010-01-05 08:21:19 +0000 |
115 | @@ -140,8 +140,7 @@ |
116 | def test_junkBranchMovedToProductNeedsKarma(self): |
117 | # A junk branch that moves to a product needs karma allocated. |
118 | author = self.factory.makePerson() |
119 | - rev = self.factory.makeRevision( |
120 | - author=author.preferredemail.email) |
121 | + rev = self.factory.makeRevision(author=author) |
122 | branch = self.factory.makePersonalBranch() |
123 | branch.createBranchRevision(1, rev) |
124 | # Once the branch is connected to the revision, we now specify |
125 | @@ -152,6 +151,20 @@ |
126 | self.assertEqual( |
127 | [rev], list(RevisionSet.getRevisionsNeedingKarmaAllocated())) |
128 | |
129 | + def test_junkBranchMovedToPackageNeedsKarma(self): |
130 | + # A junk branch that moves to a package needs karma allocated. |
131 | + author = self.factory.makePerson() |
132 | + rev = self.factory.makeRevision(author=author) |
133 | + branch = self.factory.makePersonalBranch() |
134 | + branch.createBranchRevision(1, rev) |
135 | + # Once the branch is connected to the revision, we now specify |
136 | + # a product for the branch. |
137 | + source_package = self.factory.makeSourcePackage() |
138 | + branch.setTarget(user=branch.owner, source_package=source_package) |
139 | + # The revision is now identified as needing karma allocated. |
140 | + self.assertEqual( |
141 | + [rev], list(RevisionSet.getRevisionsNeedingKarmaAllocated())) |
142 | + |
143 | def test_newRevisionAuthorLinkNeedsKarma(self): |
144 | # If Launchpad knows of revisions by a particular author, and later |
145 | # that authoer registers with launchpad, the revisions need karma |
146 | @@ -178,17 +191,39 @@ |
147 | # is set to be the time that the revision was created. |
148 | author = self.factory.makePerson() |
149 | rev = self.factory.makeRevision( |
150 | - author=author.preferredemail.email, |
151 | + author=author, |
152 | revision_date=datetime.now(pytz.UTC) + timedelta(days=5)) |
153 | branch = self.factory.makeProductBranch() |
154 | - branch.createBranchRevision(1, rev) |
155 | - # Get the karma event. |
156 | - [karma] = list(Store.of(author).find( |
157 | - Karma, |
158 | - Karma.person == author, |
159 | - Karma.product == branch.product)) |
160 | + karma = rev.allocateKarma(branch) |
161 | self.assertEqual(karma.datecreated, rev.date_created) |
162 | |
163 | + def test_allocateKarma_personal_branch(self): |
164 | + # A personal branch gets no karma event. |
165 | + author = self.factory.makePerson() |
166 | + rev = self.factory.makeRevision(author=author) |
167 | + branch = self.factory.makePersonalBranch() |
168 | + karma = rev.allocateKarma(branch) |
169 | + self.assertIs(None, karma) |
170 | + |
171 | + def test_allocateKarma_package_branch(self): |
172 | + # A revision on a package branch gets karma. |
173 | + author = self.factory.makePerson() |
174 | + rev = self.factory.makeRevision(author=author) |
175 | + branch = self.factory.makePackageBranch() |
176 | + karma = rev.allocateKarma(branch) |
177 | + self.assertEqual(author, karma.person) |
178 | + self.assertEqual(branch.distribution, karma.distribution) |
179 | + self.assertEqual(branch.sourcepackagename, karma.sourcepackagename) |
180 | + |
181 | + def test_allocateKarma_product_branch(self): |
182 | + # A revision on a product branch gets karma. |
183 | + author = self.factory.makePerson() |
184 | + rev = self.factory.makeRevision(author=author) |
185 | + branch = self.factory.makeProductBranch() |
186 | + karma = rev.allocateKarma(branch) |
187 | + self.assertEqual(author, karma.person) |
188 | + self.assertEqual(branch.product, karma.product) |
189 | + |
190 | |
191 | class TestRevisionGetBranch(TestCaseWithFactory): |
192 | """Test the `getBranch` method of the revision.""" |
193 | |
194 | === modified file 'lib/lp/code/scripts/revisionkarma.py' |
195 | --- lib/lp/code/scripts/revisionkarma.py 2009-06-25 04:06:00 +0000 |
196 | +++ lib/lp/code/scripts/revisionkarma.py 2010-01-05 08:21:19 +0000 |
197 | @@ -39,8 +39,9 @@ |
198 | count = 0 |
199 | revision_set = getUtility(IRevisionSet) |
200 | # Break into bits. |
201 | - revisions = list( |
202 | - revision_set.getRevisionsNeedingKarmaAllocated()[:100]) |
203 | + result_set = revision_set.getRevisionsNeedingKarmaAllocated() |
204 | + self.logger.info("%d revisions to update", result_set.count()) |
205 | + revisions = list(result_set[:100]) |
206 | while len(revisions) > 0: |
207 | for revision in revisions: |
208 | # Find the appropriate branch, and allocate karma to it. |
209 | |
210 | === modified file 'lib/lp/registry/interfaces/person.py' |
211 | --- lib/lp/registry/interfaces/person.py 2009-12-09 23:11:31 +0000 |
212 | +++ lib/lp/registry/interfaces/person.py 2010-01-05 08:21:19 +0000 |
213 | @@ -966,13 +966,16 @@ |
214 | """ |
215 | |
216 | def assignKarma(action_name, product=None, distribution=None, |
217 | - sourcepackagename=None): |
218 | + sourcepackagename=None, datecreated=None): |
219 | """Assign karma for the action named <action_name> to this person. |
220 | |
221 | This karma will be associated with the given product or distribution. |
222 | If a distribution is given, then product must be None and an optional |
223 | sourcepackagename may also be given. If a product is given, then |
224 | distribution and sourcepackagename must be None. |
225 | + |
226 | + If a datecreated is specified, the karma will be created with that |
227 | + date. This is how historic karma events can be created. |
228 | """ |
229 | |
230 | def latestKarma(quantity=25): |
231 | |
232 | === modified file 'lib/lp/registry/model/person.py' |
233 | --- lib/lp/registry/model/person.py 2009-12-05 18:37:28 +0000 |
234 | +++ lib/lp/registry/model/person.py 2010-01-05 08:21:19 +0000 |
235 | @@ -1050,7 +1050,7 @@ |
236 | return False |
237 | |
238 | def assignKarma(self, action_name, product=None, distribution=None, |
239 | - sourcepackagename=None): |
240 | + sourcepackagename=None, datecreated=None): |
241 | """See `IPerson`.""" |
242 | # Teams don't get Karma. Inactive accounts don't get Karma. |
243 | # The system user and janitor, does not get karma. |
244 | @@ -1074,9 +1074,12 @@ |
245 | raise AssertionError( |
246 | "No KarmaAction found with name '%s'." % action_name) |
247 | |
248 | + if datecreated is None: |
249 | + datecreated = UTC_NOW |
250 | karma = Karma( |
251 | person=self, action=action, product=product, |
252 | - distribution=distribution, sourcepackagename=sourcepackagename) |
253 | + distribution=distribution, sourcepackagename=sourcepackagename, |
254 | + datecreated=datecreated) |
255 | notify(KarmaAssignedEvent(self, karma)) |
256 | return karma |
257 | |
258 | |
259 | === modified file 'lib/lp/testing/factory.py' |
260 | --- lib/lp/testing/factory.py 2009-12-12 09:47:05 +0000 |
261 | +++ lib/lp/testing/factory.py 2010-01-05 08:21:19 +0000 |
262 | @@ -108,7 +108,7 @@ |
263 | from lp.registry.interfaces.mailinglistsubscription import ( |
264 | MailingListAutoSubscribePolicy) |
265 | from lp.registry.interfaces.person import ( |
266 | - IPersonSet, PersonCreationRationale, TeamSubscriptionPolicy) |
267 | + IPerson, IPersonSet, PersonCreationRationale, TeamSubscriptionPolicy) |
268 | from lp.registry.interfaces.poll import IPollSet, PollAlgorithm, PollSecrecy |
269 | from lp.registry.interfaces.product import IProductSet, License |
270 | from lp.registry.interfaces.productseries import IProductSeries |
271 | @@ -904,6 +904,8 @@ |
272 | """Create a single `Revision`.""" |
273 | if author is None: |
274 | author = self.getUniqueString('author') |
275 | + elif IPerson.providedBy(author): |
276 | + author = author.preferredemail.email |
277 | if revision_date is None: |
278 | revision_date = datetime.now(pytz.UTC) |
279 | if parent_ids is None: |
The revision allocation script has been broken for a little while. It gets itself into an infinite loop. I have had the LOSAs kill it for now.
The fundamental problem was someone fixing Revision.getBranch to be source package aware. Unfortunately the allocation of karma wasn't, and so as soon as a revision that was in a product branch (which is what the query checked for) but the Revision.getBranch returned a package branch, the script got in a loop.
This branch has the following fixes: makeRevision can now take a Person for the author, and will use that person's email address
* factory.
* the revision karma allocation script logs at the start of the run how many revisions it thinks it needs to allocate karma for
* getting revision needing karma allocated now check for related package branches
* the actual karma allocation is done by the branch target, and the method returns the karma object