~launchpad-pqm/launchpad/devel

8687.15.17 by Karl Fogel
Add the copyright header block to the rest of the files under lib/lp/.
1
# Copyright 2009 Canonical Ltd.  This software is licensed under the
2
# GNU Affero General Public License version 3 (see the file LICENSE).
7362.4.13 by Jonathan Lange
Add a basic context variable, call it "container" to avoid decorates() confusion.
3
7779.1.2 by Jonathan Lange
Rename the modules to branchtarget from branchcontainer
4
"""Branch targets."""
7362.4.13 by Jonathan Lange
Add a basic context variable, call it "container" to avoid decorates() confusion.
5
6
__metaclass__ = type
7
__all__ = [
7849.5.3 by Jonathan Lange
Make IBranchTarget the IPrimaryContext for a branch.
8
    'branch_to_target',
7779.1.4 by Jonathan Lange
Rename the implementations to FooBranchTarget.
9
    'PackageBranchTarget',
10
    'PersonBranchTarget',
11
    'ProductBranchTarget',
7362.4.13 by Jonathan Lange
Add a basic context variable, call it "container" to avoid decorates() confusion.
12
    ]
13
11994.2.21 by Ian Booth
Refactor related branches methods to IBranchTarget and show distro series instead of source package for related package branches
14
from operator import attrgetter
15
8269.7.7 by Paul Hummer
Added getCollection to IBranchTarget
16
from zope.component import getUtility
7362.4.13 by Jonathan Lange
Add a basic context variable, call it "container" to avoid decorates() confusion.
17
from zope.interface import implements
9113.7.4 by Jonathan Lange
Update many imports of pocket.
18
from zope.security.proxy import isinstance as zope_isinstance
7362.4.13 by Jonathan Lange
Add a basic context variable, call it "container" to avoid decorates() confusion.
19
11994.2.21 by Ian Booth
Refactor related branches methods to IBranchTarget and show distro series instead of source package for related package branches
20
from lp.code.errors import NoLinkedBranch
8269.7.10 by Paul Hummer
Working on tests
21
from lp.code.interfaces.branchcollection import IAllBranches
7675.122.12 by Tim Penhey
Merge in db-devel and resolve conflicts.
22
from lp.code.interfaces.branchtarget import (
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
23
    check_default_stacked_on,
24
    IBranchTarget,
25
    )
10454.9.2 by James Westby
Add a method to IBranchTarget to allow creating a code import directly.
26
from lp.code.interfaces.codeimport import ICodeImportSet
11994.2.21 by Ian Booth
Refactor related branches methods to IBranchTarget and show distro series instead of source package for related package branches
27
from lp.code.interfaces.linkedbranch import get_linked_to_branch
9113.7.4 by Jonathan Lange
Update many imports of pocket.
28
from lp.registry.interfaces.pocket import PackagePublishingPocket
11994.2.21 by Ian Booth
Refactor related branches methods to IBranchTarget and show distro series instead of source package for related package branches
29
from lp.registry.interfaces.series import SeriesStatus
14612.2.1 by William Grant
format-imports on lib/. So many imports.
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
7362.4.13 by Jonathan Lange
Add a basic context variable, call it "container" to avoid decorates() confusion.
33
34
7849.5.3 by Jonathan Lange
Make IBranchTarget the IPrimaryContext for a branch.
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
10454.9.2 by James Westby
Add a method to IBranchTarget to allow creating a code import directly.
48
    def newCodeImport(self, registrant, branch_name, rcs_type, url=None,
10454.10.9 by James Westby
Merge code-imports-for-source-packages.
49
                      cvs_root=None, cvs_module=None, owner=None):
10454.10.7 by James Westby
Allow specifying an owner in addition to the registrant.
50
        """See `IBranchTarget`."""
10454.9.2 by James Westby
Add a method to IBranchTarget to allow creating a code import directly.
51
        return getUtility(ICodeImportSet).new(
52
            registrant, self, branch_name, rcs_type, url=url,
10454.10.7 by James Westby
Allow specifying an owner in addition to the registrant.
53
            cvs_root=cvs_root, cvs_module=cvs_module, owner=owner)
10454.9.2 by James Westby
Add a method to IBranchTarget to allow creating a code import directly.
54
11994.2.24 by Ian Booth
Add option to limit the related branches results. UI only displays top 5 most recent branches
55
    def getRelatedSeriesBranchInfo(self, parent_branch, limit_results=None):
11994.2.21 by Ian Booth
Refactor related branches methods to IBranchTarget and show distro series instead of source package for related package branches
56
        """See `IBranchTarget`."""
57
        return []
58
11994.2.24 by Ian Booth
Add option to limit the related branches results. UI only displays top 5 most recent branches
59
    def getRelatedPackageBranchInfo(self, parent_branch, limit_results=None):
11994.2.21 by Ian Booth
Refactor related branches methods to IBranchTarget and show distro series instead of source package for related package branches
60
        """See `IBranchTarget`."""
61
        return []
62
7849.5.3 by Jonathan Lange
Make IBranchTarget the IPrimaryContext for a branch.
63
64
class PackageBranchTarget(_BaseBranchTarget):
7779.1.3 by Jonathan Lange
Rename the interface to IBranchTarget, and expand the module docstring
65
    implements(IBranchTarget)
7362.4.13 by Jonathan Lange
Add a basic context variable, call it "container" to avoid decorates() confusion.
66
7362.13.14 by Jonathan Lange
Simplify PackageContainer by making take a SourcePackage.
67
    def __init__(self, sourcepackage):
68
        self.sourcepackage = sourcepackage
7362.4.13 by Jonathan Lange
Add a basic context variable, call it "container" to avoid decorates() confusion.
69
70
    @property
71
    def name(self):
7779.1.3 by Jonathan Lange
Rename the interface to IBranchTarget, and expand the module docstring
72
        """See `IBranchTarget`."""
7362.13.14 by Jonathan Lange
Simplify PackageContainer by making take a SourcePackage.
73
        return self.sourcepackage.path
7362.4.22 by Jonathan Lange
Rename JunkContainer to the slightly less punful PersonContainer
74
7849.5.1 by Jonathan Lange
Add a 'context' attribute to target.
75
    @property
7847.1.19 by Jonathan Lange
Add a 'components' attribute to branch target, that returns the objects
76
    def components(self):
77
        """See `IBranchTarget`."""
78
        return [
79
            self.sourcepackage.distribution,
80
            self.sourcepackage.distroseries,
81
            self.sourcepackage,
82
            ]
83
84
    @property
7849.5.1 by Jonathan Lange
Add a 'context' attribute to target.
85
    def context(self):
86
        """See `IBranchTarget`."""
87
        return self.sourcepackage
88
7659.3.27 by Aaron Bentley
Get namespace from target container.
89
    def getNamespace(self, owner):
7779.1.3 by Jonathan Lange
Rename the interface to IBranchTarget, and expand the module docstring
90
        """See `IBranchTarget`."""
8138.1.2 by Jonathan Lange
Run migrater over lp.code. Many tests broken and imports failing.
91
        from lp.code.model.branchnamespace import (
7659.3.27 by Aaron Bentley
Get namespace from target container.
92
            PackageNamespace)
93
        return PackageNamespace(owner, self.sourcepackage)
94
8269.7.10 by Paul Hummer
Working on tests
95
    @property
96
    def collection(self):
8269.7.7 by Paul Hummer
Added getCollection to IBranchTarget
97
        """See `IBranchTarget`."""
98
        return getUtility(IAllBranches).inSourcePackage(self.sourcepackage)
99
8031.4.6 by Jonathan Lange
Use the correct stacked-on branch for package branches.
100
    @property
101
    def default_stacked_on_branch(self):
102
        """See `IBranchTarget`."""
8031.4.13 by Jonathan Lange
Use the check for package stacked branches.
103
        return check_default_stacked_on(
104
            self.sourcepackage.development_version.getBranch(
105
                PackagePublishingPocket.RELEASE))
8031.4.6 by Jonathan Lange
Use the correct stacked-on branch for package branches.
106
8120.2.21 by Jonathan Lange
Add a displayname attribute to IBranchTarget.
107
    @property
8377.8.6 by Tim Penhey
Add default_merge_target to IBranchTarget.
108
    def default_merge_target(self):
109
        """See `IBranchTarget`."""
110
        return self.sourcepackage.getBranch(PackagePublishingPocket.RELEASE)
111
112
    @property
8120.2.21 by Jonathan Lange
Add a displayname attribute to IBranchTarget.
113
    def displayname(self):
114
        """See `IBranchTarget`."""
115
        return self.sourcepackage.displayname
116
8377.8.1 by Tim Penhey
Add a supports_merge_proposals attribute to branch namespaces.
117
    @property
118
    def supports_merge_proposals(self):
119
        """See `IBranchTarget`."""
120
        return True
121
10454.9.1 by James Westby
Add a check that the IBranchTarget supports code imports.
122
    @property
9080.8.25 by Tim Penhey
Replace IBranch.is_personal_branch with IBranchTarget.supports_short_identities.
123
    def supports_short_identites(self):
124
        """See `IBranchTarget`."""
125
        return True
126
9080.8.28 by Tim Penhey
Merge devel and resolve conflicts.
127
    @property
10454.9.1 by James Westby
Add a check that the IBranchTarget supports code imports.
128
    def supports_code_imports(self):
129
        """See `IBranchTarget`."""
10454.11.1 by James Westby
Flip the bit on PackageBranchTarget.
130
        return True
10454.9.1 by James Westby
Add a check that the IBranchTarget supports code imports.
131
8377.8.2 by Tim Penhey
add IBranchTarget.areBranchesMergeable and tests.
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
7675.439.13 by Tim Penhey
change how we allocate backdated karma
152
    def assignKarma(self, person, action_name, date_created=None):
8377.8.20 by Tim Penhey
Move assignKarma to IBranchTarget.
153
        """See `IBranchTarget`."""
7675.439.1 by Tim Penhey
Return the karma allocated for the branch target.
154
        return person.assignKarma(
8377.8.20 by Tim Penhey
Move assignKarma to IBranchTarget.
155
            action_name,
156
            distribution=self.context.distribution,
7675.439.13 by Tim Penhey
change how we allocate backdated karma
157
            sourcepackagename=self.context.sourcepackagename,
158
            datecreated=date_created)
8377.8.20 by Tim Penhey
Move assignKarma to IBranchTarget.
159
8752.4.21 by Paul Hummer
Responded to review
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
8971.24.27 by Tim Penhey
Updates following review. Just some more tests for error conditions to go.
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
8971.24.4 by Tim Penhey
Added BranchTarget.retargetBranch.
176
11994.2.24 by Ian Booth
Add option to limit the related branches results. UI only displays top 5 most recent branches
177
    def getRelatedPackageBranchInfo(self, parent_branch, limit_results=None):
11994.2.21 by Ian Booth
Refactor related branches methods to IBranchTarget and show distro series instead of source package for related package branches
178
        """See `IBranchTarget`."""
179
        result = []
180
        linked_branches = self.sourcepackage.linkedBranches()
11994.2.23 by Ian Booth
Some refactoring
181
        distroseries = self.sourcepackage.distroseries
11994.2.21 by Ian Booth
Refactor related branches methods to IBranchTarget and show distro series instead of source package for related package branches
182
        result.extend(
11994.2.23 by Ian Booth
Some refactoring
183
                [(branch, distroseries)
11994.2.21 by Ian Booth
Refactor related branches methods to IBranchTarget and show distro series instead of source package for related package branches
184
                 for branch in linked_branches.values()
185
                 if (check_permission('launchpad.View', branch) and
11994.2.23 by Ian Booth
Some refactoring
186
                    branch != parent_branch and
187
                    distroseries.status != SeriesStatus.OBSOLETE)])
11994.2.21 by Ian Booth
Refactor related branches methods to IBranchTarget and show distro series instead of source package for related package branches
188
11994.2.24 by Ian Booth
Add option to limit the related branches results. UI only displays top 5 most recent branches
189
        result = sorted_version_numbers(result, key=lambda branch_info: (
11994.2.21 by Ian Booth
Refactor related branches methods to IBranchTarget and show distro series instead of source package for related package branches
190
                    getattr(branch_info[1], 'name')))
191
11994.2.24 by Ian Booth
Add option to limit the related branches results. UI only displays top 5 most recent branches
192
        if limit_results is not None:
193
            # We only want the most recent branches
194
            result = result[:limit_results]
195
        return result
196
7362.4.22 by Jonathan Lange
Rename JunkContainer to the slightly less punful PersonContainer
197
7849.5.3 by Jonathan Lange
Make IBranchTarget the IPrimaryContext for a branch.
198
class PersonBranchTarget(_BaseBranchTarget):
7779.1.3 by Jonathan Lange
Rename the interface to IBranchTarget, and expand the module docstring
199
    implements(IBranchTarget)
7362.4.22 by Jonathan Lange
Rename JunkContainer to the slightly less punful PersonContainer
200
12599.4.2 by Leonard Richardson
Merge from trunk.
201
    name = u'+junk'
8031.2.2 by Jonathan Lange
Junk branches -- easy.
202
    default_stacked_on_branch = None
8377.8.6 by Tim Penhey
Add default_merge_target to IBranchTarget.
203
    default_merge_target = None
7362.4.22 by Jonathan Lange
Rename JunkContainer to the slightly less punful PersonContainer
204
205
    def __init__(self, person):
7362.4.27 by Jonathan Lange
The lower thread no longer has the getBranches stuff. We'll keep it here.
206
        self.person = person
207
7849.5.1 by Jonathan Lange
Add a 'context' attribute to target.
208
    @property
7847.1.19 by Jonathan Lange
Add a 'components' attribute to branch target, that returns the objects
209
    def components(self):
210
        """See `IBranchTarget`."""
211
        return [self.person]
212
213
    @property
7849.5.1 by Jonathan Lange
Add a 'context' attribute to target.
214
    def context(self):
215
        """See `IBranchTarget`."""
216
        return self.person
217
8120.2.21 by Jonathan Lange
Add a displayname attribute to IBranchTarget.
218
    @property
219
    def displayname(self):
220
        """See `IBranchTarget`."""
221
        return "~%s/+junk" % self.person.name
222
7659.3.27 by Aaron Bentley
Get namespace from target container.
223
    def getNamespace(self, owner):
7779.1.3 by Jonathan Lange
Rename the interface to IBranchTarget, and expand the module docstring
224
        """See `IBranchTarget`."""
8138.1.2 by Jonathan Lange
Run migrater over lp.code. Many tests broken and imports failing.
225
        from lp.code.model.branchnamespace import (
7659.3.27 by Aaron Bentley
Get namespace from target container.
226
            PersonalNamespace)
227
        return PersonalNamespace(owner)
228
8269.7.10 by Paul Hummer
Working on tests
229
    @property
230
    def collection(self):
8269.7.7 by Paul Hummer
Added getCollection to IBranchTarget
231
        """See `IBranchTarget`."""
8269.7.12 by Paul Hummer
Added isJunk
232
        return getUtility(IAllBranches).ownedBy(self.person).isJunk()
8269.7.7 by Paul Hummer
Added getCollection to IBranchTarget
233
8377.8.1 by Tim Penhey
Add a supports_merge_proposals attribute to branch namespaces.
234
    @property
235
    def supports_merge_proposals(self):
236
        """See `IBranchTarget`."""
237
        return False
238
10454.9.1 by James Westby
Add a check that the IBranchTarget supports code imports.
239
    @property
9080.8.25 by Tim Penhey
Replace IBranch.is_personal_branch with IBranchTarget.supports_short_identities.
240
    def supports_short_identites(self):
241
        """See `IBranchTarget`."""
242
        return False
243
9080.8.28 by Tim Penhey
Merge devel and resolve conflicts.
244
    @property
10454.9.1 by James Westby
Add a check that the IBranchTarget supports code imports.
245
    def supports_code_imports(self):
246
        """See `IBranchTarget`."""
247
        return False
248
8377.8.2 by Tim Penhey
add IBranchTarget.areBranchesMergeable and tests.
249
    def areBranchesMergeable(self, other_target):
250
        """See `IBranchTarget`."""
251
        return False
252
7675.439.13 by Tim Penhey
change how we allocate backdated karma
253
    def assignKarma(self, person, action_name, date_created=None):
8377.8.20 by Tim Penhey
Move assignKarma to IBranchTarget.
254
        """See `IBranchTarget`."""
255
        # Does nothing. No karma for +junk.
7675.439.1 by Tim Penhey
Return the karma allocated for the branch target.
256
        return None
8377.8.20 by Tim Penhey
Move assignKarma to IBranchTarget.
257
8752.4.21 by Paul Hummer
Responded to review
258
    def getBugTask(self, bug):
259
        """See `IBranchTarget`."""
260
        return bug.default_bugtask
261
8971.24.27 by Tim Penhey
Updates following review. Just some more tests for error conditions to go.
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
8971.24.4 by Tim Penhey
Added BranchTarget.retargetBranch.
271
7362.4.23 by Jonathan Lange
Add a ProductContainer.
272
7849.5.3 by Jonathan Lange
Make IBranchTarget the IPrimaryContext for a branch.
273
class ProductBranchTarget(_BaseBranchTarget):
7779.1.3 by Jonathan Lange
Rename the interface to IBranchTarget, and expand the module docstring
274
    implements(IBranchTarget)
7362.4.23 by Jonathan Lange
Add a ProductContainer.
275
276
    def __init__(self, product):
277
        self.product = product
278
279
    @property
7847.1.19 by Jonathan Lange
Add a 'components' attribute to branch target, that returns the objects
280
    def components(self):
281
        """See `IBranchTarget`."""
282
        return [self.product]
283
284
    @property
7849.5.1 by Jonathan Lange
Add a 'context' attribute to target.
285
    def context(self):
286
        """See `IBranchTarget`."""
287
        return self.product
288
289
    @property
8120.2.21 by Jonathan Lange
Add a displayname attribute to IBranchTarget.
290
    def displayname(self):
291
        """See `IBranchTarget`."""
292
        return self.product.displayname
293
294
    @property
7362.4.23 by Jonathan Lange
Add a ProductContainer.
295
    def name(self):
7779.1.3 by Jonathan Lange
Rename the interface to IBranchTarget, and expand the module docstring
296
        """See `IBranchTarget`."""
7362.4.23 by Jonathan Lange
Add a ProductContainer.
297
        return self.product.name
7659.3.27 by Aaron Bentley
Get namespace from target container.
298
8031.2.1 by Jonathan Lange
Move the default stacking product doctests to be branch target unit tests
299
    @property
300
    def default_stacked_on_branch(self):
301
        """See `IBranchTarget`."""
8137.2.1 by Jonathan Lange
Remove backwards compatibility shims for user_branch and series_branch.
302
        return check_default_stacked_on(self.product.development_focus.branch)
8031.2.1 by Jonathan Lange
Move the default stacking product doctests to be branch target unit tests
303
8377.8.6 by Tim Penhey
Add default_merge_target to IBranchTarget.
304
    @property
305
    def default_merge_target(self):
306
        """See `IBranchTarget`."""
307
        return self.product.development_focus.branch
308
7659.3.27 by Aaron Bentley
Get namespace from target container.
309
    def getNamespace(self, owner):
7779.1.3 by Jonathan Lange
Rename the interface to IBranchTarget, and expand the module docstring
310
        """See `IBranchTarget`."""
8138.1.2 by Jonathan Lange
Run migrater over lp.code. Many tests broken and imports failing.
311
        from lp.code.model.branchnamespace import (
7659.3.27 by Aaron Bentley
Get namespace from target container.
312
            ProductNamespace)
313
        return ProductNamespace(owner, self.product)
7847.1.24 by Jonathan Lange
Allow us to get a canonical_url for a branch target.
314
8269.7.10 by Paul Hummer
Working on tests
315
    @property
316
    def collection(self):
8269.7.7 by Paul Hummer
Added getCollection to IBranchTarget
317
        """See `IBranchTarget`."""
318
        return getUtility(IAllBranches).inProduct(self.product)
319
8377.8.1 by Tim Penhey
Add a supports_merge_proposals attribute to branch namespaces.
320
    @property
321
    def supports_merge_proposals(self):
322
        """See `IBranchTarget`."""
323
        return True
324
10454.9.1 by James Westby
Add a check that the IBranchTarget supports code imports.
325
    @property
9080.8.25 by Tim Penhey
Replace IBranch.is_personal_branch with IBranchTarget.supports_short_identities.
326
    def supports_short_identites(self):
327
        """See `IBranchTarget`."""
328
        return True
329
9080.8.28 by Tim Penhey
Merge devel and resolve conflicts.
330
    @property
10454.9.1 by James Westby
Add a check that the IBranchTarget supports code imports.
331
    def supports_code_imports(self):
332
        """See `IBranchTarget`."""
333
        return True
334
8377.8.2 by Tim Penhey
add IBranchTarget.areBranchesMergeable and tests.
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
7675.439.13 by Tim Penhey
change how we allocate backdated karma
353
    def assignKarma(self, person, action_name, date_created=None):
8377.8.20 by Tim Penhey
Move assignKarma to IBranchTarget.
354
        """See `IBranchTarget`."""
7675.439.13 by Tim Penhey
change how we allocate backdated karma
355
        return person.assignKarma(
356
            action_name, product=self.product, datecreated=date_created)
8377.8.20 by Tim Penhey
Move assignKarma to IBranchTarget.
357
8752.4.21 by Paul Hummer
Responded to review
358
    def getBugTask(self, bug):
359
        """See `IBranchTarget`."""
9877.2.9 by Curtis Hovey
Replace get_branch_target_for_productseries with ProductSeriesBranchTarget to avoid
360
        task = bug.getBugTask(self.product)
8752.4.21 by Paul Hummer
Responded to review
361
        if task is None:
362
            # Just choose the first task for the bug.
363
            task = bug.bugtasks[0]
364
        return task
8377.8.20 by Tim Penhey
Move assignKarma to IBranchTarget.
365
8971.24.27 by Tim Penhey
Updates following review. Just some more tests for error conditions to go.
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
8971.24.4 by Tim Penhey
Added BranchTarget.retargetBranch.
375
11994.2.24 by Ian Booth
Add option to limit the related branches results. UI only displays top 5 most recent branches
376
    def getRelatedSeriesBranchInfo(self, parent_branch, limit_results=None):
11994.2.21 by Ian Booth
Refactor related branches methods to IBranchTarget and show distro series instead of source package for related package branches
377
        """See `IBranchTarget`."""
11994.2.23 by Ian Booth
Some refactoring
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
11994.2.21 by Ian Booth
Refactor related branches methods to IBranchTarget and show distro series instead of source package for related package branches
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
11994.2.24 by Ian Booth
Add option to limit the related branches results. UI only displays top 5 most recent branches
400
        if limit_results is not None:
401
            # We only want the most recent branches
402
            result = result[:limit_results]
11994.2.21 by Ian Booth
Refactor related branches methods to IBranchTarget and show distro series instead of source package for related package branches
403
        return result
404
11994.2.24 by Ian Booth
Add option to limit the related branches results. UI only displays top 5 most recent branches
405
    def getRelatedPackageBranchInfo(self, parent_branch, limit_results=None):
11994.2.21 by Ian Booth
Refactor related branches methods to IBranchTarget and show distro series instead of source package for related package branches
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
11994.2.24 by Ian Booth
Add option to limit the related branches results. UI only displays top 5 most recent branches
420
        result = sorted_version_numbers(result, key=lambda branch_info: (
11994.2.21 by Ian Booth
Refactor related branches methods to IBranchTarget and show distro series instead of source package for related package branches
421
                    getattr(branch_info[1], 'name')))
422
11994.2.24 by Ian Booth
Add option to limit the related branches results. UI only displays top 5 most recent branches
423
        if limit_results is not None:
424
            # We only want the most recent branches
425
            result = result[:limit_results]
426
        return result
427
7847.1.24 by Jonathan Lange
Allow us to get a canonical_url for a branch target.
428
429
def get_canonical_url_data_for_target(branch_target):
430
    """Return the `ICanonicalUrlData` for an `IBranchTarget`."""
431
    return ICanonicalUrlData(branch_target.context)
11270.2.13 by Tim Penhey
Add an adapter from product series to branch target.
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)
11270.2.14 by Tim Penhey
Add an adapter for distribution source package to branch target.
437
11994.2.22 by Ian Booth
Lint fixes
438
11270.2.14 by Tim Penhey
Add an adapter for distribution source package to branch target.
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)