Merge lp://staging/~intellectronica/launchpad/update-max-heat into lp://staging/launchpad/db-devel

Proposed by Eleanor Berger
Status: Merged
Approved by: Deryck Hodge
Approved revision: not available
Merged at revision: not available
Proposed branch: lp://staging/~intellectronica/launchpad/update-max-heat
Merge into: lp://staging/launchpad/db-devel
Diff against target: 272 lines (+141/-5)
8 files modified
database/schema/security.cfg (+5/-4)
lib/lp/bugs/browser/bugtask.py (+2/-0)
lib/lp/bugs/doc/bug-heat.txt (+56/-0)
lib/lp/bugs/interfaces/bugtarget.py (+4/-0)
lib/lp/bugs/model/bug.py (+7/-0)
lib/lp/bugs/model/bugtarget.py (+56/-0)
lib/lp/bugs/model/bugtask.py (+9/-0)
lib/lp/registry/configure.zcml (+2/-1)
To merge this branch: bzr merge lp://staging/~intellectronica/launchpad/update-max-heat
Reviewer Review Type Date Requested Status
Deryck Hodge (community) code Approve
Review via email: mp+20237@code.staging.launchpad.net
To post a comment you must log in.
Revision history for this message
Eleanor Berger (intellectronica) wrote :

This branch adds a step which calculates a target's max_bug_heat when a bug's heat is set, or when a change to a bugtask or the addition of a task is made.

Revision history for this message
Deryck Hodge (deryck) wrote :

Hi, Tom.

As we discussed on IRC, we should replace the UNION ALL queries with something like the following for each target type:

SELECT Bug.heat
    FROM Bug, Bugtask, DistroSeries
    WHERE Bugtask.bug = Bug.id
    AND Bugtask.distroseries = DistroSeries.id
    OR Bugtask.distribution = DistroSeries.distribution
    AND Bugtask.distribution = 1
    ORDER BY Bug.heat DESC LIMIT 1;

This goes from 4 second queries to .2 ms queries. Given these savings, this can land with those changes. We don't have to worry about cowboy'ing on to staging unless you happen to catch Gavin on IRC before his cowboy'ed patch.

Thanks for working on this! The code looks good otherwise and will be a good to have done for the rollout.

This is marked approved assuming these changes are applied.

Cheers,
deryck

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'database/schema/security.cfg'
--- database/schema/security.cfg 2010-02-26 11:31:12 +0000
+++ database/schema/security.cfg 2010-03-01 17:41:14 +0000
@@ -1494,12 +1494,14 @@
1494public.archive = SELECT1494public.archive = SELECT
1495public.archivearch = SELECT1495public.archivearch = SELECT
1496public.component = SELECT1496public.component = SELECT
1497public.distribution = SELECT1497public.distribution = SELECT, UPDATE
1498public.distributionsourcepackage = SELECT, INSERT, UPDATE
1498public.distrocomponentuploader = SELECT1499public.distrocomponentuploader = SELECT
1500public.distroseries = SELECT
1499public.archivepermission = SELECT1501public.archivepermission = SELECT
1500public.distroseries = SELECT1502public.distroseries = SELECT
1501public.project = SELECT1503public.project = SELECT, UPDATE
1502public.product = SELECT1504public.product = SELECT, UPDATE
1503public.productseries = SELECT1505public.productseries = SELECT
1504public.packagebugsupervisor = SELECT1506public.packagebugsupervisor = SELECT
1505public.sourcepackagename = SELECT1507public.sourcepackagename = SELECT
@@ -1566,7 +1568,6 @@
1566public.codereviewmessage = SELECT, INSERT1568public.codereviewmessage = SELECT, INSERT
1567public.codereviewvote = SELECT, INSERT, UPDATE1569public.codereviewvote = SELECT, INSERT, UPDATE
1568public.diff = SELECT, INSERT, UPDATE1570public.diff = SELECT, INSERT, UPDATE
1569public.distribution = SELECT
1570public.distroseries = SELECT1571public.distroseries = SELECT
1571public.job = SELECT, INSERT, UPDATE1572public.job = SELECT, INSERT, UPDATE
1572public.mergedirectivejob = SELECT, INSERT1573public.mergedirectivejob = SELECT, INSERT
15731574
=== modified file 'lib/lp/bugs/browser/bugtask.py'
--- lib/lp/bugs/browser/bugtask.py 2010-02-28 10:00:45 +0000
+++ lib/lp/bugs/browser/bugtask.py 2010-03-01 17:41:14 +0000
@@ -1090,6 +1090,8 @@
1090 """Calculate the number of heat 'flames' to display."""1090 """Calculate the number of heat 'flames' to display."""
1091 heat = float(heat)1091 heat = float(heat)
1092 max_bug_heat = float(max_bug_heat)1092 max_bug_heat = float(max_bug_heat)
1093 if max_bug_heat == 0:
1094 return 0
1093 if heat / max_bug_heat < 0.33333:1095 if heat / max_bug_heat < 0.33333:
1094 return 01096 return 0
1095 if heat / max_bug_heat < 0.66666 or max_bug_heat < 2:1097 if heat / max_bug_heat < 0.66666 or max_bug_heat < 2:
10961098
=== modified file 'lib/lp/bugs/doc/bug-heat.txt'
--- lib/lp/bugs/doc/bug-heat.txt 2010-02-27 14:45:01 +0000
+++ lib/lp/bugs/doc/bug-heat.txt 2010-03-01 17:41:14 +0000
@@ -60,3 +60,59 @@
6060
61 >>> bug_1.heat > 061 >>> bug_1.heat > 0
62 True62 True
63
64
65Caculating the maximum heat for a target
66----------------------------------------
67
68When we update the heat value for a bug, the maximum heat value for the targets
69for all of its tasks is calculated and cached.
70
71 >>> product = factory.makeProduct()
72 >>> bug = factory.makeBug(product=product)
73 >>> print product.max_bug_heat
74 None
75 >>> bug.setHeat(123)
76 >>> print product.max_bug_heat
77 123
78
79The maximum heat for a project is the value for tasks on all its products.
80
81 >>> project = factory.makeProject()
82 >>> product.project = project
83 >>> bug.setHeat(123)
84 >>> print project.max_bug_heat
85 123
86
87A DistributionSourcePackage has its own maximum heat.
88
89 >>> dsp = factory.makeDistributionSourcePackage()
90 >>> dsp_task = bug.addTask(bug.owner, dsp)
91 >>> print dsp.max_bug_heat
92 123
93
94Transitioning from one target to another, calculates the value for the new
95target.
96
97 >>> another_product = factory.makeProduct()
98 >>> bug.bugtasks[0].transitionToTarget(another_product)
99 >>> print another_product.max_bug_heat
100 123
101
102ProductSeries and DistroSeries simply delegate to their corresponding Product
103or Distribution.
104
105 >>> product_series = factory.makeProductSeries()
106 >>> ps_task = bug.addTask(bug.owner, product_series)
107 >>> print product_series.max_bug_heat
108 123
109 >>> print product_series.product.max_bug_heat
110 123
111
112 >>> distro_series = factory.makeDistroSeries()
113 >>> ds_task = bug.addTask(bug.owner, distro_series)
114 >>> print distro_series.max_bug_heat
115 123
116 >>> print distro_series.distribution.max_bug_heat
117 123
118
63119
=== modified file 'lib/lp/bugs/interfaces/bugtarget.py'
--- lib/lp/bugs/interfaces/bugtarget.py 2010-02-27 21:18:10 +0000
+++ lib/lp/bugs/interfaces/bugtarget.py 2010-03-01 17:41:14 +0000
@@ -207,6 +207,10 @@
207 def setMaxBugHeat(heat):207 def setMaxBugHeat(heat):
208 """Set the max_bug_heat for this context."""208 """Set the max_bug_heat for this context."""
209209
210 def recalculateMaxBugHeat():
211 """Recalculate and set the max_bug_heat for this context."""
212
213
210214
211class IBugTarget(IHasBugs):215class IBugTarget(IHasBugs):
212 """An entity on which a bug can be reported.216 """An entity on which a bug can be reported.
213217
=== modified file 'lib/lp/bugs/model/bug.py'
--- lib/lp/bugs/model/bug.py 2010-02-26 05:13:11 +0000
+++ lib/lp/bugs/model/bug.py 2010-03-01 17:41:14 +0000
@@ -100,6 +100,7 @@
100from lp.registry.interfaces.person import validate_public_person100from lp.registry.interfaces.person import validate_public_person
101from lp.registry.interfaces.product import IProduct101from lp.registry.interfaces.product import IProduct
102from lp.registry.interfaces.productseries import IProductSeries102from lp.registry.interfaces.productseries import IProductSeries
103from lp.registry.interfaces.projectgroup import IProjectGroup
103from lp.registry.interfaces.sourcepackage import ISourcePackage104from lp.registry.interfaces.sourcepackage import ISourcePackage
104from lp.registry.model.mentoringoffer import MentoringOffer105from lp.registry.model.mentoringoffer import MentoringOffer
105from lp.registry.model.person import Person, ValidPersonCache106from lp.registry.model.person import Person, ValidPersonCache
@@ -876,6 +877,10 @@
876 distroseries=distro_series,877 distroseries=distro_series,
877 sourcepackagename=source_package_name)878 sourcepackagename=source_package_name)
878879
880 # When a new task is added the bug's heat becomes relevant to the
881 # target's max_bug_heat.
882 target.recalculateMaxBugHeat()
883
879 return new_task884 return new_task
880885
881 def addWatch(self, bugtracker, remotebug, owner):886 def addWatch(self, bugtracker, remotebug, owner):
@@ -1529,6 +1534,8 @@
1529 def setHeat(self, heat):1534 def setHeat(self, heat):
1530 """See `IBug`."""1535 """See `IBug`."""
1531 self.heat = heat1536 self.heat = heat
1537 for task in self.bugtasks:
1538 task.target.recalculateMaxBugHeat()
15321539
15331540
1534class BugSet:1541class BugSet:
15351542
=== modified file 'lib/lp/bugs/model/bugtarget.py'
--- lib/lp/bugs/model/bugtarget.py 2010-02-26 05:45:47 +0000
+++ lib/lp/bugs/model/bugtarget.py 2010-03-01 17:41:14 +0000
@@ -25,10 +25,13 @@
25from canonical.launchpad.webapp.interfaces import ILaunchBag25from canonical.launchpad.webapp.interfaces import ILaunchBag
26from lp.bugs.interfaces.bugtarget import IOfficialBugTag26from lp.bugs.interfaces.bugtarget import IOfficialBugTag
27from lp.registry.interfaces.distribution import IDistribution27from lp.registry.interfaces.distribution import IDistribution
28from lp.registry.interfaces.distroseries import IDistroSeries
28from lp.registry.interfaces.distributionsourcepackage import (29from lp.registry.interfaces.distributionsourcepackage import (
29 IDistributionSourcePackage)30 IDistributionSourcePackage)
30from lp.registry.interfaces.product import IProduct31from lp.registry.interfaces.product import IProduct
32from lp.registry.interfaces.productseries import IProductSeries
31from lp.registry.interfaces.projectgroup import IProjectGroup33from lp.registry.interfaces.projectgroup import IProjectGroup
34from lp.registry.interfaces.sourcepackage import ISourcePackage
32from lp.bugs.interfaces.bugtask import (35from lp.bugs.interfaces.bugtask import (
33 BugTagsSearchCombinator, BugTaskImportance, BugTaskSearchParams,36 BugTagsSearchCombinator, BugTaskImportance, BugTaskSearchParams,
34 BugTaskStatus, RESOLVED_BUGTASK_STATUSES, UNRESOLVED_BUGTASK_STATUSES)37 BugTaskStatus, RESOLVED_BUGTASK_STATUSES, UNRESOLVED_BUGTASK_STATUSES)
@@ -170,6 +173,59 @@
170 else:173 else:
171 raise NotImplementedError174 raise NotImplementedError
172175
176 def recalculateMaxBugHeat(self):
177 """See `IHasBugs`."""
178 if IProductSeries.providedBy(self):
179 return self.product.recalculateMaxBugHeat()
180 if IDistroSeries.providedBy(self):
181 return self.distribution.recalculateMaxBugHeat()
182 if ISourcePackage.providedBy(self):
183 # Should only happen for nominations, so we can safely skip
184 # recalculating max_heat.
185 return
186
187 if IDistribution.providedBy(self):
188 sql = """SELECT Bug.heat
189 FROM Bug, Bugtask, DistroSeries
190 WHERE Bugtask.bug = Bug.id
191 AND Bugtask.distroseries = DistroSeries.id
192 OR Bugtask.distribution = DistroSeries.distribution
193 AND Bugtask.distribution = %s
194 ORDER BY Bug.heat DESC LIMIT 1""" % sqlvalues(self)
195 elif IProduct.providedBy(self):
196 sql = """SELECT Bug.heat
197 FROM Bug, Bugtask, ProductSeries
198 WHERE Bugtask.bug = Bug.id
199 AND Bugtask.productseries = ProductSeries.id
200 OR Bugtask.product = ProductSeries.product
201 AND Bugtask.product = %s
202 ORDER BY Bug.heat DESC LIMIT 1""" % sqlvalues(self)
203 elif IProjectGroup.providedBy(self):
204 sql = """SELECT MAX(heat)
205 FROM Bug, Bugtask, Product
206 WHERE Bugtask.bug = Bug.id AND
207 Bugtask.product = Product.id AND
208 Product.project = %s""" % sqlvalues(self)
209 elif IDistributionSourcePackage.providedBy(self):
210 sql = """SELECT MAX(heat)
211 FROM Bug, Bugtask
212 WHERE Bugtask.bug = Bug.id AND
213 Bugtask.distribution = %s AND
214 Bugtask.sourcepackagename = %s""" % sqlvalues(
215 self.distribution, self.sourcepackagename)
216 else:
217 raise NotImplementedError
218
219 cur = cursor()
220 cur.execute(sql)
221 self.setMaxBugHeat(cur.fetchone()[0])
222
223 # If the product is part of a project group we calculate the maximum
224 # heat for the project group too.
225 if IProduct.providedBy(self) and self.project is not None:
226 self.project.recalculateMaxBugHeat()
227
228
173 def getBugCounts(self, user, statuses=None):229 def getBugCounts(self, user, statuses=None):
174 """See `IHasBugs`."""230 """See `IHasBugs`."""
175 if statuses is None:231 if statuses is None:
176232
=== modified file 'lib/lp/bugs/model/bugtask.py'
--- lib/lp/bugs/model/bugtask.py 2010-02-27 20:20:03 +0000
+++ lib/lp/bugs/model/bugtask.py 2010-03-01 17:41:14 +0000
@@ -969,6 +969,9 @@
969 enforced implicitly by the code in969 enforced implicitly by the code in
970 lib/canonical/launchpad/browser/bugtask.py#BugTaskEditView.970 lib/canonical/launchpad/browser/bugtask.py#BugTaskEditView.
971 """971 """
972
973 target_before_change = self.target
974
972 if (self.milestone is not None and975 if (self.milestone is not None and
973 self.milestone.target != target):976 self.milestone.target != target):
974 # If the milestone for this bugtask is set, we977 # If the milestone for this bugtask is set, we
@@ -993,6 +996,12 @@
993 "Distribution bug tasks may only be re-targeted "996 "Distribution bug tasks may only be re-targeted "
994 "to a package in the same distribution.")997 "to a package in the same distribution.")
995998
999 # After the target has changed, we need to recalculate the maximum bug
1000 # heat for the new and old targets.
1001 if self.target != target_before_change:
1002 target_before_change.recalculateMaxBugHeat()
1003 self.target.recalculateMaxBugHeat()
1004
996 def updateTargetNameCache(self, newtarget=None):1005 def updateTargetNameCache(self, newtarget=None):
997 """See `IBugTask`."""1006 """See `IBugTask`."""
998 if newtarget is None:1007 if newtarget is None:
9991008
=== modified file 'lib/lp/registry/configure.zcml'
--- lib/lp/registry/configure.zcml 2010-02-27 20:20:03 +0000
+++ lib/lp/registry/configure.zcml 2010-03-01 17:41:14 +0000
@@ -408,7 +408,8 @@
408 findRelatedArchives408 findRelatedArchives
409 findRelatedArchivePublications409 findRelatedArchivePublications
410 userHasBugSubscriptions410 userHasBugSubscriptions
411 max_bug_heat"/>411 max_bug_heat
412 recalculateMaxBugHeat"/>
412 <require413 <require
413 permission="launchpad.AnyPerson"414 permission="launchpad.AnyPerson"
414 attributes="415 attributes="

Subscribers

People subscribed via source and target branches

to status/vote changes: