Merge lp:~intellectronica/launchpad/update-max-heat into lp: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:~intellectronica/launchpad/update-max-heat
Merge into: lp: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:~intellectronica/launchpad/update-max-heat
Reviewer Review Type Date Requested Status
Deryck Hodge (community) code Approve
Review via email: mp+20237@code.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
1=== modified file 'database/schema/security.cfg'
2--- database/schema/security.cfg 2010-02-26 11:31:12 +0000
3+++ database/schema/security.cfg 2010-03-01 17:41:14 +0000
4@@ -1494,12 +1494,14 @@
5 public.archive = SELECT
6 public.archivearch = SELECT
7 public.component = SELECT
8-public.distribution = SELECT
9+public.distribution = SELECT, UPDATE
10+public.distributionsourcepackage = SELECT, INSERT, UPDATE
11 public.distrocomponentuploader = SELECT
12+public.distroseries = SELECT
13 public.archivepermission = SELECT
14 public.distroseries = SELECT
15-public.project = SELECT
16-public.product = SELECT
17+public.project = SELECT, UPDATE
18+public.product = SELECT, UPDATE
19 public.productseries = SELECT
20 public.packagebugsupervisor = SELECT
21 public.sourcepackagename = SELECT
22@@ -1566,7 +1568,6 @@
23 public.codereviewmessage = SELECT, INSERT
24 public.codereviewvote = SELECT, INSERT, UPDATE
25 public.diff = SELECT, INSERT, UPDATE
26-public.distribution = SELECT
27 public.distroseries = SELECT
28 public.job = SELECT, INSERT, UPDATE
29 public.mergedirectivejob = SELECT, INSERT
30
31=== modified file 'lib/lp/bugs/browser/bugtask.py'
32--- lib/lp/bugs/browser/bugtask.py 2010-02-28 10:00:45 +0000
33+++ lib/lp/bugs/browser/bugtask.py 2010-03-01 17:41:14 +0000
34@@ -1090,6 +1090,8 @@
35 """Calculate the number of heat 'flames' to display."""
36 heat = float(heat)
37 max_bug_heat = float(max_bug_heat)
38+ if max_bug_heat == 0:
39+ return 0
40 if heat / max_bug_heat < 0.33333:
41 return 0
42 if heat / max_bug_heat < 0.66666 or max_bug_heat < 2:
43
44=== modified file 'lib/lp/bugs/doc/bug-heat.txt'
45--- lib/lp/bugs/doc/bug-heat.txt 2010-02-27 14:45:01 +0000
46+++ lib/lp/bugs/doc/bug-heat.txt 2010-03-01 17:41:14 +0000
47@@ -60,3 +60,59 @@
48
49 >>> bug_1.heat > 0
50 True
51+
52+
53+Caculating the maximum heat for a target
54+----------------------------------------
55+
56+When we update the heat value for a bug, the maximum heat value for the targets
57+for all of its tasks is calculated and cached.
58+
59+ >>> product = factory.makeProduct()
60+ >>> bug = factory.makeBug(product=product)
61+ >>> print product.max_bug_heat
62+ None
63+ >>> bug.setHeat(123)
64+ >>> print product.max_bug_heat
65+ 123
66+
67+The maximum heat for a project is the value for tasks on all its products.
68+
69+ >>> project = factory.makeProject()
70+ >>> product.project = project
71+ >>> bug.setHeat(123)
72+ >>> print project.max_bug_heat
73+ 123
74+
75+A DistributionSourcePackage has its own maximum heat.
76+
77+ >>> dsp = factory.makeDistributionSourcePackage()
78+ >>> dsp_task = bug.addTask(bug.owner, dsp)
79+ >>> print dsp.max_bug_heat
80+ 123
81+
82+Transitioning from one target to another, calculates the value for the new
83+target.
84+
85+ >>> another_product = factory.makeProduct()
86+ >>> bug.bugtasks[0].transitionToTarget(another_product)
87+ >>> print another_product.max_bug_heat
88+ 123
89+
90+ProductSeries and DistroSeries simply delegate to their corresponding Product
91+or Distribution.
92+
93+ >>> product_series = factory.makeProductSeries()
94+ >>> ps_task = bug.addTask(bug.owner, product_series)
95+ >>> print product_series.max_bug_heat
96+ 123
97+ >>> print product_series.product.max_bug_heat
98+ 123
99+
100+ >>> distro_series = factory.makeDistroSeries()
101+ >>> ds_task = bug.addTask(bug.owner, distro_series)
102+ >>> print distro_series.max_bug_heat
103+ 123
104+ >>> print distro_series.distribution.max_bug_heat
105+ 123
106+
107
108=== modified file 'lib/lp/bugs/interfaces/bugtarget.py'
109--- lib/lp/bugs/interfaces/bugtarget.py 2010-02-27 21:18:10 +0000
110+++ lib/lp/bugs/interfaces/bugtarget.py 2010-03-01 17:41:14 +0000
111@@ -207,6 +207,10 @@
112 def setMaxBugHeat(heat):
113 """Set the max_bug_heat for this context."""
114
115+ def recalculateMaxBugHeat():
116+ """Recalculate and set the max_bug_heat for this context."""
117+
118+
119
120 class IBugTarget(IHasBugs):
121 """An entity on which a bug can be reported.
122
123=== modified file 'lib/lp/bugs/model/bug.py'
124--- lib/lp/bugs/model/bug.py 2010-02-26 05:13:11 +0000
125+++ lib/lp/bugs/model/bug.py 2010-03-01 17:41:14 +0000
126@@ -100,6 +100,7 @@
127 from lp.registry.interfaces.person import validate_public_person
128 from lp.registry.interfaces.product import IProduct
129 from lp.registry.interfaces.productseries import IProductSeries
130+from lp.registry.interfaces.projectgroup import IProjectGroup
131 from lp.registry.interfaces.sourcepackage import ISourcePackage
132 from lp.registry.model.mentoringoffer import MentoringOffer
133 from lp.registry.model.person import Person, ValidPersonCache
134@@ -876,6 +877,10 @@
135 distroseries=distro_series,
136 sourcepackagename=source_package_name)
137
138+ # When a new task is added the bug's heat becomes relevant to the
139+ # target's max_bug_heat.
140+ target.recalculateMaxBugHeat()
141+
142 return new_task
143
144 def addWatch(self, bugtracker, remotebug, owner):
145@@ -1529,6 +1534,8 @@
146 def setHeat(self, heat):
147 """See `IBug`."""
148 self.heat = heat
149+ for task in self.bugtasks:
150+ task.target.recalculateMaxBugHeat()
151
152
153 class BugSet:
154
155=== modified file 'lib/lp/bugs/model/bugtarget.py'
156--- lib/lp/bugs/model/bugtarget.py 2010-02-26 05:45:47 +0000
157+++ lib/lp/bugs/model/bugtarget.py 2010-03-01 17:41:14 +0000
158@@ -25,10 +25,13 @@
159 from canonical.launchpad.webapp.interfaces import ILaunchBag
160 from lp.bugs.interfaces.bugtarget import IOfficialBugTag
161 from lp.registry.interfaces.distribution import IDistribution
162+from lp.registry.interfaces.distroseries import IDistroSeries
163 from lp.registry.interfaces.distributionsourcepackage import (
164 IDistributionSourcePackage)
165 from lp.registry.interfaces.product import IProduct
166+from lp.registry.interfaces.productseries import IProductSeries
167 from lp.registry.interfaces.projectgroup import IProjectGroup
168+from lp.registry.interfaces.sourcepackage import ISourcePackage
169 from lp.bugs.interfaces.bugtask import (
170 BugTagsSearchCombinator, BugTaskImportance, BugTaskSearchParams,
171 BugTaskStatus, RESOLVED_BUGTASK_STATUSES, UNRESOLVED_BUGTASK_STATUSES)
172@@ -170,6 +173,59 @@
173 else:
174 raise NotImplementedError
175
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+
229 def getBugCounts(self, user, statuses=None):
230 """See `IHasBugs`."""
231 if statuses is None:
232
233=== modified file 'lib/lp/bugs/model/bugtask.py'
234--- lib/lp/bugs/model/bugtask.py 2010-02-27 20:20:03 +0000
235+++ lib/lp/bugs/model/bugtask.py 2010-03-01 17:41:14 +0000
236@@ -969,6 +969,9 @@
237 enforced implicitly by the code in
238 lib/canonical/launchpad/browser/bugtask.py#BugTaskEditView.
239 """
240+
241+ target_before_change = self.target
242+
243 if (self.milestone is not None and
244 self.milestone.target != target):
245 # If the milestone for this bugtask is set, we
246@@ -993,6 +996,12 @@
247 "Distribution bug tasks may only be re-targeted "
248 "to a package in the same distribution.")
249
250+ # After the target has changed, we need to recalculate the maximum bug
251+ # heat for the new and old targets.
252+ if self.target != target_before_change:
253+ target_before_change.recalculateMaxBugHeat()
254+ self.target.recalculateMaxBugHeat()
255+
256 def updateTargetNameCache(self, newtarget=None):
257 """See `IBugTask`."""
258 if newtarget is None:
259
260=== modified file 'lib/lp/registry/configure.zcml'
261--- lib/lp/registry/configure.zcml 2010-02-27 20:20:03 +0000
262+++ lib/lp/registry/configure.zcml 2010-03-01 17:41:14 +0000
263@@ -408,7 +408,8 @@
264 findRelatedArchives
265 findRelatedArchivePublications
266 userHasBugSubscriptions
267- max_bug_heat"/>
268+ max_bug_heat
269+ recalculateMaxBugHeat"/>
270 <require
271 permission="launchpad.AnyPerson"
272 attributes="

Subscribers

People subscribed via source and target branches

to status/vote changes: