← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~wgrant/launchpad/remove-heat-ratio-flag into lp:launchpad

 

William Grant has proposed merging lp:~wgrant/launchpad/remove-heat-ratio-flag into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  Bug #915267 in Launchpad itself: "bug heat flames are expensive to calculate"
  https://bugs.launchpad.net/launchpad/+bug/915267

For more details, see:
https://code.launchpad.net/~wgrant/launchpad/remove-heat-ratio-flag/+merge/91777

This branch drops the heat display feature flag, and the old disabled ratio display code. This is the next phase of the campaign against max_bug_heat.

I also fixed the other visible users of max_bug_heat, DistroSeries.getPrioritized*. They now use bug_count * 10, which seems to work reasonably well.
-- 
https://code.launchpad.net/~wgrant/launchpad/remove-heat-ratio-flag/+merge/91777
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~wgrant/launchpad/remove-heat-ratio-flag into lp:launchpad.
=== modified file 'lib/lp/bugs/browser/bugtask.py'
--- lib/lp/bugs/browser/bugtask.py	2012-01-25 05:34:12 +0000
+++ lib/lp/bugs/browser/bugtask.py	2012-02-07 08:14:20 +0000
@@ -14,7 +14,6 @@
     'bugtarget_renderer',
     'BugTargetTraversalMixin',
     'BugTargetView',
-    'bugtask_heat_html',
     'BugTaskBreadcrumb',
     'BugTaskContextMenu',
     'BugTaskCreateQuestionView',
@@ -32,7 +31,6 @@
     'BugTaskTableRowView',
     'BugTaskTextView',
     'BugTaskView',
-    'calculate_heat_display',
     'get_buglisting_search_filter_url',
     'get_comments_for_bugtask',
     'get_sortorder_from_request',
@@ -1089,16 +1087,9 @@
     @property
     def bug_heat_html(self):
         """HTML representation of the bug heat."""
-        if getFeatureFlag('bugs.heat_ratio_display.disabled'):
-            return (
-                '<span><a href="/+help-bugs/bug-heat.html" target="help" '
-                'class="sprite flame">%d</a></span>' % self.context.bug.heat)
-        else:
-            if IDistributionSourcePackage.providedBy(self.context.target):
-                return bugtask_heat_html(
-                    self.context, target=self.context.distribution)
-            else:
-                return bugtask_heat_html(self.context)
+        return (
+            '<span><a href="/+help-bugs/bug-heat.html" target="help" '
+            'class="sprite flame">%d</a></span>' % self.context.bug.heat)
 
     @property
     def privacy_notice_classes(self):
@@ -1108,40 +1099,6 @@
             return ''
 
 
-def calculate_heat_display(heat, max_bug_heat):
-    """Calculate the number of heat 'flames' to display."""
-    heat = float(heat)
-    max_bug_heat = float(max_bug_heat)
-    if max_bug_heat == 0:
-        return 0
-    if heat / max_bug_heat < 0.33333:
-        return 0
-    if heat / max_bug_heat < 0.66666 or max_bug_heat < 2:
-        return int(floor((heat / max_bug_heat) * 4))
-    else:
-        heat_index = int(floor((log(heat) / log(max_bug_heat)) * 4))
-        # ensure that we never return a value > 4, even if
-        # max_bug_heat is outdated.
-        return min(heat_index, 4)
-
-
-def bugtask_heat_html(bugtask, target=None):
-    """Render the HTML representing bug heat for a given bugask."""
-    if target is None:
-        target = bugtask.target
-    max_bug_heat = target.max_bug_heat
-    if max_bug_heat is None:
-        max_bug_heat = 5000
-    heat_ratio = calculate_heat_display(bugtask.bug.heat, max_bug_heat)
-    html = (
-        '<span><a href="/+help-bugs/bug-heat.html" target="help" '
-        'class="icon"><img src="/@@/bug-heat-%(ratio)i.png" '
-        'alt="%(ratio)i out of 4 heat flames" title="Heat: %(heat)i" /></a>'
-        '</span>'
-        % {'ratio': heat_ratio, 'heat': bugtask.bug.heat})
-    return html
-
-
 class BugTaskBatchedCommentsAndActivityView(BugTaskView):
     """A view for displaying batches of bug comments and activity."""
 
@@ -2224,20 +2181,12 @@
     @property
     def bug_heat_html(self):
         """Returns the bug heat flames HTML."""
-        if getFeatureFlag('bugs.heat_ratio_display.disabled'):
-            if getFeatureFlag('bugs.dynamic_bug_listings.enabled'):
-                return (
-                    '<span class="sprite flame">%d</span>'
-                    % self.bugtask.bug.heat)
-            else:
-                return str(self.bugtask.bug.heat)
+        if getFeatureFlag('bugs.dynamic_bug_listings.enabled'):
+            return (
+                '<span class="sprite flame">%d</span>'
+                % self.bugtask.bug.heat)
         else:
-            return bugtask_heat_html(self.bugtask, target=self.target_context)
-
-    @property
-    def center_bug_heat(self):
-        """Returns whether the bug_heat_html should be centered."""
-        return not getFeatureFlag('bugs.heat_ratio_display.disabled')
+            return str(self.bugtask.bug.heat)
 
     @property
     def model(self):

=== removed file 'lib/lp/bugs/browser/tests/bug-heat-view.txt'
--- lib/lp/bugs/browser/tests/bug-heat-view.txt	2011-12-24 15:18:32 +0000
+++ lib/lp/bugs/browser/tests/bug-heat-view.txt	1970-01-01 00:00:00 +0000
@@ -1,119 +0,0 @@
-= Bug heat view =
-
-Bug heat is represented as four flame icons. The quantity of flames that are
-coloured is dependent on the value of the heat field. The function
-bugtask_heat_html is used to render the flames.
-
-    >>> MAX_HEAT = 5000.0
-    >>> from lp.testing import login, logout
-    >>> from zope.security.proxy import removeSecurityProxy
-    >>> from BeautifulSoup import BeautifulSoup
-    >>> from lp.bugs.browser.bugtask import bugtask_heat_html
-    >>> def print_flames(bugtask, target=None):
-    ...     html = bugtask_heat_html(bugtask, target=target)
-    ...     soup = BeautifulSoup(html)
-    ...     for img in soup.span.a.contents:
-    ...         print img['src']
-    ...         print img['alt']
-    ...         print img['title']
-    >>> login('foo.bar@xxxxxxxxxxxxx')
-    >>> bug = factory.makeBug()
-
-The maximum heat is defined as a constant in browser/bug.py. A bug with
-a heat of half the maximum will result in a display of two coloured flames
-and two black-and-white flames.
-
-    >>> removeSecurityProxy(bug.default_bugtask.target).max_bug_heat = MAX_HEAT
-    >>> removeSecurityProxy(bug).heat = MAX_HEAT / 2
-    >>> print_flames(bug.default_bugtask)
-    /@@/bug-heat-2.png
-    2 out of 4 heat flames
-    Heat: 2500
-
-A bug with a maximum heat will display all four flames coloured.
-
-    >>> removeSecurityProxy(bug).heat = MAX_HEAT
-    >>> print_flames(bug.default_bugtask)
-    /@@/bug-heat-4.png
-    4 out of 4 heat flames
-    Heat: 5000
-
-A heat of less than a quarter of the maximum will display no coloured flames.
-
-    >>> removeSecurityProxy(bug).heat = 0.1 * MAX_HEAT
-    >>> print_flames(bug.default_bugtask)
-    /@@/bug-heat-0.png
-    0 out of 4 heat flames
-    Heat: 500
-
-
-== Specifying the target ==
-
-Some bugs can be viewed in a context different from their task's target. For
-example, bugs with tasks on packages can be viewed in the context of the entire
-distribution. In such cases, we want to explicitly specify the target, rather
-than use the bugtask's. We can do that by passing the target as a keyword
-parameter.
-
-    >>> bug = factory.makeBug()
-    >>> distro = factory.makeDistribution()
-    >>> dsp = factory.makeDistributionSourcePackage(distribution=distro)
-    >>> dsp_task = bug.addTask(bug.owner, dsp)
-    >>> removeSecurityProxy(distro).max_bug_heat = MAX_HEAT
-    >>> removeSecurityProxy(dsp).max_bug_heat = MAX_HEAT / 2
-    >>> removeSecurityProxy(bug).heat = MAX_HEAT / 4
-    >>> print_flames(dsp_task)
-    /@@/bug-heat-2.png
-    2 out of 4 heat flames
-    Heat: 1250
-    >>> print_flames(dsp_task, target=distro)
-    /@@/bug-heat-0.png
-    0 out of 4 heat flames
-    Heat: 1250
-
-    >>> logout()
-
-
-== Scaling Bug Heat ==
-
-To ensure a reasonable proportion of cold and hot bugs, the number used to
-calculate the number of flames to display is not a straight-forward ratio.
-Instead, we transform it by forcing low heat bugs to produce no flames and
-scaling the hottest bugs logarithmically.
-
-    >>> from lp.bugs.browser.bugtask import calculate_heat_display
-    >>> from math import floor
-
-Heat values less than a third of the maximum heat don't produce any flames.
-
-    >>> print int(floor((300.0 / 1000.0) * 4))
-    1
-    >>> print calculate_heat_display(300.0, 1000.0)
-    0
-
-Heat values higher than a third of the max but lower than two thirds are treated
-as a straightforward ratio.
-
-    >>> print int(floor((500.0 / 1000.0) * 4))
-    2
-    >>> print calculate_heat_display(500.0, 1000.0)
-    2
-
-Heat values higher than two thirds of the maximum heat are scaled upwards.
-
-    >>> print int(floor((700.0 / 1000.0) * 4))
-    2
-    >>> print calculate_heat_display(800.0, 1000.0)
-    3
-
-A max heat value of 1 works too, because we don't divide by
-log(max_heat) in this case.
-
-    >>> print calculate_heat_display(1, 1)
-    4
-
-Even if the max heat value is smaller than the heat value itself,
-calculate_heat_display() does not return a value greater than 4.
-
-    >>> print calculate_heat_display(2000, 1000)
-    4

=== modified file 'lib/lp/bugs/browser/tests/test_bugtask.py'
--- lib/lp/bugs/browser/tests/test_bugtask.py	2012-01-25 05:34:12 +0000
+++ lib/lp/bugs/browser/tests/test_bugtask.py	2012-02-07 08:14:20 +0000
@@ -1909,11 +1909,11 @@
         self.useContext(person_logged_in(owner))
         with dynamic_listings():
             view = self.makeView(item.bugtask)
-        cache = IJSONRequestCache(view.request)
-        items = cache.objects['mustache_model']['items']
-        self.assertEqual(1, len(items))
-        combined = dict(item.model)
-        combined.update(view.search().field_visibility)
+            cache = IJSONRequestCache(view.request)
+            items = cache.objects['mustache_model']['items']
+            self.assertEqual(1, len(items))
+            combined = dict(item.model)
+            combined.update(view.search().field_visibility)
         self.assertEqual(combined, items[0])
 
     def test_no_next_prev_for_single_batch(self):
@@ -2407,15 +2407,3 @@
                 2001, 1, 1, tzinfo=UTC)
             self.assertEqual(
                 'on 2001-01-01', item.model['last_updated'])
-
-    def test_model_numeric_heat(self):
-        """bug_heat_html contains just the number if the flag is enabled."""
-        with FeatureFixture({'bugs.heat_ratio_display.disabled': 'true'}):
-            with dynamic_listings():
-                owner, item = make_bug_task_listing_item(self.factory)
-                self.assertNotIn('/@@/bug-heat', item.bug_heat_html)
-                self.assertIn('sprite flame', item.bug_heat_html)
-                with person_logged_in(owner):
-                    model = item.model
-                    self.assertEqual(
-                        item.bug_heat_html, model['bug_heat_html'])

=== modified file 'lib/lp/bugs/help/bug-heat.html'
--- lib/lp/bugs/help/bug-heat.html	2010-07-14 10:51:14 +0000
+++ lib/lp/bugs/help/bug-heat.html	2012-02-07 08:14:20 +0000
@@ -13,40 +13,13 @@
 
     <p>
       Launchpad helps you to appraise a bug by giving you a calculated measure
-      &mdash; called bug heat &mdash; of its likely significance.
-    </p>
-
-    <p>
-      You can see bug heat in bug listings, and also on individual bug pages,
-      as four flames: the more flames that are lit, the higher the bug's
-      <em>heat</em>.
-    </p>
-
-    <p>
-      Launchpad works out how many flames to light by calculating a bug heat
-      score and then seeing how that compares with the bug heat of other bugs
-      for that project. If you want to see the bug heat score itself, hover
-      your mouse over the flames icon.
-    </p>
-
-    <p>
-      To calculate the bug heat score, Launchpad looks at the bug's:
-    </p>
-
-    <ul>
-      <li>privacy status</li>
-      <li>security status</li>
-      <li>number of subscribers</li>
-      <li>number of duplicates</li>
-      <li>number of people who've selected the "this bug affects me" option
-      <li>length of time since the last action.</li>
-    </ul>
-
-    <h2>How Launchpad calculates bug heat &mdash; the detailed version</h2>
-
-    <p>
-      Here's exactly how Launchpad calculates the bug heat score. First
-      Launchpad gives the bug a base score:
+      &mdash; called bug heat &mdash; of its likely significance. You can see
+      bug heat in bug listings, and also on individual bug pages, as a
+      number next to a flame icon.
+    </p>
+
+    <p>
+      Here's how Launchpad calculates the bug heat score:
     </p>
 
     <table width="100%">
@@ -104,88 +77,5 @@
         </td>
       </tr>
     </table>
-
-    <p>
-      Next, it adjusts the score depending on how active the bug is:
-    </p>
-
-    <table width="100%">
-      <tr>
-        <th>
-          <strong>Nature of recent activity</strong>
-        </th>
-        <th>
-          <strong>Calculation</strong>
-        </th>
-      </tr>
-
-      <tr>
-        <td>
-          Bug has been active* within the past 24 hours
-        </td>
-        <td>
-          Add 25% of the project's <em>hottest</em> bug's score divided by the
-          number of days since the first activity on the bug in question
-        </td>
-      </tr>
-      <tr>
-        <td>
-          Bug has <strong>not</strong> been active* in within the past 24
-          hours
-        </td>
-        <td>
-          Subtract 1% of the bug heat score for every day of inactivity
-        </td>
-      </tr>
-    </table>
-
-    <p>
-      <small><em>* Activity is either a comment posted or some other update to
-      the bug report.</em></small>
-    </p>
-
-    <p>
-      Some bug statuses will also affect the overall score:
-    </p>
-
-    <table width="100%">
-      <tr>
-        <th>
-          <strong>Status</strong>
-        </th>
-        <th>
-          <strong>Affect on bug heat score</strong>
-        </th>
-      </tr>
-      <tr>
-        <td>
-          Fix Released
-        </td>
-        <td rowspan="4">
-          Reduces the total bug heat score to zero
-        </td>
-      <tr>
-        <td>
-          Invalid
-        </td>
-      </tr>
-      <tr>
-        <td>
-          Won't Fix
-        </td>
-      </tr>
-      <tr>
-        <td>
-          Expired
-        </td>
-      </tr>
-    </table>
-
-    <p>To view the code that calculates bug heat, look for
-      calculate_bug_heat in the file
-      <a target="_blank"
-      href="http://bazaar.launchpad.net/~launchpad-pqm/launchpad/db-devel/annotate/head:/database/schema/trusted.sql";>trusted.sql</a>
-    </p>
-
   </body>
 </html>

=== modified file 'lib/lp/bugs/stories/bugs/xx-bug-heat-on-bug-page.txt'
--- lib/lp/bugs/stories/bugs/xx-bug-heat-on-bug-page.txt	2012-01-13 08:22:04 +0000
+++ lib/lp/bugs/stories/bugs/xx-bug-heat-on-bug-page.txt	2012-02-07 08:14:20 +0000
@@ -1,47 +1,8 @@
 = Bug heat on bug page =
 
-By default the bug heat appears on the bug index page as four flames.
+Bug heat appears on the bug index page:
 
-    >>> def print_heat():
-    ...     content = find_main_content(anon_browser.contents)
-    ...     print content.find('a', href='/+help-bugs/bug-heat.html')
     >>> anon_browser.open('http://bugs.launchpad.dev/firefox/+bug/1')
-    >>> print_heat()
-    <a href="/+help-bugs/bug-heat.html" target="help" class="icon"><img src="/@@/bug-heat-0.png" alt="0 out of 4 heat flames" title="Heat: 0" /></a>
-
-But the bugs.heat_ratio_display.disabled feature flag displays it as a number
-instead.
-
-    >>> from lp.services.features.testing import FeatureFixture
-    >>> with FeatureFixture({'bugs.heat_ratio_display.disabled': 'true'}):
-    ...     anon_browser.open('http://bugs.launchpad.dev/firefox/+bug/1')
-    >>> print_heat()
+    >>> content = find_main_content(anon_browser.contents)
+    >>> print content.find('a', href='/+help-bugs/bug-heat.html')
     <a href="/+help-bugs/bug-heat.html" target="help" class="sprite flame">0</a>
-
-
-Packages use their distribution as context when generating flame icons
-for a bug page, since a package of low heat value could be displayed
-with four flames if there are few reported bugs against the package.
-
-    >>> from lp.testing import login, logout
-    >>> from lp.services.webapp import canonical_url
-    >>> from lp.bugs.interfaces.bug import CreateBugParams
-    >>> login('foo.bar@xxxxxxxxxxxxx')
-    >>> max_heat = 5000.0
-    >>> distro = factory.makeDistribution()
-    >>> dsp = factory.makeDistributionSourcePackage(distribution=distro)
-    >>> person = factory.makePerson()
-    >>> bug_params = CreateBugParams(
-    ...     owner=person,
-    ...     title="A new bug",
-    ...     comment="This is a new bug")
-    >>> dsp_bug = dsp.createBug(bug_params)
-    >>> dsp_bug.setHeat(max_heat/4)
-    >>> dsp.setMaxBugHeat(max_heat/4)
-    >>> distro.setMaxBugHeat(max_heat)
-    >>> logout()
-
-    >>> anon_browser.open(canonical_url(dsp_bug))
-    >>> print find_main_content(
-    ...     anon_browser.contents).find('img', src='/@@/bug-heat-0.png')
-    <img src="/@@/bug-heat-0.png" alt="0 out of 4 heat flames" title="Heat: 1250" />

=== modified file 'lib/lp/bugs/templates/bugs-listing-table-without-navlinks.pt'
--- lib/lp/bugs/templates/bugs-listing-table-without-navlinks.pt	2012-01-13 08:09:30 +0000
+++ lib/lp/bugs/templates/bugs-listing-table-without-navlinks.pt	2012-02-07 08:14:20 +0000
@@ -70,9 +70,7 @@
             tal:condition="context/show_column/date_last_updated|nothing">
             2007-11-25</td>
         <td tal:condition="context/show_column/heat|nothing"
-            tal:content="structure bugtask/bug_heat_html"
-            tal:attributes="style python: 'text-align: center' if
-              bugtask.center_bug_heat else ''">
+            tal:content="structure bugtask/bug_heat_html">
             HEAT</td>
       </tr>
     </tbody>

=== modified file 'lib/lp/registry/model/distroseries.py'
--- lib/lp/registry/model/distroseries.py	2012-01-18 05:22:41 +0000
+++ lib/lp/registry/model/distroseries.py	2012-02-07 08:14:20 +0000
@@ -483,13 +483,13 @@
     def getPrioritizedUnlinkedSourcePackages(self):
         """See `IDistroSeries`.
 
-        The prioritization is a heuristic rule using bug heat,
+        The prioritization is a heuristic rule using bug count,
         translatable messages, and the source package release's component.
         """
         find_spec = (
             SourcePackageName,
             SQL("""
-                coalesce(total_bug_heat, 0) + coalesce(po_messages, 0) +
+                coalesce(bug_count * 10, 0) + coalesce(po_messages, 0) +
                 CASE WHEN component = 1 THEN 1000 ELSE 0 END AS score"""),
             SQL("coalesce(bug_count, 0) AS bug_count"),
             SQL("coalesce(total_messages, 0) AS total_messages"))
@@ -502,7 +502,6 @@
         spr.sourcepackagename,
         spr.component,
         bug_count,
-        total_bug_heat,
         SUM(POTemplate.messagecount) * %(po_message_weight)s AS po_messages,
         SUM(POTemplate.messagecount) AS total_messages
     FROM
@@ -532,7 +531,7 @@
                 Packaging.sourcepackagename = spr.sourcepackagename
                 AND Packaging.distroseries = spph.distroseries)
     GROUP BY
-        spr.sourcepackagename, spr.component, bug_count, total_bug_heat
+        spr.sourcepackagename, spr.component, bug_count
     ) AS spn_info""" % sqlvalues(
             po_message_weight=self._current_sourcepackage_po_weight,
             distroseries=self,
@@ -571,7 +570,7 @@
                 Packaging.*,
                 spr.component AS spr_component,
                 SourcePackageName.name AS spn_name,
-                total_bug_heat,
+                bug_count,
                 po_messages
             FROM %(joins)s
             WHERE %(conditions)s
@@ -585,7 +584,7 @@
         return IStore(self).using(origin).find(Packaging).order_by('''
                 (CASE WHEN spr_component = 1 THEN 1000 ELSE 0 END
                 + CASE WHEN Product.bugtracker IS NULL
-                    THEN coalesce(total_bug_heat, 10) ELSE 0 END
+                    THEN coalesce(bug_count * 10, 10) ELSE 0 END
                 + CASE WHEN ProductSeries.translations_autoimport_mode = 1
                     THEN coalesce(po_messages, 10) ELSE 0 END
                 + CASE WHEN ProductSeries.branch IS NULL THEN 500 ELSE 0 END

=== modified file 'lib/lp/registry/tests/test_distroseries.py'
--- lib/lp/registry/tests/test_distroseries.py	2012-01-01 02:58:52 +0000
+++ lib/lp/registry/tests/test_distroseries.py	2012-02-07 08:14:20 +0000
@@ -321,22 +321,21 @@
         self.universe_component = component_set['universe']
         self.makeSeriesPackage('normal')
         self.makeSeriesPackage('translatable', messages=800)
-        hot_package = self.makeSeriesPackage('hot', heat=500)
+        hot_package = self.makeSeriesPackage('hot', bugs=50)
         # Create a second SPPH for 'hot', to verify that duplicates are
         # eliminated in the queries.
         self.factory.makeSourcePackagePublishingHistory(
             sourcepackagename=hot_package.sourcepackagename,
             distroseries=self.series,
             component=self.universe_component, section_name='web')
-        self.makeSeriesPackage('hot-translatable', heat=250, messages=1000)
+        self.makeSeriesPackage('hot-translatable', bugs=25, messages=1000)
         self.makeSeriesPackage('main', is_main=True)
         self.makeSeriesPackage('linked')
         self.linkPackage('linked')
         transaction.commit()
         login(ANONYMOUS)
 
-    def makeSeriesPackage(self, name,
-                          is_main=False, heat=None, messages=None,
+    def makeSeriesPackage(self, name, is_main=False, bugs=None, messages=None,
                           is_translations=False):
         # Make a published source package.
         if is_main:
@@ -353,10 +352,10 @@
             component=component, section_name=section)
         source_package = self.factory.makeSourcePackage(
             sourcepackagename=sourcepackagename, distroseries=self.series)
-        if heat is not None:
-            bugtask = self.factory.makeBugTask(
-                target=source_package, owner=self.user)
-            bugtask.bug.setHeat(heat)
+        if bugs is not None:
+            dsp = removeSecurityProxy(
+                source_package.distribution_sourcepackage)
+            dsp.bug_count = bugs
         if messages is not None:
             template = self.factory.makePOTemplate(
                 distroseries=self.series, sourcepackagename=sourcepackagename,