Merge lp:~gmb/launchpad/gbwnu-next-check-goodness-544943 into lp:launchpad/db-devel
- gbwnu-next-check-goodness-544943
- Merge into db-devel
Proposed by
Graham Binns
Status: | Merged |
---|---|
Approved by: | Eleanor Berger |
Approved revision: | no longer in the source branch. |
Merged at revision: | not available |
Proposed branch: | lp:~gmb/launchpad/gbwnu-next-check-goodness-544943 |
Merge into: | lp:launchpad/db-devel |
Diff against target: |
589 lines (+115/-79) 17 files modified
database/sampledata/current.sql (+1/-1) lib/lp/bugs/configure.zcml (+9/-1) lib/lp/bugs/doc/bugtracker.txt (+25/-22) lib/lp/bugs/doc/checkwatches.txt (+11/-10) lib/lp/bugs/doc/externalbugtracker-bugzilla.txt (+1/-1) lib/lp/bugs/doc/externalbugtracker-debbugs.txt (+21/-7) lib/lp/bugs/doc/externalbugtracker-mantis-csv.txt (+1/-1) lib/lp/bugs/doc/externalbugtracker-mantis.txt (+1/-1) lib/lp/bugs/doc/externalbugtracker-trac.txt (+1/-1) lib/lp/bugs/interfaces/bugtracker.py (+10/-6) lib/lp/bugs/interfaces/bugwatch.py (+3/-0) lib/lp/bugs/model/bugtracker.py (+10/-14) lib/lp/bugs/model/bugwatch.py (+1/-0) lib/lp/bugs/scripts/checkwatches.py (+7/-11) lib/lp/bugs/scripts/tests/test_bugimport.py (+2/-2) lib/lp/bugs/stories/webservice/xx-bug.txt (+2/-0) lib/lp/testing/factory.py (+9/-1) |
To merge this branch: | bzr merge lp:~gmb/launchpad/gbwnu-next-check-goodness-544943 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Eleanor Berger (community) | code | Approve | |
Review via email: mp+22013@code.launchpad.net |
Commit message
BugTracker.
Description of the change
This branch updates BugTracker.
I've updated the IBugTracker interface and BugTracker model accordingly, as well as associated tests. getBugWatchesNe
To post a comment you must log in.
Revision history for this message
Eleanor Berger (intellectronica) : | # |
review:
Approve
(code)
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'database/sampledata/current.sql' | |||
2 | --- database/sampledata/current.sql 2010-03-21 16:57:00 +0000 | |||
3 | +++ database/sampledata/current.sql 2010-03-24 22:10:48 +0000 | |||
4 | @@ -2486,7 +2486,7 @@ | |||
5 | 2486 | INSERT INTO emailaddress (id, email, person, status, date_created, account) VALUES (54, 'rosetta@launchpad.net', 30, 4, '2006-10-16 18:31:43.626521', NULL); | 2486 | INSERT INTO emailaddress (id, email, person, status, date_created, account) VALUES (54, 'rosetta@launchpad.net', 30, 4, '2006-10-16 18:31:43.626521', NULL); |
6 | 2487 | INSERT INTO emailaddress (id, email, person, status, date_created, account) VALUES (55, 'salgado@ubuntu.com', 29, 1, '2006-10-16 18:31:43.626932', 291); | 2487 | INSERT INTO emailaddress (id, email, person, status, date_created, account) VALUES (55, 'salgado@ubuntu.com', 29, 1, '2006-10-16 18:31:43.626932', 291); |
7 | 2488 | INSERT INTO emailaddress (id, email, person, status, date_created, account) VALUES (56, 'cprov@ubuntu.com', 28, 2, '2006-10-16 18:31:43.627318', 281); | 2488 | INSERT INTO emailaddress (id, email, person, status, date_created, account) VALUES (56, 'cprov@ubuntu.com', 28, 2, '2006-10-16 18:31:43.627318', 281); |
9 | 2489 | INSERT INTO emailaddress (id, email, person, status, date_created, account) VALUES (57, 'bugwatch@bugs.launchpad.net', 62, 1, '2006-10-16 18:31:43.62774', 621); | 2489 | INSERT INTO emailaddress (id, email, person, status, date_created, account) VALUES (57, 'bugwatch@bugs.launchpad.net', 62, 4, '2006-10-16 18:31:43.62774', 621); |
10 | 2490 | INSERT INTO emailaddress (id, email, person, status, date_created, account) VALUES (58, 'karl@canonical.com', 63, 4, '2006-10-16 18:31:43.628123', 631); | 2490 | INSERT INTO emailaddress (id, email, person, status, date_created, account) VALUES (58, 'karl@canonical.com', 63, 4, '2006-10-16 18:31:43.628123', 631); |
11 | 2491 | INSERT INTO emailaddress (id, email, person, status, date_created, account) VALUES (59, 'limi@plone.org', 10, 4, '2006-10-16 18:31:43.628504', 101); | 2491 | INSERT INTO emailaddress (id, email, person, status, date_created, account) VALUES (59, 'limi@plone.org', 10, 4, '2006-10-16 18:31:43.628504', 101); |
12 | 2492 | INSERT INTO emailaddress (id, email, person, status, date_created, account) VALUES (60, 'janitor@launchpad.net', 65, 4, '2006-10-17 23:23:23.232323', 651); | 2492 | INSERT INTO emailaddress (id, email, person, status, date_created, account) VALUES (60, 'janitor@launchpad.net', 65, 4, '2006-10-17 23:23:23.232323', 651); |
13 | 2493 | 2493 | ||
14 | === modified file 'lib/lp/bugs/configure.zcml' | |||
15 | --- lib/lp/bugs/configure.zcml 2010-03-19 17:42:12 +0000 | |||
16 | +++ lib/lp/bugs/configure.zcml 2010-03-24 22:10:48 +0000 | |||
17 | @@ -839,6 +839,7 @@ | |||
18 | 839 | lastchanged | 839 | lastchanged |
19 | 840 | lastchecked | 840 | lastchecked |
20 | 841 | needscheck | 841 | needscheck |
21 | 842 | next_check | ||
22 | 842 | owner | 843 | owner |
23 | 843 | remote_importance | 844 | remote_importance |
24 | 844 | remotebug | 845 | remotebug |
25 | @@ -858,7 +859,14 @@ | |||
26 | 858 | addComment | 859 | addComment |
27 | 859 | updateImportance | 860 | updateImportance |
28 | 860 | updateStatus" | 861 | updateStatus" |
30 | 861 | set_attributes="last_error_type lastchanged lastchecked needscheck remote_importance remotestatus"/> | 862 | set_attributes=" |
31 | 863 | last_error_type | ||
32 | 864 | lastchanged | ||
33 | 865 | lastchecked | ||
34 | 866 | needscheck | ||
35 | 867 | next_check | ||
36 | 868 | remote_importance | ||
37 | 869 | remotestatus"/> | ||
38 | 862 | </class> | 870 | </class> |
39 | 863 | 871 | ||
40 | 864 | <!-- https://launchpad.net/bugs/98639 --> | 872 | <!-- https://launchpad.net/bugs/98639 --> |
41 | 865 | 873 | ||
42 | === modified file 'lib/lp/bugs/doc/bugtracker.txt' | |||
43 | --- lib/lp/bugs/doc/bugtracker.txt 2010-03-22 10:36:53 +0000 | |||
44 | +++ lib/lp/bugs/doc/bugtracker.txt 2010-03-24 22:10:48 +0000 | |||
45 | @@ -16,7 +16,7 @@ | |||
46 | 16 | 16 | ||
47 | 17 | >>> def print_watches(bugtracker): | 17 | >>> def print_watches(bugtracker): |
48 | 18 | ... watches = sorted( | 18 | ... watches = sorted( |
50 | 19 | ... bugtracker.getBugWatchesNeedingUpdate(23), | 19 | ... bugtracker.getBugWatchesNeedingUpdate(), |
51 | 20 | ... key=lambda watch: (watch.remotebug, watch.bug.id)) | 20 | ... key=lambda watch: (watch.remotebug, watch.bug.id)) |
52 | 21 | ... | 21 | ... |
53 | 22 | ... for bug_watch in watches: | 22 | ... for bug_watch in watches: |
54 | @@ -28,8 +28,8 @@ | |||
55 | 28 | >>> login('foo.bar@canonical.com') | 28 | >>> login('foo.bar@canonical.com') |
56 | 29 | 29 | ||
57 | 30 | We can get a list of all the bug tracker's bug watches needing to be | 30 | We can get a list of all the bug tracker's bug watches needing to be |
60 | 31 | updated. The current criteria for needing a update is that it hasn't | 31 | updated. A bug watch is considered to be needing an update when its |
61 | 32 | been updated in the last 23 hours. | 32 | next_check time is in the past. |
62 | 33 | 33 | ||
63 | 34 | >>> bug_watches = mozilla_bugzilla.watches | 34 | >>> bug_watches = mozilla_bugzilla.watches |
64 | 35 | >>> print bug_watches.count() | 35 | >>> print bug_watches.count() |
65 | @@ -37,7 +37,7 @@ | |||
66 | 37 | 37 | ||
67 | 38 | >>> print bug_watches[0].remotebug, bug_watches[0].bug.id | 38 | >>> print bug_watches[0].remotebug, bug_watches[0].bug.id |
68 | 39 | 2000 1 | 39 | 2000 1 |
70 | 40 | >>> bug_watches[0].lastchecked = None | 40 | >>> bug_watches[0].next_check = now - timedelta(hours=1) |
71 | 41 | 41 | ||
72 | 42 | >>> print bug_watches[1].remotebug, bug_watches[1].bug.id | 42 | >>> print bug_watches[1].remotebug, bug_watches[1].bug.id |
73 | 43 | 123543 1 | 43 | 123543 1 |
74 | @@ -48,11 +48,11 @@ | |||
75 | 48 | 48 | ||
76 | 49 | >>> print bug_watches[2].remotebug, bug_watches[2].bug.id | 49 | >>> print bug_watches[2].remotebug, bug_watches[2].bug.id |
77 | 50 | 42 1 | 50 | 42 1 |
79 | 51 | >>> bug_watches[2].lastchecked = now - timedelta(hours=36) | 51 | >>> bug_watches[2].next_check = now - timedelta(hours=36) |
80 | 52 | 52 | ||
81 | 53 | >>> print bug_watches[3].remotebug, bug_watches[3].bug.id | 53 | >>> print bug_watches[3].remotebug, bug_watches[3].bug.id |
82 | 54 | 42 2 | 54 | 42 2 |
84 | 55 | >>> bug_watches[3].lastchecked = now - timedelta(days=1) | 55 | >>> bug_watches[3].next_check = now - timedelta(days=1) |
85 | 56 | 56 | ||
86 | 57 | The watches needing updating should the ones with old statuses, 2000 and 42: | 57 | The watches needing updating should the ones with old statuses, 2000 and 42: |
87 | 58 | 58 | ||
88 | @@ -593,31 +593,34 @@ | |||
89 | 593 | resetWatches() method. | 593 | resetWatches() method. |
90 | 594 | 594 | ||
91 | 595 | >>> from operator import attrgetter | 595 | >>> from operator import attrgetter |
92 | 596 | >>> from pytz import utc | ||
93 | 597 | >>> definitely_now = datetime.now(utc) | ||
94 | 596 | 598 | ||
95 | 597 | >>> def print_tracker_watch_times(bugtracker): | 599 | >>> def print_tracker_watch_times(bugtracker): |
96 | 598 | ... for watch in sorted( | 600 | ... for watch in sorted( |
97 | 599 | ... bugtracker.watches, key=attrgetter('id')): | 601 | ... bugtracker.watches, key=attrgetter('id')): |
100 | 600 | ... if watch.lastchecked is not None: | 602 | ... if watch.next_check != definitely_now: |
101 | 601 | ... print watch.bug.id, "not None" | 603 | ... print watch.bug.id, "not now" |
102 | 602 | ... else: | 604 | ... else: |
104 | 603 | ... print watch.bug.id, watch.lastchecked | 605 | ... print watch.bug.id, "now" |
105 | 604 | 606 | ||
106 | 605 | >>> print_tracker_watch_times(mozilla_bugzilla) | 607 | >>> print_tracker_watch_times(mozilla_bugzilla) |
116 | 606 | 2 not None | 608 | 2 not now |
117 | 607 | 1 None | 609 | 1 not now |
118 | 608 | 1 not None | 610 | 1 not now |
119 | 609 | 1 not None | 611 | 1 not now |
120 | 610 | 612 | ||
121 | 611 | Calling resetWatches() on mozilla_bugzilla will set all its | 613 | Calling resetWatches() on mozilla_bugzilla will set all its next_check |
122 | 612 | lastchecked times to None. | 614 | times to datetime.now(). For testing purposes, we'll pass it a value to |
123 | 613 | 615 | use in place of now(). | |
124 | 614 | >>> mozilla_bugzilla.resetWatches() | 616 | |
125 | 617 | >>> mozilla_bugzilla.resetWatches(now=definitely_now) | ||
126 | 615 | >>> transaction.commit() | 618 | >>> transaction.commit() |
127 | 616 | >>> print_tracker_watch_times(mozilla_bugzilla) | 619 | >>> print_tracker_watch_times(mozilla_bugzilla) |
132 | 617 | 2 None | 620 | 2 now |
133 | 618 | 1 None | 621 | 1 now |
134 | 619 | 1 None | 622 | 1 now |
135 | 620 | 1 None | 623 | 1 now |
136 | 621 | 624 | ||
137 | 622 | resetWatches() also clears the last_error_type field of the watches. | 625 | resetWatches() also clears the last_error_type field of the watches. |
138 | 623 | 626 | ||
139 | 624 | 627 | ||
140 | === modified file 'lib/lp/bugs/doc/checkwatches.txt' | |||
141 | --- lib/lp/bugs/doc/checkwatches.txt 2010-03-24 06:53:49 +0000 | |||
142 | +++ lib/lp/bugs/doc/checkwatches.txt 2010-03-24 22:10:48 +0000 | |||
143 | @@ -103,6 +103,8 @@ | |||
144 | 103 | 103 | ||
145 | 104 | First, we create some bug watches to test with: | 104 | First, we create some bug watches to test with: |
146 | 105 | 105 | ||
147 | 106 | >>> from datetime import datetime | ||
148 | 107 | >>> from pytz import utc | ||
149 | 106 | >>> from canonical.launchpad.database import BugTracker | 108 | >>> from canonical.launchpad.database import BugTracker |
150 | 107 | >>> from canonical.launchpad.interfaces import ( | 109 | >>> from canonical.launchpad.interfaces import ( |
151 | 108 | ... BugTrackerType, IBugSet) | 110 | ... BugTrackerType, IBugSet) |
152 | @@ -128,6 +130,7 @@ | |||
153 | 128 | >>> example_bugwatch = example_bug.addWatch( | 130 | >>> example_bugwatch = example_bug.addWatch( |
154 | 129 | ... example_bug_tracker, '1', | 131 | ... example_bug_tracker, '1', |
155 | 130 | ... getUtility(ILaunchpadCelebrities).janitor) | 132 | ... getUtility(ILaunchpadCelebrities).janitor) |
156 | 133 | >>> example_bugwatch.next_check = datetime.now(utc) | ||
157 | 131 | 134 | ||
158 | 132 | >>> login('no-priv@canonical.com') | 135 | >>> login('no-priv@canonical.com') |
159 | 133 | 136 | ||
160 | @@ -206,7 +209,7 @@ | |||
161 | 206 | ... bugtrackertype.name, bugtracker.name) | 209 | ... bugtrackertype.name, bugtracker.name) |
162 | 207 | 210 | ||
163 | 208 | >>> login(ANONYMOUS) | 211 | >>> login(ANONYMOUS) |
165 | 209 | >>> example_bugwatch.lastchecked = None | 212 | >>> example_bugwatch.next_check = datetime.now(utc) |
166 | 210 | >>> try: | 213 | >>> try: |
167 | 211 | ... externalbugtracker.get_external_bugtracker = ( | 214 | ... externalbugtracker.get_external_bugtracker = ( |
168 | 212 | ... broken_get_external_bugtracker) | 215 | ... broken_get_external_bugtracker) |
169 | @@ -242,6 +245,7 @@ | |||
170 | 242 | ... example_bugwatch = example_bug.addWatch( | 245 | ... example_bugwatch = example_bug.addWatch( |
171 | 243 | ... example_bug_tracker, str(bug_id), | 246 | ... example_bug_tracker, str(bug_id), |
172 | 244 | ... getUtility(ILaunchpadCelebrities).janitor) | 247 | ... getUtility(ILaunchpadCelebrities).janitor) |
173 | 248 | ... example_bugwatch.next_check = datetime.now(utc) | ||
174 | 245 | 249 | ||
175 | 246 | Since we know how many bugwatches example_bug has we will be able to see | 250 | Since we know how many bugwatches example_bug has we will be able to see |
176 | 247 | when checkwatches only updates a subset of them. | 251 | when checkwatches only updates a subset of them. |
177 | @@ -294,15 +298,13 @@ | |||
178 | 294 | We'll set the lastchecked time on that Savannah instance to make sure | 298 | We'll set the lastchecked time on that Savannah instance to make sure |
179 | 295 | that it looks as though it has been updated recently | 299 | that it looks as though it has been updated recently |
180 | 296 | 300 | ||
181 | 297 | >>> import pytz | ||
182 | 298 | >>> from datetime import datetime | ||
183 | 299 | >>> login('test@canonical.com') | 301 | >>> login('test@canonical.com') |
184 | 300 | >>> for watch in savannah.watches: | 302 | >>> for watch in savannah.watches: |
186 | 301 | ... watch.lastchecked = datetime.now(pytz.timezone('UTC')) | 303 | ... watch.lastchecked = datetime.now(utc) |
187 | 302 | 304 | ||
188 | 303 | So our Savannah instance now has no watches that need checking. | 305 | So our Savannah instance now has no watches that need checking. |
189 | 304 | 306 | ||
191 | 305 | >>> savannah.getBugWatchesNeedingUpdate(23).count() | 307 | >>> savannah.getBugWatchesNeedingUpdate().count() |
192 | 306 | 0 | 308 | 0 |
193 | 307 | 309 | ||
194 | 308 | However, forceUpdateAll() will update every watch, whether they've | 310 | However, forceUpdateAll() will update every watch, whether they've |
195 | @@ -368,10 +370,11 @@ | |||
196 | 368 | ... def _updateBugTracker(self, bug_tracker, batch_size): | 370 | ... def _updateBugTracker(self, bug_tracker, batch_size): |
197 | 369 | ... # Update as many watches as the batch size says. | 371 | ... # Update as many watches as the batch size says. |
198 | 370 | ... watches_to_update = ( | 372 | ... watches_to_update = ( |
200 | 371 | ... bug_tracker.getBugWatchesNeedingUpdate(23)[:batch_size]) | 373 | ... bug_tracker.getBugWatchesNeedingUpdate()[:batch_size]) |
201 | 372 | ... | 374 | ... |
202 | 373 | ... for watch in watches_to_update: | 375 | ... for watch in watches_to_update: |
204 | 374 | ... watch.lastchecked = datetime.now(pytz.timezone('UTC')) | 376 | ... watch.lastchecked = datetime.now(utc) |
205 | 377 | ... watch.next_check = None | ||
206 | 375 | 378 | ||
207 | 376 | >>> non_connecting_updater = NonConnectingUpdater(transaction) | 379 | >>> non_connecting_updater = NonConnectingUpdater(transaction) |
208 | 377 | >>> non_connecting_updater.log = FakeLogger() | 380 | >>> non_connecting_updater.log = FakeLogger() |
209 | @@ -397,8 +400,6 @@ | |||
210 | 397 | 400 | ||
211 | 398 | We'll create a non-functioning ExternalBugtracker to demonstrate this. | 401 | We'll create a non-functioning ExternalBugtracker to demonstrate this. |
212 | 399 | 402 | ||
213 | 400 | >>> import pytz | ||
214 | 401 | >>> from datetime import datetime | ||
215 | 402 | >>> from zope.interface import implements | 403 | >>> from zope.interface import implements |
216 | 403 | >>> from lp.bugs.interfaces.bugtask import ( | 404 | >>> from lp.bugs.interfaces.bugtask import ( |
217 | 404 | ... BugTaskStatus, BugTaskImportance) | 405 | ... BugTaskStatus, BugTaskImportance) |
218 | @@ -407,7 +408,7 @@ | |||
219 | 407 | ... ISupportsBackLinking) | 408 | ... ISupportsBackLinking) |
220 | 408 | >>> from lp.bugs.externalbugtracker.base import ExternalBugTracker | 409 | >>> from lp.bugs.externalbugtracker.base import ExternalBugTracker |
221 | 409 | 410 | ||
223 | 410 | >>> nowish = datetime.now(pytz.timezone('UTC')) | 411 | >>> nowish = datetime.now(utc) |
224 | 411 | >>> class UselessExternalBugTracker(ExternalBugTracker): | 412 | >>> class UselessExternalBugTracker(ExternalBugTracker): |
225 | 412 | ... | 413 | ... |
226 | 413 | ... implements( | 414 | ... implements( |
227 | 414 | 415 | ||
228 | === modified file 'lib/lp/bugs/doc/externalbugtracker-bugzilla.txt' | |||
229 | --- lib/lp/bugs/doc/externalbugtracker-bugzilla.txt 2010-03-19 16:47:08 +0000 | |||
230 | +++ lib/lp/bugs/doc/externalbugtracker-bugzilla.txt 2010-03-24 22:10:48 +0000 | |||
231 | @@ -422,7 +422,7 @@ | |||
232 | 422 | 422 | ||
233 | 423 | >>> from canonical.database.sqlbase import flush_database_updates | 423 | >>> from canonical.database.sqlbase import flush_database_updates |
234 | 424 | >>> flush_database_updates() | 424 | >>> flush_database_updates() |
236 | 425 | >>> gnome_bugzilla.getBugWatchesNeedingUpdate(23).count() | 425 | >>> gnome_bugzilla.getBugWatchesNeedingUpdate().count() |
237 | 426 | 0 | 426 | 0 |
238 | 427 | 427 | ||
239 | 428 | If the status isn't different, the lastchanged attribute doesn't get | 428 | If the status isn't different, the lastchanged attribute doesn't get |
240 | 429 | 429 | ||
241 | === modified file 'lib/lp/bugs/doc/externalbugtracker-debbugs.txt' | |||
242 | --- lib/lp/bugs/doc/externalbugtracker-debbugs.txt 2010-01-06 14:35:52 +0000 | |||
243 | +++ lib/lp/bugs/doc/externalbugtracker-debbugs.txt 2010-03-24 22:10:48 +0000 | |||
244 | @@ -91,13 +91,27 @@ | |||
245 | 91 | 91 | ||
246 | 92 | >>> from canonical.launchpad.interfaces import IBugTrackerSet | 92 | >>> from canonical.launchpad.interfaces import IBugTrackerSet |
247 | 93 | >>> debbugs = getUtility(IBugTrackerSet).getByName('debbugs') | 93 | >>> debbugs = getUtility(IBugTrackerSet).getByName('debbugs') |
249 | 94 | >>> bug_watches = list(debbugs.getBugWatchesNeedingUpdate(23)) | 94 | >>> bug_watches = list(debbugs.getBugWatchesNeedingUpdate()) |
250 | 95 | >>> len(bug_watches) | ||
251 | 96 | 0 | ||
252 | 97 | |||
253 | 98 | It looks as though there are no bug watches needing to be updated. | ||
254 | 99 | That's because the check for whether a bug watch needs to be updated | ||
255 | 100 | looks at its next_check field, which is None by default. Updating the | ||
256 | 101 | bug watches should solve that problem. | ||
257 | 102 | |||
258 | 103 | >>> from datetime import datetime | ||
259 | 104 | >>> from pytz import utc | ||
260 | 105 | >>> for watch in debbugs.watches: | ||
261 | 106 | ... watch.next_check = datetime.now(utc) | ||
262 | 107 | |||
263 | 108 | >>> bug_watches = list(debbugs.getBugWatchesNeedingUpdate()) | ||
264 | 95 | >>> len(bug_watches) | 109 | >>> len(bug_watches) |
265 | 96 | 5 | 110 | 5 |
266 | 97 | 111 | ||
270 | 98 | Log in and kick off the update. The importing of comments, which is | 112 | Now there are some watches to update we can run the update against them. |
271 | 99 | controlled by a configuration option, is disabled here and will be | 113 | The importing of comments, which is controlled by a configuration |
272 | 100 | tested later. | 114 | option, is disabled here and will be tested later. |
273 | 101 | 115 | ||
274 | 102 | >>> from lp.bugs.scripts.checkwatches import BugWatchUpdater | 116 | >>> from lp.bugs.scripts.checkwatches import BugWatchUpdater |
275 | 103 | >>> bug_watch_updater = BugWatchUpdater(txn) | 117 | >>> bug_watch_updater = BugWatchUpdater(txn) |
276 | @@ -116,11 +130,11 @@ | |||
277 | 116 | 327549: open important security | 130 | 327549: open important security |
278 | 117 | 308994: open important | 131 | 308994: open important |
279 | 118 | 132 | ||
282 | 119 | The lastchecked attribute got updated for each bug watch, so no more | 133 | The next_check value for all the watches got set to null when they |
283 | 120 | watches are in need of an update. | 134 | were updated, so there are no watches left needing an update. |
284 | 121 | 135 | ||
285 | 122 | >>> flush_database_updates() | 136 | >>> flush_database_updates() |
287 | 123 | >>> watches = debbugs.getBugWatchesNeedingUpdate(23) | 137 | >>> watches = debbugs.getBugWatchesNeedingUpdate() |
288 | 124 | >>> watches.count() | 138 | >>> watches.count() |
289 | 125 | 0 | 139 | 0 |
290 | 126 | 140 | ||
291 | 127 | 141 | ||
292 | === modified file 'lib/lp/bugs/doc/externalbugtracker-mantis-csv.txt' | |||
293 | --- lib/lp/bugs/doc/externalbugtracker-mantis-csv.txt 2009-10-09 20:56:00 +0000 | |||
294 | +++ lib/lp/bugs/doc/externalbugtracker-mantis-csv.txt 2010-03-24 22:10:48 +0000 | |||
295 | @@ -164,7 +164,7 @@ | |||
296 | 164 | 164 | ||
297 | 165 | >>> from canonical.database.sqlbase import flush_database_updates | 165 | >>> from canonical.database.sqlbase import flush_database_updates |
298 | 166 | >>> flush_database_updates() | 166 | >>> flush_database_updates() |
300 | 167 | >>> example_bug_tracker.getBugWatchesNeedingUpdate(23).count() | 167 | >>> example_bug_tracker.getBugWatchesNeedingUpdate().count() |
301 | 168 | 0 | 168 | 0 |
302 | 169 | 169 | ||
303 | 170 | If the status isn't different, the lastchanged attribute doesn't get | 170 | If the status isn't different, the lastchanged attribute doesn't get |
304 | 171 | 171 | ||
305 | === modified file 'lib/lp/bugs/doc/externalbugtracker-mantis.txt' | |||
306 | --- lib/lp/bugs/doc/externalbugtracker-mantis.txt 2010-03-17 13:40:49 +0000 | |||
307 | +++ lib/lp/bugs/doc/externalbugtracker-mantis.txt 2010-03-24 22:10:48 +0000 | |||
308 | @@ -179,7 +179,7 @@ | |||
309 | 179 | 179 | ||
310 | 180 | >>> from canonical.database.sqlbase import flush_database_updates | 180 | >>> from canonical.database.sqlbase import flush_database_updates |
311 | 181 | >>> flush_database_updates() | 181 | >>> flush_database_updates() |
313 | 182 | >>> example_bug_tracker.getBugWatchesNeedingUpdate(23).count() | 182 | >>> example_bug_tracker.getBugWatchesNeedingUpdate().count() |
314 | 183 | 0 | 183 | 0 |
315 | 184 | 184 | ||
316 | 185 | If the status isn't different, the lastchanged attribute doesn't get | 185 | If the status isn't different, the lastchanged attribute doesn't get |
317 | 186 | 186 | ||
318 | === modified file 'lib/lp/bugs/doc/externalbugtracker-trac.txt' | |||
319 | --- lib/lp/bugs/doc/externalbugtracker-trac.txt 2010-01-11 13:54:42 +0000 | |||
320 | +++ lib/lp/bugs/doc/externalbugtracker-trac.txt 2010-03-24 22:10:48 +0000 | |||
321 | @@ -369,7 +369,7 @@ | |||
322 | 369 | now no bug watches are in need of updating: | 369 | now no bug watches are in need of updating: |
323 | 370 | 370 | ||
324 | 371 | >>> flush_database_updates() | 371 | >>> flush_database_updates() |
326 | 372 | >>> example_bug_tracker.getBugWatchesNeedingUpdate(23).count() | 372 | >>> example_bug_tracker.getBugWatchesNeedingUpdate().count() |
327 | 373 | 0 | 373 | 0 |
328 | 374 | 374 | ||
329 | 375 | If the status isn't different, the lastchanged attribute doesn't get | 375 | If the status isn't different, the lastchanged attribute doesn't get |
330 | 376 | 376 | ||
331 | === modified file 'lib/lp/bugs/interfaces/bugtracker.py' | |||
332 | --- lib/lp/bugs/interfaces/bugtracker.py 2010-03-10 19:00:59 +0000 | |||
333 | +++ lib/lp/bugs/interfaces/bugtracker.py 2010-03-24 22:10:48 +0000 | |||
334 | @@ -273,12 +273,11 @@ | |||
335 | 273 | def getBugsWatching(remotebug): | 273 | def getBugsWatching(remotebug): |
336 | 274 | """Get the bugs watching the given remote bug in this bug tracker.""" | 274 | """Get the bugs watching the given remote bug in this bug tracker.""" |
337 | 275 | 275 | ||
339 | 276 | def getBugWatchesNeedingUpdate(hours_since_last_check): | 276 | def getBugWatchesNeedingUpdate(): |
340 | 277 | """Get the bug watches needing to be updated. | 277 | """Get the bug watches needing to be updated. |
341 | 278 | 278 | ||
345 | 279 | All bug watches not being updated for the last | 279 | All bug watches with a next_check time in the past are |
346 | 280 | :hours_since_last_check: hours are considered needing to be | 280 | considered to be needing an update. |
344 | 281 | updated. | ||
347 | 282 | """ | 281 | """ |
348 | 283 | 282 | ||
349 | 284 | def getLinkedPersonByName(name): | 283 | def getLinkedPersonByName(name): |
350 | @@ -318,8 +317,13 @@ | |||
351 | 318 | def destroySelf(): | 317 | def destroySelf(): |
352 | 319 | """Delete this bug tracker.""" | 318 | """Delete this bug tracker.""" |
353 | 320 | 319 | ||
356 | 321 | def resetWatches(): | 320 | def resetWatches(now=None): |
357 | 322 | """Reset the lastchecked times of this BugTracker's `BugWatch`es.""" | 321 | """Reset the next_check times of this BugTracker's `BugWatch`es. |
358 | 322 | |||
359 | 323 | :param now: If specified, contains the datetime to which to set | ||
360 | 324 | the BugWatches' next_check times. Defaults to | ||
361 | 325 | datetime.now(). | ||
362 | 326 | """ | ||
363 | 323 | 327 | ||
364 | 324 | 328 | ||
365 | 325 | class IBugTrackerSet(Interface): | 329 | class IBugTrackerSet(Interface): |
366 | 326 | 330 | ||
367 | === modified file 'lib/lp/bugs/interfaces/bugwatch.py' | |||
368 | --- lib/lp/bugs/interfaces/bugwatch.py 2010-03-22 10:36:53 +0000 | |||
369 | +++ lib/lp/bugs/interfaces/bugwatch.py 2010-03-24 22:10:48 +0000 | |||
370 | @@ -136,6 +136,9 @@ | |||
371 | 136 | Reference(title=_('Owner'), required=True, | 136 | Reference(title=_('Owner'), required=True, |
372 | 137 | readonly=True, schema=Interface)) | 137 | readonly=True, schema=Interface)) |
373 | 138 | activity = Attribute('The activity history of this BugWatch.') | 138 | activity = Attribute('The activity history of this BugWatch.') |
374 | 139 | next_check = exported( | ||
375 | 140 | Datetime(title=_('Next Check')), | ||
376 | 141 | exported_as='date_next_checked') | ||
377 | 139 | 142 | ||
378 | 140 | # Useful joins. | 143 | # Useful joins. |
379 | 141 | bugtasks = exported( | 144 | bugtasks = exported( |
380 | 142 | 145 | ||
381 | === modified file 'lib/lp/bugs/model/bugtracker.py' | |||
382 | --- lib/lp/bugs/model/bugtracker.py 2009-12-09 14:55:30 +0000 | |||
383 | +++ lib/lp/bugs/model/bugtracker.py 2010-03-24 22:10:48 +0000 | |||
384 | @@ -25,7 +25,7 @@ | |||
385 | 25 | BoolCol, ForeignKey, OR, SQLMultipleJoin, SQLObjectNotFound, StringCol) | 25 | BoolCol, ForeignKey, OR, SQLMultipleJoin, SQLObjectNotFound, StringCol) |
386 | 26 | from sqlobject.sqlbuilder import AND | 26 | from sqlobject.sqlbuilder import AND |
387 | 27 | 27 | ||
389 | 28 | from storm.expr import Or | 28 | from storm.expr import Not, Or |
390 | 29 | from storm.locals import Bool | 29 | from storm.locals import Bool |
391 | 30 | from storm.store import Store | 30 | from storm.store import Store |
392 | 31 | 31 | ||
393 | @@ -348,26 +348,19 @@ | |||
394 | 348 | distinct=True, | 348 | distinct=True, |
395 | 349 | orderBy=['datecreated'])) | 349 | orderBy=['datecreated'])) |
396 | 350 | 350 | ||
398 | 351 | def getBugWatchesNeedingUpdate(self, hours_since_last_check): | 351 | def getBugWatchesNeedingUpdate(self): |
399 | 352 | """See `IBugTracker`. | 352 | """See `IBugTracker`. |
400 | 353 | 353 | ||
401 | 354 | :return: The UNION of the bug watches that need checking and | 354 | :return: The UNION of the bug watches that need checking and |
402 | 355 | those with unpushed comments. | 355 | those with unpushed comments. |
403 | 356 | """ | 356 | """ |
404 | 357 | lastchecked_cutoff = ( | ||
405 | 358 | datetime.now(timezone('UTC')) - | ||
406 | 359 | timedelta(hours=hours_since_last_check)) | ||
407 | 360 | |||
408 | 361 | lastchecked_clause = Or( | ||
409 | 362 | BugWatch.lastchecked < lastchecked_cutoff, | ||
410 | 363 | BugWatch.lastchecked == None) | ||
411 | 364 | |||
412 | 365 | store = Store.of(self) | 357 | store = Store.of(self) |
413 | 366 | 358 | ||
414 | 367 | bug_watches_needing_checking = store.find( | 359 | bug_watches_needing_checking = store.find( |
415 | 368 | BugWatch, | 360 | BugWatch, |
416 | 369 | BugWatch.bugtracker == self, | 361 | BugWatch.bugtracker == self, |
418 | 370 | lastchecked_clause) | 362 | Not(BugWatch.next_check == None), |
419 | 363 | BugWatch.next_check <= datetime.now(timezone('UTC'))) | ||
420 | 371 | 364 | ||
421 | 372 | bug_watches_with_unpushed_comments = store.find( | 365 | bug_watches_with_unpushed_comments = store.find( |
422 | 373 | BugWatch, | 366 | BugWatch, |
423 | @@ -486,12 +479,15 @@ | |||
424 | 486 | 479 | ||
425 | 487 | return person | 480 | return person |
426 | 488 | 481 | ||
428 | 489 | def resetWatches(self): | 482 | def resetWatches(self, now=None): |
429 | 490 | """See `IBugTracker`.""" | 483 | """See `IBugTracker`.""" |
430 | 484 | if now is None: | ||
431 | 485 | now = datetime.now(timezone('UTC')) | ||
432 | 486 | |||
433 | 491 | store = Store.of(self) | 487 | store = Store.of(self) |
434 | 492 | store.execute( | 488 | store.execute( |
437 | 493 | "UPDATE BugWatch SET lastchecked = NULL WHERE bugtracker = %s" % | 489 | "UPDATE BugWatch SET next_check = %s WHERE bugtracker = %s" % |
438 | 494 | sqlvalues(self)) | 490 | sqlvalues(now, self)) |
439 | 495 | 491 | ||
440 | 496 | 492 | ||
441 | 497 | class BugTrackerSet: | 493 | class BugTrackerSet: |
442 | 498 | 494 | ||
443 | === modified file 'lib/lp/bugs/model/bugwatch.py' | |||
444 | --- lib/lp/bugs/model/bugwatch.py 2010-03-22 13:57:43 +0000 | |||
445 | +++ lib/lp/bugs/model/bugwatch.py 2010-03-24 22:10:48 +0000 | |||
446 | @@ -83,6 +83,7 @@ | |||
447 | 83 | owner = ForeignKey( | 83 | owner = ForeignKey( |
448 | 84 | dbName='owner', foreignKey='Person', | 84 | dbName='owner', foreignKey='Person', |
449 | 85 | storm_validator=validate_public_person, notNull=True) | 85 | storm_validator=validate_public_person, notNull=True) |
450 | 86 | next_check = UtcDateTimeCol() | ||
451 | 86 | 87 | ||
452 | 87 | @property | 88 | @property |
453 | 88 | def bugtasks(self): | 89 | def bugtasks(self): |
454 | 89 | 90 | ||
455 | === modified file 'lib/lp/bugs/scripts/checkwatches.py' | |||
456 | --- lib/lp/bugs/scripts/checkwatches.py 2010-03-24 06:53:49 +0000 | |||
457 | +++ lib/lp/bugs/scripts/checkwatches.py 2010-03-24 22:10:48 +0000 | |||
458 | @@ -408,7 +408,7 @@ | |||
459 | 408 | self.txn.begin() | 408 | self.txn.begin() |
460 | 409 | if not self.updateBugTracker(bug_tracker, batch_size): | 409 | if not self.updateBugTracker(bug_tracker, batch_size): |
461 | 410 | break | 410 | break |
463 | 411 | watches_left = bug_tracker.getBugWatchesNeedingUpdate(23).count() | 411 | watches_left = bug_tracker.getBugWatchesNeedingUpdate().count() |
464 | 412 | self.log.info( | 412 | self.log.info( |
465 | 413 | "%s watches left to check on bug tracker '%s'" % | 413 | "%s watches left to check on bug tracker '%s'" % |
466 | 414 | (watches_left, bug_tracker_name)) | 414 | (watches_left, bug_tracker_name)) |
467 | @@ -466,16 +466,7 @@ | |||
468 | 466 | 466 | ||
469 | 467 | def _updateBugTracker(self, bug_tracker, batch_size=None): | 467 | def _updateBugTracker(self, bug_tracker, batch_size=None): |
470 | 468 | """Updates the given bug trackers's bug watches.""" | 468 | """Updates the given bug trackers's bug watches.""" |
481 | 469 | # XXX 2007-01-18 gmb: | 469 | bug_watches_to_update = bug_tracker.getBugWatchesNeedingUpdate() |
472 | 470 | # Once we start running checkwatches more frequently we need | ||
473 | 471 | # to update the comment and the call to | ||
474 | 472 | # getBugWatchesNeedingUpdate() below. We'll be checking | ||
475 | 473 | # those watches which haven't been checked for 24 hours, not | ||
476 | 474 | # 23. | ||
477 | 475 | # We want 1 day, but we'll use 23 hours because we can't count | ||
478 | 476 | # on the cron job hitting exactly the same time every day | ||
479 | 477 | bug_watches_to_update = ( | ||
480 | 478 | bug_tracker.getBugWatchesNeedingUpdate(23)) | ||
482 | 479 | 470 | ||
483 | 480 | if bug_watches_to_update.count() > 0: | 471 | if bug_watches_to_update.count() > 0: |
484 | 481 | # XXX: GavinPanella 2010-01-18 bug=509223 : Ask remote | 472 | # XXX: GavinPanella 2010-01-18 bug=509223 : Ask remote |
485 | @@ -495,6 +486,7 @@ | |||
486 | 495 | for bug_watch in bug_watches_to_update: | 486 | for bug_watch in bug_watches_to_update: |
487 | 496 | bug_watch.last_error_type = error_type | 487 | bug_watch.last_error_type = error_type |
488 | 497 | bug_watch.lastchecked = UTC_NOW | 488 | bug_watch.lastchecked = UTC_NOW |
489 | 489 | bug_watch.next_check = None | ||
490 | 498 | 490 | ||
491 | 499 | message = ( | 491 | message = ( |
492 | 500 | "ExternalBugtracker for BugTrackerType '%s' is not " | 492 | "ExternalBugtracker for BugTrackerType '%s' is not " |
493 | @@ -734,6 +726,7 @@ | |||
494 | 734 | for bug_watch_id in bug_watch_ids: | 726 | for bug_watch_id in bug_watch_ids: |
495 | 735 | bugwatch = getUtility(IBugWatchSet).get(bug_watch_id) | 727 | bugwatch = getUtility(IBugWatchSet).get(bug_watch_id) |
496 | 736 | bugwatch.lastchecked = UTC_NOW | 728 | bugwatch.lastchecked = UTC_NOW |
497 | 729 | bugwatch.next_check = None | ||
498 | 737 | bugwatch.last_error_type = errortype | 730 | bugwatch.last_error_type = errortype |
499 | 738 | self.txn.commit() | 731 | self.txn.commit() |
500 | 739 | raise | 732 | raise |
501 | @@ -764,6 +757,7 @@ | |||
502 | 764 | for bug_watch_id in bug_watch_ids: | 757 | for bug_watch_id in bug_watch_ids: |
503 | 765 | bugwatch = getUtility(IBugWatchSet).get(bug_watch_id) | 758 | bugwatch = getUtility(IBugWatchSet).get(bug_watch_id) |
504 | 766 | bugwatch.lastchecked = UTC_NOW | 759 | bugwatch.lastchecked = UTC_NOW |
505 | 760 | bugwatch.next_check = None | ||
506 | 767 | bugwatch.last_error_type = errortype | 761 | bugwatch.last_error_type = errortype |
507 | 768 | self.txn.commit() | 762 | self.txn.commit() |
508 | 769 | raise | 763 | raise |
509 | @@ -817,6 +811,7 @@ | |||
510 | 817 | 811 | ||
511 | 818 | for bug_watch in bug_watches: | 812 | for bug_watch in bug_watches: |
512 | 819 | bug_watch.lastchecked = UTC_NOW | 813 | bug_watch.lastchecked = UTC_NOW |
513 | 814 | bug_watch.next_check = None | ||
514 | 820 | if remote_bug_id in unmodified_remote_ids: | 815 | if remote_bug_id in unmodified_remote_ids: |
515 | 821 | continue | 816 | continue |
516 | 822 | 817 | ||
517 | @@ -911,6 +906,7 @@ | |||
518 | 911 | errortype = get_bugwatcherrortype_for_error(error) | 906 | errortype = get_bugwatcherrortype_for_error(error) |
519 | 912 | for bug_watch in bug_watches: | 907 | for bug_watch in bug_watches: |
520 | 913 | bug_watch.lastchecked = UTC_NOW | 908 | bug_watch.lastchecked = UTC_NOW |
521 | 909 | bug_watch.next_check = None | ||
522 | 914 | bug_watch.last_error_type = errortype | 910 | bug_watch.last_error_type = errortype |
523 | 915 | bug_watch.addActivity(result=errortype, oops_id=oops_id) | 911 | bug_watch.addActivity(result=errortype, oops_id=oops_id) |
524 | 916 | # We need to commit the transaction, in case the next | 912 | # We need to commit the transaction, in case the next |
525 | 917 | 913 | ||
526 | === modified file 'lib/lp/bugs/scripts/tests/test_bugimport.py' | |||
527 | --- lib/lp/bugs/scripts/tests/test_bugimport.py 2010-03-23 11:39:09 +0000 | |||
528 | +++ lib/lp/bugs/scripts/tests/test_bugimport.py 2010-03-24 22:10:48 +0000 | |||
529 | @@ -825,7 +825,7 @@ | |||
530 | 825 | self.test_bug_one = test_bug_one | 825 | self.test_bug_one = test_bug_one |
531 | 826 | self.test_bug_two = test_bug_two | 826 | self.test_bug_two = test_bug_two |
532 | 827 | 827 | ||
534 | 828 | def getBugWatchesNeedingUpdate(self, hours): | 828 | def getBugWatchesNeedingUpdate(self): |
535 | 829 | """Returns a sequence of teo bug watches for testing.""" | 829 | """Returns a sequence of teo bug watches for testing.""" |
536 | 830 | return TestResultSequence([ | 830 | return TestResultSequence([ |
537 | 831 | TestBugWatch(1, self.test_bug_one, failing=True), | 831 | TestBugWatch(1, self.test_bug_one, failing=True), |
538 | @@ -905,7 +905,7 @@ | |||
539 | 905 | """ | 905 | """ |
540 | 906 | return [ | 906 | return [ |
541 | 907 | bug_watch for bug_watch in ( | 907 | bug_watch for bug_watch in ( |
543 | 908 | self.bugtracker.getBugWatchesNeedingUpdate(0)) | 908 | self.bugtracker.getBugWatchesNeedingUpdate()) |
544 | 909 | if (bug_watch.remotebug == remote_bug_id and | 909 | if (bug_watch.remotebug == remote_bug_id and |
545 | 910 | bug_watch.id in bug_watch_ids) | 910 | bug_watch.id in bug_watch_ids) |
546 | 911 | ] | 911 | ] |
547 | 912 | 912 | ||
548 | === modified file 'lib/lp/bugs/stories/webservice/xx-bug.txt' | |||
549 | --- lib/lp/bugs/stories/webservice/xx-bug.txt 2010-03-22 23:02:50 +0000 | |||
550 | +++ lib/lp/bugs/stories/webservice/xx-bug.txt 2010-03-24 22:10:48 +0000 | |||
551 | @@ -1025,6 +1025,7 @@ | |||
552 | 1025 | date_created: u'2004-10-04T01:00:00+00:00' | 1025 | date_created: u'2004-10-04T01:00:00+00:00' |
553 | 1026 | date_last_changed: u'2004-10-04T01:00:00+00:00' | 1026 | date_last_changed: u'2004-10-04T01:00:00+00:00' |
554 | 1027 | date_last_checked: u'2004-10-04T01:00:00+00:00' | 1027 | date_last_checked: u'2004-10-04T01:00:00+00:00' |
555 | 1028 | date_next_checked: None | ||
556 | 1028 | last_error_type: None | 1029 | last_error_type: None |
557 | 1029 | owner_link: u'http://.../~mark' | 1030 | owner_link: u'http://.../~mark' |
558 | 1030 | remote_bug: u'2000' | 1031 | remote_bug: u'2000' |
559 | @@ -1096,6 +1097,7 @@ | |||
560 | 1096 | date_created: u'...' | 1097 | date_created: u'...' |
561 | 1097 | date_last_changed: None | 1098 | date_last_changed: None |
562 | 1098 | date_last_checked: None | 1099 | date_last_checked: None |
563 | 1100 | date_next_checked: None | ||
564 | 1099 | last_error_type: None | 1101 | last_error_type: None |
565 | 1100 | owner_link: u'http://.../~salgado' | 1102 | owner_link: u'http://.../~salgado' |
566 | 1101 | remote_bug: u'9876' | 1103 | remote_bug: u'9876' |
567 | 1102 | 1104 | ||
568 | === modified file 'lib/lp/testing/factory.py' | |||
569 | --- lib/lp/testing/factory.py 2010-03-22 07:20:05 +0000 | |||
570 | +++ lib/lp/testing/factory.py 2010-03-24 22:10:48 +0000 | |||
571 | @@ -1209,9 +1209,17 @@ | |||
572 | 1209 | if owner is None: | 1209 | if owner is None: |
573 | 1210 | owner = self.makePerson() | 1210 | owner = self.makePerson() |
574 | 1211 | 1211 | ||
576 | 1212 | return getUtility(IBugWatchSet).createBugWatch( | 1212 | bug_watch = getUtility(IBugWatchSet).createBugWatch( |
577 | 1213 | bug, owner, bugtracker, str(remote_bug)) | 1213 | bug, owner, bugtracker, str(remote_bug)) |
578 | 1214 | 1214 | ||
579 | 1215 | # You need to be an admin to set next_check on a BugWatch. | ||
580 | 1216 | def set_next_check(bug_watch): | ||
581 | 1217 | bug_watch.next_check = datetime.now(pytz.timezone('UTC')) | ||
582 | 1218 | |||
583 | 1219 | person = getUtility(IPersonSet).getByName('name16') | ||
584 | 1220 | run_with_login(person, set_next_check, bug_watch) | ||
585 | 1221 | return bug_watch | ||
586 | 1222 | |||
587 | 1215 | def makeBugAttachment(self, bug=None, owner=None, data=None, | 1223 | def makeBugAttachment(self, bug=None, owner=None, data=None, |
588 | 1216 | comment=None, filename=None, content_type=None, | 1224 | comment=None, filename=None, content_type=None, |
589 | 1217 | description=None, is_patch=_DEFAULT): | 1225 | description=None, is_patch=_DEFAULT): |