Merge lp:~gmb/launchpad/stored-proc-for-bug-heat-bug-582195 into lp:launchpad/db-devel

Proposed by Graham Binns on 2010-05-26
Status: Superseded
Proposed branch: lp:~gmb/launchpad/stored-proc-for-bug-heat-bug-582195
Merge into: lp:launchpad/db-devel
Diff against target: 154 lines (+150/-0)
1 file modified
database/schema/pending/stored-proc-for-bug-heat.sql (+150/-0)
To merge this branch: bzr merge lp:~gmb/launchpad/stored-proc-for-bug-heat-bug-582195
Reviewer Review Type Date Requested Status
Björn Tillenius db 2010-05-26 Pending
Stuart Bishop db 2010-05-26 Pending
Canonical Launchpad Engineering code 2010-05-26 Pending
Review via email: mp+26032@code.launchpad.net

This proposal has been superseded by a proposal from 2010-05-26.

Commit Message

A stored procedure has been added for calculating bug heat in the database rather than in code.

Description of the Change

This branch takes the code in lib/lp/bugs/scripts/bugheat.py and converts it into a plpython stored procedure.

The idea behind this is that we need to do bug heat calculations in the DB rather than in code, since the setup and teardown surrounding doing the calculations in code is far to slow and cumbersome.

The stored procedure is currently not hooked up to anything, but once the patch is applied it's easy to check the results of the stored procedure against the existing code using psql and an iharness session, thus:

In psql:

  SELECT calculate_bug_heat(1);

In iharness:

  >>> from lp.bugs.scripts.bugheat import BugHeatCalculator
  >>> bug_1 = getUtility(IBugSet).get(1)
  >>> calculator = BugHeatCalculator(bug_1)
  >>> calculator.getBugHeat()

To post a comment you must log in.
Björn Tillenius (bjornt) wrote :

On Wed, May 26, 2010 at 10:50:49AM -0000, Graham Binns wrote:
> The stored procedure is currently not hooked up to anything, but once
> the patch is applied it's easy to check the results of the stored
> procedure against the existing code using psql and an iharness session,
> thus:

Why not hook it up directly? It would be useful to have this tested,
since it's hard to check that it's doing what it should be doing.

--
Björn Tillenius | https://launchpad.net/~bjornt

Graham Binns (gmb) wrote :

2010/5/26 Björn Tillenius <email address hidden>:
> On Wed, May 26, 2010 at 10:50:49AM -0000, Graham Binns wrote:
>> The stored procedure is currently not hooked up to anything, but once
>> the patch is applied it's easy to check the results of the stored
>> procedure against the existing code using psql and an iharness session,
>> thus:
>
> Why not hook it up directly? It would be useful to have this tested,
> since it's hard to check that it's doing what it should be doing.
>

I was going to do it in a subsequent branch, but since it's now late
in stub's day anyway I'll do it in this one and resubmit.

--
Graham Binns | PGP Key: EC66FA7D

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'database/schema/pending/stored-proc-for-bug-heat.sql'
2--- database/schema/pending/stored-proc-for-bug-heat.sql 1970-01-01 00:00:00 +0000
3+++ database/schema/pending/stored-proc-for-bug-heat.sql 2010-05-26 11:02:28 +0000
4@@ -0,0 +1,150 @@
5+SET client_min_messages=ERROR;
6+
7+CREATE OR REPLACE FUNCTION calculate_bug_heat(bug_id integer) RETURNS integer
8+LANGUAGE plpythonu AS $$
9+ from datetime import datetime
10+
11+ class BugHeatConstants:
12+ PRIVACY = 150
13+ SECURITY = 250
14+ DUPLICATE = 6
15+ AFFECTED_USER = 4
16+ SUBSCRIBER = 2
17+
18+
19+ def get_max_heat_for_bug(bug_id):
20+ bug_tasks = plpy.execute(
21+ "SELECT * FROM BugTask WHERE bug = %s" % bug_id)
22+
23+ max_heats = []
24+ for bug_task in bug_tasks:
25+ if bug_task['product'] is not None:
26+ product = plpy.execute(
27+ "SELECT max_bug_heat FROM Product WHERE id = %s" %
28+ bug_task['product'])[0]
29+ max_heats.append(product['max_bug_heat'])
30+ elif bug_task['distribution']:
31+ distribution = plpy.execute(
32+ "SELECT max_bug_heat FROM Distribution WHERE id = %s" %
33+ bug_task['distribution'])[0]
34+ max_heats.append(distribution['max_bug_heat'])
35+ elif bug_task['productseries'] is not None:
36+ product_series = plpy.execute("""
37+ SELECT Product.max_bug_heat
38+ FROM ProductSeries, Product
39+ WHERE ProductSeries.Product = Product.id
40+ AND ProductSeries.id = %s"""%
41+ bug_task['productseries'])[0]
42+ max_heats.append(product_series['max_bug_heat'])
43+ elif bug_task['distroseries']:
44+ distro_series = plpy.execute("""
45+ SELECT Distribution.max_bug_heat
46+ FROM DistroSeries, Distribution
47+ WHERE DistroSeries.Distribution = Distribution.id
48+ AND DistroSeries.id = %s"""%
49+ bug_task['distroseries'])[0]
50+ max_heats.append(distro_series['max_bug_heat'])
51+ else:
52+ pass
53+
54+ return max(max_heats)
55+
56+
57+ # It would be nice to be able to just SELECT * here, but we need to
58+ # format the timestamps so that datetime.strptime() won't choke on
59+ # them.
60+ bug_data = plpy.execute("""
61+ SELECT
62+ duplicateof,
63+ private,
64+ security_related,
65+ number_of_duplicates,
66+ users_affected_count,
67+ TO_CHAR(datecreated, '%(date_format)s')
68+ AS formatted_date_created,
69+ TO_CHAR(date_last_updated, '%(date_format)s')
70+ AS formatted_date_last_updated,
71+ TO_CHAR(date_last_message, '%(date_format)s')
72+ AS formatted_date_last_message
73+ FROM Bug WHERE id = %(bug_id)s""" % {
74+ 'bug_id': bug_id,
75+ 'date_format': 'YYYY-MM-DD HH24:MI:SS',
76+ })
77+
78+ if bug_data.nrows() == 0:
79+ return 0
80+
81+ bug = bug_data[0]
82+ if bug['duplicateof'] is not None:
83+ return 0
84+
85+ heat = {}
86+ heat['dupes'] = (
87+ BugHeatConstants.DUPLICATE * bug['number_of_duplicates'])
88+ heat['affected_users'] = (
89+ BugHeatConstants.AFFECTED_USER *
90+ bug['users_affected_count'])
91+
92+ if bug['private']:
93+ heat['privacy'] = BugHeatConstants.PRIVACY
94+ if bug['security_related']:
95+ heat['security'] = BugHeatConstants.SECURITY
96+
97+ # Get the heat from subscribers.
98+ sub_count = plpy.execute("""
99+ SELECT bug, COUNT(id) AS sub_count
100+ FROM BugSubscription
101+ WHERE bug = %s
102+ GROUP BY bug
103+ """ % bug_id)
104+
105+ if sub_count.nrows() > 0:
106+ heat['subscribers'] = (
107+ BugHeatConstants.SUBSCRIBER * sub_count[0]['sub_count'])
108+
109+ # Get the heat from subscribers via duplicates.
110+ subs_from_dupes = plpy.execute("""
111+ SELECT bug.duplicateof AS dupe_of,
112+ COUNT(bugsubscription.id) AS dupe_sub_count
113+ FROM bugsubscription, bug
114+ WHERE bug.duplicateof = %s
115+ AND Bugsubscription.bug = bug.id
116+ GROUP BY Bug.duplicateof""" % bug_id)
117+ if subs_from_dupes.nrows() > 0:
118+ heat['subcribers_from_dupes'] = (
119+ BugHeatConstants.SUBSCRIBER * sub_count[0]['dupe_sub_count'])
120+
121+ total_heat = sum(heat.values())
122+
123+ # Bugs decay over time. Every day the bug isn't touched its heat
124+ # decreases by 1%.
125+ pg_datetime_fmt = "%Y-%m-%d %H:%M:%S"
126+ date_last_updated = datetime.strptime(
127+ bug['formatted_date_last_updated'], pg_datetime_fmt)
128+ days_since_last_update = (datetime.utcnow() - date_last_updated).days
129+ total_heat = int(total_heat * (0.99 ** days_since_last_update))
130+
131+ if days_since_last_update > 0:
132+ # Bug heat increases by a quarter of the maximum bug heat
133+ # divided by the number of days since the bug's creation date.
134+ date_created = datetime.strptime(
135+ bug['formatted_date_created'], pg_datetime_fmt)
136+
137+ if bug['formatted_date_last_message'] is not None:
138+ date_last_message = datetime.strptime(
139+ bug['formatted_date_last_message'], pg_datetime_fmt)
140+ oldest_date = max(date_last_updated, date_last_message)
141+ else:
142+ date_last_message = None
143+ oldest_date = date_last_updated
144+
145+ days_since_last_activity = (datetime.utcnow() - oldest_date).days
146+ days_since_created = (datetime.utcnow() - date_created).days
147+ max_heat = get_max_heat_for_bug(bug_id)
148+ if max_heat is not None and days_since_created > 0:
149+ return total_heat + (max_heat * 0.25 / days_since_created)
150+
151+ return int(total_heat)
152+$$;
153+
154+INSERT INTO LaunchpadDatabaseRevision VALUES (2207, 99, 0);

Subscribers

People subscribed via source and target branches

to status/vote changes: