Merge lp:~edwin-grubbs/launchpad/bug-535430-needspackaging-timeout-part2 into lp:launchpad/db-devel
- bug-535430-needspackaging-timeout-part2
- Merge into db-devel
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | 9449 | ||||
Proposed branch: | lp:~edwin-grubbs/launchpad/bug-535430-needspackaging-timeout-part2 | ||||
Merge into: | lp:launchpad/db-devel | ||||
Diff against target: |
636 lines (+296/-94) 12 files modified
database/schema/comments.sql (+5/-1) database/schema/patch-2207-56-0.sql (+24/-0) database/schema/security.cfg (+5/-1) lib/lp/bugs/doc/bug-heat.txt (+14/-0) lib/lp/bugs/interfaces/bugtarget.py (+7/-2) lib/lp/bugs/model/bug.py (+2/-2) lib/lp/bugs/model/bugtarget.py (+8/-12) lib/lp/bugs/model/bugtask.py (+2/-2) lib/lp/bugs/tests/test_bugheat.py (+82/-0) lib/lp/registry/interfaces/distributionsourcepackage.py (+17/-1) lib/lp/registry/model/distributionsourcepackage.py (+125/-71) lib/lp/soyuz/model/publishing.py (+5/-2) |
||||
To merge this branch: | bzr merge lp:~edwin-grubbs/launchpad/bug-535430-needspackaging-timeout-part2 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Björn Tillenius (community) | db | Abstain | |
Brad Crittenden (community) | code | Approve | |
Review via email: mp+26830@code.launchpad.net |
Commit message
Description of the change
Summary
-------
This branch is a follow-up to
lp:~edwin-grubbs/launchpad/bug-535430-needspackaging-timeout-part2
It adds the attributes to the DistributionSou
DistributionSou
DistributionSou
DistributionSou
classes is deferred, since it is not certain yet whether errors would
occur if a DSP object required a SPPH row to be created first. I added
code to report a silent oops if there is an unexpected creation of a
DSP.
This branch also popuplates total_bug_heat and bug_count columns in the
DistributionSou
po_message_count column will be populated in a later branch.
Implementation details
-------
Allow db users 'lucille', 'uploader', and 'queued' to create a DSP entry
when a SPPH record is created. Allow db user 'processmail' to read the
section table, since that is necessary to check the foreign key on the
DSP table.
database/
Added tests.
lib/
lib/
Automatically insert into DistributionSou
SourcePackagePu
Renamed recalculateMaxB
overrode it in the model's DistributionSou
total_bug_heat and bug_count in addition to max_bug_heat, which it was
already setting.
lib/
lib/
lib/
lib/
lib/
lib/
lib/
Tests
-----
./bin/test -vv -t 'bug-heat.
Edwin Grubbs (edwin-grubbs) wrote : | # |
I assume the changes to database/
I forgot to create this merge proposal with the previous branch as the dependency, so you will see the same schema changes that have already been reviewed.
Björn Tillenius (bjornt) wrote : | # |
Changes to security.cfg don't need a DB review, just make sure they are tested.
Preview Diff
1 | === modified file 'database/schema/comments.sql' |
2 | --- database/schema/comments.sql 2010-06-03 22:21:24 +0000 |
3 | +++ database/schema/comments.sql 2010-06-10 01:43:25 +0000 |
4 | @@ -472,7 +472,11 @@ |
5 | |
6 | COMMENT ON TABLE DistributionSourcePackage IS 'Representing a sourcepackage in a distribution across all distribution series.'; |
7 | COMMENT ON COLUMN DistributionSourcePackage.bug_reporting_guidelines IS 'Guidelines to the end user for reporting bugs on a particular a source package in a distribution.'; |
8 | -COMMENT ON COLUMN DistributionSourcePackage.max_bug_heat IS 'The highest heat value across bugs for this source package.'; |
9 | +COMMENT ON COLUMN DistributionSourcePackage.max_bug_heat IS 'The highest heat value across bugs for this source package. NULL means it has not yet been calculated.'; |
10 | +COMMENT ON COLUMN DistributionSourcePackage.total_bug_heat IS 'Sum of bug heat matching the package distribution and sourcepackagename. NULL means it has not yet been calculated.'; |
11 | +COMMENT ON COLUMN DistributionSourcePackage.bug_count IS 'Number of bugs matching the package distribution and sourcepackagename. NULL means it has not yet been calculated.'; |
12 | +COMMENT ON COLUMN DistributionSourcePackage.po_message_count IS 'Number of translations matching the package distribution and sourcepackagename. NULL means it has not yet been calculated.'; |
13 | +COMMENT ON COLUMN DistributionSourcePackage.section IS 'Cached section matching the latest SourcePackagePublishingHistory record by distribution and sourcepackagename whose archive purpose is PRIMARY and whose distroseries releasestatus is CURRENT.'; |
14 | COMMENT ON COLUMN DistributionSourcePackage.bug_reported_acknowledgement IS 'A message of acknowledgement to display to a bug reporter after they\'ve reported a new bug.'; |
15 | |
16 | -- DistributionSourcePackageCache |
17 | |
18 | === added file 'database/schema/patch-2207-56-0.sql' |
19 | --- database/schema/patch-2207-56-0.sql 1970-01-01 00:00:00 +0000 |
20 | +++ database/schema/patch-2207-56-0.sql 2010-06-10 01:43:25 +0000 |
21 | @@ -0,0 +1,24 @@ |
22 | +-- Copyright 2010 Canonical Ltd. This software is licensed under the |
23 | +-- GNU Affero General Public License version 3 (see the file LICENSE). |
24 | + |
25 | +SET client_min_messages=ERROR; |
26 | + |
27 | +INSERT INTO LaunchpadDatabaseRevision VALUES (2207, 56, 0); |
28 | + |
29 | +/* |
30 | +Existing Schema: |
31 | + |
32 | +CREATE TABLE distributionsourcepackage ( |
33 | + id integer NOT NULL, |
34 | + distribution integer NOT NULL, |
35 | + sourcepackagename integer NOT NULL, |
36 | + bug_reporting_guidelines text, |
37 | + max_bug_heat integer |
38 | +); |
39 | +*/ |
40 | + |
41 | +ALTER TABLE DistributionSourcePackage ADD COLUMN total_bug_heat INTEGER; |
42 | +ALTER TABLE DistributionSourcePackage ADD COLUMN bug_count INTEGER; |
43 | +ALTER TABLE DistributionSourcePackage ADD COLUMN po_message_count INTEGER; |
44 | +ALTER TABLE DistributionSourcePackage |
45 | + ADD COLUMN section INTEGER NOT NULL REFERENCES section(id); |
46 | |
47 | === modified file 'database/schema/security.cfg' |
48 | --- database/schema/security.cfg 2010-06-07 10:24:03 +0000 |
49 | +++ database/schema/security.cfg 2010-06-10 01:43:25 +0000 |
50 | @@ -746,6 +746,7 @@ |
51 | public.binarypackagepublishinghistory = SELECT, INSERT, UPDATE, DELETE |
52 | public.sourcepackagepublishinghistory = SELECT, INSERT, UPDATE, DELETE |
53 | public.sourcepackagepublishinghistory = SELECT |
54 | +public.distributionsourcepackage = SELECT, INSERT, UPDATE |
55 | |
56 | # Closing bugs for publication copies. |
57 | public.bug = SELECT, UPDATE |
58 | @@ -1051,6 +1052,7 @@ |
59 | public.distroseries = SELECT, UPDATE |
60 | public.distroarchseries = SELECT |
61 | public.sourcepackagepublishinghistory = SELECT |
62 | +public.distributionsourcepackage = SELECT, INSERT, UPDATE |
63 | public.sourcepackagefilepublishing = SELECT |
64 | public.binarypackagefilepublishing = SELECT |
65 | public.binarypackagepublishinghistory = SELECT |
66 | @@ -1175,8 +1177,9 @@ |
67 | public.sourcepackagefilepublishing = SELECT |
68 | public.binarypackagefilepublishing = SELECT |
69 | public.sourcepackagepublishinghistory = SELECT, INSERT, UPDATE |
70 | +public.distributionsourcepackage = SELECT, INSERT, UPDATE |
71 | public.binarypackagepublishinghistory = SELECT, INSERT, UPDATE |
72 | -public.sourcepackagerecipebuildjob = SELECT, INSERT, UPDATE |
73 | +public.sourcepackagerecipebuildjob = SELECT, INSERT, UPDATE |
74 | public.component = SELECT |
75 | public.section = SELECT |
76 | public.componentselection = SELECT |
77 | @@ -1464,6 +1467,7 @@ |
78 | public.sourcepackagerelease = SELECT |
79 | public.sourcepackagepublishinghistory = SELECT |
80 | public.structuralsubscription = SELECT |
81 | +public.section = SELECT |
82 | |
83 | # Karma |
84 | public.karma = SELECT, INSERT |
85 | |
86 | === modified file 'lib/lp/bugs/doc/bug-heat.txt' |
87 | --- lib/lp/bugs/doc/bug-heat.txt 2010-05-27 16:40:54 +0000 |
88 | +++ lib/lp/bugs/doc/bug-heat.txt 2010-06-10 01:43:25 +0000 |
89 | @@ -308,6 +308,20 @@ |
90 | >>> print dsp.max_bug_heat |
91 | 123 |
92 | |
93 | +A DistributionSourcePackage also has a cached value for bug_count and |
94 | +total_bug_heat. |
95 | + |
96 | + >>> print dsp.bug_count |
97 | + 1 |
98 | + >>> print dsp.total_bug_heat |
99 | + 123 |
100 | + >>> dsp_task2 = factory.makeBugTask(target=dsp) |
101 | + >>> dsp_task2.bug.setHeat(7) |
102 | + >>> print dsp.bug_count |
103 | + 2 |
104 | + >>> print dsp.total_bug_heat |
105 | + 130 |
106 | + |
107 | Transitioning from one target to another, calculates the value for the new |
108 | target. |
109 | |
110 | |
111 | === modified file 'lib/lp/bugs/interfaces/bugtarget.py' |
112 | --- lib/lp/bugs/interfaces/bugtarget.py 2010-06-09 08:26:26 +0000 |
113 | +++ lib/lp/bugs/interfaces/bugtarget.py 2010-06-10 01:43:25 +0000 |
114 | @@ -272,8 +272,13 @@ |
115 | def setMaxBugHeat(heat): |
116 | """Set the max_bug_heat for this context.""" |
117 | |
118 | - def recalculateMaxBugHeat(): |
119 | - """Recalculate and set the max_bug_heat for this context.""" |
120 | + def recalculateBugHeatCache(): |
121 | + """Recalculate and set the various bug heat values for this context. |
122 | + |
123 | + Several different objects cache max_bug_heat. |
124 | + When DistributionSourcePackage is the target, the total_bug_heat |
125 | + and bug_count are also cached. |
126 | + """ |
127 | |
128 | |
129 | class BugDistroSeriesTargetDetails: |
130 | |
131 | === modified file 'lib/lp/bugs/model/bug.py' |
132 | --- lib/lp/bugs/model/bug.py 2010-06-04 09:12:26 +0000 |
133 | +++ lib/lp/bugs/model/bug.py 2010-06-10 01:43:25 +0000 |
134 | @@ -887,7 +887,7 @@ |
135 | |
136 | # When a new task is added the bug's heat becomes relevant to the |
137 | # target's max_bug_heat. |
138 | - target.recalculateMaxBugHeat() |
139 | + target.recalculateBugHeatCache() |
140 | |
141 | return new_task |
142 | |
143 | @@ -1569,7 +1569,7 @@ |
144 | self.heat = heat |
145 | self.heat_last_updated = timestamp |
146 | for task in self.bugtasks: |
147 | - task.target.recalculateMaxBugHeat() |
148 | + task.target.recalculateBugHeatCache() |
149 | |
150 | def updateHeat(self): |
151 | """See `IBug`.""" |
152 | |
153 | === modified file 'lib/lp/bugs/model/bugtarget.py' |
154 | --- lib/lp/bugs/model/bugtarget.py 2010-05-21 04:38:42 +0000 |
155 | +++ lib/lp/bugs/model/bugtarget.py 2010-06-10 01:43:25 +0000 |
156 | @@ -235,12 +235,15 @@ |
157 | else: |
158 | raise NotImplementedError |
159 | |
160 | - def recalculateMaxBugHeat(self): |
161 | - """See `IHasBugHeat`.""" |
162 | + def recalculateBugHeatCache(self): |
163 | + """See `IHasBugHeat`. |
164 | + |
165 | + DistributionSourcePackage overrides this method. |
166 | + """ |
167 | if IProductSeries.providedBy(self): |
168 | - return self.product.recalculateMaxBugHeat() |
169 | + return self.product.recalculateBugHeatCache() |
170 | if IDistroSeries.providedBy(self): |
171 | - return self.distribution.recalculateMaxBugHeat() |
172 | + return self.distribution.recalculateBugHeatCache() |
173 | if ISourcePackage.providedBy(self): |
174 | # Should only happen for nominations, so we can safely skip |
175 | # recalculating max_heat. |
176 | @@ -276,13 +279,6 @@ |
177 | WHERE Bugtask.bug = Bug.id AND |
178 | Bugtask.product = Product.id AND |
179 | Product.project = %s""" % sqlvalues(self)] |
180 | - elif IDistributionSourcePackage.providedBy(self): |
181 | - sql = ["""SELECT MAX(heat) |
182 | - FROM Bug, Bugtask |
183 | - WHERE Bugtask.bug = Bug.id AND |
184 | - Bugtask.distribution = %s AND |
185 | - Bugtask.sourcepackagename = %s""" % sqlvalues( |
186 | - self.distribution, self.sourcepackagename)] |
187 | else: |
188 | raise NotImplementedError |
189 | |
190 | @@ -299,7 +295,7 @@ |
191 | # If the product is part of a project group we calculate the maximum |
192 | # heat for the project group too. |
193 | if IProduct.providedBy(self) and self.project is not None: |
194 | - self.project.recalculateMaxBugHeat() |
195 | + self.project.recalculateBugHeatCache() |
196 | |
197 | |
198 | |
199 | |
200 | === modified file 'lib/lp/bugs/model/bugtask.py' |
201 | --- lib/lp/bugs/model/bugtask.py 2010-06-07 18:11:08 +0000 |
202 | +++ lib/lp/bugs/model/bugtask.py 2010-06-10 01:43:25 +0000 |
203 | @@ -1045,8 +1045,8 @@ |
204 | # After the target has changed, we need to recalculate the maximum bug |
205 | # heat for the new and old targets. |
206 | if self.target != target_before_change: |
207 | - target_before_change.recalculateMaxBugHeat() |
208 | - self.target.recalculateMaxBugHeat() |
209 | + target_before_change.recalculateBugHeatCache() |
210 | + self.target.recalculateBugHeatCache() |
211 | |
212 | def updateTargetNameCache(self, newtarget=None): |
213 | """See `IBugTask`.""" |
214 | |
215 | === modified file 'lib/lp/bugs/tests/test_bugheat.py' |
216 | --- lib/lp/bugs/tests/test_bugheat.py 2010-06-01 09:21:32 +0000 |
217 | +++ lib/lp/bugs/tests/test_bugheat.py 2010-06-10 01:43:25 +0000 |
218 | @@ -7,8 +7,11 @@ |
219 | |
220 | import unittest |
221 | |
222 | +from storm.store import Store |
223 | + |
224 | from canonical.testing import LaunchpadZopelessLayer |
225 | |
226 | +from lp.testing import TestCaseWithFactory |
227 | from lp.testing.factory import LaunchpadObjectFactory |
228 | |
229 | |
230 | @@ -58,6 +61,85 @@ |
231 | def setUp(self): |
232 | self.target = self.factory.makeDistributionSourcePackage() |
233 | |
234 | +class DistributionSourcePackageNullBugHeatCacheTest( |
235 | + TestCaseWithFactory): |
236 | + """Ensure distro source package cache values start at None.""" |
237 | + |
238 | + layer = LaunchpadZopelessLayer |
239 | + |
240 | + def setUp(self): |
241 | + TestCaseWithFactory.setUp(self) |
242 | + self.target = self.factory.makeDistributionSourcePackage() |
243 | + |
244 | + def test_null_max_bug_heat(self): |
245 | + self.assertEqual(None, self.target.max_bug_heat) |
246 | + |
247 | + def test_null_total_bug_heat(self): |
248 | + self.assertEqual(None, self.target.total_bug_heat) |
249 | + |
250 | + def test_null_bug_count(self): |
251 | + self.assertEqual(None, self.target.bug_count) |
252 | + |
253 | + |
254 | +class DistributionSourcePackageZeroRecalculateBugHeatCacheTest( |
255 | + TestCaseWithFactory): |
256 | + """Ensure distro source package cache values become zero properly.""" |
257 | + |
258 | + layer = LaunchpadZopelessLayer |
259 | + |
260 | + def setUp(self): |
261 | + TestCaseWithFactory.setUp(self) |
262 | + self.target = self.factory.makeDistributionSourcePackage() |
263 | + self.target.recalculateBugHeatCache() |
264 | + |
265 | + def test_zero_max_bug_heat(self): |
266 | + self.assertEqual(0, self.target.max_bug_heat) |
267 | + |
268 | + def test_zero_total_bug_heat(self): |
269 | + self.assertEqual(0, self.target.total_bug_heat) |
270 | + |
271 | + def test_zero_bug_count(self): |
272 | + self.assertEqual(0, self.target.bug_count) |
273 | + |
274 | + |
275 | +class DistributionSourcePackageMultipleBugsRecalculateBugHeatCacheTest( |
276 | + TestCaseWithFactory): |
277 | + """Ensure distro source package cache values are set properly.""" |
278 | + |
279 | + layer = LaunchpadZopelessLayer |
280 | + |
281 | + def setUp(self): |
282 | + TestCaseWithFactory.setUp(self) |
283 | + self.target = self.factory.makeDistributionSourcePackage() |
284 | + self.bugtask1 = self.factory.makeBugTask(target=self.target) |
285 | + self.bugtask2 = self.factory.makeBugTask(target=self.target) |
286 | + # Bug heat gets calculated by complicated rules in a db |
287 | + # stored procedure. We will override them here to avoid |
288 | + # testing inconsitencies if those values are calculated |
289 | + # differently in the future. |
290 | + # target.recalculateBugHeatCache() should be called |
291 | + # automatically by bug.setHeat(). |
292 | + bug1 = self.bugtask1.bug |
293 | + bug2 = self.bugtask2.bug |
294 | + bug1.setHeat(7) |
295 | + bug2.setHeat(19) |
296 | + Store.of(bug1).flush() |
297 | + self.max_heat = max(bug1.heat, bug2.heat) |
298 | + self.total_heat = sum([bug1.heat, bug2.heat]) |
299 | + |
300 | + def test_max_bug_heat(self): |
301 | + self.assertEqual(self.max_heat, self.target.max_bug_heat) |
302 | + |
303 | + def test_total_bug_heat(self): |
304 | + self.assertEqual(self.total_heat, self.target.total_bug_heat) |
305 | + self.failUnless( |
306 | + self.target.total_bug_heat > self.target.max_bug_heat, |
307 | + "Total bug heat should be more than the max bug heat, " |
308 | + "since we know that multiple bugs have nonzero heat.") |
309 | + |
310 | + def test_bug_count(self): |
311 | + self.assertEqual(2, self.target.bug_count) |
312 | + |
313 | |
314 | class SourcePackageMaxHeatByTargetTest( |
315 | MaxHeatByTargetBase, unittest.TestCase): |
316 | |
317 | === modified file 'lib/lp/registry/interfaces/distributionsourcepackage.py' |
318 | --- lib/lp/registry/interfaces/distributionsourcepackage.py 2010-02-12 13:39:56 +0000 |
319 | +++ lib/lp/registry/interfaces/distributionsourcepackage.py 2010-06-10 01:43:25 +0000 |
320 | @@ -78,9 +78,25 @@ |
321 | "no such package -- this occurs when there is no current series for " |
322 | "the distribution.") |
323 | |
324 | + total_bug_heat = Attribute( |
325 | + "Sum of the bug heat for all the bugs matching the distribution " |
326 | + "and sourcepackagename of the IDistributionSourcePackage.") |
327 | + |
328 | + max_bug_heat = Attribute( |
329 | + "Maximum bug heat for a single bug matching the distribution " |
330 | + "and sourcepackagename of the IDistributionSourcePackage.") |
331 | + |
332 | + bug_count = Attribute( |
333 | + "Number of bugs matching the distribution and sourcepackagename " |
334 | + "of the IDistributionSourcePackage.") |
335 | + |
336 | + po_message_count = Attribute( |
337 | + "Number of translations matching the distribution and " |
338 | + "sourcepackagename of the IDistributionSourcePackage.") |
339 | + |
340 | def getReleasesAndPublishingHistory(): |
341 | """Return a list of all releases of this source package in this |
342 | - distribution and their correspodning publishing history. |
343 | + distribution and their corresponding publishing history. |
344 | |
345 | Items in the list are tuples comprised of a |
346 | DistributionSourcePackage and a list of |
347 | |
348 | === modified file 'lib/lp/registry/model/distributionsourcepackage.py' |
349 | --- lib/lp/registry/model/distributionsourcepackage.py 2010-06-08 13:07:44 +0000 |
350 | +++ lib/lp/registry/model/distributionsourcepackage.py 2010-06-10 01:43:25 +0000 |
351 | @@ -15,13 +15,17 @@ |
352 | import operator |
353 | |
354 | from sqlobject.sqlbuilder import SQLConstant |
355 | -from storm.expr import And, Desc, In, Join, Lower |
356 | +from storm.expr import And, Count, Desc, In, Join, Lower, Max, Sum |
357 | from storm.store import EmptyResultSet |
358 | from storm.locals import Int, Reference, Store, Storm, Unicode |
359 | +from zope.component import getUtility |
360 | +from zope.error.interfaces import IErrorReportingUtility |
361 | from zope.interface import implements |
362 | |
363 | + |
364 | from canonical.database.sqlbase import sqlvalues |
365 | from canonical.launchpad.database.emailaddress import EmailAddress |
366 | +from lp.registry.interfaces.series import SeriesStatus |
367 | from lp.registry.model.distroseries import DistroSeries |
368 | from lp.registry.model.packaging import Packaging |
369 | from lp.registry.model.structuralsubscription import ( |
370 | @@ -30,7 +34,7 @@ |
371 | from canonical.lazr.utils import smartquote |
372 | from lp.answers.interfaces.questiontarget import IQuestionTarget |
373 | from lp.bugs.interfaces.bugtarget import IHasBugHeat |
374 | -from lp.bugs.model.bug import BugSet, get_bug_tags_open_count |
375 | +from lp.bugs.model.bug import Bug, BugSet, get_bug_tags_open_count |
376 | from lp.bugs.model.bugtarget import BugTargetBase, HasBugHeatMixin |
377 | from lp.bugs.model.bugtask import BugTask |
378 | from lp.code.model.hasbranches import HasBranchesMixin, HasMergeProposalsMixin |
379 | @@ -43,6 +47,7 @@ |
380 | SourcePackage, SourcePackageQuestionTargetMixin) |
381 | from lp.soyuz.interfaces.archive import ArchivePurpose |
382 | from lp.soyuz.interfaces.publishing import PackagePublishingStatus |
383 | +from lp.soyuz.interfaces.section import ISectionSet |
384 | from lp.soyuz.model.archive import Archive |
385 | from lp.soyuz.model.distributionsourcepackagerelease import ( |
386 | DistributionSourcePackageRelease) |
387 | @@ -54,6 +59,40 @@ |
388 | CustomLanguageCode, HasCustomLanguageCodesMixin) |
389 | |
390 | |
391 | +class DistributionSourcePackageProperty: |
392 | + def __init__(self, attrname): |
393 | + self.attrname = attrname |
394 | + |
395 | + def __get__(self, obj, class_): |
396 | + return getattr(obj._self_in_database, self.attrname, None) |
397 | + |
398 | + def __set__(self, obj, value): |
399 | + if obj._self_in_database is None: |
400 | + # Log an oops without raising an error. |
401 | + exception = AssertionError( |
402 | + "DistributionSourcePackage record should have been created " |
403 | + "earlier in the database for distro=%s, sourcepackagename=%s" |
404 | + % (obj.distribution.name, obj.sourcepackagename.name)) |
405 | + getUtility(IErrorReportingUtility).raising( |
406 | + (exception.__class__, exception, None)) |
407 | + spph = Store.of(obj.distribution).find( |
408 | + SourcePackagePublishingHistory, |
409 | + SourcePackagePublishingHistory.distroseriesID == |
410 | + DistroSeries.id, |
411 | + DistroSeries.distributionID == obj.distribution.id, |
412 | + SourcePackagePublishingHistory.sourcepackagereleaseID == |
413 | + SourcePackageRelease.id, |
414 | + SourcePackageRelease.sourcepackagenameID == |
415 | + obj.sourcepackagename.id |
416 | + ).order_by(Desc(SourcePackagePublishingHistory.id)).first() |
417 | + if spph is None: |
418 | + section = getUtility(ISectionSet)['misc'] |
419 | + else: |
420 | + section = spph.section |
421 | + obj._new(obj.distribution, obj.sourcepackagename, section) |
422 | + setattr(obj._self_in_database, self.attrname, value) |
423 | + |
424 | + |
425 | class DistributionSourcePackage(BugTargetBase, |
426 | SourcePackageQuestionTargetMixin, |
427 | StructuralSubscriptionTargetMixin, |
428 | @@ -72,6 +111,16 @@ |
429 | IDistributionSourcePackage, IHasBugHeat, IHasCustomLanguageCodes, |
430 | IQuestionTarget) |
431 | |
432 | + bug_reporting_guidelines = DistributionSourcePackageProperty( |
433 | + 'bug_reporting_guidelines') |
434 | + bug_reported_acknowledgement = DistributionSourcePackageProperty( |
435 | + 'bug_reported_acknowledgement') |
436 | + max_bug_heat = DistributionSourcePackageProperty('max_bug_heat') |
437 | + total_bug_heat = DistributionSourcePackageProperty('total_bug_heat') |
438 | + bug_count = DistributionSourcePackageProperty('bug_count') |
439 | + po_message_count = DistributionSourcePackageProperty('po_message_count') |
440 | + section = DistributionSourcePackageProperty('section') |
441 | + |
442 | def __init__(self, distribution, sourcepackagename): |
443 | self.distribution = distribution |
444 | self.sourcepackagename = sourcepackagename |
445 | @@ -118,75 +167,34 @@ |
446 | # measure while DistributionSourcePackage is not yet hooked |
447 | # into the database but we need access to some of the fields |
448 | # in the database. |
449 | - return Store.of(self.distribution).find( |
450 | - DistributionSourcePackageInDatabase, |
451 | - DistributionSourcePackageInDatabase.sourcepackagename == ( |
452 | - self.sourcepackagename), |
453 | - DistributionSourcePackageInDatabase.distribution == ( |
454 | - self.distribution) |
455 | - ).one() |
456 | - |
457 | - def _get_bug_reporting_guidelines(self): |
458 | - """See `IBugTarget`.""" |
459 | - dsp_in_db = self._self_in_database |
460 | - if dsp_in_db is not None: |
461 | - return dsp_in_db.bug_reporting_guidelines |
462 | - return None |
463 | - |
464 | - def _set_bug_reporting_guidelines(self, value): |
465 | - """See `IBugTarget`.""" |
466 | - dsp_in_db = self._self_in_database |
467 | - if dsp_in_db is None: |
468 | - dsp_in_db = DistributionSourcePackageInDatabase() |
469 | - dsp_in_db.sourcepackagename = self.sourcepackagename |
470 | - dsp_in_db.distribution = self.distribution |
471 | - Store.of(self.distribution).add(dsp_in_db) |
472 | - dsp_in_db.bug_reporting_guidelines = value |
473 | - |
474 | - bug_reporting_guidelines = property( |
475 | - _get_bug_reporting_guidelines, |
476 | - _set_bug_reporting_guidelines) |
477 | - |
478 | - def _get_bug_reported_acknowledgement(self): |
479 | - """See `IBugTarget`.""" |
480 | - dsp_in_db = self._self_in_database |
481 | - if dsp_in_db is not None: |
482 | - return dsp_in_db.bug_reported_acknowledgement |
483 | - return None |
484 | - |
485 | - def _set_bug_reported_acknowledgement(self, value): |
486 | - """See `IBugTarget`.""" |
487 | - dsp_in_db = self._self_in_database |
488 | - if dsp_in_db is None: |
489 | - dsp_in_db = DistributionSourcePackageInDatabase() |
490 | - dsp_in_db.sourcepackagename = self.sourcepackagename |
491 | - dsp_in_db.distribution = self.distribution |
492 | - Store.of(self.distribution).add(dsp_in_db) |
493 | - dsp_in_db.bug_reported_acknowledgement = value |
494 | - |
495 | - bug_reported_acknowledgement = property( |
496 | - _get_bug_reported_acknowledgement, |
497 | - _set_bug_reported_acknowledgement) |
498 | - |
499 | - def _get_max_bug_heat(self): |
500 | - """See `IHasBugs`.""" |
501 | - dsp_in_db = self._self_in_database |
502 | - if dsp_in_db is None: |
503 | - return None |
504 | - else: |
505 | - return dsp_in_db.max_bug_heat |
506 | - |
507 | - def _set_max_bug_heat(self, value): |
508 | - """See `IHasBugs`.""" |
509 | - dsp_in_db = self._self_in_database |
510 | - if dsp_in_db is None: |
511 | - dsp_in_db = DistributionSourcePackageInDatabase() |
512 | - dsp_in_db.sourcepackagename = self.sourcepackagename |
513 | - dsp_in_db.distribution = self.distribution |
514 | - Store.of(self.distribution).add(dsp_in_db) |
515 | - dsp_in_db.max_bug_heat = value |
516 | - |
517 | - max_bug_heat = property(_get_max_bug_heat, _set_max_bug_heat) |
518 | + return self._get(self.distribution, self.sourcepackagename) |
519 | + |
520 | + def recalculateBugHeatCache(self): |
521 | + """See `IHasBugHeat`.""" |
522 | + self.max_bug_heat = IStore(Bug).find( |
523 | + Max(Bug.heat), |
524 | + BugTask.bug == Bug.id, |
525 | + BugTask.distributionID == self.distribution.id, |
526 | + BugTask.sourcepackagenameID == self.sourcepackagename.id).one() |
527 | + if self.max_bug_heat is None: |
528 | + # SELECT MAX(Bug.heat) returns NULL if zero rows match. |
529 | + self.max_bug_heat = 0 |
530 | + |
531 | + self.total_bug_heat = IStore(Bug).find( |
532 | + Sum(Bug.heat), |
533 | + BugTask.bug == Bug.id, |
534 | + BugTask.distributionID == self.distribution.id, |
535 | + BugTask.sourcepackagenameID == self.sourcepackagename.id).one() |
536 | + if self.total_bug_heat is None: |
537 | + self.total_bug_heat = 0 |
538 | + |
539 | + self.bug_count = IStore(Bug).find( |
540 | + Count(Bug.heat), |
541 | + BugTask.bug == Bug.id, |
542 | + BugTask.distributionID == self.distribution.id, |
543 | + BugTask.sourcepackagenameID == self.sourcepackagename.id).one() |
544 | + if self.bug_count is None: |
545 | + self.bug_count = 0 |
546 | |
547 | @property |
548 | def latest_overall_publication(self): |
549 | @@ -504,6 +512,47 @@ |
550 | In(Lower(EmailAddress.email), email_addresses)) |
551 | return result_set |
552 | |
553 | + @classmethod |
554 | + def _get(cls, distribution, sourcepackagename): |
555 | + return Store.of(distribution).find( |
556 | + DistributionSourcePackageInDatabase, |
557 | + DistributionSourcePackageInDatabase.sourcepackagename == |
558 | + sourcepackagename, |
559 | + DistributionSourcePackageInDatabase.distribution == |
560 | + distribution |
561 | + ).one() |
562 | + |
563 | + @classmethod |
564 | + def _new(cls, distribution, sourcepackagename, section): |
565 | + dsp = DistributionSourcePackageInDatabase() |
566 | + dsp.distribution = distribution |
567 | + dsp.sourcepackagename = sourcepackagename |
568 | + dsp.section = section |
569 | + Store.of(distribution).add(dsp) |
570 | + return dsp |
571 | + |
572 | + @classmethod |
573 | + def ensure(cls, spph): |
574 | + """Create DistributionSourcePackage record, if necessary. |
575 | + |
576 | + Only create a record for primary archives (i.e. not for PPAs). |
577 | + If it already exists, update the section attribute. |
578 | + """ |
579 | + # Import here to avoid import loop. |
580 | + sourcepackagename = spph.sourcepackagerelease.sourcepackagename |
581 | + distribution = spph.distroseries.distribution |
582 | + section = spph.section |
583 | + |
584 | + if spph.archive.purpose == ArchivePurpose.PRIMARY: |
585 | + dsp = cls._get(distribution, sourcepackagename) |
586 | + if dsp is None: |
587 | + dsp = cls._new(distribution, sourcepackagename, section) |
588 | + elif spph.distroseries.status == SeriesStatus.CURRENT: |
589 | + # If the distroseries.status is not CURRENT, it should |
590 | + # not override the section from a previously created |
591 | + # SourcePackagePublishingHistory. |
592 | + dsp.section = section |
593 | + |
594 | |
595 | class DistributionSourcePackageInDatabase(Storm): |
596 | """Temporary class to allow access to the database.""" |
597 | @@ -529,4 +578,9 @@ |
598 | bug_reported_acknowledgement = Unicode() |
599 | |
600 | max_bug_heat = Int() |
601 | + total_bug_heat = Int() |
602 | + bug_count = Int() |
603 | + po_message_count = Int() |
604 | |
605 | + section_id = Int(name='section') |
606 | + section = Reference(section_id, 'Section.id') |
607 | |
608 | === modified file 'lib/lp/soyuz/model/publishing.py' |
609 | --- lib/lp/soyuz/model/publishing.py 2010-05-21 12:25:51 +0000 |
610 | +++ lib/lp/soyuz/model/publishing.py 2010-06-10 01:43:25 +0000 |
611 | @@ -49,12 +49,12 @@ |
612 | from lp.soyuz.model.binarypackagename import BinaryPackageName |
613 | from lp.soyuz.model.binarypackagerelease import (BinaryPackageRelease, |
614 | BinaryPackageReleaseDownloadCount) |
615 | +from lp.soyuz.interfaces.archive import ArchivePurpose |
616 | from lp.soyuz.model.files import ( |
617 | BinaryPackageFile, SourcePackageReleaseFile) |
618 | from canonical.launchpad.database.librarian import ( |
619 | LibraryFileAlias, LibraryFileContent) |
620 | from lp.soyuz.model.packagediff import PackageDiff |
621 | -from lp.soyuz.interfaces.archive import ArchivePurpose |
622 | from lp.soyuz.interfaces.archivearch import IArchiveArchSet |
623 | from lp.soyuz.interfaces.binarypackagebuild import ( |
624 | BuildSetStatus, IBinaryPackageBuildSet) |
625 | @@ -1162,7 +1162,10 @@ |
626 | section=section, |
627 | status=PackagePublishingStatus.PENDING, |
628 | datecreated=UTC_NOW) |
629 | - |
630 | + # Import here to prevent import loop. |
631 | + from lp.registry.model.distributionsourcepackage import ( |
632 | + DistributionSourcePackage) |
633 | + DistributionSourcePackage.ensure(pub) |
634 | return pub |
635 | |
636 | def getBuildsForSourceIds( |
Hi Edwin,
This change looks great. I marked a couple of niggly bits but that's
all.
--bac
> === added file 'database/ schema/ patch-2207- 56-0.sql' schema/ patch-2207- 56-0.sql 1970-01-01 00:00:00 +0000 schema/ patch-2207- 56-0.sql 2010-06-04 18:48:29 +0000
> --- database/
> +++ database/
> @@ -0,0 +1,24 @@
> +-- Copyright 2009 Canonical Ltd. This software is licensed under the
2010
(The contents of the patch are left for a db review.)
> === modified file 'lib/lp/ bugs/tests/ test_bugheat. py' bugs/tests/ test_bugheat. py 2010-06-01 09:21:32 +0000 bugs/tests/ test_bugheat. py 2010-06-04 18:48:29 +0000
> --- lib/lp/
> +++ lib/lp/
> @@ -7,8 +7,11 @@
Edwin this test is really readable and easy to follow. Good job.
> import unittest ssLayer Factory makeDistributio nSourcePackage( ) rcePackageNullB ugHeatCacheTest ( tory): ssLayer tory.setUp( self) makeDistributio nSourcePackage( ) max_bug_ heat(self) : l(None, self.target. max_bug_ heat) total_bug_ heat(self) : l(None, self.target. total_bug_ heat) bug_count( self): l(None, self.target. bug_count) rcePackageZeroR ecalculateBugHe atCacheTest( tory): ssLayer tory.setUp( self) makeDistributio nSourcePackage( ) recalculateBugH eatCache( ) max_bug_ heat(self) : max_bug_ heat) total_bug_ heat(self) : total_bug_ heat) bug_count( self): bug_count) rcePackageMulti pleBugsRecalcul ateBugHeatCache Test( tory): ssLayer tory.setUp( self) makeDistributio nSourcePackage( ) makeBugTask( target= self.target) makeBugTask( target= self.target)
>
> +from storm.store import Store
> +
> from canonical.testing import LaunchpadZopele
>
> +from lp.testing import TestCaseWithFactory
> from lp.testing.factory import LaunchpadObject
>
>
> @@ -58,6 +61,85 @@
> def setUp(self):
> self.target = self.factory.
>
> +class DistributionSou
> + TestCaseWithFac
> + """Ensure distro source package cache values start at None."""
> +
> + layer = LaunchpadZopele
> +
> + def setUp(self):
> + TestCaseWithFac
> + self.target = self.factory.
> +
> + def test_null_
> + self.assertEqua
> +
> + def test_null_
> + self.assertEqua
> +
> + def test_null_
> + self.assertEqua
> +
> +
> +class DistributionSou
> + TestCaseWithFac
> + """Ensure distro source package cache values become zero properly."""
> +
> + layer = LaunchpadZopele
> +
> + def setUp(self):
> + TestCaseWithFac
> + self.target = self.factory.
> + self.target.
> +
> + def test_zero_
> + self.assertEqual(0, self.target.
> +
> + def test_zero_
> + self.assertEqual(0, self.target.
> +
> + def test_zero_
> + self.assertEqual(0, self.target.
> +
> +
> +class DistributionSou
> + TestCaseWithFac
> + """Ensure distro source package cache values are set properly."""
> +
> + layer = LaunchpadZopele
> +
> + def setUp(self):
> + TestCaseWithFac
> + self.target = self.factory.
> + self.bugtask1 = self.factory.
> + self.bugtask2 = self.factory.
> + # Bug heat gets calculated by complicated rules in a db
> + # stored procedure. We will override them here to avoid
> + # testing inconsitencies if those values are calculated
> + # differently in the future.
> + #...