Merge lp:~thumper/launchpad/package-branch-listing-speedup into lp:launchpad
- package-branch-listing-speedup
- Merge into devel
Status: | Merged |
---|---|
Approved by: | Michael Hudson-Doyle |
Approved revision: | no longer in the source branch. |
Merged at revision: | not available |
Proposed branch: | lp:~thumper/launchpad/package-branch-listing-speedup |
Merge into: | lp:launchpad |
Diff against target: |
1396 lines (+612/-315) 17 files modified
database/schema/security.cfg (+1/-0) lib/lp/archiveuploader/tests/test_recipeuploads.py (+1/-1) lib/lp/code/browser/branchlisting.py (+5/-36) lib/lp/code/browser/tests/test_branchlisting.py (+1/-0) lib/lp/code/configure.zcml (+2/-0) lib/lp/code/interfaces/branch.py (+76/-47) lib/lp/code/interfaces/branchtarget.py (+3/-0) lib/lp/code/interfaces/linkedbranch.py (+1/-0) lib/lp/code/model/branch.py (+3/-19) lib/lp/code/model/branchtarget.py (+15/-0) lib/lp/code/model/linkedbranch.py (+124/-25) lib/lp/code/model/tests/test_branch.py (+197/-0) lib/lp/code/model/tests/test_branchtarget.py (+12/-0) lib/lp/code/model/tests/test_linkedbranch.py (+144/-0) lib/lp/code/stories/branches/xx-branch-listings.txt (+1/-4) lib/lp/code/templates/branch-listing.pt (+4/-172) lib/lp/testing/factory.py (+22/-11) |
To merge this branch: | bzr merge lp:~thumper/launchpad/package-branch-listing-speedup |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Michael Hudson-Doyle | Approve | ||
Review via email: mp+22993@code.launchpad.net |
Commit message
Refactor the bzr_identity calculation.
Description of the change
This branch changes how bzr identities are worked out.
Instead of a chunk of logic sitting in a stand alone function, the algorithm
now says: work out the links a branch has, sort them, and pick the first.
tests:
test_
TestBranchLin
TestBzrIdentity
TestLinkedBra
qa:
look at https:/
and make sure it looks right
changes:
pre and mid impl calls with mwhudson
lib/lp/
* this is mostly simplification
* the BranchListingItem now inherits from BzrIdentityMixin, this is to have
the 'self' variable pointing at the BranchListingItem rather than the
delegated branch object. This allows the associatedProdu
associatedSu
* the is_dev_focus is no longer needed, so the methods to work it out are
removed
lib/lp/
* adds a single test to make sure there are no extra queries executed
to work out the bzr_identity of a BranchListingItem
lib/lp/
* Makes the is_personal_branch, branchIdentities, and branchLinks
methods available through IBranch
lib/lp/
* Removes the bazaar_identity
* Adds the BzrIdentityMixin
lib/lp/
* Makes the context accessible from the object.
lib/lp/
* Implements the is_personal_branch and adds in the bzr identity mixin class.
* The bzr_identity function is removed as it is now provided by the mixin.
lib/lp/
* Link objects are now sortable, so the most 'important' links appear first.
lib/lp/
lib/lp/
* implement the tests mentioned above
lib/lp/
* There were many places where a string was being passed in for a
sourcepackag
sourcepackag
ability to pass in a string for the sourcepackagename, and the
correct object will be created.
Tim Penhey (thumper) wrote : | # |
> Just a few comments below, mostly simple clarity stuff. Thanks for
> fixing the icky bug!
Yeah, it'll be good to get this cleaned up.
> > + def branchIdentites():
> Missing an 'i' here :)
Fixed.
> > + """A list of aliases for a branch.
> > +
> > + Returns a list of tuples of bzr identity and context object.
> > There is + at least one alias for any branch, and that is the
> > branch itself. For + linked branches, the context object is the
> > appropriate linked object. + """
>
> I'm afraid these two docstrings both need work. The first doesn't
> explain which ICanHasLinkedBranch objects are returned, nor what being
> 'sorted' means in this context.
>
> The second I think needs to explain itself a bit more as well. It
> seems to blur the concept of 'alias' (a concept we don't have anywhere
> else) and the linked object that the alias is derived from. Can you
> just try to expand it a bit, maybe with an example or two?
>
> In this branch it's not really obvious why branchLinks and
> branchIdentities have a separate existence to the bzr_identity
> property. I guess this is preparation for that portlet you want to
> add? I hope if the implementation of that doesn't require all these
> variants we can shrink them down a bit.
Updated.
> > === modified file 'lib/lp/
> > + # When a project gets the series they are ordered
> > alphabetically + # by name.
>
> I don't think this sentence makes sense.
>
> I don't suppose it really matters, but sorting series by name isn't
> how it's usually done in Launchpad; sorting trunk first and then by
> comparing date_created is more common I think?
I looked at the product code to see how it returned them. Generally it just
sorted alphabetically, which I think is OK.
Comment updated too.
> > === modified file 'lib/lp/
> > +class TestBranchLinks
> > + """Test IBranch.branchLinks and IBranch.
> > +
> > + layer = DatabaseFunctio
> > +
> > + def test_default_
> > + # If there are no links, the branch identities is just the
> > unique + # name.
>
> This sentence isn't very grammatical, maybe "If there are no links,
> the only branch identity is the unique name"?
Updated.
> > + def test_linked_
> > + # If a branch is linked to a non-development series of a product
> > and + # not linked to the product itself, then the product is not
> > returned + # in the links.
>
> I think the more positive "only the product series is returned in the
> links" is easier to understand than "the product is not returned".
Updated too.
> > + def test_linked_
> > + # If a branch is the development focus branch for a product,
> > then it's + # bzr identity is lp:product.
>
> This comment looks entirely wrong :-)
And this.
> > class TestBzrIdentity
> > """Test IBranch.
>
> You don't test here any branches that are linked to both products and
> sou...
1 | === modified file 'lib/lp/code/configure.zcml' |
2 | --- lib/lp/code/configure.zcml 2010-04-08 02:08:15 +0000 |
3 | +++ lib/lp/code/configure.zcml 2010-04-09 01:30:22 +0000 |
4 | @@ -402,7 +402,6 @@ |
5 | composePublicURL |
6 | whiteboard |
7 | target |
8 | - is_personal_branch |
9 | mirror_status_message |
10 | private |
11 | registrant |
12 | |
13 | === modified file 'lib/lp/code/interfaces/branch.py' |
14 | --- lib/lp/code/interfaces/branch.py 2010-04-08 02:56:31 +0000 |
15 | +++ lib/lp/code/interfaces/branch.py 2010-04-09 01:46:52 +0000 |
16 | @@ -496,14 +496,6 @@ |
17 | "None if not a package branch."), |
18 | schema=Interface, required=False, readonly=True)) |
19 | |
20 | - is_personal_branch = exported( |
21 | - Bool( |
22 | - title=_("Personal Branch"), required=False, |
23 | - readonly=True, default=False, |
24 | - description=_( |
25 | - "A branch is a personal branch if it is not associated " |
26 | - "with a project nor a source package."))) |
27 | - |
28 | code_reviewer = Attribute( |
29 | "The reviewer if set, otherwise the owner of the branch.") |
30 | |
31 | @@ -932,14 +924,34 @@ |
32 | """Return the suite source packages that this branch is linked to.""" |
33 | |
34 | def branchLinks(): |
35 | - """Return a sorted list of ICanHasLinkedBranch objects.""" |
36 | - |
37 | - def branchIdentites(): |
38 | + """Return a sorted list of ICanHasLinkedBranch objects. |
39 | + |
40 | + There is one result for each related linked object that the branch is |
41 | + linked to. For example in the case where a branch is linked to the |
42 | + development series of a project, the link objects for both the project |
43 | + and the development series are returned. |
44 | + |
45 | + The sorting uses the defined order of the linked objects where the |
46 | + more important links are sorted first. |
47 | + """ |
48 | + |
49 | + def branchIdentities(): |
50 | """A list of aliases for a branch. |
51 | |
52 | Returns a list of tuples of bzr identity and context object. There is |
53 | at least one alias for any branch, and that is the branch itself. For |
54 | linked branches, the context object is the appropriate linked object. |
55 | + |
56 | + Where a branch is linked to a product series or a suite source |
57 | + package, the branch is available through a number of different urls. |
58 | + These urls are the aliases for the branch. |
59 | + |
60 | + For example, a branch linked to the development focus of the 'fooix' |
61 | + project is accessible using: |
62 | + lp:fooix - the linked object is the product fooix |
63 | + lp:fooix/trunk - the linked object is the trunk series of fooix |
64 | + lp:~owner/fooix/name - the unique name of the branch where the linked |
65 | + object is the branch itself. |
66 | """ |
67 | |
68 | # subscription-related methods |
69 | @@ -1337,7 +1349,7 @@ |
70 | def branchIdentities(self): |
71 | """See `IBranch`.""" |
72 | lp_prefix = config.codehosting.bzr_lp_prefix |
73 | - if self.private or self.is_personal_branch: |
74 | + if self.private or not self.target.supports_short_identites: |
75 | # XXX: thumper 2010-04-08, bug 261609 |
76 | # We have to get around to fixing this |
77 | identities = [] |
78 | |
79 | === modified file 'lib/lp/code/interfaces/branchtarget.py' |
80 | --- lib/lp/code/interfaces/branchtarget.py 2010-01-05 08:18:23 +0000 |
81 | +++ lib/lp/code/interfaces/branchtarget.py 2010-04-09 01:30:22 +0000 |
82 | @@ -88,6 +88,9 @@ |
83 | supports_merge_proposals = Attribute( |
84 | "Does this target support merge proposals at all?") |
85 | |
86 | + supports_short_identites = Attribute( |
87 | + "Does this target support shortened bazaar identities?") |
88 | + |
89 | def areBranchesMergeable(other_target): |
90 | """Are branches from other_target mergeable into this target.""" |
91 | |
92 | |
93 | === modified file 'lib/lp/code/model/branch.py' |
94 | --- lib/lp/code/model/branch.py 2010-04-08 02:08:45 +0000 |
95 | +++ lib/lp/code/model/branch.py 2010-04-09 01:30:22 +0000 |
96 | @@ -178,10 +178,6 @@ |
97 | target = self.product |
98 | return IBranchTarget(target) |
99 | |
100 | - @property |
101 | - def is_personal_branch(self): |
102 | - return self.product is None and self.distroseries is None |
103 | - |
104 | def setTarget(self, user, project=None, source_package=None): |
105 | """See `IBranch`.""" |
106 | if project is not None: |
107 | |
108 | === modified file 'lib/lp/code/model/branchtarget.py' |
109 | --- lib/lp/code/model/branchtarget.py 2010-01-05 08:18:23 +0000 |
110 | +++ lib/lp/code/model/branchtarget.py 2010-04-09 01:30:22 +0000 |
111 | @@ -95,6 +95,11 @@ |
112 | """See `IBranchTarget`.""" |
113 | return True |
114 | |
115 | + @property |
116 | + def supports_short_identites(self): |
117 | + """See `IBranchTarget`.""" |
118 | + return True |
119 | + |
120 | def areBranchesMergeable(self, other_target): |
121 | """See `IBranchTarget`.""" |
122 | # Branches are mergable into a PackageTarget if the source package |
123 | @@ -182,6 +187,11 @@ |
124 | """See `IBranchTarget`.""" |
125 | return False |
126 | |
127 | + @property |
128 | + def supports_short_identites(self): |
129 | + """See `IBranchTarget`.""" |
130 | + return False |
131 | + |
132 | def areBranchesMergeable(self, other_target): |
133 | """See `IBranchTarget`.""" |
134 | return False |
135 | @@ -258,6 +268,11 @@ |
136 | """See `IBranchTarget`.""" |
137 | return True |
138 | |
139 | + @property |
140 | + def supports_short_identites(self): |
141 | + """See `IBranchTarget`.""" |
142 | + return True |
143 | + |
144 | def areBranchesMergeable(self, other_target): |
145 | """See `IBranchTarget`.""" |
146 | # Branches are mergable into a PackageTarget if the source package |
147 | |
148 | === modified file 'lib/lp/code/model/linkedbranch.py' |
149 | --- lib/lp/code/model/linkedbranch.py 2010-04-08 00:40:21 +0000 |
150 | +++ lib/lp/code/model/linkedbranch.py 2010-04-09 01:46:52 +0000 |
151 | @@ -62,8 +62,8 @@ |
152 | if result != 0: |
153 | return result |
154 | else: |
155 | - # When a project gets the series they are ordered alphabetically |
156 | - # by name. |
157 | + # The sorting of the product series uses the same sorting the |
158 | + # product itself uses, which is alphabetically by name. |
159 | my_parts = ( |
160 | self.product_series.product.name, |
161 | self.product_series.name) |
162 | |
163 | === modified file 'lib/lp/code/model/tests/test_branch.py' |
164 | --- lib/lp/code/model/tests/test_branch.py 2010-04-08 02:07:15 +0000 |
165 | +++ lib/lp/code/model/tests/test_branch.py 2010-04-09 01:46:52 +0000 |
166 | @@ -489,8 +489,7 @@ |
167 | layer = DatabaseFunctionalLayer |
168 | |
169 | def test_default_identities(self): |
170 | - # If there are no links, the branch identities is just the unique |
171 | - # name. |
172 | + # If there are no links, the only branch identity is the unique name. |
173 | branch = self.factory.makeAnyBranch() |
174 | self.assertEqual( |
175 | [('lp://dev/' + branch.unique_name, branch)], |
176 | @@ -517,8 +516,8 @@ |
177 | |
178 | def test_linked_to_product_series(self): |
179 | # If a branch is linked to a non-development series of a product and |
180 | - # not linked to the product itself, then the product is not returned |
181 | - # in the links. |
182 | + # not linked to the product itself, then only the product series is |
183 | + # returned in the links. |
184 | fooix = removeSecurityProxy(self.factory.makeProduct(name='fooix')) |
185 | future = self.factory.makeProductSeries(product=fooix, name='future') |
186 | eric = self.factory.makePerson(name='eric') |
187 | |
188 | === modified file 'lib/lp/code/model/tests/test_branchtarget.py' |
189 | --- lib/lp/code/model/tests/test_branchtarget.py 2010-03-18 15:39:58 +0000 |
190 | +++ lib/lp/code/model/tests/test_branchtarget.py 2010-04-09 01:30:22 +0000 |
191 | @@ -121,6 +121,10 @@ |
192 | # Package branches do support merge proposals. |
193 | self.assertTrue(self.target.supports_merge_proposals) |
194 | |
195 | + def test_supports_short_identites(self): |
196 | + # Package branches do support short bzr identites. |
197 | + self.assertTrue(self.target.supports_short_identites) |
198 | + |
199 | def test_displayname(self): |
200 | # The display name of a source package target is the display name of |
201 | # the source package. |
202 | @@ -217,6 +221,10 @@ |
203 | # Personal branches do not support merge proposals. |
204 | self.assertFalse(self.target.supports_merge_proposals) |
205 | |
206 | + def test_supports_short_identites(self): |
207 | + # Personal branches do not support short bzr identites. |
208 | + self.assertFalse(self.target.supports_short_identites) |
209 | + |
210 | def test_displayname(self): |
211 | # The display name of a person branch target is ~$USER/+junk. |
212 | target = IBranchTarget(self.original) |
213 | @@ -320,6 +328,10 @@ |
214 | # Product branches do support merge proposals. |
215 | self.assertTrue(self.target.supports_merge_proposals) |
216 | |
217 | + def test_supports_short_identites(self): |
218 | + # Product branches do support short bzr identites. |
219 | + self.assertTrue(self.target.supports_short_identites) |
220 | + |
221 | def test_displayname(self): |
222 | # The display name of a product branch target is the display name of |
223 | # the product. |
224 | |
225 | === modified file 'lib/lp/code/model/tests/test_linkedbranch.py' |
226 | --- lib/lp/code/model/tests/test_linkedbranch.py 2010-04-07 02:51:37 +0000 |
227 | +++ lib/lp/code/model/tests/test_linkedbranch.py 2010-04-09 01:46:52 +0000 |
228 | @@ -229,8 +229,8 @@ |
229 | |
230 | def test_product_sort(self): |
231 | # If in the extremely unlikely event we have one branch linked as the |
232 | - # trunk of two different products (you never know), then the sorting |
233 | - # reverts to the name of the product. |
234 | + # trunk of two or more different products (you never know), then the |
235 | + # sorting reverts to the name of the product. |
236 | aardvark_link = ICanHasLinkedBranch( |
237 | self.factory.makeProduct(name='aardvark')) |
238 | meerkat_link = ICanHasLinkedBranch( |
239 | |
240 | === modified file 'lib/lp/code/stories/webservice/xx-branch.txt' |
241 | --- lib/lp/code/stories/webservice/xx-branch.txt 2010-04-08 02:19:13 +0000 |
242 | +++ lib/lp/code/stories/webservice/xx-branch.txt 2010-04-09 01:30:22 +0000 |
243 | @@ -115,7 +115,6 @@ |
244 | dependent_branches_collection_link: u'.../~eric/fooix/trunk/dependent_branches' |
245 | description: None |
246 | display_name: u'lp://dev/~eric/fooix/trunk' |
247 | - is_personal_branch: False |
248 | landing_candidates_collection_link: u'.../~eric/fooix/trunk/landing_candidates' |
249 | landing_targets_collection_link: u'.../~eric/fooix/trunk/landing_targets' |
250 | last_mirror_attempt: None |
Preview Diff
1 | === modified file 'database/schema/security.cfg' |
2 | --- database/schema/security.cfg 2010-04-08 08:55:10 +0000 |
3 | +++ database/schema/security.cfg 2010-04-12 05:33:38 +0000 |
4 | @@ -1677,6 +1677,7 @@ |
5 | public.product = SELECT |
6 | public.productseries = SELECT |
7 | public.project = SELECT |
8 | +public.seriessourcepackagebranch = SELECT |
9 | public.sourcepackagename = SELECT |
10 | public.staticdiff = SELECT, INSERT |
11 | public.teamparticipation = SELECT |
12 | |
13 | === modified file 'lib/lp/archiveuploader/tests/test_recipeuploads.py' |
14 | --- lib/lp/archiveuploader/tests/test_recipeuploads.py 2010-03-06 04:57:40 +0000 |
15 | +++ lib/lp/archiveuploader/tests/test_recipeuploads.py 2010-04-12 05:33:38 +0000 |
16 | @@ -33,7 +33,7 @@ |
17 | self.recipe = self.factory.makeSourcePackageRecipe() |
18 | self.build = getUtility(ISourcePackageRecipeBuildSource).new( |
19 | sourcepackage=self.factory.makeSourcePackage( |
20 | - sourcename='bar', distroseries=self.breezy), |
21 | + sourcepackagename='bar', distroseries=self.breezy), |
22 | recipe=self.recipe, archive=self.factory.makeArchive( |
23 | distribution=self.ubuntu, owner=self.recipe.owner), |
24 | requester=self.recipe.owner) |
25 | |
26 | === modified file 'lib/lp/code/browser/branchlisting.py' |
27 | --- lib/lp/code/browser/branchlisting.py 2010-02-16 20:36:48 +0000 |
28 | +++ lib/lp/code/browser/branchlisting.py 2010-04-12 05:33:38 +0000 |
29 | @@ -74,7 +74,7 @@ |
30 | from lp.code.enums import ( |
31 | BranchLifecycleStatus, BranchLifecycleStatusFilter, BranchType) |
32 | from lp.code.interfaces.branch import ( |
33 | - bazaar_identity, DEFAULT_BRANCH_STATUS_IN_LISTING, IBranch, |
34 | + BzrIdentityMixin, DEFAULT_BRANCH_STATUS_IN_LISTING, IBranch, |
35 | IBranchBatchNavigator, IBranchListingQueryOptimiser) |
36 | from lp.code.interfaces.branchcollection import IAllBranches |
37 | from lp.code.interfaces.branchnamespace import IBranchNamespacePolicy |
38 | @@ -137,7 +137,7 @@ |
39 | return HasBadgeBase.getBadge(self, badge_name) |
40 | |
41 | |
42 | -class BranchListingItem(BranchBadges): |
43 | +class BranchListingItem(BzrIdentityMixin, BranchBadges): |
44 | """A decorated branch. |
45 | |
46 | Some attributes that we want to display are too convoluted or expensive |
47 | @@ -148,7 +148,7 @@ |
48 | |
49 | def __init__(self, branch, last_commit, now, show_bug_badge, |
50 | show_blueprint_badge, show_mp_badge, |
51 | - associated_product_series, suite_source_packages, is_dev_focus): |
52 | + associated_product_series, suite_source_packages): |
53 | BranchBadges.__init__(self, branch) |
54 | self.last_commit = last_commit |
55 | self.show_bug_badge = show_bug_badge |
56 | @@ -157,7 +157,6 @@ |
57 | self._now = now |
58 | self.associated_product_series = associated_product_series |
59 | self.suite_source_packages = suite_source_packages |
60 | - self.is_development_focus = is_dev_focus |
61 | |
62 | def associatedProductSeries(self): |
63 | """Override the IBranch.associatedProductSeries.""" |
64 | @@ -173,11 +172,6 @@ |
65 | if series.status != SeriesStatus.OBSOLETE] |
66 | |
67 | @property |
68 | - def bzr_identity(self): |
69 | - """Produce the bzr identity from our known associated series.""" |
70 | - return bazaar_identity(self, self.is_development_focus) |
71 | - |
72 | - @property |
73 | def since_updated(self): |
74 | """How long since the branch was last updated.""" |
75 | return self._now - self.context.date_last_modified |
76 | @@ -397,30 +391,6 @@ |
77 | self._distro_series_map[distribution] = result |
78 | return result |
79 | |
80 | - def isBranchDevFocus(self, branch, |
81 | - associated_product_series, suite_source_packages): |
82 | - """Is the branch the development focus? |
83 | - |
84 | - For product branches this means that the branch is linked to the |
85 | - development focus series. |
86 | - |
87 | - For package branches this means that the branch is linked to the |
88 | - release pocket of the development series. |
89 | - """ |
90 | - # Refactor this code to work for model.branch too? |
91 | - # Do we care if a non-product branch is linked to the product series? |
92 | - # Do we care if a non-package branch is linked to the package? |
93 | - # A) not right now. |
94 | - for series in associated_product_series: |
95 | - if series.product.development_focus == series: |
96 | - return True |
97 | - for ssp in suite_source_packages: |
98 | - if (ssp.pocket == PackagePublishingPocket.RELEASE and |
99 | - ssp.distroseries == self.getDistroDevelSeries( |
100 | - ssp.distribution)): |
101 | - return True |
102 | - return False |
103 | - |
104 | @cachedproperty |
105 | def branch_ids_with_bug_links(self): |
106 | """Return a set of branch ids that should show bug badges.""" |
107 | @@ -470,12 +440,10 @@ |
108 | show_mp_badge = branch.id in self.branch_ids_with_merge_proposals |
109 | associated_product_series = self.getProductSeries(branch) |
110 | suite_source_packages = self.getSuiteSourcePackages(branch) |
111 | - is_dev_focus = self.isBranchDevFocus( |
112 | - branch, associated_product_series, suite_source_packages) |
113 | return BranchListingItem( |
114 | branch, last_commit, self._now, show_bug_badge, |
115 | show_blueprint_badge, show_mp_badge, |
116 | - associated_product_series, suite_source_packages, is_dev_focus) |
117 | + associated_product_series, suite_source_packages) |
118 | |
119 | def decoratedBranches(self, branches): |
120 | """Return the decorated branches for the branches passed in.""" |
121 | @@ -1351,6 +1319,7 @@ |
122 | no_sort_by = (BranchListingSort.DEFAULT,) |
123 | extra_columns = ('author', 'product') |
124 | label_template = 'Bazaar branches of %(displayname)s' |
125 | + show_series_links = True |
126 | |
127 | def _getCollection(self): |
128 | return getUtility(IAllBranches).inProject(self.context) |
129 | |
130 | === modified file 'lib/lp/code/browser/tests/test_branchlisting.py' |
131 | --- lib/lp/code/browser/tests/test_branchlisting.py 2010-03-30 09:56:10 +0000 |
132 | +++ lib/lp/code/browser/tests/test_branchlisting.py 2010-04-12 05:33:38 +0000 |
133 | @@ -335,6 +335,7 @@ |
134 | # There is only one branch. |
135 | batch = view.branches() |
136 | [view_branch] = batch.branches |
137 | + self.assertStatementCount(0, getattr, view_branch, 'bzr_identity') |
138 | self.assertEqual(identity, view_branch.bzr_identity) |
139 | |
140 | |
141 | |
142 | === modified file 'lib/lp/code/configure.zcml' |
143 | --- lib/lp/code/configure.zcml 2010-04-06 20:17:04 +0000 |
144 | +++ lib/lp/code/configure.zcml 2010-04-12 05:33:38 +0000 |
145 | @@ -456,6 +456,8 @@ |
146 | associatedProductSeries |
147 | getProductSeriesPushingTranslations |
148 | associatedSuiteSourcePackages |
149 | + branchIdentities |
150 | + branchLinks |
151 | subscribe |
152 | getSubscription |
153 | hasSubscription |
154 | |
155 | === modified file 'lib/lp/code/interfaces/branch.py' |
156 | --- lib/lp/code/interfaces/branch.py 2010-04-06 17:19:23 +0000 |
157 | +++ lib/lp/code/interfaces/branch.py 2010-04-12 05:33:38 +0000 |
158 | @@ -8,7 +8,6 @@ |
159 | __metaclass__ = type |
160 | |
161 | __all__ = [ |
162 | - 'bazaar_identity', |
163 | 'BRANCH_NAME_VALIDATION_ERROR_MESSAGE', |
164 | 'branch_name_validator', |
165 | 'BranchCannotBePrivate', |
166 | @@ -21,6 +20,7 @@ |
167 | 'BranchExists', |
168 | 'BranchTargetError', |
169 | 'BranchTypeError', |
170 | + 'BzrIdentityMixin', |
171 | 'CannotDeleteBranch', |
172 | 'DEFAULT_BRANCH_STATUS_IN_LISTING', |
173 | 'get_blacklisted_hostnames', |
174 | @@ -32,12 +32,10 @@ |
175 | 'IBranchNavigationMenu', |
176 | 'IBranchSet', |
177 | 'NoSuchBranch', |
178 | - 'UnknownBranchTypeError', |
179 | 'user_has_special_branch_access', |
180 | ] |
181 | |
182 | from cgi import escape |
183 | -from operator import attrgetter |
184 | import re |
185 | |
186 | from zope.component import getUtility |
187 | @@ -69,12 +67,14 @@ |
188 | ) |
189 | from lp.code.interfaces.branchlookup import IBranchLookup |
190 | from lp.code.interfaces.branchtarget import IHasBranchTarget |
191 | +from lp.code.interfaces.linkedbranch import ICanHasLinkedBranch |
192 | from lp.code.interfaces.hasbranches import IHasMergeProposals |
193 | from lp.code.interfaces.hasrecipes import IHasRecipes |
194 | from canonical.launchpad.interfaces.launchpad import ( |
195 | ILaunchpadCelebrities, IPrivacy) |
196 | from lp.registry.interfaces.role import IHasOwner |
197 | from lp.registry.interfaces.person import IPerson |
198 | +from lp.registry.interfaces.pocket import PackagePublishingPocket |
199 | from canonical.launchpad.webapp.interfaces import ( |
200 | ITableBatchNavigator, NameLookupFailed) |
201 | from canonical.launchpad.webapp.menu import structured |
202 | @@ -925,6 +925,37 @@ |
203 | def associatedSuiteSourcePackages(): |
204 | """Return the suite source packages that this branch is linked to.""" |
205 | |
206 | + def branchLinks(): |
207 | + """Return a sorted list of ICanHasLinkedBranch objects. |
208 | + |
209 | + There is one result for each related linked object that the branch is |
210 | + linked to. For example in the case where a branch is linked to the |
211 | + development series of a project, the link objects for both the project |
212 | + and the development series are returned. |
213 | + |
214 | + The sorting uses the defined order of the linked objects where the |
215 | + more important links are sorted first. |
216 | + """ |
217 | + |
218 | + def branchIdentities(): |
219 | + """A list of aliases for a branch. |
220 | + |
221 | + Returns a list of tuples of bzr identity and context object. There is |
222 | + at least one alias for any branch, and that is the branch itself. For |
223 | + linked branches, the context object is the appropriate linked object. |
224 | + |
225 | + Where a branch is linked to a product series or a suite source |
226 | + package, the branch is available through a number of different urls. |
227 | + These urls are the aliases for the branch. |
228 | + |
229 | + For example, a branch linked to the development focus of the 'fooix' |
230 | + project is accessible using: |
231 | + lp:fooix - the linked object is the product fooix |
232 | + lp:fooix/trunk - the linked object is the trunk series of fooix |
233 | + lp:~owner/fooix/name - the unique name of the branch where the linked |
234 | + object is the branch itself. |
235 | + """ |
236 | + |
237 | # subscription-related methods |
238 | @operation_parameters( |
239 | person=Reference( |
240 | @@ -1303,50 +1334,48 @@ |
241 | """ |
242 | |
243 | |
244 | -def bazaar_identity(branch, is_dev_focus): |
245 | - """Return the shortest lp: style branch identity.""" |
246 | - lp_prefix = config.codehosting.bzr_lp_prefix |
247 | - |
248 | - # XXX: TimPenhey 2008-05-06 bug=227602: Since at this stage the launchpad |
249 | - # name resolution is not authenticated, we can't resolve series branches |
250 | - # that end up pointing to private branches, so don't show short names for |
251 | - # the branch if it is private. |
252 | - if branch.private: |
253 | - return lp_prefix + branch.unique_name |
254 | - |
255 | - use_series = None |
256 | - # XXX: JonathanLange 2009-03-21 spec=package-branches: This should |
257 | - # probably delegate to IBranch.target. I would do it now if I could figure |
258 | - # what all the optimization code is for. |
259 | - if branch.product is not None: |
260 | - if is_dev_focus: |
261 | - return lp_prefix + branch.product.name |
262 | - |
263 | - # If there are no associated series, then use the unique name. |
264 | - associated_series = sorted( |
265 | - branch.associatedProductSeries(), key=attrgetter('datecreated')) |
266 | - if len(associated_series) == 0: |
267 | - return lp_prefix + branch.unique_name |
268 | - # Use the most recently created series. |
269 | - use_series = associated_series[-1] |
270 | - return "%(prefix)s%(product)s/%(series)s" % { |
271 | - 'prefix': lp_prefix, |
272 | - 'product': use_series.product.name, |
273 | - 'series': use_series.name} |
274 | - |
275 | - if branch.sourcepackage is not None: |
276 | - if is_dev_focus: |
277 | - return "%(prefix)s%(distro)s/%(packagename)s" % { |
278 | - 'prefix': lp_prefix, |
279 | - 'distro': branch.distroseries.distribution.name, |
280 | - 'packagename': branch.sourcepackagename.name} |
281 | - suite_sourcepackages = branch.associatedSuiteSourcePackages() |
282 | - # Take the first link if there is one. |
283 | - if len(suite_sourcepackages) > 0: |
284 | - suite_source_package = suite_sourcepackages[0] |
285 | - return lp_prefix + suite_source_package.path |
286 | - |
287 | - return lp_prefix + branch.unique_name |
288 | +class BzrIdentityMixin: |
289 | + """This mixin class determines the bazaar identities. |
290 | + |
291 | + Used by both the model branch class and the browser branch listing item. |
292 | + This allows the browser code to cache the associated links which reduces |
293 | + query counts. |
294 | + """ |
295 | + |
296 | + @property |
297 | + def bzr_identity(self): |
298 | + """See `IBranch`.""" |
299 | + identity, context = self.branchIdentities()[0] |
300 | + return identity |
301 | + |
302 | + def branchIdentities(self): |
303 | + """See `IBranch`.""" |
304 | + lp_prefix = config.codehosting.bzr_lp_prefix |
305 | + if self.private or not self.target.supports_short_identites: |
306 | + # XXX: thumper 2010-04-08, bug 261609 |
307 | + # We have to get around to fixing this |
308 | + identities = [] |
309 | + else: |
310 | + identities = [ |
311 | + (lp_prefix + link.bzr_path, link.context) |
312 | + for link in self.branchLinks()] |
313 | + identities.append((lp_prefix + self.unique_name, self)) |
314 | + return identities |
315 | + |
316 | + def branchLinks(self): |
317 | + """See `IBranch`.""" |
318 | + links = [] |
319 | + for suite_sp in self.associatedSuiteSourcePackages(): |
320 | + links.append(ICanHasLinkedBranch(suite_sp)) |
321 | + if (suite_sp.distribution.currentseries == suite_sp.distroseries |
322 | + and suite_sp.pocket == PackagePublishingPocket.RELEASE): |
323 | + links.append(ICanHasLinkedBranch( |
324 | + suite_sp.sourcepackage.distribution_sourcepackage)) |
325 | + for series in self.associatedProductSeries(): |
326 | + links.append(ICanHasLinkedBranch(series)) |
327 | + if series.product.development_focus == series: |
328 | + links.append(ICanHasLinkedBranch(series.product)) |
329 | + return sorted(links) |
330 | |
331 | |
332 | def user_has_special_branch_access(user): |
333 | |
334 | === modified file 'lib/lp/code/interfaces/branchtarget.py' |
335 | --- lib/lp/code/interfaces/branchtarget.py 2010-03-26 02:36:01 +0000 |
336 | +++ lib/lp/code/interfaces/branchtarget.py 2010-04-12 05:33:38 +0000 |
337 | @@ -88,6 +88,9 @@ |
338 | supports_merge_proposals = Attribute( |
339 | "Does this target support merge proposals at all?") |
340 | |
341 | + supports_short_identites = Attribute( |
342 | + "Does this target support shortened bazaar identities?") |
343 | + |
344 | supports_code_imports = Attribute( |
345 | "Does this target support code imports at all?") |
346 | |
347 | |
348 | === modified file 'lib/lp/code/interfaces/linkedbranch.py' |
349 | --- lib/lp/code/interfaces/linkedbranch.py 2009-11-12 04:28:28 +0000 |
350 | +++ lib/lp/code/interfaces/linkedbranch.py 2010-04-12 05:33:38 +0000 |
351 | @@ -25,6 +25,7 @@ |
352 | class ICanHasLinkedBranch(Interface): |
353 | """Something that has a linked branch.""" |
354 | |
355 | + context = Attribute("The object that can have a linked branch.") |
356 | branch = Attribute("The linked branch.") |
357 | bzr_path = Attribute( |
358 | 'The Bazaar branch path for the linked branch. ' |
359 | |
360 | === modified file 'lib/lp/code/model/branch.py' |
361 | --- lib/lp/code/model/branch.py 2010-03-24 18:48:35 +0000 |
362 | +++ lib/lp/code/model/branch.py 2010-04-12 05:33:38 +0000 |
363 | @@ -62,8 +62,8 @@ |
364 | from lp.code.model.seriessourcepackagebranch import SeriesSourcePackageBranch |
365 | from lp.code.event.branchmergeproposal import NewBranchMergeProposalEvent |
366 | from lp.code.interfaces.branch import ( |
367 | - bazaar_identity, BranchCannotBePrivate, BranchCannotBePublic, |
368 | - BranchTargetError, BranchTypeError, CannotDeleteBranch, |
369 | + BranchCannotBePrivate, BranchCannotBePublic, |
370 | + BranchTargetError, BranchTypeError, BzrIdentityMixin, CannotDeleteBranch, |
371 | DEFAULT_BRANCH_STATUS_IN_LISTING, IBranch, |
372 | IBranchNavigationMenu, IBranchSet, user_has_special_branch_access) |
373 | from lp.code.interfaces.branchcollection import IAllBranches |
374 | @@ -73,7 +73,6 @@ |
375 | from lp.code.interfaces.branchnamespace import IBranchNamespacePolicy |
376 | from lp.code.interfaces.branchpuller import IBranchPuller |
377 | from lp.code.interfaces.branchtarget import IBranchTarget |
378 | -from lp.code.interfaces.linkedbranch import ICanHasLinkedBranch |
379 | from lp.code.interfaces.seriessourcepackagebranch import ( |
380 | IFindOfficialBranchLinks) |
381 | from lp.registry.interfaces.person import ( |
382 | @@ -83,7 +82,7 @@ |
383 | NotificationRecipientSet) |
384 | |
385 | |
386 | -class Branch(SQLBase): |
387 | +class Branch(SQLBase, BzrIdentityMixin): |
388 | """A sequence of ordered revisions in Bazaar.""" |
389 | |
390 | implements(IBranch, IBranchNavigationMenu) |
391 | @@ -464,21 +463,6 @@ |
392 | def browse_source_url(self): |
393 | return self.codebrowse_url('files') |
394 | |
395 | - @property |
396 | - def bzr_identity(self): |
397 | - """See `IBranch`.""" |
398 | - # Should probably put this into the branch target. |
399 | - if self.product is not None: |
400 | - series_branch = self.product.development_focus.branch |
401 | - is_dev_focus = (series_branch == self) |
402 | - elif self.distroseries is not None: |
403 | - distro_package = self.sourcepackage.distribution_sourcepackage |
404 | - linked_branch = ICanHasLinkedBranch(distro_package) |
405 | - is_dev_focus = (linked_branch.branch == self) |
406 | - else: |
407 | - is_dev_focus = False |
408 | - return bazaar_identity(self, is_dev_focus) |
409 | - |
410 | def composePublicURL(self, scheme='http'): |
411 | """See `IBranch`.""" |
412 | # Not all protocols work for private branches. |
413 | |
414 | === modified file 'lib/lp/code/model/branchtarget.py' |
415 | --- lib/lp/code/model/branchtarget.py 2010-03-26 02:36:01 +0000 |
416 | +++ lib/lp/code/model/branchtarget.py 2010-04-12 05:33:38 +0000 |
417 | @@ -103,6 +103,11 @@ |
418 | return True |
419 | |
420 | @property |
421 | + def supports_short_identites(self): |
422 | + """See `IBranchTarget`.""" |
423 | + return True |
424 | + |
425 | + @property |
426 | def supports_code_imports(self): |
427 | """See `IBranchTarget`.""" |
428 | return False |
429 | @@ -195,6 +200,11 @@ |
430 | return False |
431 | |
432 | @property |
433 | + def supports_short_identites(self): |
434 | + """See `IBranchTarget`.""" |
435 | + return False |
436 | + |
437 | + @property |
438 | def supports_code_imports(self): |
439 | """See `IBranchTarget`.""" |
440 | return False |
441 | @@ -276,6 +286,11 @@ |
442 | return True |
443 | |
444 | @property |
445 | + def supports_short_identites(self): |
446 | + """See `IBranchTarget`.""" |
447 | + return True |
448 | + |
449 | + @property |
450 | def supports_code_imports(self): |
451 | """See `IBranchTarget`.""" |
452 | return True |
453 | |
454 | === modified file 'lib/lp/code/model/linkedbranch.py' |
455 | --- lib/lp/code/model/linkedbranch.py 2009-08-28 06:39:38 +0000 |
456 | +++ lib/lp/code/model/linkedbranch.py 2010-04-12 05:33:38 +0000 |
457 | @@ -11,6 +11,9 @@ |
458 | from zope.component import adapts |
459 | from zope.interface import implements |
460 | |
461 | +from lazr.enum import EnumeratedType, Item |
462 | + |
463 | +from lp.archivepublisher.debversion import Version |
464 | from lp.code.interfaces.linkedbranch import ICanHasLinkedBranch |
465 | from lp.registry.interfaces.distributionsourcepackage import ( |
466 | IDistributionSourcePackage) |
467 | @@ -21,98 +24,194 @@ |
468 | from lp.registry.interfaces.suitesourcepackage import ISuiteSourcePackage |
469 | |
470 | |
471 | -class ProductSeriesLinkedBranch: |
472 | +class LinkedBranchOrder(EnumeratedType): |
473 | + """An enum used only for ordering.""" |
474 | + |
475 | + PRODUCT = Item('Product') |
476 | + DISTRIBUTION_SOURCE_PACKAGE = Item('Distribution Source Package') |
477 | + PRODUCT_SERIES = Item('Product Series') |
478 | + SUITE_SOURCE_PACKAGE = Item('Suite Source Package') |
479 | + |
480 | + |
481 | +class BaseLinkedBranch: |
482 | + """Provides the common sorting algorithm.""" |
483 | + |
484 | + def __cmp__(self, other): |
485 | + if not ICanHasLinkedBranch.providedBy(other): |
486 | + raise AssertionError("Can't compare with: %r" % other) |
487 | + return cmp(self.sort_order, other.sort_order) |
488 | + |
489 | + |
490 | +class ProductSeriesLinkedBranch(BaseLinkedBranch): |
491 | """Implement a linked branch for a product series.""" |
492 | |
493 | adapts(IProductSeries) |
494 | implements(ICanHasLinkedBranch) |
495 | |
496 | + sort_order = LinkedBranchOrder.PRODUCT_SERIES |
497 | + |
498 | def __init__(self, product_series): |
499 | - self._product_series = product_series |
500 | + self.context = product_series |
501 | + |
502 | + @property |
503 | + def product_series(self): |
504 | + return self.context |
505 | + |
506 | + def __cmp__(self, other): |
507 | + result = super(ProductSeriesLinkedBranch, self).__cmp__(other) |
508 | + if result != 0: |
509 | + return result |
510 | + else: |
511 | + # The sorting of the product series uses the same sorting the |
512 | + # product itself uses, which is alphabetically by name. |
513 | + my_parts = ( |
514 | + self.product_series.product.name, |
515 | + self.product_series.name) |
516 | + other_parts = ( |
517 | + other.product_series.product.name, |
518 | + other.product_series.name) |
519 | + return cmp(my_parts, other_parts) |
520 | |
521 | @property |
522 | def branch(self): |
523 | """See `ICanHasLinkedBranch`.""" |
524 | - return self._product_series.branch |
525 | + return self.product_series.branch |
526 | |
527 | @property |
528 | def bzr_path(self): |
529 | """See `ICanHasLinkedBranch`.""" |
530 | return '/'.join( |
531 | - [self._product_series.product.name, self._product_series.name]) |
532 | + [self.product_series.product.name, self.product_series.name]) |
533 | |
534 | def setBranch(self, branch, registrant=None): |
535 | """See `ICanHasLinkedBranch`.""" |
536 | - self._product_series.branch = branch |
537 | - |
538 | - |
539 | -class ProductLinkedBranch: |
540 | + self.product_series.branch = branch |
541 | + |
542 | + |
543 | +class ProductLinkedBranch(BaseLinkedBranch): |
544 | """Implement a linked branch for a product.""" |
545 | |
546 | adapts(IProduct) |
547 | implements(ICanHasLinkedBranch) |
548 | |
549 | + sort_order = LinkedBranchOrder.PRODUCT |
550 | + |
551 | def __init__(self, product): |
552 | - self._product = product |
553 | + self.context = product |
554 | + |
555 | + @property |
556 | + def product(self): |
557 | + return self.context |
558 | + |
559 | + def __cmp__(self, other): |
560 | + result = super(ProductLinkedBranch, self).__cmp__(other) |
561 | + if result != 0: |
562 | + return result |
563 | + else: |
564 | + return cmp(self.product.name, other.product.name) |
565 | |
566 | @property |
567 | def branch(self): |
568 | """See `ICanHasLinkedBranch`.""" |
569 | - return ICanHasLinkedBranch(self._product.development_focus).branch |
570 | + return ICanHasLinkedBranch(self.product.development_focus).branch |
571 | |
572 | @property |
573 | def bzr_path(self): |
574 | """See `ICanHasLinkedBranch`.""" |
575 | - return self._product.name |
576 | + return self.product.name |
577 | |
578 | def setBranch(self, branch, registrant=None): |
579 | """See `ICanHasLinkedBranch`.""" |
580 | - ICanHasLinkedBranch(self._product.development_focus).setBranch( |
581 | + ICanHasLinkedBranch(self.product.development_focus).setBranch( |
582 | branch, registrant) |
583 | |
584 | |
585 | -class PackageLinkedBranch: |
586 | +class PackageLinkedBranch(BaseLinkedBranch): |
587 | """Implement a linked branch for a source package pocket.""" |
588 | |
589 | adapts(ISuiteSourcePackage) |
590 | implements(ICanHasLinkedBranch) |
591 | |
592 | + sort_order = LinkedBranchOrder.SUITE_SOURCE_PACKAGE |
593 | + |
594 | def __init__(self, suite_sourcepackage): |
595 | - self._suite_sourcepackage = suite_sourcepackage |
596 | + self.context = suite_sourcepackage |
597 | + |
598 | + @property |
599 | + def suite_sourcepackage(self): |
600 | + return self.context |
601 | + |
602 | + def __cmp__(self, other): |
603 | + result = super(PackageLinkedBranch, self).__cmp__(other) |
604 | + if result != 0: |
605 | + return result |
606 | + # The versions are reversed as we want the greater Version to sort |
607 | + # before the lesser one. Hence self in the other tuple, and other in |
608 | + # the self tuple. Next compare the distribution name. |
609 | + my_parts = ( |
610 | + self.suite_sourcepackage.distribution.name, |
611 | + Version(other.suite_sourcepackage.distroseries.version), |
612 | + self.suite_sourcepackage.sourcepackagename.name, |
613 | + self.suite_sourcepackage.pocket) |
614 | + other_parts = ( |
615 | + other.suite_sourcepackage.distribution.name, |
616 | + Version(self.suite_sourcepackage.distroseries.version), |
617 | + other.suite_sourcepackage.sourcepackagename.name, |
618 | + other.suite_sourcepackage.pocket) |
619 | + return cmp(my_parts, other_parts) |
620 | |
621 | @property |
622 | def branch(self): |
623 | """See `ICanHasLinkedBranch`.""" |
624 | - package = self._suite_sourcepackage.sourcepackage |
625 | - pocket = self._suite_sourcepackage.pocket |
626 | + package = self.suite_sourcepackage.sourcepackage |
627 | + pocket = self.suite_sourcepackage.pocket |
628 | return package.getBranch(pocket) |
629 | |
630 | @property |
631 | def bzr_path(self): |
632 | """See `ICanHasLinkedBranch`.""" |
633 | - return self._suite_sourcepackage.path |
634 | + return self.suite_sourcepackage.path |
635 | |
636 | def setBranch(self, branch, registrant): |
637 | """See `ICanHasLinkedBranch`.""" |
638 | - package = self._suite_sourcepackage.sourcepackage |
639 | - pocket = self._suite_sourcepackage.pocket |
640 | + package = self.suite_sourcepackage.sourcepackage |
641 | + pocket = self.suite_sourcepackage.pocket |
642 | package.setBranch(pocket, branch, registrant) |
643 | |
644 | |
645 | -class DistributionPackageLinkedBranch: |
646 | +class DistributionPackageLinkedBranch(BaseLinkedBranch): |
647 | """Implement a linked branch for an `IDistributionSourcePackage`.""" |
648 | |
649 | adapts(IDistributionSourcePackage) |
650 | implements(ICanHasLinkedBranch) |
651 | |
652 | + sort_order = LinkedBranchOrder.DISTRIBUTION_SOURCE_PACKAGE |
653 | + |
654 | def __init__(self, distribution_sourcepackage): |
655 | - self._distribution_sourcepackage = distribution_sourcepackage |
656 | + self.context = distribution_sourcepackage |
657 | + |
658 | + @property |
659 | + def distribution_sourcepackage(self): |
660 | + return self.context |
661 | + |
662 | + def __cmp__(self, other): |
663 | + result = super(DistributionPackageLinkedBranch, self).__cmp__(other) |
664 | + if result != 0: |
665 | + return result |
666 | + else: |
667 | + my_names = ( |
668 | + self.distribution_sourcepackage.distribution.name, |
669 | + self.distribution_sourcepackage.sourcepackagename.name) |
670 | + other_names = ( |
671 | + other.distribution_sourcepackage.distribution.name, |
672 | + other.distribution_sourcepackage.sourcepackagename.name) |
673 | + return cmp(my_names, other_names) |
674 | |
675 | @property |
676 | def branch(self): |
677 | """See `ICanHasLinkedBranch`.""" |
678 | development_package = ( |
679 | - self._distribution_sourcepackage.development_version) |
680 | + self.distribution_sourcepackage.development_version) |
681 | if development_package is None: |
682 | return None |
683 | suite_sourcepackage = development_package.getSuiteSourcePackage( |
684 | @@ -123,13 +222,13 @@ |
685 | def bzr_path(self): |
686 | """See `ICanHasLinkedBranch`.""" |
687 | return '/'.join( |
688 | - [self._distribution_sourcepackage.distribution.name, |
689 | - self._distribution_sourcepackage.sourcepackagename.name]) |
690 | + [self.distribution_sourcepackage.distribution.name, |
691 | + self.distribution_sourcepackage.sourcepackagename.name]) |
692 | |
693 | def setBranch(self, branch, registrant): |
694 | """See `ICanHasLinkedBranch`.""" |
695 | development_package = ( |
696 | - self._distribution_sourcepackage.development_version) |
697 | + self.distribution_sourcepackage.development_version) |
698 | if development_package is None: |
699 | raise NoSuchDistroSeries('no current series') |
700 | suite_sourcepackage = development_package.getSuiteSourcePackage( |
701 | |
702 | === modified file 'lib/lp/code/model/tests/test_branch.py' |
703 | --- lib/lp/code/model/tests/test_branch.py 2010-04-06 17:58:06 +0000 |
704 | +++ lib/lp/code/model/tests/test_branch.py 2010-04-12 05:33:38 +0000 |
705 | @@ -483,6 +483,203 @@ |
706 | self.assertFalse(branch.upgrade_pending) |
707 | |
708 | |
709 | +class TestBranchLinksAndIdentites(TestCaseWithFactory): |
710 | + """Test IBranch.branchLinks and IBranch.branchIdentities.""" |
711 | + |
712 | + layer = DatabaseFunctionalLayer |
713 | + |
714 | + def test_default_identities(self): |
715 | + # If there are no links, the only branch identity is the unique name. |
716 | + branch = self.factory.makeAnyBranch() |
717 | + self.assertEqual( |
718 | + [('lp://dev/' + branch.unique_name, branch)], |
719 | + branch.branchIdentities()) |
720 | + |
721 | + def test_linked_to_product(self): |
722 | + # If a branch is linked to the product, it is also by definition |
723 | + # linked to the development focus of the product. |
724 | + fooix = removeSecurityProxy(self.factory.makeProduct(name='fooix')) |
725 | + fooix.development_focus.name = 'devel' |
726 | + eric = self.factory.makePerson(name='eric') |
727 | + branch = self.factory.makeProductBranch( |
728 | + product=fooix, owner=eric, name='trunk') |
729 | + linked_branch = ICanHasLinkedBranch(fooix) |
730 | + linked_branch.setBranch(branch) |
731 | + self.assertEqual( |
732 | + [linked_branch, ICanHasLinkedBranch(fooix.development_focus)], |
733 | + branch.branchLinks()) |
734 | + self.assertEqual( |
735 | + [('lp://dev/fooix', fooix), |
736 | + ('lp://dev/fooix/devel', fooix.development_focus), |
737 | + ('lp://dev/~eric/fooix/trunk', branch)], |
738 | + branch.branchIdentities()) |
739 | + |
740 | + def test_linked_to_product_series(self): |
741 | + # If a branch is linked to a non-development series of a product and |
742 | + # not linked to the product itself, then only the product series is |
743 | + # returned in the links. |
744 | + fooix = removeSecurityProxy(self.factory.makeProduct(name='fooix')) |
745 | + future = self.factory.makeProductSeries(product=fooix, name='future') |
746 | + eric = self.factory.makePerson(name='eric') |
747 | + branch = self.factory.makeProductBranch( |
748 | + product=fooix, owner=eric, name='trunk') |
749 | + linked_branch = ICanHasLinkedBranch(future) |
750 | + linked_branch.setBranch(branch) |
751 | + self.assertEqual( |
752 | + [linked_branch], |
753 | + branch.branchLinks()) |
754 | + self.assertEqual( |
755 | + [('lp://dev/fooix/future', future), |
756 | + ('lp://dev/~eric/fooix/trunk', branch)], |
757 | + branch.branchIdentities()) |
758 | + |
759 | + def test_linked_to_package(self): |
760 | + # If a branch is linked to a suite source package where the |
761 | + # distroseries is the current series for the distribution, there is a |
762 | + # link for both the distribution source package and the suite source |
763 | + # package. |
764 | + mint = self.factory.makeDistribution(name='mint') |
765 | + dev = self.factory.makeDistroSeries( |
766 | + distribution=mint, version='1.0', name='dev') |
767 | + eric = self.factory.makePerson(name='eric') |
768 | + branch = self.factory.makePackageBranch( |
769 | + distroseries=dev, sourcepackagename='choc', name='tip', |
770 | + owner=eric) |
771 | + dsp = self.factory.makeDistributionSourcePackage('choc', mint) |
772 | + distro_link = ICanHasLinkedBranch(dsp) |
773 | + development_package = dsp.development_version |
774 | + suite_sourcepackage = development_package.getSuiteSourcePackage( |
775 | + PackagePublishingPocket.RELEASE) |
776 | + suite_sp_link = ICanHasLinkedBranch(suite_sourcepackage) |
777 | + |
778 | + registrant = getUtility( |
779 | + ILaunchpadCelebrities).ubuntu_branches.teamowner |
780 | + run_with_login( |
781 | + registrant, |
782 | + suite_sp_link.setBranch, branch, registrant) |
783 | + |
784 | + self.assertEqual( |
785 | + [distro_link, suite_sp_link], |
786 | + branch.branchLinks()) |
787 | + self.assertEqual( |
788 | + [('lp://dev/mint/choc', dsp), |
789 | + ('lp://dev/mint/dev/choc', suite_sourcepackage), |
790 | + ('lp://dev/~eric/mint/dev/choc/tip', branch)], |
791 | + branch.branchIdentities()) |
792 | + |
793 | + def test_linked_to_package_not_release_pocket(self): |
794 | + # If a branch is linked to a suite source package where the |
795 | + # distroseries is the current series for the distribution, but the |
796 | + # pocket is not the RELEASE pocket, then there is only the link for |
797 | + # the suite source package. |
798 | + mint = self.factory.makeDistribution(name='mint') |
799 | + dev = self.factory.makeDistroSeries( |
800 | + distribution=mint, version='1.0', name='dev') |
801 | + eric = self.factory.makePerson(name='eric') |
802 | + branch = self.factory.makePackageBranch( |
803 | + distroseries=dev, sourcepackagename='choc', name='tip', |
804 | + owner=eric) |
805 | + dsp = self.factory.makeDistributionSourcePackage('choc', mint) |
806 | + development_package = dsp.development_version |
807 | + suite_sourcepackage = development_package.getSuiteSourcePackage( |
808 | + PackagePublishingPocket.BACKPORTS) |
809 | + suite_sp_link = ICanHasLinkedBranch(suite_sourcepackage) |
810 | + |
811 | + registrant = getUtility( |
812 | + ILaunchpadCelebrities).ubuntu_branches.teamowner |
813 | + run_with_login( |
814 | + registrant, |
815 | + suite_sp_link.setBranch, branch, registrant) |
816 | + |
817 | + self.assertEqual( |
818 | + [suite_sp_link], |
819 | + branch.branchLinks()) |
820 | + self.assertEqual( |
821 | + [('lp://dev/mint/dev-backports/choc', suite_sourcepackage), |
822 | + ('lp://dev/~eric/mint/dev/choc/tip', branch)], |
823 | + branch.branchIdentities()) |
824 | + |
825 | + def test_linked_to_package_not_current_series(self): |
826 | + # If the branch is linked to a suite source package where the distro |
827 | + # series is not the current series, only the suite source package is |
828 | + # returned in the links. |
829 | + mint = self.factory.makeDistribution(name='mint') |
830 | + self.factory.makeDistroSeries( |
831 | + distribution=mint, version='1.0', name='dev') |
832 | + supported = self.factory.makeDistroSeries( |
833 | + distribution=mint, version='0.9', name='supported') |
834 | + eric = self.factory.makePerson(name='eric') |
835 | + branch = self.factory.makePackageBranch( |
836 | + distroseries=supported, sourcepackagename='choc', name='tip', |
837 | + owner=eric) |
838 | + suite_sp = self.factory.makeSuiteSourcePackage( |
839 | + distroseries=supported, sourcepackagename='choc', |
840 | + pocket=PackagePublishingPocket.RELEASE) |
841 | + suite_sp_link = ICanHasLinkedBranch(suite_sp) |
842 | + |
843 | + registrant = getUtility( |
844 | + ILaunchpadCelebrities).ubuntu_branches.teamowner |
845 | + run_with_login( |
846 | + registrant, |
847 | + suite_sp_link.setBranch, branch, registrant) |
848 | + |
849 | + self.assertEqual( |
850 | + [suite_sp_link], |
851 | + branch.branchLinks()) |
852 | + self.assertEqual( |
853 | + [('lp://dev/mint/supported/choc', suite_sp), |
854 | + ('lp://dev/~eric/mint/supported/choc/tip', branch)], |
855 | + branch.branchIdentities()) |
856 | + |
857 | + def test_linked_across_project_to_package(self): |
858 | + # If a product branch is linked to a suite source package, the links |
859 | + # are the same as if it was a source package branch. |
860 | + mint = self.factory.makeDistribution(name='mint') |
861 | + self.factory.makeDistroSeries( |
862 | + distribution=mint, version='1.0', name='dev') |
863 | + eric = self.factory.makePerson(name='eric') |
864 | + fooix = self.factory.makeProduct(name='fooix') |
865 | + branch = self.factory.makeProductBranch( |
866 | + product=fooix, owner=eric, name='trunk') |
867 | + dsp = self.factory.makeDistributionSourcePackage('choc', mint) |
868 | + distro_link = ICanHasLinkedBranch(dsp) |
869 | + development_package = dsp.development_version |
870 | + suite_sourcepackage = development_package.getSuiteSourcePackage( |
871 | + PackagePublishingPocket.RELEASE) |
872 | + suite_sp_link = ICanHasLinkedBranch(suite_sourcepackage) |
873 | + |
874 | + registrant = getUtility( |
875 | + ILaunchpadCelebrities).ubuntu_branches.teamowner |
876 | + run_with_login( |
877 | + registrant, |
878 | + suite_sp_link.setBranch, branch, registrant) |
879 | + |
880 | + self.assertEqual( |
881 | + [distro_link, suite_sp_link], |
882 | + branch.branchLinks()) |
883 | + self.assertEqual( |
884 | + [('lp://dev/mint/choc', dsp), |
885 | + ('lp://dev/mint/dev/choc', suite_sourcepackage), |
886 | + ('lp://dev/~eric/fooix/trunk', branch)], |
887 | + branch.branchIdentities()) |
888 | + |
889 | + def test_junk_branch_links(self): |
890 | + # If a junk branch has links, those links are returned in the |
891 | + # branchLinks, but the branchIdentities just has the branch unique |
892 | + # name. |
893 | + eric = self.factory.makePerson(name='eric') |
894 | + branch = self.factory.makePersonalBranch(owner=eric, name='foo') |
895 | + fooix = removeSecurityProxy(self.factory.makeProduct()) |
896 | + linked_branch = ICanHasLinkedBranch(fooix) |
897 | + linked_branch.setBranch(branch) |
898 | + self.assertEqual( |
899 | + [linked_branch, ICanHasLinkedBranch(fooix.development_focus)], |
900 | + branch.branchLinks()) |
901 | + self.assertEqual( |
902 | + [('lp://dev/~eric/+junk/foo', branch)], |
903 | + branch.branchIdentities()) |
904 | + |
905 | + |
906 | class TestBzrIdentity(TestCaseWithFactory): |
907 | """Test IBranch.bzr_identity.""" |
908 | |
909 | |
910 | === modified file 'lib/lp/code/model/tests/test_branchtarget.py' |
911 | --- lib/lp/code/model/tests/test_branchtarget.py 2010-03-18 19:57:20 +0000 |
912 | +++ lib/lp/code/model/tests/test_branchtarget.py 2010-04-12 05:33:38 +0000 |
913 | @@ -122,6 +122,10 @@ |
914 | # Package branches do support merge proposals. |
915 | self.assertTrue(self.target.supports_merge_proposals) |
916 | |
917 | + def test_supports_short_identites(self): |
918 | + # Package branches do support short bzr identites. |
919 | + self.assertTrue(self.target.supports_short_identites) |
920 | + |
921 | def test_displayname(self): |
922 | # The display name of a source package target is the display name of |
923 | # the source package. |
924 | @@ -228,6 +232,10 @@ |
925 | # Personal branches do not support merge proposals. |
926 | self.assertFalse(self.target.supports_merge_proposals) |
927 | |
928 | + def test_supports_short_identites(self): |
929 | + # Personal branches do not support short bzr identites. |
930 | + self.assertFalse(self.target.supports_short_identites) |
931 | + |
932 | def test_displayname(self): |
933 | # The display name of a person branch target is ~$USER/+junk. |
934 | target = IBranchTarget(self.original) |
935 | @@ -341,6 +349,10 @@ |
936 | # Product branches do support merge proposals. |
937 | self.assertTrue(self.target.supports_merge_proposals) |
938 | |
939 | + def test_supports_short_identites(self): |
940 | + # Product branches do support short bzr identites. |
941 | + self.assertTrue(self.target.supports_short_identites) |
942 | + |
943 | def test_displayname(self): |
944 | # The display name of a product branch target is the display name of |
945 | # the product. |
946 | |
947 | === modified file 'lib/lp/code/model/tests/test_linkedbranch.py' |
948 | --- lib/lp/code/model/tests/test_linkedbranch.py 2009-08-28 06:39:38 +0000 |
949 | +++ lib/lp/code/model/tests/test_linkedbranch.py 2010-04-12 05:33:38 +0000 |
950 | @@ -201,5 +201,149 @@ |
951 | CannotHaveLinkedBranch, get_linked_branch, project) |
952 | |
953 | |
954 | +class TestLinkedBranchSorting(TestCaseWithFactory): |
955 | + |
956 | + layer = DatabaseFunctionalLayer |
957 | + |
958 | + def test_sorting_different_types(self): |
959 | + # The different types can be sorted together, and sort so that the |
960 | + # results are ordered like: |
961 | + # Product Link |
962 | + # Distribution Source Package Link |
963 | + # Product Series Link |
964 | + # Package Link |
965 | + product_link = ICanHasLinkedBranch(self.factory.makeProduct()) |
966 | + product_series_link = ICanHasLinkedBranch( |
967 | + self.factory.makeProductSeries()) |
968 | + distro_sp_link = ICanHasLinkedBranch( |
969 | + self.factory.makeDistributionSourcePackage()) |
970 | + package_link = ICanHasLinkedBranch( |
971 | + self.factory.makeSuiteSourcePackage()) |
972 | + |
973 | + links = sorted( |
974 | + [package_link, product_series_link, distro_sp_link, product_link]) |
975 | + self.assertIs(product_link, links[0]) |
976 | + self.assertIs(distro_sp_link, links[1]) |
977 | + self.assertIs(product_series_link, links[2]) |
978 | + self.assertIs(package_link, links[3]) |
979 | + |
980 | + def test_product_sort(self): |
981 | + # If in the extremely unlikely event we have one branch linked as the |
982 | + # trunk of two or more different products (you never know), then the |
983 | + # sorting reverts to the name of the product. |
984 | + aardvark_link = ICanHasLinkedBranch( |
985 | + self.factory.makeProduct(name='aardvark')) |
986 | + meerkat_link = ICanHasLinkedBranch( |
987 | + self.factory.makeProduct(name='meerkat')) |
988 | + zebra_link = ICanHasLinkedBranch( |
989 | + self.factory.makeProduct(name='zebra')) |
990 | + links = sorted( |
991 | + [zebra_link, aardvark_link, meerkat_link]) |
992 | + self.assertIs(aardvark_link, links[0]) |
993 | + self.assertIs(meerkat_link, links[1]) |
994 | + self.assertIs(zebra_link, links[2]) |
995 | + |
996 | + def test_product_series_sort(self): |
997 | + # Sorting by product series checks the product name first, then series |
998 | + # name. |
999 | + aardvark = self.factory.makeProduct(name='aardvark') |
1000 | + zebra = self.factory.makeProduct(name='zebra') |
1001 | + aardvark_devel = ICanHasLinkedBranch( |
1002 | + self.factory.makeProductSeries( |
1003 | + product=aardvark, name='devel')) |
1004 | + aardvark_testing = ICanHasLinkedBranch( |
1005 | + self.factory.makeProductSeries( |
1006 | + product=aardvark, name='testing')) |
1007 | + zebra_devel = ICanHasLinkedBranch( |
1008 | + self.factory.makeProductSeries( |
1009 | + product=zebra, name='devel')) |
1010 | + zebra_mashup = ICanHasLinkedBranch( |
1011 | + self.factory.makeProductSeries( |
1012 | + product=zebra, name='mashup')) |
1013 | + |
1014 | + links = sorted( |
1015 | + [zebra_mashup, aardvark_testing, zebra_devel, aardvark_devel]) |
1016 | + self.assertIs(aardvark_devel, links[0]) |
1017 | + self.assertIs(aardvark_testing, links[1]) |
1018 | + self.assertIs(zebra_devel, links[2]) |
1019 | + self.assertIs(zebra_mashup, links[3]) |
1020 | + |
1021 | + def test_distribution_source_package_sort(self): |
1022 | + # Sorting of distribution source packages sorts firstly on the |
1023 | + # distribution name, then the package name. |
1024 | + aardvark = self.factory.makeDistribution(name='aardvark') |
1025 | + zebra = self.factory.makeDistribution(name='zebra') |
1026 | + aardvark_devel = ICanHasLinkedBranch( |
1027 | + self.factory.makeDistributionSourcePackage( |
1028 | + distribution=aardvark, sourcepackagename='devel')) |
1029 | + aardvark_testing = ICanHasLinkedBranch( |
1030 | + self.factory.makeDistributionSourcePackage( |
1031 | + distribution=aardvark, sourcepackagename='testing')) |
1032 | + zebra_devel = ICanHasLinkedBranch( |
1033 | + self.factory.makeDistributionSourcePackage( |
1034 | + distribution=zebra, sourcepackagename='devel')) |
1035 | + zebra_mashup = ICanHasLinkedBranch( |
1036 | + self.factory.makeDistributionSourcePackage( |
1037 | + distribution=zebra, sourcepackagename='mashup')) |
1038 | + |
1039 | + links = sorted( |
1040 | + [zebra_mashup, aardvark_testing, zebra_devel, aardvark_devel]) |
1041 | + self.assertIs(aardvark_devel, links[0]) |
1042 | + self.assertIs(aardvark_testing, links[1]) |
1043 | + self.assertIs(zebra_devel, links[2]) |
1044 | + self.assertIs(zebra_mashup, links[3]) |
1045 | + |
1046 | + def test_suite_source_package_sort(self): |
1047 | + # The sorting of suite source packages checks the distribution first, |
1048 | + # then the distroseries version, followed by the source package name, |
1049 | + # and finally the pocket. |
1050 | + aardvark = ICanHasLinkedBranch( |
1051 | + self.factory.makeSuiteSourcePackage( |
1052 | + distroseries=self.factory.makeDistroSeries( |
1053 | + self.factory.makeDistribution(name='aardvark')))) |
1054 | + zebra = ICanHasLinkedBranch( |
1055 | + self.factory.makeSuiteSourcePackage( |
1056 | + distroseries=self.factory.makeDistroSeries( |
1057 | + self.factory.makeDistribution(name='zebra')))) |
1058 | + meerkat = self.factory.makeDistribution(name='meerkat') |
1059 | + meerkat_1 = ICanHasLinkedBranch( |
1060 | + self.factory.makeSuiteSourcePackage( |
1061 | + self.factory.makeDistroSeries(meerkat, "1.0"))) |
1062 | + meerkat_2 = self.factory.makeDistroSeries(meerkat, "2.0") |
1063 | + meerkat_3 = ICanHasLinkedBranch( |
1064 | + self.factory.makeSuiteSourcePackage( |
1065 | + self.factory.makeDistroSeries(meerkat, "3.0"))) |
1066 | + meerkat_2_devel_release = ICanHasLinkedBranch( |
1067 | + self.factory.makeSuiteSourcePackage( |
1068 | + meerkat_2, 'devel', PackagePublishingPocket.RELEASE)) |
1069 | + meerkat_2_devel_updates = ICanHasLinkedBranch( |
1070 | + self.factory.makeSuiteSourcePackage( |
1071 | + meerkat_2, 'devel', PackagePublishingPocket.UPDATES)) |
1072 | + meerkat_2_devel_backports = ICanHasLinkedBranch( |
1073 | + self.factory.makeSuiteSourcePackage( |
1074 | + meerkat_2, 'devel', PackagePublishingPocket.BACKPORTS)) |
1075 | + meerkat_2_apples = ICanHasLinkedBranch( |
1076 | + self.factory.makeSuiteSourcePackage( |
1077 | + meerkat_2, 'apples')) |
1078 | + |
1079 | + links = sorted( |
1080 | + [meerkat_3, |
1081 | + meerkat_2_devel_updates, |
1082 | + zebra, |
1083 | + meerkat_2_apples, |
1084 | + aardvark, |
1085 | + meerkat_2_devel_backports, |
1086 | + meerkat_1, |
1087 | + meerkat_2_devel_release]) |
1088 | + self.assertIs(aardvark, links[0]) |
1089 | + self.assertIs(meerkat_3, links[1]) |
1090 | + self.assertIs(meerkat_2_apples, links[2]) |
1091 | + self.assertIs(meerkat_2_devel_release, links[3]) |
1092 | + self.assertIs(meerkat_2_devel_updates, links[4]) |
1093 | + self.assertIs(meerkat_2_devel_backports, links[5]) |
1094 | + self.assertIs(meerkat_1, links[6]) |
1095 | + self.assertIs(zebra, links[7]) |
1096 | + |
1097 | + |
1098 | def test_suite(): |
1099 | return unittest.TestLoader().loadTestsFromName(__name__) |
1100 | |
1101 | === modified file 'lib/lp/code/stories/branches/xx-branch-listings.txt' |
1102 | --- lib/lp/code/stories/branches/xx-branch-listings.txt 2009-08-14 18:02:29 +0000 |
1103 | +++ lib/lp/code/stories/branches/xx-branch-listings.txt 2010-04-12 05:33:38 +0000 |
1104 | @@ -365,8 +365,7 @@ |
1105 | |
1106 | Now when we look at the branches for gnome-terminal, the main branch |
1107 | now shows as the "focus of development". This is indicated by |
1108 | -both the series link in the first column with the branch unique name, |
1109 | -but also with a star with the badges. |
1110 | +both the series link in the first column with the branch unique name. |
1111 | |
1112 | >>> browser.open('http://code.launchpad.dev/gnome-terminal') |
1113 | >>> table = find_tag_by_id(browser.contents, 'branchtable') |
1114 | @@ -375,8 +374,6 @@ |
1115 | >>> cols = row.fetch('td') |
1116 | >>> print extract_text(cols[0]) |
1117 | lp://dev/gnome-terminal Series: trunk |
1118 | - >>> print cols[1].renderContents() |
1119 | - <img src="/@@/favourite-yes" title="focus of development" /> |
1120 | |
1121 | If a branch is associated with more than one series, then the links |
1122 | are comma separated and in alphabetical order. |
1123 | |
1124 | === modified file 'lib/lp/code/templates/branch-listing.pt' |
1125 | --- lib/lp/code/templates/branch-listing.pt 2009-12-03 18:33:22 +0000 |
1126 | +++ lib/lp/code/templates/branch-listing.pt 2010-04-12 05:33:38 +0000 |
1127 | @@ -41,159 +41,6 @@ |
1128 | } |
1129 | registerLaunchpadFunction(hookUpFilterSubmission); |
1130 | |
1131 | -LPS.use('io-base', 'node', 'json-parse', function(Y) { |
1132 | - |
1133 | -function doUpdate(transaction_id, response, args) { |
1134 | - json_values = Y.JSON.parse(response.responseText); |
1135 | - // Make sure that the branch isn't empty, and that there |
1136 | - // have been commits in the last 90 days |
1137 | - |
1138 | - // Logic has been inverted in the next line to avoid |
1139 | - // breaking XHTML compliance of the template due to |
1140 | - // ampersand usage. |
1141 | - if(!(!json_values.commits || !json_values.count > 0)) { |
1142 | - document.getElementById(args.container).innerHTML = ''; |
1143 | - sparkline(args.container, json_values); |
1144 | - } |
1145 | -} |
1146 | - |
1147 | - |
1148 | -function renderSpark() { |
1149 | - |
1150 | - for (spark_div in branch_sparks) { |
1151 | - |
1152 | - spark_div = branch_sparks[spark_div][0]; |
1153 | - |
1154 | - if (Y.one('#' + spark_div) !== null) { |
1155 | - json_url = branch_sparks[0][1]; |
1156 | - var container = spark_div; |
1157 | - var uri = json_url; |
1158 | - var xhr_config = { |
1159 | - on: { |
1160 | - success: doUpdate, |
1161 | - }, |
1162 | - arguments: { |
1163 | - 'container': container |
1164 | - } |
1165 | - }; |
1166 | - |
1167 | - Y.io(uri, xhr_config); |
1168 | - } |
1169 | - } |
1170 | -} |
1171 | - |
1172 | - |
1173 | -function sparkline(branch_id, o) { |
1174 | - var spark_div = document.getElementById(branch_id); |
1175 | - var number_commits = parseInt(o.commits.length); |
1176 | - var w = 160; |
1177 | - var min = 9999; |
1178 | - var max = -1; |
1179 | - var h = 12; |
1180 | - var co = document.createElement("canvas"); |
1181 | - |
1182 | - co.style.height = h; |
1183 | - co.style.width = w; |
1184 | - co.width = w; |
1185 | - co.height = h; |
1186 | - |
1187 | - spark_div.appendChild(co); |
1188 | - |
1189 | - // Figure out the max and min number of commits |
1190 | - |
1191 | - // Logic has been inverted in the next line to avoid breaking |
1192 | - // XHTML compliance of the template due to ``less than`` symbol |
1193 | - // usage (using ``greater than`` is fine). |
1194 | - for (var i = 0; number_commits > i; i++) { |
1195 | - o.commits[i] = o.commits[i] - 0; |
1196 | - if ( min > o.commits[i] ) min = o.commits[i]; |
1197 | - if ( o.commits[i] > max ) max = o.commits[i]; |
1198 | - } |
1199 | - |
1200 | - if (co.getContext) { |
1201 | - var c = co.getContext("2d"); |
1202 | - c.strokeStyle = "black"; |
1203 | - c.lineWidth = 1.0; |
1204 | - c.beginPath(); |
1205 | - |
1206 | - // Logic has been inverted in the next line to avoid breaking |
1207 | - // XHTML compliance of the template due to ``less than`` symbol |
1208 | - // usage (using ``greater than`` is fine). |
1209 | - for (var i = 0; number_commits > i; i++) { |
1210 | - x_pos = (w / number_commits) * i; |
1211 | - // We subtract 1 pixel here to compensate for the base line |
1212 | - y_pos = (h-1) - (((o.commits[i] - min) / (max - min)) * (h-1)); |
1213 | - |
1214 | - if (i == 0) { |
1215 | - c.moveTo(x_pos, y_pos); |
1216 | - } |
1217 | - c.lineTo(x_pos, y_pos); |
1218 | - |
1219 | - // When was the last commit? |
1220 | - if(o.commits[i] > 0) { |
1221 | - last_commit = i+1; |
1222 | - } |
1223 | - |
1224 | - } |
1225 | - c.stroke(); |
1226 | - |
1227 | - // Draw the base line |
1228 | - c.beginPath(); |
1229 | - c.strokeStyle = "gray"; |
1230 | - c.moveTo(0,h); |
1231 | - c.lineTo(w,h); |
1232 | - c.stroke(); |
1233 | - |
1234 | - // Marks the point with the most commits |
1235 | - c.fillStyle = 'red'; |
1236 | - c.beginPath(); |
1237 | - c.arc((w / number_commits) * o.max_commits, |
1238 | - h+2 - (((o.commits[o.max_commits] - min) / (max - min)) * h), |
1239 | - 2, 0, (Math.PI*2), true); |
1240 | - c.fill(); |
1241 | - |
1242 | - // Shows the number of max commits |
1243 | - new_text = document.createTextNode(o.commits[o.max_commits] + ' max commits'); |
1244 | - new_div = document.createElement('div'); |
1245 | - new_div.setAttribute('id', 'txt_max_' + branch_id); |
1246 | - new_div.appendChild(new_text); |
1247 | - document.getElementById(branch_id).appendChild(new_div); |
1248 | - new_div.style.fontSize = '8px'; |
1249 | - new_div.style.color = 'red'; |
1250 | - new_div.style.position = 'relative'; |
1251 | - new_div.style.top = '-22px'; |
1252 | - new_div.style.left = (((w / number_commits) * o.max_commits)-14) + 'px'; |
1253 | - |
1254 | - |
1255 | - // Marks the point of last commit |
1256 | - c.fillStyle = 'blue'; |
1257 | - c.beginPath(); |
1258 | - c.arc((w / number_commits) * last_commit, h-2, |
1259 | - 2, 0, (Math.PI*2), true); |
1260 | - c.fill(); |
1261 | - |
1262 | - // Shows the date of the last commit and time frame |
1263 | - new_text = document.createTextNode('90 days - Last commit ' + o.last_commit); |
1264 | - new_div = document.createElement('div'); |
1265 | - new_div.setAttribute('id', 'txt_' + branch_id); |
1266 | - new_div.appendChild(new_text); |
1267 | - document.getElementById(branch_id).appendChild(new_div); |
1268 | - new_div.style.fontSize = '8px'; |
1269 | - new_div.style.color = 'blue'; |
1270 | - new_div.style.position = 'relative'; |
1271 | - new_div.style.left = '2px'; |
1272 | - new_div.style.top = '-10px'; |
1273 | - |
1274 | - } |
1275 | -} |
1276 | - |
1277 | -// XXX TimPenhey 2009-08-03 bug 408207 - disable sparklines |
1278 | -// Y.on("domready", renderSpark); |
1279 | - |
1280 | -}); |
1281 | - |
1282 | - |
1283 | - |
1284 | </script> |
1285 | |
1286 | <tal:needs-batch condition="context/has_multiple_pages"> |
1287 | @@ -255,9 +102,6 @@ |
1288 | </tal:associated-series> |
1289 | </td> |
1290 | <td align="right" style="padding-right: 5px"> |
1291 | - <tal:dev_focus condition="branch/is_development_focus"> |
1292 | - <img src="/@@/favourite-yes" title="focus of development"/> |
1293 | - </tal:dev_focus> |
1294 | <tal:badges replace="structure branch/badges:small"/> |
1295 | </td> |
1296 | <td> |
1297 | @@ -292,15 +136,10 @@ |
1298 | tal:content="branch/date_last_modified/fmt:datetime"> |
1299 | 2005-02-12 13:45 EST |
1300 | </span> |
1301 | - <div id="dev_focus_spark" style="top:10px; position:relative; height:18px;" |
1302 | - tal:omit-tag="not: branch/is_development_focus"> |
1303 | - <div tal:attributes="id string:b-${repeat/branch/number}"> |
1304 | - <span tal:attributes="title branch/date_last_modified/fmt:datetime" |
1305 | - tal:content="branch/since_updated/fmt:approximateduration/use-digits"> |
1306 | - sometime |
1307 | - </span> ago |
1308 | - </div> |
1309 | - </div> |
1310 | + <span tal:attributes="title branch/date_last_modified/fmt:datetime" |
1311 | + tal:content="branch/since_updated/fmt:approximateduration/use-digits"> |
1312 | + sometime |
1313 | + </span> ago |
1314 | </td> |
1315 | |
1316 | <tal:no_commit condition="not: branch/last_commit"> |
1317 | @@ -368,10 +207,3 @@ |
1318 | <tal:navigation replace="structure context/@@+navigation-links-lower" /> |
1319 | |
1320 | </div> |
1321 | - |
1322 | -<tal:script |
1323 | - replace="structure string:<script id='branch-sparks' type='text/javascript'>" /> |
1324 | -<tal:omit tal:omit-tag=""> |
1325 | -var branch_sparks = <tal:array replace="context/branch_sparks"/>; |
1326 | -</tal:omit> |
1327 | -<tal:script replace="structure string:</script>" /> |
1328 | |
1329 | === modified file 'lib/lp/testing/factory.py' |
1330 | --- lib/lp/testing/factory.py 2010-04-08 17:17:15 +0000 |
1331 | +++ lib/lp/testing/factory.py 2010-04-12 05:33:38 +0000 |
1332 | @@ -1773,8 +1773,11 @@ |
1333 | owner = self.makePerson() |
1334 | if distroseries is None: |
1335 | distroseries = self.makeDistroSeries() |
1336 | - if sourcepackagename is None: |
1337 | - sourcepackagename = self.makeSourcePackageName() |
1338 | + # Make sure we have a real sourcepackagename object. |
1339 | + if (sourcepackagename is None or |
1340 | + isinstance(sourcepackagename, basestring)): |
1341 | + sourcepackagename = self.getOrMakeSourcePackageName( |
1342 | + sourcepackagename) |
1343 | if name is None: |
1344 | name = self.getUniqueString().decode('utf8') |
1345 | if description is None: |
1346 | @@ -1789,7 +1792,7 @@ |
1347 | sourcename=None): |
1348 | """Make a new SourcePackageRecipeBuild.""" |
1349 | if sourcepackage is None: |
1350 | - sourcepackage = self.makeSourcePackage(sourcename=sourcename) |
1351 | + sourcepackage = self.makeSourcePackage(sourcename) |
1352 | if recipe is None: |
1353 | recipe = self.makeSourcePackageRecipe() |
1354 | if requester is None: |
1355 | @@ -2061,11 +2064,13 @@ |
1356 | return self.makeSourcePackageName() |
1357 | return getUtility(ISourcePackageNameSet).getOrCreateByName(name) |
1358 | |
1359 | - def makeSourcePackage( |
1360 | - self, sourcepackagename=None, distroseries=None, sourcename=None): |
1361 | + def makeSourcePackage(self, sourcepackagename=None, distroseries=None): |
1362 | """Make an `ISourcePackage`.""" |
1363 | - if sourcepackagename is None: |
1364 | - sourcepackagename = self.makeSourcePackageName(sourcename) |
1365 | + # Make sure we have a real sourcepackagename object. |
1366 | + if (sourcepackagename is None or |
1367 | + isinstance(sourcepackagename, basestring)): |
1368 | + sourcepackagename = self.getOrMakeSourcePackageName( |
1369 | + sourcepackagename) |
1370 | if distroseries is None: |
1371 | distroseries = self.makeDistroRelease() |
1372 | return distroseries.getSourcePackage(sourcepackagename) |
1373 | @@ -2249,14 +2254,20 @@ |
1374 | distroseries = self.makeDistroRelease() |
1375 | if pocket is None: |
1376 | pocket = self.getAnyPocket() |
1377 | - if sourcepackagename is None: |
1378 | - sourcepackagename = self.makeSourcePackageName() |
1379 | + # Make sure we have a real sourcepackagename object. |
1380 | + if (sourcepackagename is None or |
1381 | + isinstance(sourcepackagename, basestring)): |
1382 | + sourcepackagename = self.getOrMakeSourcePackageName( |
1383 | + sourcepackagename) |
1384 | return SuiteSourcePackage(distroseries, pocket, sourcepackagename) |
1385 | |
1386 | def makeDistributionSourcePackage(self, sourcepackagename=None, |
1387 | distribution=None): |
1388 | - if sourcepackagename is None: |
1389 | - sourcepackagename = self.makeSourcePackageName() |
1390 | + # Make sure we have a real sourcepackagename object. |
1391 | + if (sourcepackagename is None or |
1392 | + isinstance(sourcepackagename, basestring)): |
1393 | + sourcepackagename = self.getOrMakeSourcePackageName( |
1394 | + sourcepackagename) |
1395 | if distribution is None: |
1396 | distribution = self.makeDistribution() |
1397 |
Overall, yay, this branch makes things simpler and more correct.
The mixin is still a bit odd, but hopefully noone will have to touch
this code for a few years now :-)
Just a few comments below, mostly simple clarity stuff. Thanks for
fixing the icky bug!
Cheers,
mwh
> === modified file 'lib/lp/ code/interfaces /branch. py' code/interfaces /branch. py 2010-04-06 17:19:23 +0000 code/interfaces /branch. py 2010-04-08 04:14:00 +0000 NAME_VALIDATION _ERROR_ MESSAGE' , name_validator' , Private' , ror', anch', BRANCH_ STATUS_ IN_LISTING' , d_hostnames' , ionMenu' , ypeError' , special_ branch_ access' , interfaces. branchlookup import IBranchLookup interfaces. branchtarget import IHasBranchTarget interfaces. linkedbranch import ICanHasLinkedBranch interfaces. hasbranches import IHasMergeProposals interfaces. hasrecipes import IHasRecipes launchpad. interfaces. launchpad import ( rities, IPrivacy) interfaces. role import IHasOwner interfaces. person import IPerson interfaces. pocket import PackagePublishi ngPocket launchpad. webapp. interfaces import ( gator, NameLookupFailed) launchpad. webapp. menu import structured
> --- lib/lp/
> +++ lib/lp/
> @@ -8,7 +8,6 @@
> __metaclass__ = type
>
> __all__ = [
> - 'bazaar_identity',
> 'BRANCH_
> 'branch_
> 'BranchCannotBe
> @@ -21,6 +20,7 @@
> 'BranchExists',
> 'BranchTargetEr
> 'BranchTypeError',
> + 'BzrIdentityMixin',
> 'CannotDeleteBr
> 'DEFAULT_
> 'get_blackliste
> @@ -32,12 +32,10 @@
> 'IBranchNavigat
> 'IBranchSet',
> 'NoSuchBranch',
> - 'UnknownBranchT
> 'user_has_
> ]
>
> from cgi import escape
> -from operator import attrgetter
> import re
>
> from zope.component import getUtility
> @@ -69,12 +67,14 @@
> )
> from lp.code.
> from lp.code.
> +from lp.code.
> from lp.code.
> from lp.code.
> from canonical.
> ILaunchpadCeleb
> from lp.registry.
> from lp.registry.
> +from lp.registry.
> from canonical.
> ITableBatchNavi
> from canonical.
> @@ -498,6 +498,14 @@
> "None if not a package branch."),
> schema=Interface, required=False, readonly=True))
>
> + is_personal_branch = exported(
> + Bool(
> + title=_("Personal Branch"), required=False,
> + readonly=True, default=False,
> + description=_(
> + "A branch is a personal branch if it is not associated "
> + "with a project nor a source package.")))
I guess a direct test for this would be nice, although it's so very
simple perhaps it's not needed...
> code_reviewer = Attribute( SourcePackages( ):
> "The reviewer if set, otherwise the owner of the branch.")
>
> @@ -925,6 +933,17 @@
> def associatedSuite
> """Return the suite source packages that this branch is linked to."""
>
> + def branchLinks():
> + """Return a sorted list of ICanHasLinkedBranch objects."""
> +
> + def branchIdentites():
Missing an 'i' here :)
> + """A list of aliases for a branch.
> +
> + Returns a list of tuples of bzr identity and context object. There is
> + at least one alias for any branch, and that is the branch itself. For
> + linked branches, the co...