~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/lp/code/model/branchtarget.py

add revno to main template

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright 2009 Canonical Ltd.  This software is licensed under the
2
 
# GNU Affero General Public License version 3 (see the file LICENSE).
3
 
 
4
 
"""Branch targets."""
5
 
 
6
 
__metaclass__ = type
7
 
__all__ = [
8
 
    'branch_to_target',
9
 
    'PackageBranchTarget',
10
 
    'PersonBranchTarget',
11
 
    'ProductBranchTarget',
12
 
    ]
13
 
 
14
 
from operator import attrgetter
15
 
 
16
 
from zope.component import getUtility
17
 
from zope.interface import implements
18
 
from zope.security.proxy import isinstance as zope_isinstance
19
 
 
20
 
from lp.code.errors import NoLinkedBranch
21
 
from lp.code.interfaces.branchcollection import IAllBranches
22
 
from lp.code.interfaces.branchtarget import (
23
 
    check_default_stacked_on,
24
 
    IBranchTarget,
25
 
    )
26
 
from lp.code.interfaces.codeimport import ICodeImportSet
27
 
from lp.code.interfaces.linkedbranch import get_linked_to_branch
28
 
from lp.registry.interfaces.pocket import PackagePublishingPocket
29
 
from lp.registry.interfaces.series import SeriesStatus
30
 
from lp.services.webapp.authorization import check_permission
31
 
from lp.services.webapp.interfaces import ICanonicalUrlData
32
 
from lp.services.webapp.sorting import sorted_version_numbers
33
 
 
34
 
 
35
 
def branch_to_target(branch):
36
 
    """Adapt an IBranch to an IBranchTarget."""
37
 
    return branch.target
38
 
 
39
 
 
40
 
class _BaseBranchTarget:
41
 
 
42
 
    def __eq__(self, other):
43
 
        return self.context == other.context
44
 
 
45
 
    def __ne__(self, other):
46
 
        return self.context != other.context
47
 
 
48
 
    def newCodeImport(self, registrant, branch_name, rcs_type, url=None,
49
 
                      cvs_root=None, cvs_module=None, owner=None):
50
 
        """See `IBranchTarget`."""
51
 
        return getUtility(ICodeImportSet).new(
52
 
            registrant, self, branch_name, rcs_type, url=url,
53
 
            cvs_root=cvs_root, cvs_module=cvs_module, owner=owner)
54
 
 
55
 
    def getRelatedSeriesBranchInfo(self, parent_branch, limit_results=None):
56
 
        """See `IBranchTarget`."""
57
 
        return []
58
 
 
59
 
    def getRelatedPackageBranchInfo(self, parent_branch, limit_results=None):
60
 
        """See `IBranchTarget`."""
61
 
        return []
62
 
 
63
 
 
64
 
class PackageBranchTarget(_BaseBranchTarget):
65
 
    implements(IBranchTarget)
66
 
 
67
 
    def __init__(self, sourcepackage):
68
 
        self.sourcepackage = sourcepackage
69
 
 
70
 
    @property
71
 
    def name(self):
72
 
        """See `IBranchTarget`."""
73
 
        return self.sourcepackage.path
74
 
 
75
 
    @property
76
 
    def components(self):
77
 
        """See `IBranchTarget`."""
78
 
        return [
79
 
            self.sourcepackage.distribution,
80
 
            self.sourcepackage.distroseries,
81
 
            self.sourcepackage,
82
 
            ]
83
 
 
84
 
    @property
85
 
    def context(self):
86
 
        """See `IBranchTarget`."""
87
 
        return self.sourcepackage
88
 
 
89
 
    def getNamespace(self, owner):
90
 
        """See `IBranchTarget`."""
91
 
        from lp.code.model.branchnamespace import (
92
 
            PackageNamespace)
93
 
        return PackageNamespace(owner, self.sourcepackage)
94
 
 
95
 
    @property
96
 
    def collection(self):
97
 
        """See `IBranchTarget`."""
98
 
        return getUtility(IAllBranches).inSourcePackage(self.sourcepackage)
99
 
 
100
 
    @property
101
 
    def default_stacked_on_branch(self):
102
 
        """See `IBranchTarget`."""
103
 
        return check_default_stacked_on(
104
 
            self.sourcepackage.development_version.getBranch(
105
 
                PackagePublishingPocket.RELEASE))
106
 
 
107
 
    @property
108
 
    def default_merge_target(self):
109
 
        """See `IBranchTarget`."""
110
 
        return self.sourcepackage.getBranch(PackagePublishingPocket.RELEASE)
111
 
 
112
 
    @property
113
 
    def displayname(self):
114
 
        """See `IBranchTarget`."""
115
 
        return self.sourcepackage.displayname
116
 
 
117
 
    @property
118
 
    def supports_merge_proposals(self):
119
 
        """See `IBranchTarget`."""
120
 
        return True
121
 
 
122
 
    @property
123
 
    def supports_short_identites(self):
124
 
        """See `IBranchTarget`."""
125
 
        return True
126
 
 
127
 
    @property
128
 
    def supports_code_imports(self):
129
 
        """See `IBranchTarget`."""
130
 
        return True
131
 
 
132
 
    def areBranchesMergeable(self, other_target):
133
 
        """See `IBranchTarget`."""
134
 
        # Branches are mergable into a PackageTarget if the source package
135
 
        # name is the same, or the branch is associated with the linked
136
 
        # product.
137
 
        if zope_isinstance(other_target, PackageBranchTarget):
138
 
            my_sourcepackagename = self.context.sourcepackagename
139
 
            other_sourcepackagename = other_target.context.sourcepackagename
140
 
            return my_sourcepackagename == other_sourcepackagename
141
 
        elif zope_isinstance(other_target, ProductBranchTarget):
142
 
            # If the sourcepackage has a related product, then branches of
143
 
            # that product are mergeable.
144
 
            product_series = self.sourcepackage.productseries
145
 
            if product_series is None:
146
 
                return False
147
 
            else:
148
 
                return other_target.context == product_series.product
149
 
        else:
150
 
            return False
151
 
 
152
 
    def assignKarma(self, person, action_name, date_created=None):
153
 
        """See `IBranchTarget`."""
154
 
        return person.assignKarma(
155
 
            action_name,
156
 
            distribution=self.context.distribution,
157
 
            sourcepackagename=self.context.sourcepackagename,
158
 
            datecreated=date_created)
159
 
 
160
 
    def getBugTask(self, bug):
161
 
        """See `IBranchTarget`."""
162
 
        # XXX: rockstar - See bug 397251.  Basically, source packages may have
163
 
        # specific bug tasks.  This should return those specific bugtasks in
164
 
        # those cases.
165
 
        return bug.default_bugtask
166
 
 
167
 
    def _retargetBranch(self, branch):
168
 
        """Set the branch target to refer to this target.
169
 
 
170
 
        This only updates the target related attributes of the branch, and
171
 
        expects a branch without a security proxy as a parameter.
172
 
        """
173
 
        branch.product = None
174
 
        branch.distroseries = self.sourcepackage.distroseries
175
 
        branch.sourcepackagename = self.sourcepackage.sourcepackagename
176
 
 
177
 
    def getRelatedPackageBranchInfo(self, parent_branch, limit_results=None):
178
 
        """See `IBranchTarget`."""
179
 
        result = []
180
 
        linked_branches = self.sourcepackage.linkedBranches()
181
 
        distroseries = self.sourcepackage.distroseries
182
 
        result.extend(
183
 
                [(branch, distroseries)
184
 
                 for branch in linked_branches.values()
185
 
                 if (check_permission('launchpad.View', branch) and
186
 
                    branch != parent_branch and
187
 
                    distroseries.status != SeriesStatus.OBSOLETE)])
188
 
 
189
 
        result = sorted_version_numbers(result, key=lambda branch_info: (
190
 
                    getattr(branch_info[1], 'name')))
191
 
 
192
 
        if limit_results is not None:
193
 
            # We only want the most recent branches
194
 
            result = result[:limit_results]
195
 
        return result
196
 
 
197
 
 
198
 
class PersonBranchTarget(_BaseBranchTarget):
199
 
    implements(IBranchTarget)
200
 
 
201
 
    name = u'+junk'
202
 
    default_stacked_on_branch = None
203
 
    default_merge_target = None
204
 
 
205
 
    def __init__(self, person):
206
 
        self.person = person
207
 
 
208
 
    @property
209
 
    def components(self):
210
 
        """See `IBranchTarget`."""
211
 
        return [self.person]
212
 
 
213
 
    @property
214
 
    def context(self):
215
 
        """See `IBranchTarget`."""
216
 
        return self.person
217
 
 
218
 
    @property
219
 
    def displayname(self):
220
 
        """See `IBranchTarget`."""
221
 
        return "~%s/+junk" % self.person.name
222
 
 
223
 
    def getNamespace(self, owner):
224
 
        """See `IBranchTarget`."""
225
 
        from lp.code.model.branchnamespace import (
226
 
            PersonalNamespace)
227
 
        return PersonalNamespace(owner)
228
 
 
229
 
    @property
230
 
    def collection(self):
231
 
        """See `IBranchTarget`."""
232
 
        return getUtility(IAllBranches).ownedBy(self.person).isJunk()
233
 
 
234
 
    @property
235
 
    def supports_merge_proposals(self):
236
 
        """See `IBranchTarget`."""
237
 
        return False
238
 
 
239
 
    @property
240
 
    def supports_short_identites(self):
241
 
        """See `IBranchTarget`."""
242
 
        return False
243
 
 
244
 
    @property
245
 
    def supports_code_imports(self):
246
 
        """See `IBranchTarget`."""
247
 
        return False
248
 
 
249
 
    def areBranchesMergeable(self, other_target):
250
 
        """See `IBranchTarget`."""
251
 
        return False
252
 
 
253
 
    def assignKarma(self, person, action_name, date_created=None):
254
 
        """See `IBranchTarget`."""
255
 
        # Does nothing. No karma for +junk.
256
 
        return None
257
 
 
258
 
    def getBugTask(self, bug):
259
 
        """See `IBranchTarget`."""
260
 
        return bug.default_bugtask
261
 
 
262
 
    def _retargetBranch(self, branch):
263
 
        """Set the branch target to refer to this target.
264
 
 
265
 
        This only updates the target related attributes of the branch, and
266
 
        expects a branch without a security proxy as a parameter.
267
 
        """
268
 
        branch.product = None
269
 
        branch.distroseries = None
270
 
        branch.sourcepackagename = None
271
 
 
272
 
 
273
 
class ProductBranchTarget(_BaseBranchTarget):
274
 
    implements(IBranchTarget)
275
 
 
276
 
    def __init__(self, product):
277
 
        self.product = product
278
 
 
279
 
    @property
280
 
    def components(self):
281
 
        """See `IBranchTarget`."""
282
 
        return [self.product]
283
 
 
284
 
    @property
285
 
    def context(self):
286
 
        """See `IBranchTarget`."""
287
 
        return self.product
288
 
 
289
 
    @property
290
 
    def displayname(self):
291
 
        """See `IBranchTarget`."""
292
 
        return self.product.displayname
293
 
 
294
 
    @property
295
 
    def name(self):
296
 
        """See `IBranchTarget`."""
297
 
        return self.product.name
298
 
 
299
 
    @property
300
 
    def default_stacked_on_branch(self):
301
 
        """See `IBranchTarget`."""
302
 
        return check_default_stacked_on(self.product.development_focus.branch)
303
 
 
304
 
    @property
305
 
    def default_merge_target(self):
306
 
        """See `IBranchTarget`."""
307
 
        return self.product.development_focus.branch
308
 
 
309
 
    def getNamespace(self, owner):
310
 
        """See `IBranchTarget`."""
311
 
        from lp.code.model.branchnamespace import (
312
 
            ProductNamespace)
313
 
        return ProductNamespace(owner, self.product)
314
 
 
315
 
    @property
316
 
    def collection(self):
317
 
        """See `IBranchTarget`."""
318
 
        return getUtility(IAllBranches).inProduct(self.product)
319
 
 
320
 
    @property
321
 
    def supports_merge_proposals(self):
322
 
        """See `IBranchTarget`."""
323
 
        return True
324
 
 
325
 
    @property
326
 
    def supports_short_identites(self):
327
 
        """See `IBranchTarget`."""
328
 
        return True
329
 
 
330
 
    @property
331
 
    def supports_code_imports(self):
332
 
        """See `IBranchTarget`."""
333
 
        return True
334
 
 
335
 
    def areBranchesMergeable(self, other_target):
336
 
        """See `IBranchTarget`."""
337
 
        # Branches are mergable into a PackageTarget if the source package
338
 
        # name is the same, or the branch is associated with the linked
339
 
        # product.
340
 
        if zope_isinstance(other_target, ProductBranchTarget):
341
 
            return self.product == other_target.context
342
 
        elif zope_isinstance(other_target, PackageBranchTarget):
343
 
            # If the sourcepackage has a related product, and that product is
344
 
            # the same as ours, then the branches are mergeable.
345
 
            product_series = other_target.context.productseries
346
 
            if product_series is None:
347
 
                return False
348
 
            else:
349
 
                return self.product == product_series.product
350
 
        else:
351
 
            return False
352
 
 
353
 
    def assignKarma(self, person, action_name, date_created=None):
354
 
        """See `IBranchTarget`."""
355
 
        return person.assignKarma(
356
 
            action_name, product=self.product, datecreated=date_created)
357
 
 
358
 
    def getBugTask(self, bug):
359
 
        """See `IBranchTarget`."""
360
 
        task = bug.getBugTask(self.product)
361
 
        if task is None:
362
 
            # Just choose the first task for the bug.
363
 
            task = bug.bugtasks[0]
364
 
        return task
365
 
 
366
 
    def _retargetBranch(self, branch):
367
 
        """Set the branch target to refer to this target.
368
 
 
369
 
        This only updates the target related attributes of the branch, and
370
 
        expects a branch without a security proxy as a parameter.
371
 
        """
372
 
        branch.product = self.product
373
 
        branch.distroseries = None
374
 
        branch.sourcepackagename = None
375
 
 
376
 
    def getRelatedSeriesBranchInfo(self, parent_branch, limit_results=None):
377
 
        """See `IBranchTarget`."""
378
 
        sorted_series = []
379
 
        for series in self.product.series:
380
 
            if (series.status != SeriesStatus.OBSOLETE
381
 
                and series != self.product.development_focus):
382
 
                sorted_series.append(series)
383
 
        # Now sort the list by name with newer versions before older.
384
 
        sorted_series = sorted_version_numbers(sorted_series,
385
 
                                             key=attrgetter('name'))
386
 
        # Add the development focus first.
387
 
        sorted_series.insert(0, self.product.development_focus)
388
 
 
389
 
        result = []
390
 
        for series in sorted_series:
391
 
            try:
392
 
                branch = get_linked_to_branch(series).branch
393
 
                if (branch not in result and branch != parent_branch and
394
 
                    check_permission('launchpad.View', branch)):
395
 
                    result.append((branch, series))
396
 
            except NoLinkedBranch:
397
 
                # If there's no branch for a particular series, we don't care.
398
 
                pass
399
 
 
400
 
        if limit_results is not None:
401
 
            # We only want the most recent branches
402
 
            result = result[:limit_results]
403
 
        return result
404
 
 
405
 
    def getRelatedPackageBranchInfo(self, parent_branch, limit_results=None):
406
 
        """See `IBranchTarget`."""
407
 
        result = []
408
 
        for distrosourcepackage in self.product.distrosourcepackages:
409
 
            try:
410
 
                branch = get_linked_to_branch(distrosourcepackage).branch
411
 
                series = distrosourcepackage.distribution.currentseries
412
 
                if (branch != parent_branch and series is not None and
413
 
                    check_permission('launchpad.View', branch)):
414
 
                        result.append((branch, series))
415
 
            except NoLinkedBranch:
416
 
                # If there's no branch for a particular source package,
417
 
                # we don't care.
418
 
                pass
419
 
 
420
 
        result = sorted_version_numbers(result, key=lambda branch_info: (
421
 
                    getattr(branch_info[1], 'name')))
422
 
 
423
 
        if limit_results is not None:
424
 
            # We only want the most recent branches
425
 
            result = result[:limit_results]
426
 
        return result
427
 
 
428
 
 
429
 
def get_canonical_url_data_for_target(branch_target):
430
 
    """Return the `ICanonicalUrlData` for an `IBranchTarget`."""
431
 
    return ICanonicalUrlData(branch_target.context)
432
 
 
433
 
 
434
 
def product_series_to_branch_target(product_series):
435
 
    """The Product itself is the branch target given a ProductSeries."""
436
 
    return ProductBranchTarget(product_series.product)
437
 
 
438
 
 
439
 
def distribution_sourcepackage_to_branch_target(distro_sourcepackage):
440
 
    """The development version of the distro sourcepackage is the target."""
441
 
    dev_version = distro_sourcepackage.development_version
442
 
    # It is possible for distributions to not have any series, and if that is
443
 
    # the case, the dev_version is None.
444
 
    if dev_version is None:
445
 
        return None
446
 
    return PackageBranchTarget(dev_version)