launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #05203
[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