Merge lp:~edwin-grubbs/launchpad/bug-535430-needspackaging-timeout-part2 into lp:launchpad/db-devel

Proposed by Edwin Grubbs
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
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

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 DistributionSourcePackageInDatabase and
DistributionSourcePackage classes to access the columns added to the
DistributionSourcePackage table in the last branch. Merging the
DistributionSourcePackageInDatabase and DistributionSourcePackage
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
DistributionSourcePackage table when a bugtask is added. The
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/schema/security.cfg

Added tests.
    lib/lp/bugs/doc/bug-heat.txt
    lib/lp/bugs/tests/test_bugheat.py

Automatically insert into DistributionSourcePackage table when a
SourcePackagePublishingHistory record is added. (See ensure() method.)
Renamed recalculateMaxBugHeat() to recalculateBugHeatCache() and
overrode it in the model's DistributionSourcePackage class to set
total_bug_heat and bug_count in addition to max_bug_heat, which it was
already setting.
    lib/lp/bugs/interfaces/bugtarget.py
    lib/lp/registry/interfaces/distributionsourcepackage.py
    lib/lp/bugs/model/bug.py
    lib/lp/bugs/model/bugtarget.py
    lib/lp/bugs/model/bugtask.py
    lib/lp/registry/model/distributionsourcepackage.py
    lib/lp/soyuz/model/publishing.py

Tests
-----

./bin/test -vv -t 'bug-heat.txt|test_bugheat'

To post a comment you must log in.
Revision history for this message
Brad Crittenden (bac) wrote :
Download full text (8.3 KiB)

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'
> --- database/schema/patch-2207-56-0.sql 1970-01-01 00:00:00 +0000
> +++ database/schema/patch-2207-56-0.sql 2010-06-04 18:48:29 +0000
> @@ -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'
> --- lib/lp/bugs/tests/test_bugheat.py 2010-06-01 09:21:32 +0000
> +++ lib/lp/bugs/tests/test_bugheat.py 2010-06-04 18:48:29 +0000
> @@ -7,8 +7,11 @@

Edwin this test is really readable and easy to follow. Good job.

> import unittest
>
> +from storm.store import Store
> +
> from canonical.testing import LaunchpadZopelessLayer
>
> +from lp.testing import TestCaseWithFactory
> from lp.testing.factory import LaunchpadObjectFactory
>
>
> @@ -58,6 +61,85 @@
> def setUp(self):
> self.target = self.factory.makeDistributionSourcePackage()
>
> +class DistributionSourcePackageNullBugHeatCacheTest(
> + TestCaseWithFactory):
> + """Ensure distro source package cache values start at None."""
> +
> + layer = LaunchpadZopelessLayer
> +
> + def setUp(self):
> + TestCaseWithFactory.setUp(self)
> + self.target = self.factory.makeDistributionSourcePackage()
> +
> + def test_null_max_bug_heat(self):
> + self.assertEqual(None, self.target.max_bug_heat)
> +
> + def test_null_total_bug_heat(self):
> + self.assertEqual(None, self.target.total_bug_heat)
> +
> + def test_null_bug_count(self):
> + self.assertEqual(None, self.target.bug_count)
> +
> +
> +class DistributionSourcePackageZeroRecalculateBugHeatCacheTest(
> + TestCaseWithFactory):
> + """Ensure distro source package cache values become zero properly."""
> +
> + layer = LaunchpadZopelessLayer
> +
> + def setUp(self):
> + TestCaseWithFactory.setUp(self)
> + self.target = self.factory.makeDistributionSourcePackage()
> + self.target.recalculateBugHeatCache()
> +
> + def test_zero_max_bug_heat(self):
> + self.assertEqual(0, self.target.max_bug_heat)
> +
> + def test_zero_total_bug_heat(self):
> + self.assertEqual(0, self.target.total_bug_heat)
> +
> + def test_zero_bug_count(self):
> + self.assertEqual(0, self.target.bug_count)
> +
> +
> +class DistributionSourcePackageMultipleBugsRecalculateBugHeatCacheTest(
> + TestCaseWithFactory):
> + """Ensure distro source package cache values are set properly."""
> +
> + layer = LaunchpadZopelessLayer
> +
> + def setUp(self):
> + TestCaseWithFactory.setUp(self)
> + self.target = self.factory.makeDistributionSourcePackage()
> + self.bugtask1 = self.factory.makeBugTask(target=self.target)
> + self.bugtask2 = self.factory.makeBugTask(target=self.target)
> + # 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.
> + #...

Read more...

review: Approve (code)
Revision history for this message
Edwin Grubbs (edwin-grubbs) wrote :

I assume the changes to database/schema/security.cfg the needs to be reviewed by either Bjorn or Stub. I had to add permissions for db users that had been inserting into the SourcePackagePublishingHistory, since that will cause the DistributionSourcePackage table to be inserted into or updated. The permissions have been changed for the db users "lucille", "uploader", "queued", and "processmail".

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.

Revision history for this message
Björn Tillenius (bjornt) wrote :

Changes to security.cfg don't need a DB review, just make sure they are tested.

review: Abstain (db)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'database/schema/comments.sql'
--- database/schema/comments.sql 2010-06-03 22:21:24 +0000
+++ database/schema/comments.sql 2010-06-10 01:43:25 +0000
@@ -472,7 +472,11 @@
472472
473COMMENT ON TABLE DistributionSourcePackage IS 'Representing a sourcepackage in a distribution across all distribution series.';473COMMENT ON TABLE DistributionSourcePackage IS 'Representing a sourcepackage in a distribution across all distribution series.';
474COMMENT ON COLUMN DistributionSourcePackage.bug_reporting_guidelines IS 'Guidelines to the end user for reporting bugs on a particular a source package in a distribution.';474COMMENT ON COLUMN DistributionSourcePackage.bug_reporting_guidelines IS 'Guidelines to the end user for reporting bugs on a particular a source package in a distribution.';
475COMMENT ON COLUMN DistributionSourcePackage.max_bug_heat IS 'The highest heat value across bugs for this source package.';475COMMENT 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.';
476COMMENT 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.';
477COMMENT ON COLUMN DistributionSourcePackage.bug_count IS 'Number of bugs matching the package distribution and sourcepackagename. NULL means it has not yet been calculated.';
478COMMENT ON COLUMN DistributionSourcePackage.po_message_count IS 'Number of translations matching the package distribution and sourcepackagename. NULL means it has not yet been calculated.';
479COMMENT 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.';
476COMMENT ON COLUMN DistributionSourcePackage.bug_reported_acknowledgement IS 'A message of acknowledgement to display to a bug reporter after they\'ve reported a new bug.';480COMMENT ON COLUMN DistributionSourcePackage.bug_reported_acknowledgement IS 'A message of acknowledgement to display to a bug reporter after they\'ve reported a new bug.';
477481
478-- DistributionSourcePackageCache482-- DistributionSourcePackageCache
479483
=== added file 'database/schema/patch-2207-56-0.sql'
--- database/schema/patch-2207-56-0.sql 1970-01-01 00:00:00 +0000
+++ database/schema/patch-2207-56-0.sql 2010-06-10 01:43:25 +0000
@@ -0,0 +1,24 @@
1-- Copyright 2010 Canonical Ltd. This software is licensed under the
2-- GNU Affero General Public License version 3 (see the file LICENSE).
3
4SET client_min_messages=ERROR;
5
6INSERT INTO LaunchpadDatabaseRevision VALUES (2207, 56, 0);
7
8/*
9Existing Schema:
10
11CREATE TABLE distributionsourcepackage (
12 id integer NOT NULL,
13 distribution integer NOT NULL,
14 sourcepackagename integer NOT NULL,
15 bug_reporting_guidelines text,
16 max_bug_heat integer
17);
18*/
19
20ALTER TABLE DistributionSourcePackage ADD COLUMN total_bug_heat INTEGER;
21ALTER TABLE DistributionSourcePackage ADD COLUMN bug_count INTEGER;
22ALTER TABLE DistributionSourcePackage ADD COLUMN po_message_count INTEGER;
23ALTER TABLE DistributionSourcePackage
24 ADD COLUMN section INTEGER NOT NULL REFERENCES section(id);
025
=== modified file 'database/schema/security.cfg'
--- database/schema/security.cfg 2010-06-07 10:24:03 +0000
+++ database/schema/security.cfg 2010-06-10 01:43:25 +0000
@@ -746,6 +746,7 @@
746public.binarypackagepublishinghistory = SELECT, INSERT, UPDATE, DELETE746public.binarypackagepublishinghistory = SELECT, INSERT, UPDATE, DELETE
747public.sourcepackagepublishinghistory = SELECT, INSERT, UPDATE, DELETE747public.sourcepackagepublishinghistory = SELECT, INSERT, UPDATE, DELETE
748public.sourcepackagepublishinghistory = SELECT748public.sourcepackagepublishinghistory = SELECT
749public.distributionsourcepackage = SELECT, INSERT, UPDATE
749750
750# Closing bugs for publication copies.751# Closing bugs for publication copies.
751public.bug = SELECT, UPDATE752public.bug = SELECT, UPDATE
@@ -1051,6 +1052,7 @@
1051public.distroseries = SELECT, UPDATE1052public.distroseries = SELECT, UPDATE
1052public.distroarchseries = SELECT1053public.distroarchseries = SELECT
1053public.sourcepackagepublishinghistory = SELECT1054public.sourcepackagepublishinghistory = SELECT
1055public.distributionsourcepackage = SELECT, INSERT, UPDATE
1054public.sourcepackagefilepublishing = SELECT1056public.sourcepackagefilepublishing = SELECT
1055public.binarypackagefilepublishing = SELECT1057public.binarypackagefilepublishing = SELECT
1056public.binarypackagepublishinghistory = SELECT1058public.binarypackagepublishinghistory = SELECT
@@ -1175,8 +1177,9 @@
1175public.sourcepackagefilepublishing = SELECT1177public.sourcepackagefilepublishing = SELECT
1176public.binarypackagefilepublishing = SELECT1178public.binarypackagefilepublishing = SELECT
1177public.sourcepackagepublishinghistory = SELECT, INSERT, UPDATE1179public.sourcepackagepublishinghistory = SELECT, INSERT, UPDATE
1180public.distributionsourcepackage = SELECT, INSERT, UPDATE
1178public.binarypackagepublishinghistory = SELECT, INSERT, UPDATE1181public.binarypackagepublishinghistory = SELECT, INSERT, UPDATE
1179public.sourcepackagerecipebuildjob = SELECT, INSERT, UPDATE1182public.sourcepackagerecipebuildjob = SELECT, INSERT, UPDATE
1180public.component = SELECT1183public.component = SELECT
1181public.section = SELECT1184public.section = SELECT
1182public.componentselection = SELECT1185public.componentselection = SELECT
@@ -1464,6 +1467,7 @@
1464public.sourcepackagerelease = SELECT1467public.sourcepackagerelease = SELECT
1465public.sourcepackagepublishinghistory = SELECT1468public.sourcepackagepublishinghistory = SELECT
1466public.structuralsubscription = SELECT1469public.structuralsubscription = SELECT
1470public.section = SELECT
14671471
1468# Karma1472# Karma
1469public.karma = SELECT, INSERT1473public.karma = SELECT, INSERT
14701474
=== modified file 'lib/lp/bugs/doc/bug-heat.txt'
--- lib/lp/bugs/doc/bug-heat.txt 2010-05-27 16:40:54 +0000
+++ lib/lp/bugs/doc/bug-heat.txt 2010-06-10 01:43:25 +0000
@@ -308,6 +308,20 @@
308 >>> print dsp.max_bug_heat308 >>> print dsp.max_bug_heat
309 123309 123
310310
311A DistributionSourcePackage also has a cached value for bug_count and
312total_bug_heat.
313
314 >>> print dsp.bug_count
315 1
316 >>> print dsp.total_bug_heat
317 123
318 >>> dsp_task2 = factory.makeBugTask(target=dsp)
319 >>> dsp_task2.bug.setHeat(7)
320 >>> print dsp.bug_count
321 2
322 >>> print dsp.total_bug_heat
323 130
324
311Transitioning from one target to another, calculates the value for the new325Transitioning from one target to another, calculates the value for the new
312target.326target.
313327
314328
=== modified file 'lib/lp/bugs/interfaces/bugtarget.py'
--- lib/lp/bugs/interfaces/bugtarget.py 2010-06-09 08:26:26 +0000
+++ lib/lp/bugs/interfaces/bugtarget.py 2010-06-10 01:43:25 +0000
@@ -272,8 +272,13 @@
272 def setMaxBugHeat(heat):272 def setMaxBugHeat(heat):
273 """Set the max_bug_heat for this context."""273 """Set the max_bug_heat for this context."""
274274
275 def recalculateMaxBugHeat():275 def recalculateBugHeatCache():
276 """Recalculate and set the max_bug_heat for this context."""276 """Recalculate and set the various bug heat values for this context.
277
278 Several different objects cache max_bug_heat.
279 When DistributionSourcePackage is the target, the total_bug_heat
280 and bug_count are also cached.
281 """
277282
278283
279class BugDistroSeriesTargetDetails:284class BugDistroSeriesTargetDetails:
280285
=== modified file 'lib/lp/bugs/model/bug.py'
--- lib/lp/bugs/model/bug.py 2010-06-04 09:12:26 +0000
+++ lib/lp/bugs/model/bug.py 2010-06-10 01:43:25 +0000
@@ -887,7 +887,7 @@
887887
888 # When a new task is added the bug's heat becomes relevant to the888 # When a new task is added the bug's heat becomes relevant to the
889 # target's max_bug_heat.889 # target's max_bug_heat.
890 target.recalculateMaxBugHeat()890 target.recalculateBugHeatCache()
891891
892 return new_task892 return new_task
893893
@@ -1569,7 +1569,7 @@
1569 self.heat = heat1569 self.heat = heat
1570 self.heat_last_updated = timestamp1570 self.heat_last_updated = timestamp
1571 for task in self.bugtasks:1571 for task in self.bugtasks:
1572 task.target.recalculateMaxBugHeat()1572 task.target.recalculateBugHeatCache()
15731573
1574 def updateHeat(self):1574 def updateHeat(self):
1575 """See `IBug`."""1575 """See `IBug`."""
15761576
=== modified file 'lib/lp/bugs/model/bugtarget.py'
--- lib/lp/bugs/model/bugtarget.py 2010-05-21 04:38:42 +0000
+++ lib/lp/bugs/model/bugtarget.py 2010-06-10 01:43:25 +0000
@@ -235,12 +235,15 @@
235 else:235 else:
236 raise NotImplementedError236 raise NotImplementedError
237237
238 def recalculateMaxBugHeat(self):238 def recalculateBugHeatCache(self):
239 """See `IHasBugHeat`."""239 """See `IHasBugHeat`.
240
241 DistributionSourcePackage overrides this method.
242 """
240 if IProductSeries.providedBy(self):243 if IProductSeries.providedBy(self):
241 return self.product.recalculateMaxBugHeat()244 return self.product.recalculateBugHeatCache()
242 if IDistroSeries.providedBy(self):245 if IDistroSeries.providedBy(self):
243 return self.distribution.recalculateMaxBugHeat()246 return self.distribution.recalculateBugHeatCache()
244 if ISourcePackage.providedBy(self):247 if ISourcePackage.providedBy(self):
245 # Should only happen for nominations, so we can safely skip248 # Should only happen for nominations, so we can safely skip
246 # recalculating max_heat.249 # recalculating max_heat.
@@ -276,13 +279,6 @@
276 WHERE Bugtask.bug = Bug.id AND279 WHERE Bugtask.bug = Bug.id AND
277 Bugtask.product = Product.id AND280 Bugtask.product = Product.id AND
278 Product.project = %s""" % sqlvalues(self)]281 Product.project = %s""" % sqlvalues(self)]
279 elif IDistributionSourcePackage.providedBy(self):
280 sql = ["""SELECT MAX(heat)
281 FROM Bug, Bugtask
282 WHERE Bugtask.bug = Bug.id AND
283 Bugtask.distribution = %s AND
284 Bugtask.sourcepackagename = %s""" % sqlvalues(
285 self.distribution, self.sourcepackagename)]
286 else:282 else:
287 raise NotImplementedError283 raise NotImplementedError
288284
@@ -299,7 +295,7 @@
299 # If the product is part of a project group we calculate the maximum295 # If the product is part of a project group we calculate the maximum
300 # heat for the project group too.296 # heat for the project group too.
301 if IProduct.providedBy(self) and self.project is not None:297 if IProduct.providedBy(self) and self.project is not None:
302 self.project.recalculateMaxBugHeat()298 self.project.recalculateBugHeatCache()
303299
304300
305301
306302
=== modified file 'lib/lp/bugs/model/bugtask.py'
--- lib/lp/bugs/model/bugtask.py 2010-06-07 18:11:08 +0000
+++ lib/lp/bugs/model/bugtask.py 2010-06-10 01:43:25 +0000
@@ -1045,8 +1045,8 @@
1045 # After the target has changed, we need to recalculate the maximum bug1045 # After the target has changed, we need to recalculate the maximum bug
1046 # heat for the new and old targets.1046 # heat for the new and old targets.
1047 if self.target != target_before_change:1047 if self.target != target_before_change:
1048 target_before_change.recalculateMaxBugHeat()1048 target_before_change.recalculateBugHeatCache()
1049 self.target.recalculateMaxBugHeat()1049 self.target.recalculateBugHeatCache()
10501050
1051 def updateTargetNameCache(self, newtarget=None):1051 def updateTargetNameCache(self, newtarget=None):
1052 """See `IBugTask`."""1052 """See `IBugTask`."""
10531053
=== modified file 'lib/lp/bugs/tests/test_bugheat.py'
--- lib/lp/bugs/tests/test_bugheat.py 2010-06-01 09:21:32 +0000
+++ lib/lp/bugs/tests/test_bugheat.py 2010-06-10 01:43:25 +0000
@@ -7,8 +7,11 @@
77
8import unittest8import unittest
99
10from storm.store import Store
11
10from canonical.testing import LaunchpadZopelessLayer12from canonical.testing import LaunchpadZopelessLayer
1113
14from lp.testing import TestCaseWithFactory
12from lp.testing.factory import LaunchpadObjectFactory15from lp.testing.factory import LaunchpadObjectFactory
1316
1417
@@ -58,6 +61,85 @@
58 def setUp(self):61 def setUp(self):
59 self.target = self.factory.makeDistributionSourcePackage()62 self.target = self.factory.makeDistributionSourcePackage()
6063
64class DistributionSourcePackageNullBugHeatCacheTest(
65 TestCaseWithFactory):
66 """Ensure distro source package cache values start at None."""
67
68 layer = LaunchpadZopelessLayer
69
70 def setUp(self):
71 TestCaseWithFactory.setUp(self)
72 self.target = self.factory.makeDistributionSourcePackage()
73
74 def test_null_max_bug_heat(self):
75 self.assertEqual(None, self.target.max_bug_heat)
76
77 def test_null_total_bug_heat(self):
78 self.assertEqual(None, self.target.total_bug_heat)
79
80 def test_null_bug_count(self):
81 self.assertEqual(None, self.target.bug_count)
82
83
84class DistributionSourcePackageZeroRecalculateBugHeatCacheTest(
85 TestCaseWithFactory):
86 """Ensure distro source package cache values become zero properly."""
87
88 layer = LaunchpadZopelessLayer
89
90 def setUp(self):
91 TestCaseWithFactory.setUp(self)
92 self.target = self.factory.makeDistributionSourcePackage()
93 self.target.recalculateBugHeatCache()
94
95 def test_zero_max_bug_heat(self):
96 self.assertEqual(0, self.target.max_bug_heat)
97
98 def test_zero_total_bug_heat(self):
99 self.assertEqual(0, self.target.total_bug_heat)
100
101 def test_zero_bug_count(self):
102 self.assertEqual(0, self.target.bug_count)
103
104
105class DistributionSourcePackageMultipleBugsRecalculateBugHeatCacheTest(
106 TestCaseWithFactory):
107 """Ensure distro source package cache values are set properly."""
108
109 layer = LaunchpadZopelessLayer
110
111 def setUp(self):
112 TestCaseWithFactory.setUp(self)
113 self.target = self.factory.makeDistributionSourcePackage()
114 self.bugtask1 = self.factory.makeBugTask(target=self.target)
115 self.bugtask2 = self.factory.makeBugTask(target=self.target)
116 # Bug heat gets calculated by complicated rules in a db
117 # stored procedure. We will override them here to avoid
118 # testing inconsitencies if those values are calculated
119 # differently in the future.
120 # target.recalculateBugHeatCache() should be called
121 # automatically by bug.setHeat().
122 bug1 = self.bugtask1.bug
123 bug2 = self.bugtask2.bug
124 bug1.setHeat(7)
125 bug2.setHeat(19)
126 Store.of(bug1).flush()
127 self.max_heat = max(bug1.heat, bug2.heat)
128 self.total_heat = sum([bug1.heat, bug2.heat])
129
130 def test_max_bug_heat(self):
131 self.assertEqual(self.max_heat, self.target.max_bug_heat)
132
133 def test_total_bug_heat(self):
134 self.assertEqual(self.total_heat, self.target.total_bug_heat)
135 self.failUnless(
136 self.target.total_bug_heat > self.target.max_bug_heat,
137 "Total bug heat should be more than the max bug heat, "
138 "since we know that multiple bugs have nonzero heat.")
139
140 def test_bug_count(self):
141 self.assertEqual(2, self.target.bug_count)
142
61143
62class SourcePackageMaxHeatByTargetTest(144class SourcePackageMaxHeatByTargetTest(
63 MaxHeatByTargetBase, unittest.TestCase):145 MaxHeatByTargetBase, unittest.TestCase):
64146
=== modified file 'lib/lp/registry/interfaces/distributionsourcepackage.py'
--- lib/lp/registry/interfaces/distributionsourcepackage.py 2010-02-12 13:39:56 +0000
+++ lib/lp/registry/interfaces/distributionsourcepackage.py 2010-06-10 01:43:25 +0000
@@ -78,9 +78,25 @@
78 "no such package -- this occurs when there is no current series for "78 "no such package -- this occurs when there is no current series for "
79 "the distribution.")79 "the distribution.")
8080
81 total_bug_heat = Attribute(
82 "Sum of the bug heat for all the bugs matching the distribution "
83 "and sourcepackagename of the IDistributionSourcePackage.")
84
85 max_bug_heat = Attribute(
86 "Maximum bug heat for a single bug matching the distribution "
87 "and sourcepackagename of the IDistributionSourcePackage.")
88
89 bug_count = Attribute(
90 "Number of bugs matching the distribution and sourcepackagename "
91 "of the IDistributionSourcePackage.")
92
93 po_message_count = Attribute(
94 "Number of translations matching the distribution and "
95 "sourcepackagename of the IDistributionSourcePackage.")
96
81 def getReleasesAndPublishingHistory():97 def getReleasesAndPublishingHistory():
82 """Return a list of all releases of this source package in this98 """Return a list of all releases of this source package in this
83 distribution and their correspodning publishing history.99 distribution and their corresponding publishing history.
84100
85 Items in the list are tuples comprised of a101 Items in the list are tuples comprised of a
86 DistributionSourcePackage and a list of102 DistributionSourcePackage and a list of
87103
=== modified file 'lib/lp/registry/model/distributionsourcepackage.py'
--- lib/lp/registry/model/distributionsourcepackage.py 2010-06-08 13:07:44 +0000
+++ lib/lp/registry/model/distributionsourcepackage.py 2010-06-10 01:43:25 +0000
@@ -15,13 +15,17 @@
15import operator15import operator
1616
17from sqlobject.sqlbuilder import SQLConstant17from sqlobject.sqlbuilder import SQLConstant
18from storm.expr import And, Desc, In, Join, Lower18from storm.expr import And, Count, Desc, In, Join, Lower, Max, Sum
19from storm.store import EmptyResultSet19from storm.store import EmptyResultSet
20from storm.locals import Int, Reference, Store, Storm, Unicode20from storm.locals import Int, Reference, Store, Storm, Unicode
21from zope.component import getUtility
22from zope.error.interfaces import IErrorReportingUtility
21from zope.interface import implements23from zope.interface import implements
2224
25
23from canonical.database.sqlbase import sqlvalues26from canonical.database.sqlbase import sqlvalues
24from canonical.launchpad.database.emailaddress import EmailAddress27from canonical.launchpad.database.emailaddress import EmailAddress
28from lp.registry.interfaces.series import SeriesStatus
25from lp.registry.model.distroseries import DistroSeries29from lp.registry.model.distroseries import DistroSeries
26from lp.registry.model.packaging import Packaging30from lp.registry.model.packaging import Packaging
27from lp.registry.model.structuralsubscription import (31from lp.registry.model.structuralsubscription import (
@@ -30,7 +34,7 @@
30from canonical.lazr.utils import smartquote34from canonical.lazr.utils import smartquote
31from lp.answers.interfaces.questiontarget import IQuestionTarget35from lp.answers.interfaces.questiontarget import IQuestionTarget
32from lp.bugs.interfaces.bugtarget import IHasBugHeat36from lp.bugs.interfaces.bugtarget import IHasBugHeat
33from lp.bugs.model.bug import BugSet, get_bug_tags_open_count37from lp.bugs.model.bug import Bug, BugSet, get_bug_tags_open_count
34from lp.bugs.model.bugtarget import BugTargetBase, HasBugHeatMixin38from lp.bugs.model.bugtarget import BugTargetBase, HasBugHeatMixin
35from lp.bugs.model.bugtask import BugTask39from lp.bugs.model.bugtask import BugTask
36from lp.code.model.hasbranches import HasBranchesMixin, HasMergeProposalsMixin40from lp.code.model.hasbranches import HasBranchesMixin, HasMergeProposalsMixin
@@ -43,6 +47,7 @@
43 SourcePackage, SourcePackageQuestionTargetMixin)47 SourcePackage, SourcePackageQuestionTargetMixin)
44from lp.soyuz.interfaces.archive import ArchivePurpose48from lp.soyuz.interfaces.archive import ArchivePurpose
45from lp.soyuz.interfaces.publishing import PackagePublishingStatus49from lp.soyuz.interfaces.publishing import PackagePublishingStatus
50from lp.soyuz.interfaces.section import ISectionSet
46from lp.soyuz.model.archive import Archive51from lp.soyuz.model.archive import Archive
47from lp.soyuz.model.distributionsourcepackagerelease import (52from lp.soyuz.model.distributionsourcepackagerelease import (
48 DistributionSourcePackageRelease)53 DistributionSourcePackageRelease)
@@ -54,6 +59,40 @@
54 CustomLanguageCode, HasCustomLanguageCodesMixin)59 CustomLanguageCode, HasCustomLanguageCodesMixin)
5560
5661
62class DistributionSourcePackageProperty:
63 def __init__(self, attrname):
64 self.attrname = attrname
65
66 def __get__(self, obj, class_):
67 return getattr(obj._self_in_database, self.attrname, None)
68
69 def __set__(self, obj, value):
70 if obj._self_in_database is None:
71 # Log an oops without raising an error.
72 exception = AssertionError(
73 "DistributionSourcePackage record should have been created "
74 "earlier in the database for distro=%s, sourcepackagename=%s"
75 % (obj.distribution.name, obj.sourcepackagename.name))
76 getUtility(IErrorReportingUtility).raising(
77 (exception.__class__, exception, None))
78 spph = Store.of(obj.distribution).find(
79 SourcePackagePublishingHistory,
80 SourcePackagePublishingHistory.distroseriesID ==
81 DistroSeries.id,
82 DistroSeries.distributionID == obj.distribution.id,
83 SourcePackagePublishingHistory.sourcepackagereleaseID ==
84 SourcePackageRelease.id,
85 SourcePackageRelease.sourcepackagenameID ==
86 obj.sourcepackagename.id
87 ).order_by(Desc(SourcePackagePublishingHistory.id)).first()
88 if spph is None:
89 section = getUtility(ISectionSet)['misc']
90 else:
91 section = spph.section
92 obj._new(obj.distribution, obj.sourcepackagename, section)
93 setattr(obj._self_in_database, self.attrname, value)
94
95
57class DistributionSourcePackage(BugTargetBase,96class DistributionSourcePackage(BugTargetBase,
58 SourcePackageQuestionTargetMixin,97 SourcePackageQuestionTargetMixin,
59 StructuralSubscriptionTargetMixin,98 StructuralSubscriptionTargetMixin,
@@ -72,6 +111,16 @@
72 IDistributionSourcePackage, IHasBugHeat, IHasCustomLanguageCodes,111 IDistributionSourcePackage, IHasBugHeat, IHasCustomLanguageCodes,
73 IQuestionTarget)112 IQuestionTarget)
74113
114 bug_reporting_guidelines = DistributionSourcePackageProperty(
115 'bug_reporting_guidelines')
116 bug_reported_acknowledgement = DistributionSourcePackageProperty(
117 'bug_reported_acknowledgement')
118 max_bug_heat = DistributionSourcePackageProperty('max_bug_heat')
119 total_bug_heat = DistributionSourcePackageProperty('total_bug_heat')
120 bug_count = DistributionSourcePackageProperty('bug_count')
121 po_message_count = DistributionSourcePackageProperty('po_message_count')
122 section = DistributionSourcePackageProperty('section')
123
75 def __init__(self, distribution, sourcepackagename):124 def __init__(self, distribution, sourcepackagename):
76 self.distribution = distribution125 self.distribution = distribution
77 self.sourcepackagename = sourcepackagename126 self.sourcepackagename = sourcepackagename
@@ -118,75 +167,34 @@
118 # measure while DistributionSourcePackage is not yet hooked167 # measure while DistributionSourcePackage is not yet hooked
119 # into the database but we need access to some of the fields168 # into the database but we need access to some of the fields
120 # in the database.169 # in the database.
121 return Store.of(self.distribution).find(170 return self._get(self.distribution, self.sourcepackagename)
122 DistributionSourcePackageInDatabase,171
123 DistributionSourcePackageInDatabase.sourcepackagename == (172 def recalculateBugHeatCache(self):
124 self.sourcepackagename),173 """See `IHasBugHeat`."""
125 DistributionSourcePackageInDatabase.distribution == (174 self.max_bug_heat = IStore(Bug).find(
126 self.distribution)175 Max(Bug.heat),
127 ).one()176 BugTask.bug == Bug.id,
128177 BugTask.distributionID == self.distribution.id,
129 def _get_bug_reporting_guidelines(self):178 BugTask.sourcepackagenameID == self.sourcepackagename.id).one()
130 """See `IBugTarget`."""179 if self.max_bug_heat is None:
131 dsp_in_db = self._self_in_database180 # SELECT MAX(Bug.heat) returns NULL if zero rows match.
132 if dsp_in_db is not None:181 self.max_bug_heat = 0
133 return dsp_in_db.bug_reporting_guidelines182
134 return None183 self.total_bug_heat = IStore(Bug).find(
135184 Sum(Bug.heat),
136 def _set_bug_reporting_guidelines(self, value):185 BugTask.bug == Bug.id,
137 """See `IBugTarget`."""186 BugTask.distributionID == self.distribution.id,
138 dsp_in_db = self._self_in_database187 BugTask.sourcepackagenameID == self.sourcepackagename.id).one()
139 if dsp_in_db is None:188 if self.total_bug_heat is None:
140 dsp_in_db = DistributionSourcePackageInDatabase()189 self.total_bug_heat = 0
141 dsp_in_db.sourcepackagename = self.sourcepackagename190
142 dsp_in_db.distribution = self.distribution191 self.bug_count = IStore(Bug).find(
143 Store.of(self.distribution).add(dsp_in_db)192 Count(Bug.heat),
144 dsp_in_db.bug_reporting_guidelines = value193 BugTask.bug == Bug.id,
145194 BugTask.distributionID == self.distribution.id,
146 bug_reporting_guidelines = property(195 BugTask.sourcepackagenameID == self.sourcepackagename.id).one()
147 _get_bug_reporting_guidelines,196 if self.bug_count is None:
148 _set_bug_reporting_guidelines)197 self.bug_count = 0
149
150 def _get_bug_reported_acknowledgement(self):
151 """See `IBugTarget`."""
152 dsp_in_db = self._self_in_database
153 if dsp_in_db is not None:
154 return dsp_in_db.bug_reported_acknowledgement
155 return None
156
157 def _set_bug_reported_acknowledgement(self, value):
158 """See `IBugTarget`."""
159 dsp_in_db = self._self_in_database
160 if dsp_in_db is None:
161 dsp_in_db = DistributionSourcePackageInDatabase()
162 dsp_in_db.sourcepackagename = self.sourcepackagename
163 dsp_in_db.distribution = self.distribution
164 Store.of(self.distribution).add(dsp_in_db)
165 dsp_in_db.bug_reported_acknowledgement = value
166
167 bug_reported_acknowledgement = property(
168 _get_bug_reported_acknowledgement,
169 _set_bug_reported_acknowledgement)
170
171 def _get_max_bug_heat(self):
172 """See `IHasBugs`."""
173 dsp_in_db = self._self_in_database
174 if dsp_in_db is None:
175 return None
176 else:
177 return dsp_in_db.max_bug_heat
178
179 def _set_max_bug_heat(self, value):
180 """See `IHasBugs`."""
181 dsp_in_db = self._self_in_database
182 if dsp_in_db is None:
183 dsp_in_db = DistributionSourcePackageInDatabase()
184 dsp_in_db.sourcepackagename = self.sourcepackagename
185 dsp_in_db.distribution = self.distribution
186 Store.of(self.distribution).add(dsp_in_db)
187 dsp_in_db.max_bug_heat = value
188
189 max_bug_heat = property(_get_max_bug_heat, _set_max_bug_heat)
190198
191 @property199 @property
192 def latest_overall_publication(self):200 def latest_overall_publication(self):
@@ -504,6 +512,47 @@
504 In(Lower(EmailAddress.email), email_addresses))512 In(Lower(EmailAddress.email), email_addresses))
505 return result_set513 return result_set
506514
515 @classmethod
516 def _get(cls, distribution, sourcepackagename):
517 return Store.of(distribution).find(
518 DistributionSourcePackageInDatabase,
519 DistributionSourcePackageInDatabase.sourcepackagename ==
520 sourcepackagename,
521 DistributionSourcePackageInDatabase.distribution ==
522 distribution
523 ).one()
524
525 @classmethod
526 def _new(cls, distribution, sourcepackagename, section):
527 dsp = DistributionSourcePackageInDatabase()
528 dsp.distribution = distribution
529 dsp.sourcepackagename = sourcepackagename
530 dsp.section = section
531 Store.of(distribution).add(dsp)
532 return dsp
533
534 @classmethod
535 def ensure(cls, spph):
536 """Create DistributionSourcePackage record, if necessary.
537
538 Only create a record for primary archives (i.e. not for PPAs).
539 If it already exists, update the section attribute.
540 """
541 # Import here to avoid import loop.
542 sourcepackagename = spph.sourcepackagerelease.sourcepackagename
543 distribution = spph.distroseries.distribution
544 section = spph.section
545
546 if spph.archive.purpose == ArchivePurpose.PRIMARY:
547 dsp = cls._get(distribution, sourcepackagename)
548 if dsp is None:
549 dsp = cls._new(distribution, sourcepackagename, section)
550 elif spph.distroseries.status == SeriesStatus.CURRENT:
551 # If the distroseries.status is not CURRENT, it should
552 # not override the section from a previously created
553 # SourcePackagePublishingHistory.
554 dsp.section = section
555
507556
508class DistributionSourcePackageInDatabase(Storm):557class DistributionSourcePackageInDatabase(Storm):
509 """Temporary class to allow access to the database."""558 """Temporary class to allow access to the database."""
@@ -529,4 +578,9 @@
529 bug_reported_acknowledgement = Unicode()578 bug_reported_acknowledgement = Unicode()
530579
531 max_bug_heat = Int()580 max_bug_heat = Int()
581 total_bug_heat = Int()
582 bug_count = Int()
583 po_message_count = Int()
532584
585 section_id = Int(name='section')
586 section = Reference(section_id, 'Section.id')
533587
=== modified file 'lib/lp/soyuz/model/publishing.py'
--- lib/lp/soyuz/model/publishing.py 2010-05-21 12:25:51 +0000
+++ lib/lp/soyuz/model/publishing.py 2010-06-10 01:43:25 +0000
@@ -49,12 +49,12 @@
49from lp.soyuz.model.binarypackagename import BinaryPackageName49from lp.soyuz.model.binarypackagename import BinaryPackageName
50from lp.soyuz.model.binarypackagerelease import (BinaryPackageRelease,50from lp.soyuz.model.binarypackagerelease import (BinaryPackageRelease,
51 BinaryPackageReleaseDownloadCount)51 BinaryPackageReleaseDownloadCount)
52from lp.soyuz.interfaces.archive import ArchivePurpose
52from lp.soyuz.model.files import (53from lp.soyuz.model.files import (
53 BinaryPackageFile, SourcePackageReleaseFile)54 BinaryPackageFile, SourcePackageReleaseFile)
54from canonical.launchpad.database.librarian import (55from canonical.launchpad.database.librarian import (
55 LibraryFileAlias, LibraryFileContent)56 LibraryFileAlias, LibraryFileContent)
56from lp.soyuz.model.packagediff import PackageDiff57from lp.soyuz.model.packagediff import PackageDiff
57from lp.soyuz.interfaces.archive import ArchivePurpose
58from lp.soyuz.interfaces.archivearch import IArchiveArchSet58from lp.soyuz.interfaces.archivearch import IArchiveArchSet
59from lp.soyuz.interfaces.binarypackagebuild import (59from lp.soyuz.interfaces.binarypackagebuild import (
60 BuildSetStatus, IBinaryPackageBuildSet)60 BuildSetStatus, IBinaryPackageBuildSet)
@@ -1162,7 +1162,10 @@
1162 section=section,1162 section=section,
1163 status=PackagePublishingStatus.PENDING,1163 status=PackagePublishingStatus.PENDING,
1164 datecreated=UTC_NOW)1164 datecreated=UTC_NOW)
11651165 # Import here to prevent import loop.
1166 from lp.registry.model.distributionsourcepackage import (
1167 DistributionSourcePackage)
1168 DistributionSourcePackage.ensure(pub)
1166 return pub1169 return pub
11671170
1168 def getBuildsForSourceIds(1171 def getBuildsForSourceIds(

Subscribers

People subscribed via source and target branches

to status/vote changes: