~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/lp/registry/model/pillaraffiliation.py

  • Committer: Curtis Hovey
  • Date: 2011-08-21 14:21:06 UTC
  • mto: This revision was merged to the branch mainline in revision 13745.
  • Revision ID: curtis.hovey@canonical.com-20110821142106-x93hajd6iguma8gx
Update test that was enforcing bad grammar.

Show diffs side-by-side

added added

removed removed

Lines of Context:
28
28
    Interface,
29
29
    )
30
30
 
31
 
from lp.app.interfaces.launchpad import IHasIcon
 
31
from canonical.launchpad.interfaces.launchpad import IHasIcon
32
32
from lp.answers.interfaces.questionsperson import IQuestionsPerson
33
33
from lp.registry.interfaces.distribution import IDistribution
34
34
from lp.registry.interfaces.distributionsourcepackage import (
35
35
    IDistributionSourcePackage,
36
36
    )
37
 
from lp.registry.model.teammembership import find_team_participations
38
37
 
39
38
 
40
39
class IHasAffiliation(Interface):
43
42
    def getAffiliationBadges(persons):
44
43
        """Return the badges for the type of affiliation each person has.
45
44
 
46
 
        The return value is a list of namedtuples:
47
 
        BadgeDetails(url, label, role)
 
45
        The return value is a list of namedtuples: BadgeDetails(url, alt_text)
48
46
 
49
47
        If a person has no affiliation with this object, their entry is None.
50
48
        """
51
49
 
52
 
BadgeDetails = namedtuple('BadgeDetails', ('url', 'label', 'role'))
 
50
BadgeDetails = namedtuple('BadgeDetails', ('url', 'alt_text'))
53
51
 
54
52
 
55
53
@adapter(Interface)
56
54
class PillarAffiliation(object):
57
55
    """Default affiliation adapter.
58
56
 
59
 
    Subclasses may need to override getPillars() in order to provide the
60
 
    pillar entities for which affiliation is to be determined. A given context
61
 
    may supply for than one pillar for which affiliation can be determined.
62
 
    The default is just to use the context object directly.
 
57
    Subclasses may need to override getPillar() in order to provide the pillar
 
58
    entity for which affiliation is to be determined. The default is just to
 
59
    use the context object directly.
63
60
    """
64
61
 
65
62
    implements(IHasAffiliation)
66
63
 
67
 
    # We rank the affiliations from most important to least important.
68
 
    # Unlisted roles are given a rank of 10.
69
 
    affiliation_priorities = {
70
 
        'maintainer': 1,
71
 
        'driver': 2,
72
 
        'bug supervisor': 3,
73
 
        'security contact': 4,
74
 
    }
75
 
 
76
64
    def __init__(self, context):
77
65
        self.context = context
78
66
 
79
 
    def getPillars(self):
80
 
        return [self.context]
81
 
 
82
 
    def getIconUrl(self, pillar):
83
 
        if (IHasIcon.providedBy(self.context)
84
 
                    and self.context.icon is not None):
85
 
            icon_url = self.context.icon.getURL()
86
 
            return icon_url
87
 
        if IHasIcon.providedBy(pillar) and pillar.icon is not None:
88
 
            icon_url = pillar.icon.getURL()
89
 
            return icon_url
90
 
        if IDistribution.providedBy(pillar):
91
 
            return "/@@/distribution-badge"
92
 
        else:
93
 
            return "/@@/product-badge"
94
 
 
95
 
    def _getAffiliation(self, person, pillars):
 
67
    def getPillar(self):
 
68
        return self.context
 
69
 
 
70
    def _getAffiliationDetails(self, person, pillar):
96
71
        """ Return the affiliation information for a person, if any.
97
72
 
98
 
        Subclasses will override this method to perform specific affiliation
99
 
        checks.
100
 
        The return result is a list of AffiliationRecord.
101
 
        """
102
 
        return []
103
 
 
104
 
    def _getAffiliationTeamRoles(self, pillars):
105
 
        """ Return teams for which a person needs to belong, if affiliated.
106
 
 
107
73
        A person is affiliated with a pillar if they are in the list of
108
74
        drivers or are the maintainer.
 
75
        The return result is a list of tuples (pillar displayanme, role).
109
76
        """
110
 
        result = {}
111
 
        for pillar in pillars:
112
 
            result[BadgeDetails(
113
 
                self.getIconUrl(pillar),
114
 
                pillar.displayname, 'maintainer')] = [pillar.owner]
115
 
            result[BadgeDetails(
116
 
                self.getIconUrl(pillar),
117
 
                pillar.displayname, 'driver')] = pillar.drivers
 
77
        result = []
 
78
        if person.inTeam(pillar.owner):
 
79
            result.append((pillar.displayname, 'maintainer'))
 
80
        for driver in pillar.drivers:
 
81
            if person.inTeam(driver):
 
82
                result.append((pillar.displayname, 'driver'))
 
83
                break
118
84
        return result
119
85
 
120
86
    def getAffiliationBadges(self, persons):
121
87
        """ Return the affiliation badge details for people given a context.
122
 
 
123
 
        There are 2 ways we check for affiliation:
124
 
        1. Generic membership checks of particular teams as returned by
125
 
           _getAffiliationTeamRoles
126
 
        2. Specific affiliation checks as performed by _getAffiliation
127
88
        """
128
 
        pillars = self.getPillars()
 
89
        pillar = self.getPillar()
129
90
        result = []
130
 
 
131
 
        # We find the teams to check for participation..
132
 
        affiliation_team_details = self._getAffiliationTeamRoles(pillars)
133
 
        teams_to_check = set()
134
 
        for teams in affiliation_team_details.values():
135
 
            teams_to_check.update(teams)
136
 
        # We gather the participation for the persons.
137
 
        people_teams = find_team_participations(persons, teams_to_check)
138
 
 
139
91
        for person in persons:
140
 
            # Specific affiliations
141
 
            badges = self._getAffiliation(person, pillars)
142
 
            # Generic, team based affiliations
143
 
            affiliated_teams = people_teams.get(person, [])
144
 
            for affiliated_team in affiliated_teams:
145
 
                for badge, teams in affiliation_team_details.items():
146
 
                    if affiliated_team in teams:
147
 
                        badges.append(badge)
148
 
 
149
 
            if not badges:
 
92
            affiliation_details = self._getAffiliationDetails(person, pillar)
 
93
            if not affiliation_details:
150
94
                result.append([])
151
95
                continue
152
96
 
153
 
            # Sort the affiliation list according the the importance of each
154
 
            # affiliation role.
155
 
            badges.sort(
156
 
                key=lambda badge:
157
 
                    self.affiliation_priorities.get(badge.role, 10))
 
97
            def getIconUrl(context, pillar, default_url):
 
98
                if IHasIcon.providedBy(context) and context.icon is not None:
 
99
                    icon_url = context.icon.getURL()
 
100
                    return icon_url
 
101
                if IHasIcon.providedBy(pillar) and pillar.icon is not None:
 
102
                    icon_url = pillar.icon.getURL()
 
103
                    return icon_url
 
104
                return default_url
 
105
 
 
106
            if IDistribution.providedBy(pillar):
 
107
                default_icon_url = "/@@/distribution-badge"
 
108
            else:
 
109
                default_icon_url = "/@@/product-badge"
 
110
            icon_url = getIconUrl(self.context, pillar, default_icon_url)
 
111
            badges = []
 
112
            for affiliation in affiliation_details:
 
113
                alt_text = "%s %s" % affiliation
 
114
                badges.append(BadgeDetails(icon_url, alt_text))
158
115
            result.append(badges)
159
116
        return result
160
117
 
161
118
 
162
119
class BugTaskPillarAffiliation(PillarAffiliation):
163
120
    """An affiliation adapter for bug tasks."""
164
 
    def getPillars(self):
165
 
        result = []
166
 
        bug = self.context.bug
167
 
        for bugtask in bug.bugtasks:
168
 
            result.append(bugtask.pillar)
169
 
        return result
 
121
    def getPillar(self):
 
122
        return self.context.pillar
170
123
 
171
 
    def _getAffiliationTeamRoles(self, pillars):
 
124
    def _getAffiliationDetails(self, person, pillar):
172
125
        """ A person is affiliated with a bugtask based on (in order):
173
126
        - owner of bugtask pillar
174
127
        - driver of bugtask pillar
175
128
        - bug supervisor of bugtask pillar
176
129
        - security contact of bugtask pillar
177
130
        """
178
 
        super_instance = super(BugTaskPillarAffiliation, self)
179
 
        result = super_instance._getAffiliationTeamRoles(pillars)
180
 
        for pillar in pillars:
181
 
            result[BadgeDetails(
182
 
                self.getIconUrl(pillar),
183
 
                pillar.displayname,
184
 
                'bug supervisor')] = [pillar.bug_supervisor]
185
 
            result[BadgeDetails(
186
 
                self.getIconUrl(pillar),
187
 
                pillar.displayname,
188
 
                'security contact')] = [pillar.security_contact]
 
131
        result = super(BugTaskPillarAffiliation, self)._getAffiliationDetails(
 
132
            person, pillar)
 
133
        if person.inTeam(pillar.bug_supervisor):
 
134
            result.append((pillar.displayname, 'bug supervisor'))
 
135
        if person.inTeam(pillar.security_contact):
 
136
            result.append((pillar.displayname, 'security contact'))
189
137
        return result
190
138
 
191
139
 
192
140
class BranchPillarAffiliation(BugTaskPillarAffiliation):
193
141
    """An affiliation adapter for branches."""
194
142
 
195
 
    def getPillars(self):
196
 
        pillar = self.context.product or self.context.distribution
197
 
        if pillar is None:
198
 
            # This is a +junk branch.
199
 
            return []
200
 
        return [pillar]
 
143
    def getPillar(self):
 
144
        return self.context.product or self.context.distribution
201
145
 
202
146
    def getBranch(self):
203
147
        return self.context
204
148
 
205
 
    def _getAffiliation(self, person, pillars):
 
149
    def _getAffiliationDetails(self, person, pillar):
206
150
        super_instance = super(BranchPillarAffiliation, self)
207
 
        result = super_instance._getAffiliation(person, pillars)
208
 
        for pillar in pillars:
209
 
            if self.getBranch().isPersonTrustedReviewer(person):
210
 
                result.append(BadgeDetails(
211
 
                    self.getIconUrl(pillar),
212
 
                    pillar.displayname, 'trusted reviewer'))
 
151
        result = super_instance._getAffiliationDetails(person, pillar)
 
152
        if self.getBranch().isPersonTrustedReviewer(person):
 
153
            result.append((pillar.displayname, 'trusted reviewer'))
213
154
        return result
214
155
 
215
156
 
216
157
class CodeReviewVotePillarAffiliation(BranchPillarAffiliation):
217
158
    """An affiliation adapter for CodeReviewVotes."""
218
159
 
219
 
    def getPillars(self):
 
160
    def getPillar(self):
220
161
        """Return the target branch'pillar."""
221
162
        branch = self.getBranch()
222
 
        return [branch.product or branch.distribution]
 
163
        return branch.product or branch.distribution
223
164
 
224
165
    def getBranch(self):
225
166
        return self.context.branch_merge_proposal.target_branch
227
168
 
228
169
class DistroSeriesPillarAffiliation(PillarAffiliation):
229
170
    """An affiliation adapter for distroseries."""
230
 
    def getPillars(self):
231
 
        return [self.context.distribution]
 
171
    def getPillar(self):
 
172
        return self.context.distribution
232
173
 
233
174
 
234
175
class ProductSeriesPillarAffiliation(PillarAffiliation):
235
176
    """An affiliation adapter for productseries."""
236
 
    def getPillars(self):
237
 
        return [self.context.product]
 
177
    def getPillar(self):
 
178
        return self.context.product
238
179
 
239
180
 
240
181
class SpecificationPillarAffiliation(PillarAffiliation):
241
182
    """An affiliation adapter for blueprints."""
242
 
    def getPillars(self):
243
 
        return [self.context.target]
 
183
    def getPillar(self):
 
184
        return (self.context.target)
244
185
 
245
186
 
246
187
class QuestionPillarAffiliation(PillarAffiliation):
247
 
    """An affiliation adapter for questions.
248
 
 
249
 
    A person is affiliated with a question based on (in order):
250
 
    - answer contact for question target
251
 
    - owner of question target
252
 
    - driver of question target
253
 
    """
254
 
 
255
 
    def getPillars(self):
256
 
        return [self.context.product or self.context.distribution]
257
 
 
258
 
    def _getAffiliation(self, person, pillars):
259
 
        super_instance = super(QuestionPillarAffiliation, self)
260
 
        result = super_instance._getAffiliation(person, pillars)
 
188
    """An affiliation adapter for questions."""
 
189
    def getPillar(self):
 
190
        return self.context.product or self.context.distribution
 
191
 
 
192
    def _getAffiliationDetails(self, person, pillar):
 
193
        """ A person is affiliated with a question based on (in order):
 
194
        - answer contact for question target
 
195
        - owner of question target
 
196
        - driver of question target
 
197
        """
 
198
        result = (super(QuestionPillarAffiliation, self)
 
199
                                ._getAffiliationDetails(person, pillar))
261
200
        target = self.context.target
262
201
        if IDistributionSourcePackage.providedBy(target):
263
202
            question_targets = (target, target.distribution)
266
205
        questions_person = IQuestionsPerson(person)
267
206
        for target in questions_person.getDirectAnswerQuestionTargets():
268
207
            if target in question_targets:
269
 
                result.append(
270
 
                    BadgeDetails(
271
 
                        self.getIconUrl(pillars[0]),
272
 
                        target.displayname, 'answer contact'))
 
208
                result.append((target.displayname, 'answer contact'))
273
209
        for target in questions_person.getTeamAnswerQuestionTargets():
274
210
            if target in question_targets:
275
 
                result.append(
276
 
                    BadgeDetails(
277
 
                        self.getIconUrl(pillars[0]),
278
 
                        target.displayname, 'answer contact'))
 
211
                result.append((target.displayname, 'answer contact'))
279
212
        return result