← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~allenap/launchpad/bug-stats-key-error-bug-871076 into lp:launchpad

 

Gavin Panella has proposed merging lp:~allenap/launchpad/bug-stats-key-error-bug-871076 into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  Bug #871076 in Launchpad itself: "Product:+series OOPSes on INCOMPLETE_WITH_RESPONSE and INCOMPLETE_WITHOUT_RESPONSE bug tasks"
  https://bugs.launchpad.net/launchpad/+bug/871076

For more details, see:
https://code.launchpad.net/~allenap/launchpad/bug-stats-key-error-bug-871076/+merge/78870

INCOMPLETE_WITH_RESPONSE and INCOMPLETE_WITHOUT_RESPONSE are not mapped to INCOMPLETE when calculating bugtask stats for a product series.
-- 
https://code.launchpad.net/~allenap/launchpad/bug-stats-key-error-bug-871076/+merge/78870
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~allenap/launchpad/bug-stats-key-error-bug-871076 into lp:launchpad.
=== modified file 'lib/lp/bugs/model/bugtask.py'
--- lib/lp/bugs/model/bugtask.py	2011-10-05 18:24:09 +0000
+++ lib/lp/bugs/model/bugtask.py	2011-10-10 16:41:26 +0000
@@ -2804,13 +2804,17 @@
             # since the get_bug_privacy_filter() check for non-admins is
             # costly, don't filter those bugs at all.
             bug_privacy_filter = ''
-        cur = cursor()
-
         # The union is actually much faster than a LEFT JOIN with the
         # Milestone table, since postgres optimizes it to perform index
         # scans instead of sequential scans on the BugTask table.
         query = """
-            SELECT status, count(*)
+            SELECT
+                CASE status
+                    WHEN %(incomplete_with_response)s THEN %(incomplete)s
+                    WHEN %(incomplete_without_response)s THEN %(incomplete)s
+                    ELSE status
+                END AS norm_status,
+                COUNT(*)
             FROM (
                 SELECT BugTask.status
                 FROM BugTask
@@ -2818,9 +2822,7 @@
                 WHERE
                     BugTask.productseries = %(series)s
                     %(privacy)s
-
                 UNION ALL
-
                 SELECT BugTask.status
                 FROM BugTask
                     JOIN Bug ON BugTask.bug = Bug.id
@@ -2830,10 +2832,18 @@
                     AND Milestone.productseries = %(series)s
                     %(privacy)s
                 ) AS subquery
-            GROUP BY status
-            """ % dict(series=quote(product_series),
-                       privacy=bug_privacy_filter)
-
+            GROUP BY norm_status
+            """
+        query %= dict(
+            series=quote(product_series),
+            privacy=bug_privacy_filter,
+            incomplete=quote(BugTaskStatusSearch.INCOMPLETE),
+            incomplete_with_response=quote(
+                BugTaskStatusSearch.INCOMPLETE_WITH_RESPONSE),
+            incomplete_without_response=quote(
+                BugTaskStatusSearch.INCOMPLETE_WITHOUT_RESPONSE),
+            )
+        cur = cursor()
         cur.execute(query)
         return cur.fetchall()
 

=== modified file 'lib/lp/bugs/tests/test_bugtaskset.py'
--- lib/lp/bugs/tests/test_bugtaskset.py	2011-03-23 16:28:51 +0000
+++ lib/lp/bugs/tests/test_bugtaskset.py	2011-10-10 16:41:26 +0000
@@ -7,9 +7,11 @@
 
 from zope.component import getUtility
 
+from canonical.database.sqlbase import flush_database_updates
 from canonical.testing.layers import DatabaseFunctionalLayer
 from lp.bugs.interfaces.bugtask import (
     BugTaskStatus,
+    BugTaskStatusSearch,
     IBugTaskSet,
     )
 from lp.testing import (
@@ -90,6 +92,21 @@
             ],
             self.get_counts(None))
 
+    def test_incomplete_with_without_x_statuses(self):
+        # INCOMPLETE_WITH_RESPONSE and INCOMPLETE_WITHOUT_RESPONSE are both
+        # counted as INCOMPLETE in the reported stats.
+        statuses = [
+            BugTaskStatusSearch.INCOMPLETE,
+            BugTaskStatusSearch.INCOMPLETE_WITH_RESPONSE,
+            BugTaskStatusSearch.INCOMPLETE_WITHOUT_RESPONSE,
+            ]
+        for status in statuses:
+            self.factory.makeBug(series=self.series, status=status)
+        flush_database_updates()
+        self.assertEqual(
+            [(BugTaskStatus.INCOMPLETE, 3)],
+            self.get_counts(None))
+
 
 class TestBugTaskMilestones(TestCaseWithFactory):
     """Tests that appropriate milestones are returned for bugtasks."""

=== modified file 'lib/lp/registry/browser/tests/test_productseries_views.py'
--- lib/lp/registry/browser/tests/test_productseries_views.py	2011-07-29 14:26:49 +0000
+++ lib/lp/registry/browser/tests/test_productseries_views.py	2011-10-10 16:41:26 +0000
@@ -6,13 +6,17 @@
 __metaclass__ = type
 
 from canonical.testing.layers import DatabaseFunctionalLayer
+from lp.bugs.interfaces.bugtask import (
+    BugTaskStatus,
+    BugTaskStatusSearch,
+    )
 from lp.testing import (
     BrowserTestCase,
+    person_logged_in,
     TestCaseWithFactory,
-    person_logged_in
     )
+from lp.testing.matchers import Contains
 from lp.testing.views import create_initialized_view
-from lp.testing.matchers import Contains
 
 
 class TestProductSeriesHelp(TestCaseWithFactory):
@@ -40,3 +44,42 @@
         """Test that rendering the graph does not raise an exception."""
         productseries = self.factory.makeProductSeries()
         self.getViewBrowser(productseries, view_name='+timeline-graph')
+
+
+class TestProductSeriesStatus(TestCaseWithFactory):
+    """Tests for ProductSeries:+status."""
+
+    layer = DatabaseFunctionalLayer
+
+    def test_bugtask_status_counts(self):
+        """Test that `bugtask_status_counts` is sane."""
+        product = self.factory.makeProduct()
+        series = self.factory.makeProductSeries(product=product)
+        for status in BugTaskStatusSearch.items:
+            self.factory.makeBug(
+                series=series, status=status,
+                owner=product.owner)
+        self.factory.makeBug(
+            series=series, status=BugTaskStatus.UNKNOWN,
+            owner=product.owner)
+        with person_logged_in(product.owner):
+            view = create_initialized_view(series, '+status')
+            self.assertEqual(
+                [(BugTaskStatus.NEW, 1),
+                 # 3 because INCOMPLETE_WITH_RESPONSE and
+                 # INCOMPLETE_WITHOUT_RESPONSE both count towards the
+                 # INCOMPLETE total.
+                 (BugTaskStatus.INCOMPLETE, 3),
+                 (BugTaskStatus.OPINION, 1),
+                 (BugTaskStatus.INVALID, 1),
+                 (BugTaskStatus.WONTFIX, 1),
+                 (BugTaskStatus.EXPIRED, 1),
+                 (BugTaskStatus.CONFIRMED, 1),
+                 (BugTaskStatus.TRIAGED, 1),
+                 (BugTaskStatus.INPROGRESS, 1),
+                 (BugTaskStatus.FIXCOMMITTED, 1),
+                 (BugTaskStatus.FIXRELEASED, 1)
+                 (BugTaskStatus.UNKNOWN, 1)],
+                [(status_count.status, status_count.count)
+                 for status_count in view.bugtask_status_counts],
+                )


Follow ups