launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #13967
[Merge] lp:~stevenk/launchpad/destroy-distribution-upstreamreport into lp:launchpad
Steve Kowalik has proposed merging lp:~stevenk/launchpad/destroy-distribution-upstreamreport into lp:launchpad.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
Bug #324298 in Launchpad itself: "Upstream bug report blacklist is unweildy and should be removed"
https://bugs.launchpad.net/launchpad/+bug/324298
Bug #911575 in Launchpad itself: "Distribution:+upstreamreport timeout"
https://bugs.launchpad.net/launchpad/+bug/911575
For more details, see:
https://code.launchpad.net/~stevenk/launchpad/destroy-distribution-upstreamreport/+merge/132639
Distribution:+upstreamreport is masking a hideous query, has been hit ~ 100 times last month, and its 99% time is 9 seconds. Let's kill it.
--
https://code.launchpad.net/~stevenk/launchpad/destroy-distribution-upstreamreport/+merge/132639
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~stevenk/launchpad/destroy-distribution-upstreamreport into lp:launchpad.
=== modified file 'lib/lp/bugs/browser/configure.zcml'
--- lib/lp/bugs/browser/configure.zcml 2012-10-11 04:57:59 +0000
+++ lib/lp/bugs/browser/configure.zcml 2012-11-02 01:01:22 +0000
@@ -345,12 +345,6 @@
template="../templates/product-cvereport.pt"/>
<browser:page
for="lp.registry.interfaces.distribution.IDistribution"
- name="+upstreamreport"
- class="lp.bugs.browser.distribution_upstream_report.DistributionUpstreamReport"
- permission="zope.Public"
- template="../templates/distribution-upstream-report.pt"/>
- <browser:page
- for="lp.registry.interfaces.distribution.IDistribution"
class="lp.bugs.browser.cvereport.CVEReportView"
permission="zope.Public"
name="+cve"
=== removed file 'lib/lp/bugs/browser/distribution_upstream_report.py'
--- lib/lp/bugs/browser/distribution_upstream_report.py 2012-05-25 19:58:27 +0000
+++ lib/lp/bugs/browser/distribution_upstream_report.py 1970-01-01 00:00:00 +0000
@@ -1,391 +0,0 @@
-# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-"""Browser views for distributions."""
-
-__metaclass__ = type
-
-__all__ = [
- 'DistributionUpstreamReport',
- ]
-
-from operator import attrgetter
-
-from lp.app.enums import ServiceUsage
-from lp.bugs.browser.bugtask import get_buglisting_search_filter_url
-from lp.services.propertycache import cachedproperty
-from lp.services.webapp.publisher import (
- canonical_url,
- LaunchpadView,
- )
-from lp.services.webapp.url import urlappend
-
-# TODO: fix column sorting to work for the different colspans, or
-# alternatively implement a sort option box.
-# TODO: make the totals column also link to bug listings.
-# TODO A fourth potential count could be a count of open distribution
-# bugs that are fixed upstream, which would imply closing the loop
-# of upstream fixes back to the distribution.
-
-
-class BugReportData:
- """Represents a row of bug count data in the report.
-
- This is a base class and is directly used only for the totals row.
- See the help text in the template for a verbose description of what
- the numbers mean. Briefly, we hold three counts:
-
- 1. open: bugs with distribution tasks with a status of one of
- interfaces.bugtask.UNRESOLVED_BUGTASK_STATUSES.
- 2. triaged: bugs with distribution tasks that are TRIAGED
- 3. upstream: bugs that are triaged and have an upstream task against
- them.
- 4. watched: bugs that are triaged and have an upstream task linked
- to a watch.
-
- This class makes the three latter counts available as percentages and
- deltas to their predecessor. The report gives the impression of a
- pipeline where bugs trickle into the next count.
-
- The *_class() methods return "good" or nothing, and are intended for
- use in a CSS class. They calculate their values based on the
- UPSTREAM_THRESHOLD and WATCH_THRESHOLD class variables. The reason
- we calculate them is that until we have a way of tracking whether a
- bug is actually /not/ upstream we can't assume 100% of distribution
- bugs need upstream tasks.
- """
- TRIAGED_THRESHOLD = 75
- UPSTREAM_THRESHOLD = 90
- WATCH_THRESHOLD = 90
-
- BAD_THRESHOLD = 20
-
- def __init__(self, open_bugs=0, triaged_bugs=0, upstream_bugs=0,
- watched_bugs=0, bugs_with_upstream_patches=0):
- self.open_bugs = open_bugs
- self.triaged_bugs = triaged_bugs
- self.upstream_bugs = upstream_bugs
- self.watched_bugs = watched_bugs
- self.bugs_with_upstream_patches = bugs_with_upstream_patches
-
- @staticmethod
- def _as_percentage(number, total):
- if total:
- return round(100.0 * number / total, 2)
- else:
- return 0.0
-
- @property
- def triaged_bugs_percentage(self):
- return self._as_percentage(self.triaged_bugs, self.open_bugs)
-
- @property
- def upstream_bugs_percentage(self):
- return self._as_percentage(self.upstream_bugs, self.open_bugs)
-
- @property
- def watched_bugs_percentage(self):
- return self._as_percentage(self.watched_bugs, self.upstream_bugs)
-
- @property
- def row_class(self):
- """Return the class to be used for the current table row.
-
- :returns: 'good' if watched_bugs_percentage > WATCH_THRESHOLD;
- 'bad' if watched_bugs_percentage < BAD_THRESHOLD;
- '' otherwise.
- """
- if self.watched_bugs_percentage > self.WATCH_THRESHOLD:
- return "good"
- elif self.watched_bugs_percentage < self.BAD_THRESHOLD:
- return "bad"
- else:
- return ''
-
- @staticmethod
- def _as_value_class(percentage, threshold):
- if percentage > threshold:
- return "good"
- return ""
-
- @property
- def triaged_bugs_class(self):
- return self._as_value_class(
- self.triaged_bugs_percentage, self.TRIAGED_THRESHOLD)
-
- @property
- def upstream_bugs_class(self):
- return self._as_value_class(
- self.upstream_bugs_percentage, self.UPSTREAM_THRESHOLD)
-
- @property
- def watched_bugs_class(self):
- return self._as_value_class(
- self.watched_bugs_percentage, self.WATCH_THRESHOLD)
-
- @property
- def triaged_bugs_delta(self):
- return self.open_bugs - self.triaged_bugs
-
- @property
- def upstream_bugs_delta(self):
- return self.open_bugs - self.upstream_bugs
-
- @property
- def watched_bugs_delta(self):
- return self.upstream_bugs - self.watched_bugs
-
-
-class PackageBugReportData(BugReportData):
- """Represents a package row in the report.
-
- Apart from the counts, includes data to make it easy to link to
- pages which allow inputting missing information related to the
- package. Relevant instance variables:
-
- - dsp: an IDistributionSourcePackage
- - dssp: an IDistributionSeriesSourcepackage
- - product: an IProduct
- - bugtracker: convenience holder for the product's bugtracker
- - bug_tracking_usage: convenience enum for
- IProduct.bug_tracking_usage
- - *_url: convenience URLs
- """
-
- def __init__(self, dsp, dssp, product, open_bugs, triaged_bugs,
- upstream_bugs, watched_bugs, bugs_with_upstream_patches):
- BugReportData.__init__(self, open_bugs, triaged_bugs, upstream_bugs,
- watched_bugs, bugs_with_upstream_patches)
- self.dsp = dsp
- self.dssp = dssp
- self.product = product
-
- dsp_bugs_url = canonical_url(dsp, rootsite='bugs')
-
- self.open_bugs_url = urlappend(
- dsp_bugs_url, get_buglisting_search_filter_url())
-
- if product is not None:
- self.bug_tracking_usage = product.bug_tracking_usage
- self.branch = product.development_focus.branch
- else:
- self.bug_tracking_usage = ServiceUsage.UNKNOWN
- self.branch = None
-
- # If a product is specified, build some convenient links to
- # pages which allow filling out required information. The
- # template ensures they are only visible to people who can
- # actually change the product.
- if self.product:
- product_url = canonical_url(product)
- self.bugtracker = self.product.getExternalBugTracker()
-
- self.product_edit_url = product_url + "/+edit"
- self.bug_supervisor_url = product_url + "/+bugsupervisor"
-
- # Create a 'bugtracker_name' attribute for searching.
- if self.bugtracker is not None:
- self.bugtracker_name = self.bugtracker.title
- elif self.product.bug_tracking_usage == ServiceUsage.LAUNCHPAD:
- self.bugtracker_name = 'Launchpad'
- else:
- self.bugtracker_name = None
-
- if self.product.bug_supervisor is not None:
- self.bug_supervisor_name = (
- self.product.bug_supervisor.displayname)
- else:
- self.bug_supervisor_name = None
- else:
- # Set bug_supervisor and bugtracker to None so that the
- # sorting code doesn't choke.
- self.bug_supervisor_name = None
- self.bugtracker_name = None
-
- # Note that the +edit-packaging page allows launchpad.AnyPerson
- # so no permissions check needs to be done in the template.
- self.packaging_url = canonical_url(self.dssp) + "/+edit-packaging"
- self.triaged_bugs_url = urlappend(
- dsp_bugs_url, get_buglisting_search_filter_url(status='TRIAGED'))
-
- # The triaged delta URL links to all bugs that are open but not
- # triaged for the current DistributionSourcePackage.
- untriaged_bug_statuses = [
- 'CONFIRMED',
- 'INCOMPLETE_WITHOUT_RESPONSE',
- 'INCOMPLETE_WITH_RESPONSE',
- 'NEW',
- ]
- untriaged_search_filter_url = get_buglisting_search_filter_url(
- status=untriaged_bug_statuses)
- self.triaged_bugs_delta_url = urlappend(
- dsp_bugs_url, untriaged_search_filter_url)
-
- # The upstream URL links to all bugs that are open and have an
- # open upstream bug task or bug watch.
- upstream_search_filter_url = get_buglisting_search_filter_url(
- status_upstream='open_upstream')
- self.upstream_bugs_url = urlappend(
- dsp_bugs_url, upstream_search_filter_url)
-
- # The upstream delta URL links to all bugs that are open without
- # an upstream bug task or bug watch.
- non_upstream_search_filter_url = get_buglisting_search_filter_url(
- status_upstream='hide_upstream')
- self.upstream_bugs_delta_url = urlappend(
- dsp_bugs_url, non_upstream_search_filter_url)
-
- # The watch delta URL links to all open upstream bugs that don't
- # have a bugwatch.
- unwatched_bugs_search_filter_url = get_buglisting_search_filter_url(
- status_upstream='pending_bugwatch')
- self.watched_bugs_delta_url = urlappend(
- dsp_bugs_url, unwatched_bugs_search_filter_url)
-
- # The bugs with upstream patches URL links to all open upstream
- # bugs that don't have a bugwatch but have patches attached.
- bugs_with_upstream_patches_filter_url = (
- get_buglisting_search_filter_url(
- status_upstream='pending_bugwatch', has_patches=True))
- self.bugs_with_upstream_patches_url = urlappend(
- dsp_bugs_url, bugs_with_upstream_patches_filter_url)
-
-
-class DistributionUpstreamReport(LaunchpadView):
- """Implements the actual upstream report.
-
- Most of the work is actually done in the
- getPackagesAndPublicUpstreamBugCounts API, and in the *Data classes
- constructed from here.
- """
- LIMIT = 100
-
- valid_sort_keys = [
- 'bugtracker_name',
- 'bug_supervisor_name',
- 'bugs_with_upstream_patches',
- 'dsp',
- 'open_bugs',
- 'product',
- 'triaged_bugs',
- 'triaged_bugs_class',
- 'triaged_bugs_delta',
- 'triaged_bugs_percentage',
- 'upstream_bugs',
- 'upstream_bugs_class',
- 'upstream_bugs_delta',
- 'upstream_bugs_percentage',
- 'watched_bugs',
- 'watched_bugs_class',
- 'watched_bugs_delta',
- 'watched_bugs_percentage',
- ]
-
- arrow_up = "/@@/arrowUp"
- arrow_down = "/@@/arrowDown"
- arrow_blank = "/@@/arrowBlank"
-
- @property
- def page_title(self):
- return 'Upstream Report for %s' % self.context.title
-
- @property
- def sort_order(self):
- """Return the sort order for the report.
-
- :returns: The sort order as a dict of (sort_key, reversed).
- """
- sort_order = self.request.get('sort_by', '-open_bugs')
-
- sort_key = sort_order
- if sort_key.startswith('-'):
- sort_key = sort_key.replace('-', '')
- reversed = True
- else:
- reversed = False
-
- # Validate the sort key before proceeding.
- if sort_key not in self.valid_sort_keys:
- return ('open_bugs', True)
- else:
- return (sort_key, reversed)
-
- @cachedproperty
- def data(self):
- """Return the _data list, sorted by `sort_order`."""
- sort_key, reversed = self.sort_order
- data = sorted(
- self._data, key=attrgetter(sort_key), reverse=reversed)
-
- return data
-
- @cachedproperty
- def sort_order_links(self):
- """Return a dict of sort order links based on the current sort_order.
- """
- current_sort_key, reversed = self.sort_order
- sort_order_links = {}
-
- # Loop over the possible sort keys and work out what the link
- # should be for that column.
- base_url = canonical_url(self.context, view_name='+upstreamreport')
- for sort_key in self.valid_sort_keys:
- if sort_key == current_sort_key:
- if not reversed:
- sort_order = '-%s' % sort_key
- arrow = self.arrow_up
- else:
- sort_order = sort_key
- arrow = self.arrow_down
- else:
- sort_order = sort_key
- arrow = self.arrow_blank
-
- sort_order_links[sort_key] = {
- 'link': base_url + '?sort_by=%s' % sort_order,
- 'arrow': arrow,
- }
-
- return sort_order_links
-
- @cachedproperty
- def current_distro_series(self):
- """Cache the current distroseries.
-
- This avoids us having to reissue this query for each row we want
- to produce an IDistroSeriesSourcePackage for.
- """
- return self.context.currentseries
-
- def initialize(self):
- """Assemble self._data and self.total from upstream count report."""
- self._data = []
- self.total = BugReportData()
- packages_to_exclude = self.context.upstream_report_excluded_packages
- counts = self.context.getPackagesAndPublicUpstreamBugCounts(
- limit=self.LIMIT, exclude_packages=packages_to_exclude)
- # The upstream report is not useful if the distibution
- # does not track its bugs on Lauchpad or if it does not have a
- # current distroseries.
- self.has_upstream_report = (
- self.context.bug_tracking_usage == ServiceUsage.LAUNCHPAD and
- self.current_distro_series is not None)
- if not self.has_upstream_report:
- return
- for (dsp, product, open, triaged, upstream, watched,
- bugs_with_upstream_patches) in counts:
- # The +edit-packaging page is only available for
- # IDistributionSeriesSourcepackages, so deduce one here. If
- # the distribution doesn't have series we can't offer a link
- # to add packaging information.
- dssp = self.current_distro_series.getSourcePackage(
- dsp.sourcepackagename)
- self.total.open_bugs += open
- self.total.triaged_bugs += triaged
- self.total.upstream_bugs += upstream
- self.total.watched_bugs += watched
-
- item = PackageBugReportData(
- dsp, dssp, product, open, triaged, upstream, watched,
- bugs_with_upstream_patches)
- self._data.append(item)
=== removed file 'lib/lp/bugs/browser/tests/test_distribution_upstream_report.py'
--- lib/lp/bugs/browser/tests/test_distribution_upstream_report.py 2012-06-14 05:18:22 +0000
+++ lib/lp/bugs/browser/tests/test_distribution_upstream_report.py 1970-01-01 00:00:00 +0000
@@ -1,218 +0,0 @@
-# Copyright 2009-2012 Canonical Ltd. This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-"""Unit tests for DistributionUpstreamReport."""
-
-__metaclass__ = type
-
-
-from soupmatchers import (
- HTMLContains,
- Tag,
- )
-from testtools.matchers import Not
-from zope.component import getUtility
-
-from lp.app.enums import ServiceUsage
-from lp.app.interfaces.launchpad import ILaunchpadCelebrities
-from lp.bugs.browser.distribution_upstream_report import (
- BugReportData,
- DistributionUpstreamReport,
- )
-from lp.testing import (
- BrowserTestCase,
- person_logged_in,
- TestCase,
- TestCaseWithFactory,
- )
-from lp.testing.layers import (
- DatabaseFunctionalLayer,
- LaunchpadFunctionalLayer,
- )
-from lp.testing.views import (
- create_initialized_view,
- create_view,
- )
-
-
-class BugReportDataTestCase(TestCase):
-
- def make_bug_report_data(self):
- return BugReportData(
- open_bugs=90, triaged_bugs=50, upstream_bugs=70, watched_bugs=60)
-
- def test_init(self):
- bug_data = self.make_bug_report_data()
- self.assertEqual(90, bug_data.open_bugs)
- self.assertEqual(50, bug_data.triaged_bugs)
- self.assertEqual(70, bug_data.upstream_bugs)
- self.assertEqual(60, bug_data.watched_bugs)
-
- def test_percentage_properties(self):
- bug_data = self.make_bug_report_data()
- self.assertEqual(55.56, bug_data.triaged_bugs_percentage)
- self.assertEqual(77.78, bug_data.upstream_bugs_percentage)
- self.assertEqual(85.71, bug_data.watched_bugs_percentage)
-
- def test_as_percentage(self):
- bug_data = self.make_bug_report_data()
- self.assertEqual(55.56, bug_data._as_percentage(50, 90))
- self.assertEqual(0.0, bug_data._as_percentage(50, 0))
-
- def test_delta_properties(self):
- bug_data = self.make_bug_report_data()
- self.assertEqual(40, bug_data.triaged_bugs_delta)
- self.assertEqual(20, bug_data.upstream_bugs_delta)
- self.assertEqual(10, bug_data.watched_bugs_delta)
-
- def test_as_value_class(self):
- bug_data = self.make_bug_report_data()
- self.assertEqual('good', bug_data._as_value_class(60, 50))
- self.assertEqual('', bug_data._as_value_class(50, 50))
- self.assertEqual('', bug_data._as_value_class(40, 50))
-
- def test_value_class(self):
- bug_data = self.make_bug_report_data()
- bug_data.watched_bugs = 80
- self.assertEqual('', bug_data.triaged_bugs_class)
- self.assertEqual('', bug_data.upstream_bugs_class)
- self.assertEqual('good', bug_data.watched_bugs_class)
-
- def test_row_class(self):
- bug_data = self.make_bug_report_data()
- self.assertEqual('', bug_data.row_class)
- bug_data.watched_bugs = 80
- self.assertEqual('good', bug_data.row_class)
- bug_data.watched_bugs = 11
- self.assertEqual('bad', bug_data.row_class)
-
-
-class TestDistributionUpstreamReport(TestCaseWithFactory):
-
- layer = DatabaseFunctionalLayer
-
- def test_valid_sort_keys_are_valid(self):
- # The valid_sort_keys property of the
- # DistributionUpstreamReport view contains a list of the sort
- # keys that the view considers valid. Using any one of these
- # keys, including when prepended with a '-', will lead to it
- # being set as the view's sort_order key.
- ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
- for sort_key in DistributionUpstreamReport.valid_sort_keys:
- form = {'sort_by': sort_key}
- view = create_view(ubuntu, '+upstreamreport', form)
-
- # The sort_order property of DistributionUpstreamReport is
- # a tuple in the form (sort_key, reversed).
- view_sort_key, view_sort_reversed = view.sort_order
- self.assertEqual(view_sort_key, sort_key,
- "Expected a sort_key of '%s', got '%s'" %
- (sort_key, view_sort_key))
-
- # By default, reversed is False.
- self.assertFalse(view_sort_reversed,
- "Sort order should not be reversed for a sort_by value of "
- "%s" % sort_key)
-
- # Prepending a '-' to sort_by will reverse the sort.
- reversed_key = '-%s' % sort_key
- form = {'sort_by': reversed_key}
- view = create_view(ubuntu, '+upstreamreport', form)
-
- # The sort_key part of view.sort_order will be the same as
- # for a normal sort.
- view_sort_key, view_sort_reversed = view.sort_order
- self.assertEqual(view_sort_key, sort_key,
- "Expected a sort_key of '%s', got '%s'" %
- (sort_key, view_sort_key))
-
- # But reversed is now True.
- self.assertTrue(view_sort_reversed,
- "Sort order should be reversed for a sort_by value of "
- "%s" % reversed_key)
-
- def test_has_upstream_report__no_series_no_bug_tracking(self):
- # The property DistributionUpstreamReport.has_upstream_report
- # is False if a distribution does not use Launchpad for bug
- # tracking and if no current distroseries exists.
- distribution = self.factory.makeDistribution()
- view = create_initialized_view(distribution, '+upstreamreport')
- self.assertNotEqual(
- ServiceUsage.LAUNCHPAD, distribution.bug_tracking_usage)
- self.assertIs(None, distribution.currentseries)
- self.assertFalse(view.has_upstream_report)
-
- def test_has_upstream_report__no_distroseries_with_bug_tracking(self):
- # The property DistributionUpstreamReport.has_upstream_report
- # is False if a distribution does not have a current
- # distroseries, even if Luanchpad is used for bug tracking.
- distribution = self.factory.makeDistribution()
- view = create_initialized_view(distribution, '+upstreamreport')
- with person_logged_in(distribution.owner):
- distribution.official_malone = True
- self.assertIs(None, distribution.currentseries)
- self.assertFalse(view.has_upstream_report)
-
- def test_has_upstream_report__with_distroseries_no_bug_tracking(self):
- # The property DistributionUpstreamReport.has_upstream_report
- # is False if a distribution has a current distroseries, but
- # if Launchpad is not used for bug tracking.
- distribution = self.factory.makeDistroSeries().distribution
- view = create_initialized_view(distribution, '+upstreamreport')
- self.assertIsNot(None, distribution.currentseries)
- self.assertNotEqual(
- ServiceUsage.LAUNCHPAD, distribution.bug_tracking_usage)
- self.assertFalse(view.has_upstream_report)
-
- def test_has_upstream_report__with_distroseries_and_bug_tracking(self):
- # The property DistributionUpstreamReport.has_upstream_report
- # is True if a distribution has a current distroseries and if it
- # uses Launchpad for bug tracking.
- distribution = self.factory.makeDistroSeries().distribution
- with person_logged_in(distribution.owner):
- distribution.official_malone = True
- view = create_initialized_view(distribution, '+upstreamreport')
- self.assertIsNot(None, distribution.currentseries)
- self.assertEqual(
- ServiceUsage.LAUNCHPAD, distribution.bug_tracking_usage)
- self.assertTrue(view.has_upstream_report)
-
-
-class TestDistributionUpstreamReportPage(BrowserTestCase):
- """Tests for the +upstream report page."""
-
- layer = LaunchpadFunctionalLayer
-
- def getTagMatchers(self):
- """Return matchers for the tag saying "launchpad is not used
- for development" and the tag containing the upstream report."""
- no_lp_usage = Tag(
- 'no-lp-usage', 'div', attrs={'id': 'no-lp-usage'})
- no_bugs_filed = Tag('lp-used', 'div', attrs={'id': 'lp-used'})
- return no_lp_usage, no_bugs_filed
-
- def test_no_upstream_report_for_unconfigured_distros(self):
- # If DistributionUpstreamReport.has_upstream_report is False,
- # the +upstream-report page does not show the report.
- distribution = self.factory.makeDistribution()
- browser = self.getViewBrowser(
- distribution, '+upstreamreport', no_login=True)
- no_lp_usage, no_bugs_filed = self.getTagMatchers()
- self.assertThat(browser.contents, Not(HTMLContains(no_bugs_filed)))
- # Instead, a message tells the user that no report is
- # available.
- self.assertThat(browser.contents, HTMLContains(no_lp_usage))
-
- def test_upstream_report_for_configured_distros(self):
- # If DistributionUpstreamReport.has_upstream_report is True,
- # the +upstream-report page does shows the report.
- distribution = self.factory.makeDistroSeries().distribution
- with person_logged_in(distribution.owner):
- distribution.official_malone = True
- browser = self.getViewBrowser(
- distribution, '+upstreamreport', no_login=True)
- no_lp_usage, no_bugs_filed = self.getTagMatchers()
- self.assertThat(browser.contents, HTMLContains(no_bugs_filed))
- # A message telling the user that no report is available
- # is not shown.
- self.assertThat(browser.contents, Not(HTMLContains(no_lp_usage)))
=== removed file 'lib/lp/bugs/doc/distribution-upstream-report.txt'
--- lib/lp/bugs/doc/distribution-upstream-report.txt 2012-05-25 20:15:08 +0000
+++ lib/lp/bugs/doc/distribution-upstream-report.txt 1970-01-01 00:00:00 +0000
@@ -1,432 +0,0 @@
-Upstream reports
-================
-
-For a distribution's bug tracking process to be successful, it's vital
-that it is able to communicate upstream bugs to the relevant upstream
-project and monitor them as they change. Launchpad offers functionality
-to allow a distribution to focus on and improve this process.
-
- >>> from storm.store import Store
- >>> from lp.testing import login
- >>> from lp.bugs.tests.bug import (
- ... create_bug_from_strings)
- >>> from lp.registry.interfaces.sourcepackagename import (
- ... ISourcePackageNameSet)
- >>> from lp.registry.interfaces.distribution import IDistributionSet
- >>> from lp.registry.interfaces.product import IProductSet
- >>> from lp.registry.interfaces.packaging import (
- ... IPackagingUtil, PackagingType)
- >>> from lp.registry.interfaces.person import IPersonSet
- >>> from lp.bugs.interfaces.bugtask import IBugTaskSet, BugTaskStatus
- >>> from lp.bugs.interfaces.bugwatch import IBugWatchSet
-
- >>> distroset = getUtility(IDistributionSet)
- >>> ubuntu = distroset.getByName('ubuntu')
- >>> debian = distroset.getByName('debian')
- >>> kubuntu = distroset.getByName('kubuntu')
-
-
-The API
--------
-
-IDistribution has a special API that allows you to assemble data for a
-bug report that associates packages with upstream information linked to
-them.
-
- >>> def print_report(data):
- ... for dsp, product, open, triaged, upstream, watch, patch in data:
- ... print dsp.name, product and product.name or None
- ... print open, triaged, upstream, watch, patch
-
-A first set of reports, entirely based on sampledata. There are no
-triaged bugs, but there are some upstream ones with watches:
-
- >>> print_report(ubuntu.getPackagesAndPublicUpstreamBugCounts())
- linux-source-2.6.15 None 1 0 0 0 0
- mozilla-firefox firefox 1 0 1 1 0
- thunderbird None 1 0 1 1 0
-
- >>> print_report(debian.getPackagesAndPublicUpstreamBugCounts())
- mozilla-firefox None 3 0 2 1 0
-
- >>> print_report(kubuntu.getPackagesAndPublicUpstreamBugCounts())
-
-getPackagesAndPublicUpstreamBugCounts() accepts an `exclude_packages`
-parameter. This is a list of the source packages that shouldn't be
-included in the report that getPackagesAndPublicUpstreamBugCounts()
-returns.
-
- >>> print_report(ubuntu.getPackagesAndPublicUpstreamBugCounts(
- ... exclude_packages=['linux-source-2.6.15']))
- mozilla-firefox firefox 1 0 1 1 0
- thunderbird None 1 0 1 1 0
-
-To get the list of excluded packages for a distribution we can look at
-its `upstream_report_excluded_packages` property. For Kubuntu and
-Debian, this returns an empty list.
-
- >>> debian.upstream_report_excluded_packages
- []
-
- >>> kubuntu.upstream_report_excluded_packages
- []
-
-For Ubuntu, however, there is a list of excluded packages.
-
- >>> ubuntu.upstream_report_excluded_packages
- ['apport'...]
-
-If we triage a bugtask on firefox and thunderbird we'll see the count
-for triaged bugs updated:
-
- >>> login('foo.bar@xxxxxxxxxxxxx')
- >>> mark = getUtility(IPersonSet).getByName('mark')
- >>> ls_bug = getUtility(IBugTaskSet).get(23)
- >>> ls_bug.transitionToStatus(BugTaskStatus.TRIAGED, mark)
- >>> Store.of(ls_bug).flush()
- >>> mf_bug = getUtility(IBugTaskSet).get(17)
- >>> mf_bug.transitionToStatus(BugTaskStatus.TRIAGED, mark)
- >>> Store.of(mf_bug).flush()
- >>> print_report(ubuntu.getPackagesAndPublicUpstreamBugCounts())
- linux-source-2.6.15 None 1 0 0 0 0
- mozilla-firefox firefox 1 1 1 1 0
- thunderbird None 1 1 1 1 0
-
-We add two new bugs to pmount in Ubuntu. From now on we'll limit the
-results to 3 packages (as a demonstration of the API) so thunderbird
-will be popped off the list:
-
- >>> bug = create_bug_from_strings(distribution='ubuntu',
- ... sourcepackagename='pmount', owner='name12',
- ... summary='pmount used to work', description='fix it',
- ... status=BugTaskStatus.TRIAGED)
- >>> bug = create_bug_from_strings(distribution='ubuntu',
- ... sourcepackagename='pmount', owner='name12',
- ... summary='pmount has issues', description='fix it again',
- ... status=BugTaskStatus.TRIAGED)
- >>> ubuntu_pmount_task = bug.bugtasks[0]
- >>> print_report(ubuntu.getPackagesAndPublicUpstreamBugCounts(limit=3))
- pmount None 2 2 0 0 0
- linux-source-2.6.15 None 1 0 0 0 0
- mozilla-firefox firefox 1 1 1 1 0
-
-As you can see, there is no packaging data for pmount in Ubuntu, so no
-upstream is reported for it. Let's fix that:
-
- >>> pmount_spn = getUtility(ISourcePackageNameSet).queryByName('pmount')
- >>> name12 = getUtility(IPersonSet).getByName('name12')
- >>> pmount = getUtility(IProductSet).createProduct(
- ... name12, 'pmount', 'pmount', 'pmount', 'pmount')
- >>> packaging = getUtility(IPackagingUtil).createPackaging(
- ... pmount.getSeries('trunk'), pmount_spn,
- ... ubuntu.currentseries, PackagingType.PRIME, name12)
- >>> print_report(ubuntu.getPackagesAndPublicUpstreamBugCounts(limit=3))
- pmount pmount 2 2 0 0 0
- linux-source-2.6.15 None 1 0 0 0 0
- mozilla-firefox firefox 1 1 1 1 0
-
-We then add an upstream task to the second pmount bug:
-
- >>> task = getUtility(IBugTaskSet).createTask(bug, name12, pmount)
- >>> Store.of(task).flush()
- >>> print_report(ubuntu.getPackagesAndPublicUpstreamBugCounts(limit=3))
- pmount pmount 2 2 1 0 0
- linux-source-2.6.15 None 1 0 0 0 0
- mozilla-firefox firefox 1 1 1 1 0
-
-The last column counts those bugs with upstream tasks that have patches
-attached but which don't have an upstream bugwatch. If we add a ordinary
-attachment to our pmount bug, the value of the last column does not
-change...
-
- >>> attachment = factory.makeBugAttachment(bug)
- >>> print_report(ubuntu.getPackagesAndPublicUpstreamBugCounts(limit=3))
- pmount pmount 2 2 1 0 0
- linux-source-2.6.15 None 1 0 0 0 0
- mozilla-firefox firefox 1 1 1 1 0
-
-...but when we make this attachment a patch, the value of the column
-increases.
-
- >>> from lp.bugs.interfaces.bugattachment import BugAttachmentType
- >>> attachment.type = BugAttachmentType.PATCH
- >>> Store.of(attachment).flush()
- >>> print_report(ubuntu.getPackagesAndPublicUpstreamBugCounts(limit=3))
- pmount pmount 2 2 1 0 1
- linux-source-2.6.15 None 1 0 0 0 0
- mozilla-firefox firefox 1 1 1 1 0
-
-Note that we count only bugs with patches for products that do not
-use Malone officially.
-
- >>> pmount.official_malone = True
- >>> Store.of(pmount).flush()
- >>> print_report(ubuntu.getPackagesAndPublicUpstreamBugCounts(limit=3))
- pmount pmount 2 2 1 1 0
- linux-source-2.6.15 None 1 0 0 0 0
- mozilla-firefox firefox 1 1 1 1 0
-
- >>> pmount.official_malone = False
- >>> Store.of(pmount).flush()
-
-Linking that task to a bugwatch increases the watch counts and decreases
-the count of bugs having patches but no bug watch.
-
- >>> url = "http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=666"
- >>> [watch] = getUtility(IBugWatchSet).fromText(url, bug, name12)
- >>> task.bugwatch = watch
- >>> Store.of(task).flush()
- >>> print_report(ubuntu.getPackagesAndPublicUpstreamBugCounts(limit=3))
- pmount pmount 2 2 1 1 0
- linux-source-2.6.15 None 1 0 0 0 0
- mozilla-firefox firefox 1 1 1 1 0
-
-
-The view
---------
-
-We test that the view data is constructed sanely and without any hidden
-defects. Let's set up some helpers to make it easier for us to output
-them:
-
- >>> from lp.testing.systemdocs import create_view
-
- >>> def print_numbers(data):
- ... for f in ['open_bugs',
- ... 'triaged_bugs',
- ... 'upstream_bugs',
- ... 'watched_bugs',
- ... 'triaged_bugs_percentage',
- ... 'upstream_bugs_percentage',
- ... 'watched_bugs_percentage',
- ... 'triaged_bugs_class',
- ... 'upstream_bugs_class',
- ... 'watched_bugs_class',
- ... 'triaged_bugs_delta',
- ... 'upstream_bugs_delta',
- ... 'watched_bugs_delta']:
- ... print getattr(data, f),
-
- >>> def print_helpers(data):
- ... print data.dsp.name, data.dsp.distribution.name,
- ... if data.dssp:
- ... print data.dssp.distroseries.name
- ... else:
- ... print "NO SERIES"
- ... if data.product:
- ... print data.product.name
- ... else:
- ... print "NO PRODUCT"
- ... for f in ['bug_supervisor_url', 'product_edit_url',
- ... 'upstream_bugs_url', 'upstream_bugs_delta_url',
- ... 'watched_bugs_delta_url']:
- ... t = getattr(data, f, "NO URL")
- ... print t.replace(
- ... "http://bugs.launchpad.dev/ubuntu/+source/", "**")
- ... print "--"
-
-Get an Ubuntu view:
-
- >>> view = create_view(ubuntu, '+upstreamreport')
- >>> view.initialize()
-
-Here are the helper URLs we construct:
-
- >>> for item in view.data:
- ... print_helpers(item)
- pmount ubuntu hoary
- pmount
- http://launchpad.dev/pmount/+bugsupervisor
- http://launchpad.dev/pmount/+edit
- **pmount/+bugs?search=Search&field.status_upstream=open_upstream
- **pmount/+bugs?search=Search&field.status_upstream=hide_upstream
- **pmount/+bugs?search=Search&field.status_upstream=pending_bugwatch
- --
- linux-source-2.6.15 ubuntu hoary
- NO PRODUCT
- NO URL
- NO URL
- **linux-source-2.6.15/+bugs?...&field.status_upstream=open_upstream
- **linux-source-2.6.15/+bugs?...h&field.status_upstream=hide_upstream
- **linux-source-2.6.15/+bugs?...&field.status_upstream=pending_bugwatch
- --
- mozilla-firefox ubuntu hoary
- firefox
- http://launchpad.dev/firefox/+bugsupervisor
- http://launchpad.dev/firefox/+edit
- **mozilla-firefox/+bugs?search=Search&field.status_upstream=open_upstream
- **mozilla-firefox/+bugs?search=Search&field.status_upstream=hide_upstream
- **mozilla-firefox/+bugs?...&field.status_upstream=pending_bugwatch
- --
- thunderbird ubuntu hoary
- NO PRODUCT
- NO URL
- NO URL
- **thunderbird/+bugs?search=Search&field.status_upstream=open_upstream
- **thunderbird/+bugs?search=Search&field.status_upstream=hide_upstream
- **thunderbird/+bugs?search=Search&field.status_upstream=pending_bugwatch
- --
-
-Let's print out the counts and percentages:
-
- >>> for item in view.data:
- ... print_numbers(item)
- ... print
- 2 2 1 1 100.0 50.0 100.0 good good 0 1 0
- 1 0 0 0 0.0 0.0 0.0 1 1 0
- 1 1 1 1 100.0 100.0 100.0 good good good 0 0 0
- 1 1 1 1 100.0 100.0 100.0 good good good 0 0 0
-
-And the total line:
-
- >>> print_numbers(view.total)
- 5 4 3 3 80.0 60.0 100.0 good good 1 2 0
-
-
-Sorting the report
-------------------
-
-The upstream report is sortable by each of the columns displayed. We'll
-demonstrate this using the Ubuntu report.
-
- >>> view = create_view(ubuntu, '+upstreamreport')
-
-The view has a sort_order property. This returns a tuple of (sort_key,
-reversed), where sort_key is a string which can be mapped to one of the
-properties of PackageBugReportData and reversed is a boolean which
-indicates whether the current sort is ascending (reversed=False) or
-descending (reversed=True). By default, the sort_order for any report is
-number of open bugs, descending.
-
- >>> view.sort_order
- ('open_bugs', True)
-
-The sort order can be changed by altering the sort_by request parameter.
-
- >>> form = {'sort_by': 'product'}
-
- >>> view = create_view(ubuntu, '+upstreamreport', form)
- >>> view.sort_order
- ('product', False)
-
-Prepending a '-' to the sort_by parameter will cause the sort_order to
-be reversed.
-
- >>> form = {'sort_by': '-product'}
- >>> view = create_view(ubuntu, '+upstreamreport', form)
-
- >>> view.sort_order
- ('product', True)
-
-The DistributionUpstreamReport view has a list of valid sort keys. If
-we try to sort by a key that isn't in that list we'll get the default
-sort_order back. (See test_distribution_upstream_report.py in
-browser/tests for further testing of this).
-
- >>> form = {'sort_by': 'ifthisisvalidilleatmyhat'}
- >>> view = create_view(ubuntu, '+upstreamreport', form)
-
- >>> view.sort_order
- ('open_bugs', True)
-
-The DistributionUpstreamReport view also has a sort_order_links
-property. This is a dict of URLs which is used to create the links in
-the sortable table header on the +upstreamreport page for the
-distribution.
-
-The current sort_order is the default one.
-
- >>> view.sort_order
- ('open_bugs', True)
-
-All the links, by default will link to a standard forward sort for their
-particular sort_key. In this case, this is also true of the open_bugs
-key, since this is at the moment reverse-sorted.
-
- >>> def print_sort_order_links(view, key='link'):
- ... for sort_key in sorted(view.sort_order_links):
- ... link_dict = view.sort_order_links[sort_key]
- ... print sort_key, link_dict[key]
-
- >>> print_sort_order_links(view)
- bug_supervisor_name http://...?sort_by=bug_supervisor_name
- bugs_with_upstream_patches http:...?sort_by=bugs_with_upstream_patches
- bugtracker_name http://...?sort_by=bugtracker_name
- dsp http://...?sort_by=dsp
- open_bugs http://...?sort_by=open_bugs
- product http://...?sort_by=product
- triaged_bugs http://...?sort_by=triaged_bugs
- triaged_bugs_class http://...?sort_by=triaged_bugs_class
- triaged_bugs_delta http://...?sort_by=triaged_bugs_delta
- triaged_bugs_percentage http://...?sort_by=triaged_bugs_percentage
- upstream_bugs http://...?sort_by=upstream_bugs
- upstream_bugs_class http://...?sort_by=upstream_bugs_class
- upstream_bugs_delta http://...?sort_by=upstream_bugs_delta
- upstream_bugs_percentage http://...?sort_by=upstream_bugs_percentage
- watched_bugs http://...?sort_by=watched_bugs
- watched_bugs_class http://...?sort_by=watched_bugs_class
- watched_bugs_delta http://...?sort_by=watched_bugs_delta
- watched_bugs_percentage http://...?sort_by=watched_bugs_percentage
-
-Changing the sort_order to a forward sort of, say, bug_supervisor_name
-will change the link for that sort key. The others will remain
-unaffected.
-
- >>> form = {'sort_by': 'bug_supervisor_name'}
- >>> view = create_view(ubuntu, '+upstreamreport', form)
-
- >>> view.sort_order
- ('bug_supervisor_name', False)
-
- >>> print_sort_order_links(view)
- bug_supervisor_name http://...?sort_by=-bug_supervisor_name
- bugs_with_upstream_patches http:...?sort_by=bugs_with_upstream_patches
- bugtracker_name http://...?sort_by=bugtracker_name...
-
-Each sort_order_links dict has an 'arrow' key. This is the URL of the
-arrow icon to be displayed next to the link in the table header.
-
- >>> print_sort_order_links(view, 'arrow')
- bug_supervisor_name /@@/arrowUp
- bugs_with_upstream_patches /@@/arrowBlank
- bugtracker_name /@@/arrowBlank
- dsp /@@/arrowBlank
- open_bugs /@@/arrowBlank
- product /@@/arrowBlank
- triaged_bugs /@@/arrowBlank
- triaged_bugs_class /@@/arrowBlank
- triaged_bugs_delta /@@/arrowBlank
- triaged_bugs_percentage /@@/arrowBlank
- upstream_bugs /@@/arrowBlank
- upstream_bugs_class /@@/arrowBlank
- upstream_bugs_delta /@@/arrowBlank
- upstream_bugs_percentage /@@/arrowBlank
- watched_bugs /@@/arrowBlank
- watched_bugs_class /@@/arrowBlank
- watched_bugs_delta /@@/arrowBlank
- watched_bugs_percentage /@@/arrowBlank
-
-Altering the sort order will change the arrow for the current sort order
-accordingly.
-
- >>> form = {'sort_by': '-bug_supervisor_name'}
- >>> view = create_view(ubuntu, '+upstreamreport', form)
-
- >>> view.sort_order
- ('bug_supervisor_name', True)
-
- >>> print_sort_order_links(view, 'arrow')
- bug_supervisor_name /@@/arrowDown
- bugs_with_upstream_patches /@@/arrowBlank
- bugtracker_name /@@/arrowBlank...
-
- >>> form = {'sort_by': '-bugtracker_name'}
- >>> view = create_view(ubuntu, '+upstreamreport', form)
-
- >>> view.sort_order
- ('bugtracker_name', True)
-
- >>> print_sort_order_links(view, 'arrow')
- bug_supervisor_name /@@/arrowBlank
- bugs_with_upstream_patches /@@/arrowBlank
- bugtracker_name /@@/arrowDown...
=== removed file 'lib/lp/bugs/stories/distribution/xx-distribution-upstream-report.txt'
--- lib/lp/bugs/stories/distribution/xx-distribution-upstream-report.txt 2012-10-02 06:36:44 +0000
+++ lib/lp/bugs/stories/distribution/xx-distribution-upstream-report.txt 1970-01-01 00:00:00 +0000
@@ -1,303 +0,0 @@
-Distribution upstream report pages
-==================================
-
-Upstream report pages for distributions contain all sorts of fun
-information that describe how well the bugs are forwarded to upstreams.
-
-We start this test off by creating some bugs and tasks to ensure we have
-rich enough information to display. We create one bug against
-linux-source with a firefox task, and we add a tomcat task against
-pre-existing linux-source bug 10. We transition the Ubuntu tasks to
-TRIAGED to ensure that they are picked up by the report.
-
- >>> from lp.bugs.interfaces.bugtask import BugTaskStatus
- >>> from lp.testing import login, logout
- >>> from lp.services.database.sqlbase import flush_database_updates
- >>> from lp.bugs.tests.bug import (
- ... create_bug_from_strings, create_task_from_strings,
- ... update_task_status)
-
- >>> login('foo.bar@xxxxxxxxxxxxx')
-
- >>> watchurl = "http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=%s"
- >>> bug = create_bug_from_strings(distribution='ubuntu',
- ... sourcepackagename='linux-source-2.6.15', owner='name12',
- ... summary='take one', description='funk philosophy',
- ... status=BugTaskStatus.TRIAGED)
- >>> update_task_status(bug.bugtasks[0].id, 'mark',
- ... BugTaskStatus.TRIAGED)
- >>> update_task_status(25, 'mark', BugTaskStatus.TRIAGED)
- >>> task = create_task_from_strings(bug.id, 'name12', 'firefox')
- >>> task = create_task_from_strings(10, 'name12', 'tomcat')
- >>> update_task_status(23, 'mark', BugTaskStatus.TRIAGED)
- >>> update_task_status(17, 'mark', BugTaskStatus.TRIAGED)
-
- >>> flush_database_updates()
- >>> import transaction
- >>> transaction.commit()
- >>> logout()
-
-So let's check out the actual table:
-
- >>> browser.open("http://bugs.launchpad.dev/ubuntu/+upstreamreport")
- >>> table = find_tag_by_id(browser.contents, 'upstream-report-content')
- >>> print extract_text(table, True)
- linux-source-2.6.15 Missing corresponding project. (find) (link)
- 2 2 100.00 0 2 100.00 0 1 50.00 1 -
- mozilla-firefox Mozilla Firefox Launchpad Unspecified
- 1 1 100.00 0 1 100.00 0
- thunderbird Missing corresponding project. (find) (link)
- 1 1 100.00 0 1 100.00 0 1 100.00 0 -
-
-XXX kiko 2008-02-01 bug=188020: One thing to note from the counts above
-is that while the linux-source/tomcat task is counted as pending a
-bugwatch, the mozilla-firefox one is not -- and the reason for that is
-that mozilla-firefox's linked upstream, firefox, officially uses malone.
-We count it as "watched" even though it's really native. The problem
-becomes clearer as you look at the totals:
-
- >>> table = find_tag_by_id(browser.contents, 'upstream-report-totals')
- >>> print extract_text(table)
- Totals: 4 4 100.00 0 4 100.00 0 3 75.00 1 0
-
-This 3 in the watched column is caused by a problem similar to the
-above; if you do a count of the values in the cells it would be 2, of
-course. Reason is that watch counts against mozilla-firefox are not
-rendered, but totalized as if they were watched, and the total line
-doesn't add up.
-
-This and the display problem above should be fixed. We could avoid
-totalizing the watched bugs for native upstreams, but then we'd have a
-broken percentage when comparing with bugs marked upstream. Perhaps
-we'd solve this by separating the watched column into natively watched
-and remote watched, but I don't know how to fix this right now. See
-bug #188020 -- kiko, 2008-02-01
-
-
-Useful links
-------------
-
-The table includes a number of convenience links:
-
- >>> table = find_tag_by_id(browser.contents, 'upstream-report-content')
- >>> all_anchors = table.findAll('a')
- >>> base_href = browser.url
- >>> for anchor in all_anchors:
- ... url = extract_link_from_tag(anchor, base_href)
- ... url = url.replace("http://bugs.launchpad.dev/ubuntu", "**")
- ... url = url.replace("&", "\n&")
- ... print extract_text(anchor), url
- linux-source-2.6.15 **/+source/linux-source-2.6.15
- (find) http://bugs.launchpad.dev/projects
- (link) **/hoary/+source/linux-source-2.6.15/+edit-packaging
- 2 **/+source/linux-source-2.6.15/+bugs?search=Search
- 2 **/+source/linux-source-2.6.15/+bugs?search=Search
- &field.status=TRIAGED
- 0 **/+source/linux-source-2.6.15/+bugs?search=Search
- &field.status=CONFIRMED
- &field.status=INCOMPLETE_WITHOUT_RESPONSE
- &field.status=INCOMPLETE_WITH_RESPONSE
- &field.status=NEW
- 2 **/+source/linux-source-2.6.15/+bugs?search=Search
- &field.status_upstream=open_upstream
- 0 **/+source/linux-source-2.6.15/+bugs?search=Search
- &field.status_upstream=hide_upstream
- 1 **/+source/linux-source-2.6.15/+bugs?search=Search
- &field.status_upstream=pending_bugwatch
- ...
-
-
-Links to bug queries
-....................
-
-Let's look more carefully at the above six links associated with counts.
-
- >>> count_anchors = all_anchors[3:9]
- >>> (open_url, triaged_url, triaged_delta, upstream_url,
- ... upstream_delta_url, watch_delta_url) = \
- ... [str(extract_link_from_tag(x, base_href)) for x in count_anchors]
-
-Link 1. Open bugs (2)
-
- >>> from lp.bugs.tests.bug import print_bugtasks
- >>> browser.open(open_url)
- >>> browser.title
- 'Bugs : \xe2\x80\x9clinux-source-2.6.15\xe2\x80\x9d package : Ubuntu'
- >>> print_bugtasks(browser.contents)
- 10 another test bug linux-source-2.6.15 (Ubuntu) Medium Triaged
- 16 take one linux-source-2.6.15 (Ubuntu) Undecided Triaged
-
-Link 2. Triaged bugs (2)
-
- >>> browser.open(triaged_url)
- >>> browser.title
- 'Bugs : \xe2\x80\x9clinux-source-2.6.15\xe2\x80\x9d package : Ubuntu'
- >>> print_bugtasks(browser.contents)
- 10 another test bug linux-source-2.6.15 (Ubuntu) Medium Triaged
- 16 take one linux-source-2.6.15 (Ubuntu) Undecided Triaged
-
-Link 3: Open bugs that aren't triaged (0)
-
- >>> browser.open(triaged_delta)
- >>> browser.title
- 'Bugs : \xe2\x80\x9clinux-source-2.6.15\xe2\x80\x9d package : Ubuntu'
- >>> print extract_text(
- ... find_tag_by_id(browser.contents, 'bugs-table-listing'))
- No results for search
-
-Link 4: Triaged bugs that are upstream issues (2)
-
- >>> browser.open(upstream_url)
- >>> browser.title
- 'Bugs : \xe2\x80\x9clinux-source-2.6.15\xe2\x80\x9d package : Ubuntu'
- >>> print_bugtasks(browser.contents)
- 10 another test bug linux-source-2.6.15 (Ubuntu) Medium Triaged
- 16 take one linux-source-2.6.15 (Ubuntu) Undecided Triaged
-
-Link 5: Triaged bugs that haven't been marked upstream (0)
-
- >>> browser.open(upstream_delta_url)
- >>> browser.title
- 'Bugs : \xe2\x80\x9clinux-source-2.6.15\xe2\x80\x9d package : Ubuntu'
- >>> print extract_text(
- ... find_tag_by_id(browser.contents, 'bugs-table-listing'))
- No results for search
-
-Link 6: Triaged bugs marked upstream lacking a watch (1)
-
- >>> browser.open(watch_delta_url)
- >>> browser.title
- 'Bugs : \xe2\x80\x9clinux-source-2.6.15\xe2\x80\x9d package : Ubuntu'
- >>> print_bugtasks(browser.contents)
- 10 another test bug linux-source-2.6.15 (Ubuntu) Medium Triaged
-
-
-Links to fix registration data
-..............................
-
-As you saw before, we also offer links to find products and connect packages
-with no packaging links (though you need to be logged in to actually see or
-link them):
-
- >>> browser.open("http://bugs.launchpad.dev/ubuntu/+upstreamreport")
- >>> browser.getLink('find').click()
- >>> browser.title
- 'Projects registered in Launchpad'
- >>> user_browser.open("http://bugs.launchpad.dev/ubuntu/+upstreamreport")
- >>> user_browser.getLink('link').click()
- >>> print user_browser.title
- Link to an upstream project : ...
-
-
-If you are logged in and can edit the upstream project, you can also set
-a bug tracker and a bug supervisor. Let's first fix Firefox to stop using
-Malone officially for the sake of this test:
-
- >>> login('foo.bar@xxxxxxxxxxxxx')
- >>> from zope.component import getUtility
- >>> from lp.registry.interfaces.product import IProductSet
- >>> firefox = getUtility(IProductSet).getByName('firefox')
- >>> firefox.official_malone = False
- >>> flush_database_updates()
- >>> transaction.commit()
- >>> logout()
-
-And check out the report:
-
- >>> admin_browser.open("http://bugs.launchpad.dev/ubuntu/+upstreamreport")
- >>> admin_browser.getLink('fix').click()
- >>> print admin_browser.title
- Change Mozilla Firefox's...
-
- >>> admin_browser.open("http://bugs.launchpad.dev/ubuntu/+upstreamreport")
- >>> admin_browser.getLink('change').click()
- >>> print admin_browser.title
- Edit bug supervisor for...
-
-If you're not allowed to edit the product, no love for you, though:
-
- >>> user_browser.open("http://bugs.launchpad.dev/ubuntu/+upstreamreport")
- >>> user_browser.getLink('fix')
- Traceback (most recent call last):
- ...
- LinkNotFoundError
-
- >>> user_browser.getLink('change')
- Traceback (most recent call last):
- ...
- LinkNotFoundError
-
-Note that we also cope correctly with the case in which a project's
-bugtracker is inherited from it's project group:
-
- >>> login('foo.bar@xxxxxxxxxxxxx')
- >>> from lp.bugs.interfaces.bugtracker import IBugTrackerSet
- >>> mozilla_org = getUtility(IBugTrackerSet).getByName("mozilla.org")
- >>> firefox.project.bugtracker = mozilla_org
- >>> flush_database_updates()
- >>> transaction.commit()
- >>> logout()
-
-See how we nicely display the bugtracker in the table's second row now.
-Note also that the watch counts for linux-source and mozilla-firefox
-change to account for the fact that firefox no longer uses_malone.
-
- >>> browser.open("http://bugs.launchpad.dev/ubuntu/+upstreamreport")
- >>> table = find_tag_by_id(browser.contents, 'upstream-report-content')
- >>> print extract_text(table, True)
- linux-source-2.6.15 Missing corresponding project. (find) (link)
- 2 2 100.00 0 2 100.00 0 0 0.00 2 -
- mozilla-firefox Mozilla Firefox The Mozilla.org Bug Tracker Unspecified
- 1 1 100.00 0 1 100.00 0 0 0.00 1 -
- thunderbird Missing corresponding project. (find) (link)
- 1 1 100.00 0 1 100.00 0 1 100.00 0 -
-
-If we now add an attachment to the bug we created earlier, the number
-of bugs with patches for upstream increases for linux-source-2.6.15.
-
- >>> login('foo.bar@xxxxxxxxxxxxx')
- >>> attachment = factory.makeBugAttachment(bug, is_patch=True)
- >>> logout()
- >>> browser.open("http://bugs.launchpad.dev/ubuntu/+upstreamreport")
- >>> table = find_tag_by_id(browser.contents, 'upstream-report-content')
- >>> print extract_text(table, True)
- linux-source-2.6.15 Missing corresponding project. (find) (link)
- 2 2 100.00 0 2 100.00 0 0 0.00 2 1
- mozilla-firefox Mozilla Firefox The Mozilla.org Bug Tracker Unspecified
- 1 1 100.00 0 1 100.00 0 0 0.00 1 -
- thunderbird Missing corresponding project. (find) (link)
- 1 1 100.00 0 1 100.00 0 1 100.00 0 -
-
-Finally, you can associate default branches to products. This is done by
-specifying a branch for the product's main_series:
-
- >>> admin_browser.open("http://launchpad.dev/firefox/trunk/+linkbranch")
- >>> branch_control = admin_browser.getControl(name="field.branch")
- >>> branch_control.value = "~name12/firefox/main"
- >>> admin_browser.getControl("Update").click()
- >>> admin_browser.url
- 'http://launchpad.dev/firefox/trunk'
-
-The report now renders an icon and a link to the branch page:
-
- >>> browser.open("http://bugs.launchpad.dev/ubuntu/+upstreamreport")
- >>> browser.getLink('branch').click()
- >>> browser.url
- 'http://code.launchpad.dev/~name12/firefox/main'
-
-(This means that anyone can use bzr branch lp:firefox to pull this code.)
-
-
-Empty distributions
--------------------
-
-If a distribution does not use Launchpad for bug tracking, no
-upstream report is shown, only a message that no data is available.
-
- >>> browser.open("http://bugs.launchpad.dev/debian/+upstreamreport")
- >>> table = find_tag_by_id(browser.contents, 'upstream-report-content')
- >>> print table
- None
- >>> message = find_tag_by_id(browser.contents, 'no-lp-usage')
- >>> print extract_text(message)
- This distribution does not use Launchpad for development.
=== removed file 'lib/lp/bugs/templates/distribution-upstream-report.pt'
--- lib/lp/bugs/templates/distribution-upstream-report.pt 2012-05-25 18:39:46 +0000
+++ lib/lp/bugs/templates/distribution-upstream-report.pt 1970-01-01 00:00:00 +0000
@@ -1,298 +0,0 @@
-<distribution-upstream-report
- xmlns="http://www.w3.org/1999/xhtml"
- xmlns:tal="http://xml.zope.org/namespaces/tal"
- xmlns:metal="http://xml.zope.org/namespaces/metal"
- xmlns:i18n="http://xml.zope.org/namespaces/i18n"
- metal:use-macro="view/macro:page/main_only"
- i18n:domain="launchpad">
-
- <metal:heading fill-slot="head_epilogue">
- <style type="text/css">
- <!--
- td.amount a { display: block; }
- tr.good { background: #d7ffbf; }
- tr.bad { background: #ffbfbf; }
- -->
- </style>
- </metal:heading>
-
- <div metal:fill-slot="heading">
- <h1>
- Upstream report for
- <tal:distro replace="context/displayname" />
- </h1>
- </div>
-
- <div metal:fill-slot="main">
- <div class="top-portlet">
- <div tal:condition="not:view/has_upstream_report"
- id="no-lp-usage">
- This distribution does not use Launchpad for development.
- </div>
- <div tal:condition="view/has_upstream_report"
- id="lp-used">
- <tal:no-bugs tal:condition="not: view/data">
- <span tal:replace="context/displayname" /> has no bugs filed
- against it.
- </tal:no-bugs>
-
- <p tal:condition="view/data">
- See the
- <a href="https://wiki.ubuntu.com/Bugs/Upstream/UpstreamReport"
- >UpstreamReport wiki page</a>
- for information on how to read this report.
- </p>
-
- <table class="listing" id="upstream-report"
- tal:condition="view/data">
- <thead tal:define="links view/sort_order_links">
- <tr>
- <th>
- <a id="sort-dsp" tal:attributes="href links/dsp/link">Package</a>
- <img id="sortarrow-dsp" tal:attributes="src links/dsp/arrow" />
- </th>
- <th colspan="2">
- <a id="sort-product" tal:attributes="href links/product/link">Project</a>
- <img id="sortarrow-product" tal:attributes="src links/product/arrow" />
- </th>
- <th>
- <a id="sort-bugtracker-name"
- tal:attributes="href links/bugtracker_name/link"
- >Bugtracker</a>
- <img id="sortarrow-bugtracker-name"
- tal:attributes="src links/bugtracker_name/arrow" />
- </th>
- <th>
- <a id="sort-bug-supervisor-name"
- tal:attributes="href links/bug_supervisor_name/link"
- >Upstream Contact</a>
- <img id="sortarrow-bug-supervisor-name"
- tal:attributes="src links/bug_supervisor_name/arrow" />
- </th>
- <th class="amount">
- <a id="sort-open-bugs"
- tal:attributes="href links/open_bugs/link">Open</a>
- <img id="sortarrow-open-bugs"
- tal:attributes="src links/open_bugs/arrow" />
- </th>
- <th class="amount">
- <a id="sort-triaged-bugs"
- tal:attributes="href links/triaged_bugs/link"
- >Triaged</a>
- <img id="sortarrow-triaged-bugs"
- tal:attributes="src links/triaged_bugs/arrow" />
- </th>
- <th class="amount">
- <a id="sort-triaged-percentage"
- tal:attributes="href links/triaged_bugs_percentage/link"
- >%</a>
- <img id="sortarrow-triaged-percentage"
- tal:attributes="src links/triaged_bugs_percentage/arrow" />
- </th>
- <th class="amount">
- <a id="sort-triaged-delta"
- tal:attributes="href links/triaged_bugs_delta/link"
- >Δ</a>
- <img id="sortarrow-triaged-delta"
- tal:attributes="src links/triaged_bugs_delta/arrow" />
- </th>
- <th class="amount">
- <a id="sort-upstream-bugs"
- tal:attributes="href links/upstream_bugs/link"
- >Upstream</a>
- <img id="sortarrow-upstream-bugs"
- tal:attributes="src links/upstream_bugs/arrow" />
- </th>
- <th class="amount">
- <a id="sort-upstream-percentage"
- tal:attributes="href links/upstream_bugs_percentage/link"
- >%</a>
- <img id="sortarrow-upstream-percentage"
- tal:attributes="src links/upstream_bugs_percentage/arrow" />
- </th>
- <th class="amount">
- <a id="sort-upstream-delta"
- tal:attributes="href links/upstream_bugs_delta/link"
- >Δ</a>
- <img id="sortarrow-upstream-delta"
- tal:attributes="src links/upstream_bugs_delta/arrow" />
- </th>
- <th class="amount">
- <a id="sort-watched-bugs"
- tal:attributes="href links/watched_bugs/link">Watch</a>
- <img id="sortarrow-watched-bugs"
- tal:attributes="src links/watched_bugs/arrow" />
- </th>
- <th class="amount">
- <a id="sort-watched-percentage"
- tal:attributes="href links/watched_bugs_percentage/link"
- >%</a>
- <img id="sortarrow-watched-percentage"
- tal:attributes="src links/watched_bugs_percentage/arrow" />
- </th>
- <th class="amount">
- <a id="sort-watched-delta"
- tal:attributes="href links/watched_bugs_delta/link"
- >Δ</a>
- <img id="sortarrow-watched-delta"
- tal:attributes="src links/watched_bugs_delta/arrow" />
- </th>
- <th class="amount">
- <a id="sort-bugs-with-upstream-patches"
- tal:attributes="href links/bugs_with_upstream_patches/link"
- >Patches for upstream</a>
- <img id="sortarrow-bugs-with-upstream-patches"
- tal:attributes="src links/bugs_with_upstream_patches/arrow" />
- </th>
- </tr>
- </thead>
- <tbody id="upstream-report-content">
- <tr tal:repeat="item view/data" tal:attributes="class item/row_class">
- <td>
- <a tal:attributes="href item/dsp/fmt:url"
- tal:content="item/dsp/name"></a>
- </td>
- <tal:has-product condition="item/product">
- <td style="padding-right: 0" align="center">
- <a tal:attributes="href item/branch/fmt:url;
- title string:You can use `bzr branch lp:${item/product/name}' to fetch this project's code"
- tal:condition="item/branch">
- <img alt="(branch)" src="/@@/branch" /></a>
- </td>
- <td style="padding-left: 0" align="center">
- <a tal:attributes="href item/product/fmt:url"
- ><img src="/@@/yes"
- tal:attributes="title item/product/title" /></a>
- </td>
- <tal:has-bugtracker condition="item/bugtracker">
- <td align="center">
- <a tal:attributes="href item/bugtracker/fmt:url"
- ><img src="/@@/yes"
- tal:attributes="title item/bugtracker/title" /></a>
- </td>
- </tal:has-bugtracker>
- <tal:has-no-bugtracker condition="not: item/bugtracker">
- <td tal:condition="item/bug_tracking_usage/enumvalue:LAUNCHPAD"
- align="center">
- <img src="/@@/yes" title="Launchpad" />
- </td>
- <td tal:condition="not: item/bug_tracking_usage/enumvalue:LAUNCHPAD"
- align="center">
- <img src="/@@/no" title="Unknown" />
- <a tal:condition="item/product/required:launchpad.Edit"
- tal:attributes="href item/product_edit_url">
- <small>(fix)</small></a></td>
- </tal:has-no-bugtracker>
- <tal:has-bug-supervisor condition="item/product/bug_supervisor">
- <td align="center">
- <a tal:attributes="href item/product/bug_supervisor/fmt:url"
- ><img src="/@@/yes"
- tal:attributes="title item/product/bug_supervisor/fmt:displayname"
- /></a>
- </td>
- </tal:has-bug-supervisor>
- <tal:has-no-bug-supervisor condition="not: item/product/bug_supervisor">
- <td align="center">
- <img src="/@@/no" title="Unspecified" />
- <a tal:condition="item/product/required:launchpad.Edit"
- tal:attributes="href item/bug_supervisor_url">
- <small>(change)</small></a></td>
- </tal:has-no-bug-supervisor>
- </tal:has-product>
- <tal:has-no-product condition="not: item/product">
- <td align="center" class="bad" colspan="4">
- Missing corresponding project.
- <a href="/projects"><small>(find)</small></a>
- <a tal:attributes="href item/packaging_url">
- <small>(link)</small></a>
- </td>
- </tal:has-no-product>
- <td class="amount">
- <a tal:attributes="href item/open_bugs_url"
- tal:content="item/open_bugs"></a>
- </td>
- <td tal:attributes="class string:amount ${item/triaged_bugs_class}">
- <a tal:attributes="href item/triaged_bugs_url"
- tal:content="item/triaged_bugs"></a>
- </td>
- <td tal:attributes="class string:amount ${item/triaged_bugs_class}"
- tal:content="item/triaged_bugs_percentage/fmt:float/.2" />
- <td tal:attributes="class string:amount ${item/triaged_bugs_class}">
- <a tal:attributes="href item/triaged_bugs_delta_url"
- tal:content="item/triaged_bugs_delta"></a>
- </td>
- <td tal:attributes="class string:amount ${item/upstream_bugs_class}">
- <a tal:attributes="href item/upstream_bugs_url"
- tal:content="item/upstream_bugs"></a>
- </td>
- <td tal:attributes="class string:amount ${item/upstream_bugs_class}"
- tal:content="item/upstream_bugs_percentage/fmt:float/.2" />
- <td tal:attributes="class string:amount ${item/upstream_bugs_class}">
- <a tal:attributes="href item/upstream_bugs_delta_url"
- tal:content="item/upstream_bugs_delta"></a>
- </td>
- <tal:upstream-in-launchpad
- condition="item/bug_tracking_usage/enumvalue:LAUNCHPAD">
- <td colspan="4" class="good"> </td>
- </tal:upstream-in-launchpad>
- <tal:upstream-not-in-launchpad
- condition="not: item/bug_tracking_usage/enumvalue:LAUNCHPAD">
- <td tal:attributes="class string:amount ${item/watched_bugs_class}"
- tal:content="item/watched_bugs" />
- <td tal:attributes="class string:amount ${item/watched_bugs_class}"
- tal:content="item/watched_bugs_percentage/fmt:float/.2" />
- <td tal:attributes="class string:amount ${item/watched_bugs_class}">
- <a tal:attributes="href item/watched_bugs_delta_url"
- tal:content="item/watched_bugs_delta"></a>
- </td>
- <td tal:attributes="class string:amount ${item/watched_bugs_class}">
- <tal:has-bugs-with-upstream-patches
- condition="item/bugs_with_upstream_patches">
- <a tal:attributes="href item/bugs_with_upstream_patches_url"
- tal:content="item/bugs_with_upstream_patches"></a>
- </tal:has-bugs-with-upstream-patches>
- <tal:has-no-bugs-with-upstream-patches
- condition="not: item/bugs_with_upstream_patches">
- -
- </tal:has-no-bugs-with-upstream-patches>
- </td>
- </tal:upstream-not-in-launchpad>
- </tr>
- </tbody>
- <tfoot id="upstream-report-totals">
- <tr>
- <td colspan="5" class="amount"><b>Totals:</b></td>
- <td class="amount"
- tal:content="view/total/open_bugs" />
- <td class="amount"
- tal:content="view/total/triaged_bugs" />
- <td class="amount"
- tal:content="view/total/triaged_bugs_percentage/fmt:float/.2" />
- <td class="amount"
- tal:content="view/total/triaged_bugs_delta" />
- <td class="amount"
- tal:content="view/total/upstream_bugs" />
- <td class="amount"
- tal:content="view/total/upstream_bugs_percentage/fmt:float/.2" />
- <td class="amount"
- tal:content="view/total/upstream_bugs_delta" />
- <td class="amount"
- tal:content="view/total/watched_bugs" />
- <td class="amount"
- tal:content="view/total/watched_bugs_percentage/fmt:float/.2" />
- <td class="amount"
- tal:content="view/total/watched_bugs_delta" />
- <td class="amount"
- tal:content="view/total/bugs_with_upstream_patches" />
- </tr>
- </tfoot>
- </table>
-
- <div tal:condition="view/data"
- align="right"><small>Top <span tal:content="view/data/count:len" />
- packages listed.</small></div>
- </div>
- </div>
- </div>
-
-</distribution-upstream-report>
=== modified file 'lib/lp/registry/interfaces/distribution.py'
--- lib/lp/registry/interfaces/distribution.py 2012-10-24 09:47:43 +0000
+++ lib/lp/registry/interfaces/distribution.py 2012-11-02 01:01:22 +0000
@@ -346,10 +346,6 @@
all_distro_archive_ids = Attribute(
"A list containing the IDs of all the non-PPA archives.")
- upstream_report_excluded_packages = Attribute(
- "A list of the source packages that should not be shown on the "
- "upstream report for this Distribution.")
-
has_published_binaries = Bool(
title=_("Has Published Binaries"),
description=_("True if this distribution has binaries published "
@@ -606,25 +602,6 @@
If the component_name supplied is unknown, None is returned.
"""
- def getPackagesAndPublicUpstreamBugCounts(limit=50,
- exclude_packages=None):
- """Return list of tuples of packages, upstreams and public bug counts.
-
- :param limit: The maximum number of rows to return.
- :param exclude_packages: A list of source packages to exclude.
- These should be specified as strings which correspond with
- SourcePackageName.name.
- :returns: [(IDistroSourcePackage, IProduct, int, int, int, int), ...]
-
- This API is quite specialized; it returns a list of up to limit
- tuples containing IProducts and three different bug counts:
- - open bugs
- - triaged bugs
- - open bugs with an upstream task
- - open bugs with upstream tasks that are either linked to
- bug watches or to products that use_malone.
- """
-
def getAllowedBugInformationTypes():
"""Get the information types that a bug in this distribution can have.
=== modified file 'lib/lp/registry/model/distribution.py'
--- lib/lp/registry/model/distribution.py 2012-10-24 09:43:58 +0000
+++ lib/lp/registry/model/distribution.py 2012-11-02 01:01:22 +0000
@@ -81,10 +81,6 @@
from lp.blueprints.model.sprint import HasSprintsMixin
from lp.bugs.interfaces.bugsummary import IBugSummaryDimension
from lp.bugs.interfaces.bugsupervisor import IHasBugSupervisor
-from lp.bugs.interfaces.bugtask import (
- BugTaskStatus,
- DB_UNRESOLVED_BUGTASK_STATUSES,
- )
from lp.bugs.interfaces.bugtaskfilter import OrderedBugTask
from lp.bugs.model.bugtarget import (
BugTargetBase,
@@ -116,7 +112,6 @@
MirrorStatus,
)
from lp.registry.interfaces.oopsreferences import IHasOOPSReferences
-from lp.registry.interfaces.packaging import PackagingType
from lp.registry.interfaces.person import (
validate_person,
validate_person_or_closed_team,
@@ -152,7 +147,6 @@
from lp.services.database.enumcol import EnumCol
from lp.services.database.lpstorm import IStore
from lp.services.database.sqlbase import (
- cursor,
quote,
SQLBase,
sqlvalues,
@@ -339,8 +333,7 @@
_answers_usage = EnumCol(
dbName="answers_usage", notNull=True,
- schema=ServiceUsage,
- default=ServiceUsage.UNKNOWN)
+ schema=ServiceUsage, default=ServiceUsage.UNKNOWN)
def _get_answers_usage(self):
if self._answers_usage != ServiceUsage.UNKNOWN:
@@ -389,8 +382,7 @@
translations_usage = EnumCol(
dbName="translations_usage", notNull=True,
- schema=ServiceUsage,
- default=ServiceUsage.UNKNOWN)
+ schema=ServiceUsage, default=ServiceUsage.UNKNOWN)
@property
def codehosting_usage(self):
@@ -1424,170 +1416,6 @@
# Otherwise we defer to the caller.
return None
- @property
- def upstream_report_excluded_packages(self):
- """See `IDistribution`."""
- # If the current distribution is Ubuntu, return a specific set
- # of excluded packages. Otherwise return an empty list.
- ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
- if self == ubuntu:
- #XXX gmb 2009-02-02: bug 324298
- # This needs to be managed in a nicer, non-hardcoded
- # fashion.
- excluded_packages = [
- 'apport',
- 'casper',
- 'displayconfig-gtk',
- 'flashplugin-nonfree',
- 'gnome-app-install',
- 'nvidia-graphics-drivers-177',
- 'software-properties',
- 'sun-java6',
- 'synaptic',
- 'ubiquity',
- 'ubuntu-meta',
- 'update-manager',
- 'update-notifier',
- 'usb-creator',
- 'usplash',
- ]
- else:
- excluded_packages = []
-
- return excluded_packages
-
- def getPackagesAndPublicUpstreamBugCounts(self, limit=50,
- exclude_packages=None):
- """See `IDistribution`."""
- from lp.registry.model.product import Product
-
- if exclude_packages is None or len(exclude_packages) == 0:
- # If exclude_packages is None or an empty list we set it to
- # be a list containing a single empty string. This is so
- # that we can quote() it properly for the query below ('NOT
- # IN ()' is not valid SQL).
- exclude_packages = ['']
- else:
- # Otherwise, listify exclude_packages so that we're not
- # trying to quote() a security proxy object.
- exclude_packages = list(exclude_packages)
-
- # This method collects three open bug counts for
- # sourcepackagenames in this distribution first, and then caches
- # product information before rendering everything into a list of
- # tuples.
- cur = cursor()
- cur.execute("""
- SELECT SPN.id, SPN.name,
- COUNT(DISTINCT Bugtask.bug) AS open_bugs,
- COUNT(DISTINCT CASE WHEN Bugtask.status = %(triaged)s THEN
- Bugtask.bug END) AS bugs_triaged,
- COUNT(DISTINCT CASE WHEN Bugtask.status IN %(unresolved)s THEN
- RelatedBugTask.bug END) AS bugs_affecting_upstream,
- COUNT(DISTINCT CASE WHEN Bugtask.status in %(unresolved)s AND
- (RelatedBugTask.bugwatch IS NOT NULL OR
- RelatedProduct.official_malone IS TRUE) THEN
- RelatedBugTask.bug END) AS bugs_with_upstream_bugwatch,
- COUNT(DISTINCT CASE WHEN Bugtask.status in %(unresolved)s AND
- RelatedBugTask.bugwatch IS NULL AND
- RelatedProduct.official_malone IS FALSE AND
- Bug.latest_patch_uploaded IS NOT NULL
- THEN
- RelatedBugTask.bug END)
- AS bugs_with_upstream_patches
- FROM
- SourcePackageName AS SPN
- JOIN Bugtask ON SPN.id = Bugtask.sourcepackagename
- JOIN Bug ON Bug.id = Bugtask.bug
- LEFT OUTER JOIN Bugtask AS RelatedBugtask ON (
- RelatedBugtask.bug = Bugtask.bug
- AND RelatedBugtask.id != Bugtask.id
- AND RelatedBugtask.product IS NOT NULL
- AND RelatedBugtask.status != %(invalid)s
- )
- LEFT OUTER JOIN Product AS RelatedProduct ON (
- RelatedBugtask.product = RelatedProduct.id
- )
- WHERE
- Bugtask.distribution = %(distro)s
- AND Bugtask.sourcepackagename = spn.id
- AND Bugtask.distroseries IS NULL
- AND Bugtask.status IN %(unresolved)s
- AND Bug.information_type IN %(public_types)s
- AND Bug.duplicateof IS NULL
- AND spn.name NOT IN %(excluded_packages)s
- GROUP BY SPN.id, SPN.name
- HAVING COUNT(DISTINCT Bugtask.bug) > 0
- ORDER BY open_bugs DESC, SPN.name LIMIT %(limit)s
- """ % {'invalid': quote(BugTaskStatus.INVALID),
- 'triaged': quote(BugTaskStatus.TRIAGED),
- 'limit': limit,
- 'distro': self.id,
- 'unresolved': quote(DB_UNRESOLVED_BUGTASK_STATUSES),
- 'excluded_packages': quote(exclude_packages),
- 'public_types': quote(PUBLIC_INFORMATION_TYPES),
- })
- counts = cur.fetchall()
- cur.close()
- if not counts:
- # If no counts are returned it means that there are no
- # source package names in the database -- because the counts
- # would just return zero if no bugs existed. And if there
- # are no packages are in the database, all bets are off.
- return []
-
- # Next step is to extract which IDs actually show up in the
- # output we generate, and cache them.
- spn_ids = [item[0] for item in counts]
- list(SourcePackageName.select("SourcePackageName.id IN %s"
- % sqlvalues(spn_ids)))
-
- # Finally find out what products are attached to these source
- # packages (if any) and cache them too. The ordering of the
- # query by Packaging.id ensures that the dictionary holds the
- # latest entries for situations where we have multiple entries.
- cur = cursor()
- cur.execute("""
- SELECT Packaging.sourcepackagename, Product.id
- FROM Product, Packaging, ProductSeries, DistroSeries
- WHERE ProductSeries.product = Product.id AND
- DistroSeries.distribution = %s AND
- Packaging.distroseries = DistroSeries.id AND
- Packaging.productseries = ProductSeries.id AND
- Packaging.sourcepackagename IN %s AND
- Packaging.packaging = %s AND
- Product.active IS TRUE
- ORDER BY Packaging.id
- """ % sqlvalues(self.id, spn_ids, PackagingType.PRIME))
- sources_to_products = dict(cur.fetchall())
- cur.close()
- if sources_to_products:
- # Cache some more information to avoid us having to hit the
- # database hard for each product rendered.
- list(Product.select("Product.id IN %s" %
- sqlvalues(sources_to_products.values()),
- prejoins=["bug_supervisor", "bugtracker", "project",
- "development_focus.branch"]))
-
- # Okay, we have all the information good to go, so assemble it
- # in a reasonable data structure.
- results = []
- for (spn_id, spn_name, open_bugs, bugs_triaged,
- bugs_affecting_upstream, bugs_with_upstream_bugwatch,
- bugs_with_upstream_patches) in counts:
- sourcepackagename = SourcePackageName.get(spn_id)
- dsp = self.getSourcePackage(sourcepackagename)
- if spn_id in sources_to_products:
- product_id = sources_to_products[spn_id]
- product = Product.get(product_id)
- else:
- product = None
- results.append(
- (dsp, product, open_bugs, bugs_triaged,
- bugs_affecting_upstream, bugs_with_upstream_bugwatch,
- bugs_with_upstream_patches))
- return results
-
def getAllowedBugInformationTypes(self):
"""See `IDistribution.`"""
return FREE_INFORMATION_TYPES
Follow ups