Merge lp:~gmb/launchpad/remove-calculatebugheatjob-bug-588186 into lp:launchpad/db-devel

Proposed by Graham Binns
Status: Merged
Approved by: Graham Binns
Approved revision: no longer in the source branch.
Merged at revision: 9421
Proposed branch: lp:~gmb/launchpad/remove-calculatebugheatjob-bug-588186
Merge into: lp:launchpad/db-devel
Diff against target: 670 lines (+2/-578)
9 files modified
cronscripts/calculate-bug-heat.py (+0/-33)
lib/canonical/launchpad/scripts/garbo.py (+0/-1)
lib/lp/bugs/configure.zcml (+0/-12)
lib/lp/bugs/interfaces/bugjob.py (+1/-11)
lib/lp/bugs/model/bug.py (+0/-1)
lib/lp/bugs/model/bugheat.py (+0/-54)
lib/lp/bugs/scripts/bugheat.py (+0/-108)
lib/lp/bugs/scripts/tests/test_bugheat.py (+0/-256)
lib/lp/bugs/tests/test_bugheat.py (+1/-102)
To merge this branch: bzr merge lp:~gmb/launchpad/remove-calculatebugheatjob-bug-588186
Reviewer Review Type Date Requested Status
Michael Nelson (community) Approve
Jelmer Vernooij (community) code* Approve
Review via email: mp+26684@code.launchpad.net

Commit message

All CalculateBugHeatJob code has been removed since it is not longer used.

Description of the change

This branch removes all the CalculateBugHeatJob code since we no longer use it (bug heat calculation is now done by a stored procedure in the database).

To post a comment you must log in.
Revision history for this message
Jelmer Vernooij (jelmer) :
review: Approve (code*)
Revision history for this message
Michael Nelson (michael.nelson) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== removed file 'cronscripts/calculate-bug-heat.py'
--- cronscripts/calculate-bug-heat.py 2010-04-22 17:30:35 +0000
+++ cronscripts/calculate-bug-heat.py 1970-01-01 00:00:00 +0000
@@ -1,33 +0,0 @@
1#!/usr/bin/python2.5 -S
2#
3# Copyright 2010 Canonical Ltd. This software is licensed under the
4# GNU Affero General Public License version 3 (see the file LICENSE).
5
6# pylint: disable-msg=W0403
7
8"""Calculate bug heat."""
9
10__metaclass__ = type
11
12import _pythonpath
13
14from canonical.launchpad.webapp import errorlog
15
16from lp.services.job.runner import JobCronScript
17from lp.bugs.interfaces.bugjob import ICalculateBugHeatJobSource
18
19
20class RunCalculateBugHeat(JobCronScript):
21 """Run BranchScanJob jobs."""
22
23 config_name = 'calculate_bug_heat'
24 source_interface = ICalculateBugHeatJobSource
25
26 def main(self):
27 errorlog.globalErrorUtility.configure(self.config_name)
28 return super(RunCalculateBugHeat, self).main()
29
30
31if __name__ == '__main__':
32 script = RunCalculateBugHeat()
33 script.lock_and_run()
340
=== modified file 'lib/canonical/launchpad/scripts/garbo.py'
--- lib/canonical/launchpad/scripts/garbo.py 2010-05-27 16:49:09 +0000
+++ lib/canonical/launchpad/scripts/garbo.py 2010-06-03 10:55:38 +0000
@@ -33,7 +33,6 @@
33from lp.bugs.interfaces.bug import IBugSet33from lp.bugs.interfaces.bug import IBugSet
34from lp.bugs.model.bug import Bug34from lp.bugs.model.bug import Bug
35from lp.bugs.model.bugattachment import BugAttachment35from lp.bugs.model.bugattachment import BugAttachment
36from lp.bugs.interfaces.bugjob import ICalculateBugHeatJobSource
37from lp.bugs.model.bugnotification import BugNotification36from lp.bugs.model.bugnotification import BugNotification
38from lp.bugs.model.bugwatch import BugWatch37from lp.bugs.model.bugwatch import BugWatch
39from lp.bugs.scripts.checkwatches.scheduler import (38from lp.bugs.scripts.checkwatches.scheduler import (
4039
=== modified file 'lib/lp/bugs/configure.zcml'
--- lib/lp/bugs/configure.zcml 2010-05-26 15:24:23 +0000
+++ lib/lp/bugs/configure.zcml 2010-06-03 10:55:38 +0000
@@ -967,18 +967,6 @@
967 factory="lp.bugs.browser.bugtarget.BugsVHostBreadcrumb"967 factory="lp.bugs.browser.bugtarget.BugsVHostBreadcrumb"
968 permission="zope.Public"/>968 permission="zope.Public"/>
969969
970 <!-- CalculateBugHeatJobs -->
971 <class class="lp.bugs.model.bugheat.CalculateBugHeatJob">
972 <allow interface="lp.bugs.interfaces.bugjob.IBugJob" />
973 <allow interface="lp.bugs.interfaces.bugjob.ICalculateBugHeatJob"/>
974 </class>
975 <securedutility
976 component="lp.bugs.model.bugheat.CalculateBugHeatJob"
977 provides="lp.bugs.interfaces.bugjob.ICalculateBugHeatJobSource">
978 <allow
979 interface="lp.bugs.interfaces.bugjob.ICalculateBugHeatJobSource"/>
980 </securedutility>
981
982 <!-- ProcessApportBlobJobs -->970 <!-- ProcessApportBlobJobs -->
983 <class class="lp.bugs.model.apportjob.ProcessApportBlobJob">971 <class class="lp.bugs.model.apportjob.ProcessApportBlobJob">
984 <allow interface="lp.bugs.interfaces.apportjob.IApportJob" />972 <allow interface="lp.bugs.interfaces.apportjob.IApportJob" />
985973
=== modified file 'lib/lp/bugs/interfaces/bugjob.py'
--- lib/lp/bugs/interfaces/bugjob.py 2010-01-22 21:44:19 +0000
+++ lib/lp/bugs/interfaces/bugjob.py 2010-06-03 10:55:38 +0000
@@ -8,8 +8,6 @@
8 'BugJobType',8 'BugJobType',
9 'IBugJob',9 'IBugJob',
10 'IBugJobSource',10 'IBugJobSource',
11 'ICalculateBugHeatJob',
12 'ICalculateBugHeatJobSource',
13 ]11 ]
1412
15from zope.interface import Attribute, Interface13from zope.interface import Attribute, Interface
@@ -19,7 +17,7 @@
1917
20from lazr.enum import DBEnumeratedType, DBItem18from lazr.enum import DBEnumeratedType, DBItem
21from lp.bugs.interfaces.bug import IBug19from lp.bugs.interfaces.bug import IBug
22from lp.services.job.interfaces.job import IJob, IJobSource, IRunnableJob20from lp.services.job.interfaces.job import IJob, IJobSource
2321
2422
25class BugJobType(DBEnumeratedType):23class BugJobType(DBEnumeratedType):
@@ -57,11 +55,3 @@
5755
58 def create(bug):56 def create(bug):
59 """Create a new IBugJob for a bug."""57 """Create a new IBugJob for a bug."""
60
61
62class ICalculateBugHeatJob(IRunnableJob):
63 """A Job to calculate bug heat."""
64
65
66class ICalculateBugHeatJobSource(IBugJobSource):
67 """Interface for acquiring CalculateBugHeatJobs."""
6858
=== modified file 'lib/lp/bugs/model/bug.py'
--- lib/lp/bugs/model/bug.py 2010-05-30 04:06:48 +0000
+++ lib/lp/bugs/model/bug.py 2010-06-03 10:55:38 +0000
@@ -83,7 +83,6 @@
83from lp.bugs.interfaces.bugtracker import BugTrackerType83from lp.bugs.interfaces.bugtracker import BugTrackerType
84from lp.bugs.interfaces.bugwatch import IBugWatchSet84from lp.bugs.interfaces.bugwatch import IBugWatchSet
85from lp.bugs.interfaces.cve import ICveSet85from lp.bugs.interfaces.cve import ICveSet
86from lp.bugs.scripts.bugheat import BugHeatConstants
87from lp.bugs.model.bugattachment import BugAttachment86from lp.bugs.model.bugattachment import BugAttachment
88from lp.bugs.model.bugbranch import BugBranch87from lp.bugs.model.bugbranch import BugBranch
89from lp.bugs.model.bugcve import BugCve88from lp.bugs.model.bugcve import BugCve
9089
=== removed file 'lib/lp/bugs/model/bugheat.py'
--- lib/lp/bugs/model/bugheat.py 2010-01-21 20:46:03 +0000
+++ lib/lp/bugs/model/bugheat.py 1970-01-01 00:00:00 +0000
@@ -1,54 +0,0 @@
1# Copyright 2010 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""Job classes related to BugJobs are in here."""
5
6__metaclass__ = type
7__all__ = [
8 'CalculateBugHeatJob',
9 ]
10
11from zope.component import getUtility
12from zope.interface import classProvides, implements
13
14from canonical.launchpad.webapp.interfaces import (
15 DEFAULT_FLAVOR, IStoreSelector, MAIN_STORE)
16
17from lp.bugs.interfaces.bugjob import (
18 BugJobType, ICalculateBugHeatJob, ICalculateBugHeatJobSource)
19from lp.bugs.model.bugjob import BugJob, BugJobDerived
20from lp.bugs.scripts.bugheat import BugHeatCalculator
21from lp.services.job.model.job import Job
22
23
24class CalculateBugHeatJob(BugJobDerived):
25 """A Job to calculate bug heat."""
26 implements(ICalculateBugHeatJob)
27
28 class_job_type = BugJobType.UPDATE_HEAT
29 classProvides(ICalculateBugHeatJobSource)
30
31 def run(self):
32 """See `IRunnableJob`."""
33 calculator = BugHeatCalculator(self.bug)
34 calculated_heat = calculator.getBugHeat()
35 self.bug.setHeat(calculated_heat)
36
37 @classmethod
38 def create(cls, bug):
39 """See `ICalculateBugHeatJobSource`."""
40 # If there's already a job for the bug, don't create a new one.
41 store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
42 job_for_bug = store.find(
43 BugJob,
44 BugJob.bug == bug,
45 BugJob.job_type == cls.class_job_type,
46 BugJob.job == Job.id,
47 Job.id.is_in(Job.ready_jobs)
48 ).any()
49
50 if job_for_bug is not None:
51 return cls(job_for_bug)
52 else:
53 return super(CalculateBugHeatJob, cls).create(bug)
54
550
=== removed file 'lib/lp/bugs/scripts/bugheat.py'
--- lib/lp/bugs/scripts/bugheat.py 2010-04-29 11:31:49 +0000
+++ lib/lp/bugs/scripts/bugheat.py 1970-01-01 00:00:00 +0000
@@ -1,108 +0,0 @@
1# Copyright 2010 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""The innards of the Bug Heat cronscript."""
5
6__metaclass__ = type
7__all__ = [
8 'BugHeatCalculator',
9 'BugHeatConstants',
10 ]
11
12from datetime import datetime
13
14from lp.bugs.interfaces.bugtask import RESOLVED_BUGTASK_STATUSES
15
16class BugHeatConstants:
17
18 PRIVACY = 150
19 SECURITY = 250
20 DUPLICATE = 6
21 AFFECTED_USER = 4
22 SUBSCRIBER = 2
23
24
25class BugHeatCalculator:
26 """A class to calculate the heat for a bug."""
27 # If you change the way that bug heat is calculated, remember to update
28 # the description of how it is calculated at
29 # /lib/lp/bugs/help/bug-heat.html and
30 # https://help.launchpad.net/Bugs/BugHeat
31
32 def __init__(self, bug):
33 self.bug = bug
34
35 def _getHeatFromPrivacy(self):
36 """Return the heat generated by the bug's `private` attribute."""
37 if self.bug.private:
38 return BugHeatConstants.PRIVACY
39 else:
40 return 0
41
42 def _getHeatFromSecurity(self):
43 """Return the heat generated if the bug is security related."""
44 if self.bug.security_related:
45 return BugHeatConstants.SECURITY
46 else:
47 return 0
48
49 def _getHeatFromDuplicates(self):
50 """Return the heat generated by the bug's duplicates."""
51 return self.bug.duplicates.count() * BugHeatConstants.DUPLICATE
52
53 def _getHeatFromAffectedUsers(self):
54 """Return the heat generated by the bug's affected users."""
55 return (
56 self.bug.users_affected_count_with_dupes *
57 BugHeatConstants.AFFECTED_USER)
58
59 def _getHeatFromSubscribers(self):
60 """Return the heat generated by the bug's subscribers."""
61 direct_subscribers = self.bug.getDirectSubscribers()
62 subscribers_from_dupes = self.bug.getSubscribersFromDuplicates()
63
64 subscriber_count = (
65 len(direct_subscribers) + len(subscribers_from_dupes))
66 return subscriber_count * BugHeatConstants.SUBSCRIBER
67
68 def _bugIsComplete(self):
69 """Are all the tasks for this bug resolved?"""
70 return all([(task.status in RESOLVED_BUGTASK_STATUSES)
71 for task in self.bug.bugtasks])
72
73 def getBugHeat(self):
74 """Return the total heat for the current bug."""
75 if self._bugIsComplete():
76 return 0
77
78 total_heat = sum([
79 self._getHeatFromAffectedUsers(),
80 self._getHeatFromDuplicates(),
81 self._getHeatFromPrivacy(),
82 self._getHeatFromSecurity(),
83 self._getHeatFromSubscribers(),
84 ])
85
86 # Bugs decay over time. Every day the bug isn't touched its heat
87 # decreases by 1%.
88 days = (
89 datetime.utcnow() -
90 self.bug.date_last_updated.replace(tzinfo=None)).days
91 total_heat = int(total_heat * (0.99 ** days))
92
93 if days > 0:
94 # Bug heat increases by a quarter of the maximum bug heat divided
95 # by the number of days since the bug's creation date.
96 days_since_last_activity = (
97 datetime.utcnow() -
98 max(self.bug.date_last_updated.replace(tzinfo=None),
99 self.bug.date_last_message.replace(tzinfo=None))).days
100 days_since_created = (
101 datetime.utcnow() - self.bug.datecreated.replace(tzinfo=None)).days
102 max_heat = max(
103 task.target.max_bug_heat for task in self.bug.bugtasks)
104 if max_heat is not None and days_since_created > 0:
105 total_heat = total_heat + (max_heat * 0.25 / days_since_created)
106
107 return int(total_heat)
108
1090
=== removed file 'lib/lp/bugs/scripts/tests/test_bugheat.py'
--- lib/lp/bugs/scripts/tests/test_bugheat.py 2010-04-29 11:31:49 +0000
+++ lib/lp/bugs/scripts/tests/test_bugheat.py 1970-01-01 00:00:00 +0000
@@ -1,256 +0,0 @@
1# Copyright 2010 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""Module docstring goes here."""
5
6__metaclass__ = type
7
8import unittest
9
10from datetime import datetime, timedelta
11
12from canonical.testing import LaunchpadZopelessLayer
13
14from lp.bugs.interfaces.bugtask import BugTaskStatus
15from lp.bugs.scripts.bugheat import BugHeatCalculator, BugHeatConstants
16from lp.testing import TestCaseWithFactory
17
18from zope.security.proxy import removeSecurityProxy
19
20
21class TestBugHeatCalculator(TestCaseWithFactory):
22 """Tests for the BugHeatCalculator class."""
23 # If you change the way that bug heat is calculated, remember to update
24 # the description of how it is calculated at
25 # /lib/lp/bugs/help/bug-heat.html and
26 # https://help.launchpad.net/Bugs/BugHeat
27
28 layer = LaunchpadZopelessLayer
29
30 def setUp(self):
31 super(TestBugHeatCalculator, self).setUp()
32 self.bug = self.factory.makeBug()
33 self.calculator = BugHeatCalculator(self.bug)
34
35 def test__getHeatFromDuplicates(self):
36 # BugHeatCalculator._getHeatFromDuplicates() returns the bug
37 # heat generated by duplicates of a bug.
38 # By default, the bug has no heat from dupes
39 self.assertEqual(0, self.calculator._getHeatFromDuplicates())
40
41 # If adding duplicates, the heat generated by them will be n *
42 # BugHeatConstants.DUPLICATE, where n is the number of
43 # duplicates.
44 for i in range(5):
45 dupe = self.factory.makeBug()
46 dupe.duplicateof = self.bug
47
48 expected_heat = BugHeatConstants.DUPLICATE * 5
49 actual_heat = self.calculator._getHeatFromDuplicates()
50 self.assertEqual(
51 expected_heat, actual_heat,
52 "Heat from duplicates does not match expected heat. "
53 "Expected %s, got %s" % (expected_heat, actual_heat))
54
55 def test__getHeatFromAffectedUsers(self):
56 # BugHeatCalculator._getHeatFromAffectedUsers() returns the bug
57 # heat generated by users affected by the bug and by duplicate bugs.
58 # By default, the heat will be BugHeatConstants.AFFECTED_USER, since
59 # there will be one affected user (the user who filed the bug).
60 self.assertEqual(
61 BugHeatConstants.AFFECTED_USER,
62 self.calculator._getHeatFromAffectedUsers())
63
64 # As the number of affected users increases, the heat generated
65 # will be n * BugHeatConstants.AFFECTED_USER, where n is the number
66 # of affected users.
67 for i in range(5):
68 person = self.factory.makePerson()
69 self.bug.markUserAffected(person)
70
71 expected_heat = BugHeatConstants.AFFECTED_USER * 6
72 actual_heat = self.calculator._getHeatFromAffectedUsers()
73 self.assertEqual(
74 expected_heat, actual_heat,
75 "Heat from affected users does not match expected heat. "
76 "Expected %s, got %s" % (expected_heat, actual_heat))
77
78 # When our bug has duplicates, users affected by these duplicates
79 # are included in _getHeatFromAffectedUsers() of the main bug.
80 for i in range(3):
81 dupe = self.factory.makeBug()
82 dupe.duplicateof = self.bug
83 # Each bug reporter is by default also marked as being affected
84 # by the bug, so we have three additional affected users.
85 expected_heat += BugHeatConstants.AFFECTED_USER * 3
86
87 person = self.factory.makePerson()
88 dupe.markUserAffected(person)
89 expected_heat += BugHeatConstants.AFFECTED_USER
90 actual_heat = self.calculator._getHeatFromAffectedUsers()
91 self.assertEqual(
92 expected_heat, actual_heat,
93 "Heat from users affected by duplicate bugs does not match "
94 "expected heat. Expected %s, got %s"
95 % (expected_heat, actual_heat))
96
97 def test__getHeatFromSubscribers(self):
98 # BugHeatCalculator._getHeatFromSubscribers() returns the bug
99 # heat generated by users subscribed tothe bug.
100 # By default, the heat will be BugHeatConstants.SUBSCRIBER,
101 # since there will be one direct subscriber (the user who filed
102 # the bug).
103 self.assertEqual(
104 BugHeatConstants.SUBSCRIBER,
105 self.calculator._getHeatFromSubscribers())
106
107 # As the number of subscribers increases, the heat generated
108 # will be n * BugHeatConstants.SUBSCRIBER, where n is the number
109 # of subscribers.
110 for i in range(5):
111 person = self.factory.makePerson()
112 self.bug.subscribe(person, person)
113
114 expected_heat = BugHeatConstants.SUBSCRIBER * 6
115 actual_heat = self.calculator._getHeatFromSubscribers()
116 self.assertEqual(
117 expected_heat, actual_heat,
118 "Heat from subscribers does not match expected heat. "
119 "Expected %s, got %s" % (expected_heat, actual_heat))
120
121 # Subscribers from duplicates are included in the heat returned
122 # by _getHeatFromSubscribers()
123 dupe = self.factory.makeBug()
124 dupe.duplicateof = self.bug
125 expected_heat = BugHeatConstants.SUBSCRIBER * 7
126 actual_heat = self.calculator._getHeatFromSubscribers()
127 self.assertEqual(
128 expected_heat, actual_heat,
129 "Heat from subscribers (including duplicate-subscribers) "
130 "does not match expected heat. Expected %s, got %s" %
131 (expected_heat, actual_heat))
132
133 # Seting the bug to private will increase its heat from
134 # subscribers by 1 * BugHeatConstants.SUBSCRIBER, as the project
135 # owner will now be directly subscribed to it.
136 self.bug.setPrivate(True, self.bug.owner)
137 expected_heat = BugHeatConstants.SUBSCRIBER * 8
138 actual_heat = self.calculator._getHeatFromSubscribers()
139 self.assertEqual(
140 expected_heat, actual_heat,
141 "Heat from subscribers to private bug does not match expected "
142 "heat. Expected %s, got %s" % (expected_heat, actual_heat))
143
144 def test__getHeatFromPrivacy(self):
145 # BugHeatCalculator._getHeatFromPrivacy() returns the heat
146 # generated by the bug's private attribute. If the bug is
147 # public, this will be 0.
148 self.assertEqual(0, self.calculator._getHeatFromPrivacy())
149
150 # However, if the bug is private, _getHeatFromPrivacy() will
151 # return BugHeatConstants.PRIVACY.
152 self.bug.setPrivate(True, self.bug.owner)
153 self.assertEqual(
154 BugHeatConstants.PRIVACY, self.calculator._getHeatFromPrivacy())
155
156 def test__getHeatFromSecurity(self):
157 # BugHeatCalculator._getHeatFromSecurity() returns the heat
158 # generated by the bug's security_related attribute. If the bug
159 # is not security related, _getHeatFromSecurity() will return 0.
160 self.assertEqual(0, self.calculator._getHeatFromPrivacy())
161
162
163 # If, on the other hand, the bug is security_related,
164 # _getHeatFromSecurity() will return BugHeatConstants.SECURITY
165 self.bug.setSecurityRelated(True)
166 self.assertEqual(
167 BugHeatConstants.SECURITY, self.calculator._getHeatFromSecurity())
168
169 def test_getBugHeat(self):
170 # BugHeatCalculator.getBugHeat() returns the total heat for a
171 # given bug as the sum of the results of all _getHeatFrom*()
172 # methods.
173 # By default this will be (BugHeatConstants.AFFECTED_USER +
174 # BugHeatConstants.SUBSCRIBER) since there will be one
175 # subscriber and one affected user only.
176 expected_heat = (
177 BugHeatConstants.AFFECTED_USER + BugHeatConstants.SUBSCRIBER)
178 actual_heat = self.calculator.getBugHeat()
179 self.assertEqual(
180 expected_heat, actual_heat,
181 "Expected bug heat did not match actual bug heat. "
182 "Expected %s, got %s" % (expected_heat, actual_heat))
183
184 # Adding a duplicate and making the bug private and security
185 # related will increase its heat.
186 dupe = self.factory.makeBug()
187 dupe.duplicateof = self.bug
188 self.bug.setPrivate(True, self.bug.owner)
189 self.bug.setSecurityRelated(True)
190
191 expected_heat += (
192 BugHeatConstants.DUPLICATE +
193 BugHeatConstants.PRIVACY +
194 BugHeatConstants.SECURITY +
195 BugHeatConstants.AFFECTED_USER
196 )
197
198 # Adding the duplicate and making the bug private means it gets
199 # two new subscribers, the project owner and the duplicate's
200 # direct subscriber.
201 expected_heat += BugHeatConstants.SUBSCRIBER * 2
202 actual_heat = self.calculator.getBugHeat()
203 self.assertEqual(
204 expected_heat, actual_heat,
205 "Expected bug heat did not match actual bug heat. "
206 "Expected %s, got %s" % (expected_heat, actual_heat))
207
208 def test_getBugHeat_complete_bugs(self):
209 # Bug which are in a resolved status don't have heat at all.
210 complete_bug = self.factory.makeBug()
211 heat = BugHeatCalculator(complete_bug).getBugHeat()
212 self.assertNotEqual(
213 0, heat,
214 "Expected bug heat did not match actual bug heat. "
215 "Expected a positive value, got 0")
216 complete_bug.bugtasks[0].transitionToStatus(
217 BugTaskStatus.INVALID, complete_bug.owner)
218 heat = BugHeatCalculator(complete_bug).getBugHeat()
219 self.assertEqual(
220 0, heat,
221 "Expected bug heat did not match actual bug heat. "
222 "Expected %s, got %s" % (0, heat))
223
224 def test_getBugHeat_decay(self):
225 # Every day, a bug that wasn't touched has its heat reduced by 1%.
226 aging_bug = self.factory.makeBug()
227 fresh_heat = BugHeatCalculator(aging_bug).getBugHeat()
228 aging_bug.date_last_updated = (
229 aging_bug.date_last_updated - timedelta(days=1))
230 expected = int(fresh_heat * 0.99)
231 heat = BugHeatCalculator(aging_bug).getBugHeat()
232 self.assertEqual(
233 expected, heat,
234 "Expected bug heat did not match actual bug heat. "
235 "Expected %s, got %s" % (expected, heat))
236
237 def test_getBugHeat_activity(self):
238 # Bug heat increases by a quarter of the maximum bug heat divided by
239 # the number of days between the bug's creating and its last activity.
240 active_bug = removeSecurityProxy(self.factory.makeBug())
241 fresh_heat = BugHeatCalculator(active_bug).getBugHeat()
242 active_bug.date_last_updated = (
243 active_bug.date_last_updated - timedelta(days=10))
244 active_bug.datecreated = (active_bug.datecreated - timedelta(days=20))
245 active_bug.default_bugtask.target.setMaxBugHeat(100)
246 expected = int((fresh_heat * (0.99 ** 20)) + (100 * 0.25 / 20))
247 heat = BugHeatCalculator(active_bug).getBugHeat()
248 self.assertEqual(
249 expected, heat,
250 "Expected bug heat did not match actual bug heat. "
251 "Expected %s, got %s" % (expected, heat))
252
253
254
255def test_suite():
256 return unittest.TestLoader().loadTestsFromName(__name__)
2570
=== modified file 'lib/lp/bugs/tests/test_bugheat.py'
--- lib/lp/bugs/tests/test_bugheat.py 2010-05-27 13:56:03 +0000
+++ lib/lp/bugs/tests/test_bugheat.py 2010-06-03 10:55:38 +0000
@@ -5,114 +5,13 @@
55
6__metaclass__ = type6__metaclass__ = type
77
8import pytz
9import transaction
10import unittest8import unittest
11from datetime import datetime9
12
13from zope.component import getUtility
14
15from canonical.launchpad.scripts.tests import run_script
16from canonical.testing import LaunchpadZopelessLayer10from canonical.testing import LaunchpadZopelessLayer
1711
18from lp.bugs.adapters.bugchange import BugDescriptionChange
19from lp.bugs.interfaces.bugjob import ICalculateBugHeatJobSource
20from lp.bugs.model.bugheat import CalculateBugHeatJob
21from lp.bugs.scripts.bugheat import BugHeatCalculator
22from lp.testing import TestCaseWithFactory
23from lp.testing.factory import LaunchpadObjectFactory12from lp.testing.factory import LaunchpadObjectFactory
2413
2514
26class CalculateBugHeatJobTestCase(TestCaseWithFactory):
27 """Test case for CalculateBugHeatJob."""
28
29 layer = LaunchpadZopelessLayer
30
31 def setUp(self):
32 super(CalculateBugHeatJobTestCase, self).setUp()
33 self.bug = self.factory.makeBug()
34
35 # NB: This looks like it should go in the teardown, however
36 # creating the bug causes a job to be added for it. We clear
37 # this out so that our tests are consistent.
38 self._completeJobsAndAssertQueueEmpty()
39
40 def _completeJobsAndAssertQueueEmpty(self):
41 """Make sure that all the CalculateBugHeatJobs are completed."""
42 for bug_job in getUtility(ICalculateBugHeatJobSource).iterReady():
43 bug_job.job.start()
44 bug_job.job.complete()
45 self.assertEqual(0, self._getJobCount())
46
47 def _getJobCount(self):
48 """Return the number of CalculateBugHeatJobs in the queue."""
49 return len(self._getJobs())
50
51 def _getJobs(self):
52 """Return the pending CalculateBugHeatJobs as a list."""
53 return list(CalculateBugHeatJob.iterReady())
54
55 def test_run(self):
56 # CalculateBugHeatJob.run() sets calculates and sets the heat
57 # for a bug.
58 job = CalculateBugHeatJob.create(self.bug)
59 bug_heat_calculator = BugHeatCalculator(self.bug)
60
61 job.run()
62 self.assertEqual(
63 bug_heat_calculator.getBugHeat(), self.bug.heat)
64
65 def test_utility(self):
66 # CalculateBugHeatJobSource is a utility for acquiring
67 # CalculateBugHeatJobs.
68 utility = getUtility(ICalculateBugHeatJobSource)
69 self.assertTrue(
70 ICalculateBugHeatJobSource.providedBy(utility))
71
72 def test_create_only_creates_one(self):
73 # If there's already a CalculateBugHeatJob for a bug,
74 # CalculateBugHeatJob.create() won't create a new one.
75 job = CalculateBugHeatJob.create(self.bug)
76
77 # There will now be one job in the queue.
78 self.assertEqual(1, self._getJobCount())
79
80 new_job = CalculateBugHeatJob.create(self.bug)
81
82 # The two jobs will in fact be the same job.
83 self.assertEqual(job, new_job)
84
85 # And the queue will still have a length of 1.
86 self.assertEqual(1, self._getJobCount())
87
88 def test_cronscript_succeeds(self):
89 # The calculate-bug-heat cronscript will run all pending
90 # CalculateBugHeatJobs.
91 CalculateBugHeatJob.create(self.bug)
92 transaction.commit()
93
94 retcode, stdout, stderr = run_script(
95 'cronscripts/calculate-bug-heat.py', [],
96 expect_returncode=0)
97 self.assertEqual('', stdout)
98 self.assertIn(
99 'INFO Ran 1 CalculateBugHeatJob jobs.\n', stderr)
100
101 def test_getOopsVars(self):
102 # BugJobDerived.getOopsVars() returns the variables to be used
103 # when logging an OOPS for a bug job. We test this using
104 # CalculateBugHeatJob because BugJobDerived doesn't let us
105 # create() jobs.
106 job = CalculateBugHeatJob.create(self.bug)
107 vars = job.getOopsVars()
108
109 # The Bug ID, BugJob ID and BugJob type will be returned by
110 # getOopsVars().
111 self.assertIn(('bug_id', self.bug.id), vars)
112 self.assertIn(('bug_job_id', job.context.id), vars)
113 self.assertIn(('bug_job_type', job.context.job_type.title), vars)
114
115
116class MaxHeatByTargetBase:15class MaxHeatByTargetBase:
117 """Base class for testing a bug target's max_bug_heat attribute."""16 """Base class for testing a bug target's max_bug_heat attribute."""
11817

Subscribers

People subscribed via source and target branches

to status/vote changes: