← Back to team overview

launchpad-reviewers team mailing list archive

[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"
-                >&Delta;</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"
-                >&Delta;</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"
-                >&Delta;</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">&nbsp;</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