Merge lp:~intellectronica/launchpad/heat-decay-complete into lp:launchpad

Proposed by Eleanor Berger
Status: Merged
Approved by: Edwin Grubbs
Approved revision: not available
Merged at revision: not available
Proposed branch: lp:~intellectronica/launchpad/heat-decay-complete
Merge into: lp:launchpad
Diff against target: 130 lines (+58/-2)
3 files modified
lib/lp/bugs/doc/bug-heat.txt (+8/-0)
lib/lp/bugs/scripts/bugheat.py (+18/-0)
lib/lp/bugs/scripts/tests/test_bugheat.py (+32/-2)
To merge this branch: bzr merge lp:~intellectronica/launchpad/heat-decay-complete
Reviewer Review Type Date Requested Status
Edwin Grubbs (community) code Approve
Review via email: mp+20250@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Eleanor Berger (intellectronica) wrote :

This branch fixes two small bug heat related issues:

1. Bugs with a resolved status for all their tasks don't have any heat at all.
2. Bug heat decays over time. For every month that passes without the bug being touched it loses 10% of its heat.

I've added unit tests.

Revision history for this message
Edwin Grubbs (edwin-grubbs) wrote :
Download full text (4.8 KiB)

Hi Tom,

This is a nice branch. I have a few comments below, and there are some
lint errors.

merge-conditional

-Edwin

== Pyflakes notices ==

lib/lp/bugs/scripts/bugheat.py
    13: 'getUtility' imported but unused
    14: 'implements' imported but unused
    16: 'ITunableLoop' imported but unused
    17: 'DBLoopTuner' imported but unused

lib/lp/bugs/scripts/tests/test_bugheat.py
    10: 'datetime' imported but unused

>=== modified file 'lib/lp/bugs/scripts/bugheat.py'
>--- lib/lp/bugs/scripts/bugheat.py 2010-01-26 20:31:13 +0000
>+++ lib/lp/bugs/scripts/bugheat.py 2010-02-26 21:30:36 +0000
>@@ -8,6 +8,7 @@
> 'BugHeatCalculator',
> ]
>
>+from datetime import datetime
>
> from zope.component import getUtility
> from zope.interface import implements
>@@ -15,6 +16,8 @@
> from canonical.launchpad.interfaces.looptuner import ITunableLoop
> from canonical.launchpad.utilities.looptuner import DBLoopTuner
>
>+from lp.bugs.interfaces.bugtask import RESOLVED_BUGTASK_STATUSES
>+
>
> class BugHeatConstants:
>
>@@ -63,8 +66,16 @@
> len(direct_subscribers) + len(subscribers_from_dupes))
> return subscriber_count * BugHeatConstants.SUBSCRIBER
>
>+ def _bugIsComplete(self):
>+ """Are all the tasks for this bug resolved?"""
>+ return all([(task.status in RESOLVED_BUGTASK_STATUSES)
>+ for task in self.bug.bugtasks])
>+
> def getBugHeat(self):
> """Return the total heat for the current bug."""
>+ if self._bugIsComplete():
>+ return 0
>+
> total_heat = sum([
> self._getHeatFromAffectedUsers(),
> self._getHeatFromDuplicates(),
>@@ -73,5 +84,15 @@
> self._getHeatFromSubscribers(),
> ])
>
>+ # Bugs decay over time. Every month the bug isn't touched its heat
>+ # decreeses by 5%.

s/decreeses/decreases/

The comment says 5%, but the test says 10%. Which is it?

>+ months = (
>+ datetime.utcnow() -
>+ self.bug.date_last_updated.replace(tzinfo=None)).days / 30
>+ for i in range(months):
>+ total_heat = total_heat * 0.95

This could be simplified as:
    total_heat *= 0.95 ** months

>+
>+ total_heat = int(total_heat)
>+
> return total_heat
>
>
>=== modified file 'lib/lp/bugs/scripts/tests/test_bugheat.py'
>--- lib/lp/bugs/scripts/tests/test_bugheat.py 2010-01-12 16:41:23 +0000
>+++ lib/lp/bugs/scripts/tests/test_bugheat.py 2010-02-26 21:30:36 +0000
>@@ -1,4 +1,3 @@
>-
> # Copyright 2010 Canonical Ltd. This software is licensed under the
> # GNU Affero General Public License version 3 (see the file LICENSE).
>
>@@ -8,12 +7,14 @@
>
> import unittest
>
>+from datetime import datetime, timedelta
>+
> from canonical.testing import LaunchpadZopelessLayer
>
>+from lp.bugs.interfaces.bugtask import BugTaskStatus
> from lp.bugs.scripts.bugheat import BugHeatCalculator, BugHeatConstants
> from lp.testing import TestCaseWithFactory
>
>-
> class TestBugHeatCalculator(TestCaseWithFactory):
> """Tests for the BugHeatCalculator class."""
>
>@@ -177,6 +178,35 @@
> "Expected bug heat did not match actual bug heat. "
> "E...

Read more...

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/bugs/doc/bug-heat.txt'
--- lib/lp/bugs/doc/bug-heat.txt 2010-01-12 16:41:23 +0000
+++ lib/lp/bugs/doc/bug-heat.txt 2010-02-27 14:48:24 +0000
@@ -44,6 +44,14 @@
44 >>> bug_1.heat44 >>> bug_1.heat
45 045 0
4646
47We touch bug 1 to make sure its date_last_updated is recent enough (bug heat
48decays over time).
49
50 >>> new_comment = bug_1.newMessage(
51 ... owner=bug_1.owner, subject="...", content="...")
52 >>> import transaction ; transaction.commit()
53 >>> bug_1 = getUtility(IBugSet).get(1)
54
47 >>> update_bug_heat(chunk_size=1)55 >>> update_bug_heat(chunk_size=1)
48 DEBUG Updating 1 Bugs (starting id: ...)56 DEBUG Updating 1 Bugs (starting id: ...)
49 ...57 ...
5058
=== modified file 'lib/lp/bugs/scripts/bugheat.py'
--- lib/lp/bugs/scripts/bugheat.py 2010-01-26 20:31:13 +0000
+++ lib/lp/bugs/scripts/bugheat.py 2010-02-27 14:48:24 +0000
@@ -8,6 +8,7 @@
8 'BugHeatCalculator',8 'BugHeatCalculator',
9 ]9 ]
1010
11from datetime import datetime
1112
12from zope.component import getUtility13from zope.component import getUtility
13from zope.interface import implements14from zope.interface import implements
@@ -15,6 +16,8 @@
15from canonical.launchpad.interfaces.looptuner import ITunableLoop16from canonical.launchpad.interfaces.looptuner import ITunableLoop
16from canonical.launchpad.utilities.looptuner import DBLoopTuner17from canonical.launchpad.utilities.looptuner import DBLoopTuner
1718
19from lp.bugs.interfaces.bugtask import RESOLVED_BUGTASK_STATUSES
20
1821
19class BugHeatConstants:22class BugHeatConstants:
2023
@@ -63,8 +66,16 @@
63 len(direct_subscribers) + len(subscribers_from_dupes))66 len(direct_subscribers) + len(subscribers_from_dupes))
64 return subscriber_count * BugHeatConstants.SUBSCRIBER67 return subscriber_count * BugHeatConstants.SUBSCRIBER
6568
69 def _bugIsComplete(self):
70 """Are all the tasks for this bug resolved?"""
71 return all([(task.status in RESOLVED_BUGTASK_STATUSES)
72 for task in self.bug.bugtasks])
73
66 def getBugHeat(self):74 def getBugHeat(self):
67 """Return the total heat for the current bug."""75 """Return the total heat for the current bug."""
76 if self._bugIsComplete():
77 return 0
78
68 total_heat = sum([79 total_heat = sum([
69 self._getHeatFromAffectedUsers(),80 self._getHeatFromAffectedUsers(),
70 self._getHeatFromDuplicates(),81 self._getHeatFromDuplicates(),
@@ -73,5 +84,12 @@
73 self._getHeatFromSubscribers(),84 self._getHeatFromSubscribers(),
74 ])85 ])
7586
87 # Bugs decay over time. Every month the bug isn't touched its heat
88 # decreases by 10%.
89 months = (
90 datetime.utcnow() -
91 self.bug.date_last_updated.replace(tzinfo=None)).days / 30
92 total_heat = int(total_heat * (0.9 ** months))
93
76 return total_heat94 return total_heat
7795
7896
=== modified file 'lib/lp/bugs/scripts/tests/test_bugheat.py'
--- lib/lp/bugs/scripts/tests/test_bugheat.py 2010-01-12 16:41:23 +0000
+++ lib/lp/bugs/scripts/tests/test_bugheat.py 2010-02-27 14:48:24 +0000
@@ -1,4 +1,3 @@
1
2# Copyright 2010 Canonical Ltd. This software is licensed under the1# Copyright 2010 Canonical Ltd. This software is licensed under the
3# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
43
@@ -8,12 +7,14 @@
87
9import unittest8import unittest
109
10from datetime import datetime, timedelta
11
11from canonical.testing import LaunchpadZopelessLayer12from canonical.testing import LaunchpadZopelessLayer
1213
14from lp.bugs.interfaces.bugtask import BugTaskStatus
13from lp.bugs.scripts.bugheat import BugHeatCalculator, BugHeatConstants15from lp.bugs.scripts.bugheat import BugHeatCalculator, BugHeatConstants
14from lp.testing import TestCaseWithFactory16from lp.testing import TestCaseWithFactory
1517
16
17class TestBugHeatCalculator(TestCaseWithFactory):18class TestBugHeatCalculator(TestCaseWithFactory):
18 """Tests for the BugHeatCalculator class."""19 """Tests for the BugHeatCalculator class."""
1920
@@ -177,6 +178,35 @@
177 "Expected bug heat did not match actual bug heat. "178 "Expected bug heat did not match actual bug heat. "
178 "Expected %s, got %s" % (expected_heat, actual_heat))179 "Expected %s, got %s" % (expected_heat, actual_heat))
179180
181 def test_getBugHeat_complete_bugs(self):
182 # Bug which are in a resolved status don't have heat at all.
183 complete_bug = self.factory.makeBug()
184 heat = BugHeatCalculator(complete_bug).getBugHeat()
185 self.assertNotEqual(
186 0, heat,
187 "Expected bug heat did not match actual bug heat. "
188 "Expected a positive value, got 0")
189 complete_bug.bugtasks[0].transitionToStatus(
190 BugTaskStatus.INVALID, complete_bug.owner)
191 heat = BugHeatCalculator(complete_bug).getBugHeat()
192 self.assertEqual(
193 0, heat,
194 "Expected bug heat did not match actual bug heat. "
195 "Expected %s, got %s" % (0, heat))
196
197 def test_getBugHeat_decay(self):
198 # Every month, a bug that wasn't touched has its heat reduced by 10%.
199 aging_bug = self.factory.makeBug()
200 fresh_heat = BugHeatCalculator(aging_bug).getBugHeat()
201 aging_bug.date_last_updated = (
202 aging_bug.date_last_updated - timedelta(days=32))
203 expected = int(fresh_heat * 0.9)
204 heat = BugHeatCalculator(aging_bug).getBugHeat()
205 self.assertEqual(
206 expected, heat,
207 "Expected bug heat did not match actual bug heat. "
208 "Expected %s, got %s" % (expected, heat))
209
180210
181def test_suite():211def test_suite():
182 return unittest.TestLoader().loadTestsFromName(__name__)212 return unittest.TestLoader().loadTestsFromName(__name__)