~launchpad-pqm/launchpad/devel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# Copyright 2009 Canonical Ltd.  This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).

"""Views to generate CVE reports (as in distro and distroseries/+cve pages)."""

__metaclass__ = type

__all__ = [
    'CVEReportView',
    ]

from zope.component import getUtility

from canonical.launchpad.helpers import shortlist
from canonical.launchpad.searchbuilder import any
from canonical.launchpad.webapp import LaunchpadView
from lp.bugs.browser.bugtask import BugTaskListingItem
from lp.bugs.interfaces.bugtask import (
    BugTaskSearchParams,
    IBugTaskSet,
    RESOLVED_BUGTASK_STATUSES,
    UNRESOLVED_BUGTASK_STATUSES,
    )
from lp.bugs.interfaces.cve import ICveSet
from lp.services.propertycache import cachedproperty


class BugTaskCve:
    """An object that represents BugTasks and CVEs related to a single bug."""
    def __init__(self):
        self.bugtasks = []
        self.cves = []

    @property
    def bug(self):
        """Return the bug which this BugTaskCve represents."""
        # All the bugtasks we have should represent the same bug.
        assert self.bugtasks, "No bugtasks added before calling bug!"
        return self.bugtasks[0].bug


class CVEReportView(LaunchpadView):
    """View that builds data to be displayed in CVE reports."""
    @cachedproperty
    def open_cve_bugtasks(self):
        """Return BugTaskCves for bugs with open bugtasks in the context."""
        search_params = BugTaskSearchParams(self.user,
            status=any(*UNRESOLVED_BUGTASK_STATUSES))
        return self._buildBugTaskCves(search_params)

    @cachedproperty
    def resolved_cve_bugtasks(self):
        """Return BugTaskCves for bugs with resolved bugtasks in the context."""
        search_params = BugTaskSearchParams(self.user,
            status=any(*RESOLVED_BUGTASK_STATUSES))
        return self._buildBugTaskCves(search_params)

    def setContextForParams(self, params):
        """Update the search params for the context for a specific view."""
        raise NotImplementedError

    def _buildBugTaskCves(self, search_params):
        """Construct a list of BugTaskCve objects, sorted by bug ID."""
        search_params.has_cve = True
        bugtasks = shortlist(
            self.context.searchTasks(search_params),
            longest_expected=300)

        if not bugtasks:
            return []

        badge_properties = getUtility(IBugTaskSet).getBugTaskBadgeProperties(
            bugtasks)

        bugtaskcves = {}
        for bugtask in bugtasks:
            badges = badge_properties[bugtask]
            # Wrap the bugtask in a BugTaskListingItem, to avoid db
            # queries being issues when trying to render the badges.
            bugtask = BugTaskListingItem(
                bugtask,
                has_bug_branch=badges['has_branch'],
                has_specification=badges['has_specification'],
                has_patch=badges['has_patch'])
            if not bugtaskcves.has_key(bugtask.bug.id):
                bugtaskcves[bugtask.bug.id] = BugTaskCve()
            bugtaskcves[bugtask.bug.id].bugtasks.append(bugtask)

        bugcves = getUtility(ICveSet).getBugCvesForBugTasks(bugtasks)
        for bugcve in bugcves:
            assert bugtaskcves.has_key(bugcve.bug.id)
            bugtaskcves[bugcve.bug.id].cves.append(bugcve.cve)

        # Order the dictionary items by bug ID and then return only the
        # bugtaskcve objects.
        return [bugtaskcve for bug, bugtaskcve in sorted(bugtaskcves.items())]