Merge lp:~cjwatson/launchpad/queue-copy-archive-links into lp:launchpad
- queue-copy-archive-links
- Merge into devel
Status: | Merged |
---|---|
Approved by: | William Grant |
Approved revision: | no longer in the source branch. |
Merged at revision: | 16570 |
Proposed branch: | lp:~cjwatson/launchpad/queue-copy-archive-links |
Merge into: | lp:launchpad |
Diff against target: |
452 lines (+152/-38) 14 files modified
lib/lp/_schema_circular_imports.py (+1/-0) lib/lp/app/browser/configure.zcml (+3/-6) lib/lp/app/browser/tales.py (+28/-6) lib/lp/app/doc/tales.txt (+29/-2) lib/lp/soyuz/browser/queue.py (+7/-3) lib/lp/soyuz/browser/tests/test_queue.py (+26/-2) lib/lp/soyuz/configure.zcml (+1/-0) lib/lp/soyuz/interfaces/queue.py (+8/-0) lib/lp/soyuz/model/queue.py (+8/-0) lib/lp/soyuz/templates/build-index.pt (+1/-8) lib/lp/soyuz/templates/distroseries-queue.pt (+7/-7) lib/lp/soyuz/tests/test_packageupload.py (+25/-0) lib/lp/testing/factory.py (+3/-2) lib/lp/testing/tests/test_factory.py (+5/-2) |
To merge this branch: | bzr merge lp:~cjwatson/launchpad/queue-copy-archive-links |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
William Grant | code | Approve | |
Review via email: mp+159250@code.launchpad.net |
Commit message
Show link to source archive for copies from PPAs in DistroSeries:
Description of the change
== Summary ==
When reviewing copies in DistroSeries:
== Proposed fix ==
Linkify the archive displayname, at least for PPAs (archive URLs aren't very useful for primary archives). Add an exported copy_source_archive property to PackageUpload. Between them, these changes will make it easier to track things down by hand and should make it possible to teach our "queue" script to follow the breadcrumb trail automatically.
== LOC Rationale ==
+78. I think this is worth it to eliminate a barrier to review for ~ubuntu-release, particularly in the run-up to 13.04 release; and I should have lots of credit from past changes.
== Tests ==
bin/test -vvct lp.soyuz.
== Demo and Q/A ==
Copy a package to a frozen series from (a) Debian and (b) a PPA on dogfood, if there isn't one of each available already. Run the PCJ processor. Check that both render reasonably in the DistroSeries:+queue UI, and that the copy_source_archive property in the API is correct.
Preview Diff
1 | === modified file 'lib/lp/_schema_circular_imports.py' | |||
2 | --- lib/lp/_schema_circular_imports.py 2013-04-03 03:09:04 +0000 | |||
3 | +++ lib/lp/_schema_circular_imports.py 2013-04-17 11:10:37 +0000 | |||
4 | @@ -591,6 +591,7 @@ | |||
5 | 591 | IPackageUpload['pocket'].vocabulary = PackagePublishingPocket | 591 | IPackageUpload['pocket'].vocabulary = PackagePublishingPocket |
6 | 592 | patch_reference_property(IPackageUpload, 'distroseries', IDistroSeries) | 592 | patch_reference_property(IPackageUpload, 'distroseries', IDistroSeries) |
7 | 593 | patch_reference_property(IPackageUpload, 'archive', IArchive) | 593 | patch_reference_property(IPackageUpload, 'archive', IArchive) |
8 | 594 | patch_reference_property(IPackageUpload, 'copy_source_archive', IArchive) | ||
9 | 594 | 595 | ||
10 | 595 | # IStructuralSubscription | 596 | # IStructuralSubscription |
11 | 596 | patch_collection_property( | 597 | patch_collection_property( |
12 | 597 | 598 | ||
13 | === modified file 'lib/lp/app/browser/configure.zcml' | |||
14 | --- lib/lp/app/browser/configure.zcml 2013-04-10 07:42:31 +0000 | |||
15 | +++ lib/lp/app/browser/configure.zcml 2013-04-17 11:10:37 +0000 | |||
16 | @@ -1,4 +1,4 @@ | |||
18 | 1 | <!-- Copyright 2009-2011 Canonical Ltd. This software is licensed under the | 1 | <!-- Copyright 2009-2013 Canonical Ltd. This software is licensed under the |
19 | 2 | GNU Affero General Public License version 3 (see the file LICENSE). | 2 | GNU Affero General Public License version 3 (see the file LICENSE). |
20 | 3 | --> | 3 | --> |
21 | 4 | 4 | ||
22 | @@ -596,13 +596,10 @@ | |||
23 | 596 | /> | 596 | /> |
24 | 597 | <!-- TALES fmt: namespace --> | 597 | <!-- TALES fmt: namespace --> |
25 | 598 | 598 | ||
26 | 599 | <!-- The next directive is registered for all dicts, but we really only | ||
27 | 600 | want it to apply to the page template's CONTEXTS dict. | ||
28 | 601 | --> | ||
29 | 602 | <adapter | 599 | <adapter |
31 | 603 | for="lp.soyuz.interfaces.archive.IPPA" | 600 | for="lp.soyuz.interfaces.archive.IArchive" |
32 | 604 | provides="zope.traversing.interfaces.IPathAdapter" | 601 | provides="zope.traversing.interfaces.IPathAdapter" |
34 | 605 | factory="lp.app.browser.tales.PPAFormatterAPI" | 602 | factory="lp.app.browser.tales.ArchiveFormatterAPI" |
35 | 606 | name="fmt" | 603 | name="fmt" |
36 | 607 | /> | 604 | /> |
37 | 608 | 605 | ||
38 | 609 | 606 | ||
39 | === modified file 'lib/lp/app/browser/tales.py' | |||
40 | --- lib/lp/app/browser/tales.py 2013-04-09 08:29:04 +0000 | |||
41 | +++ lib/lp/app/browser/tales.py 2013-04-17 11:10:37 +0000 | |||
42 | @@ -98,7 +98,10 @@ | |||
43 | 98 | from lp.services.webapp.session import get_cookie_domain | 98 | from lp.services.webapp.session import get_cookie_domain |
44 | 99 | from lp.services.webapp.url import urlappend | 99 | from lp.services.webapp.url import urlappend |
45 | 100 | from lp.soyuz.enums import ArchivePurpose | 100 | from lp.soyuz.enums import ArchivePurpose |
47 | 101 | from lp.soyuz.interfaces.archive import IPPA | 101 | from lp.soyuz.interfaces.archive import ( |
48 | 102 | IArchive, | ||
49 | 103 | IPPA, | ||
50 | 104 | ) | ||
51 | 102 | from lp.soyuz.interfaces.binarypackagename import IBinaryAndSourcePackageName | 105 | from lp.soyuz.interfaces.binarypackagename import IBinaryAndSourcePackageName |
52 | 103 | 106 | ||
53 | 104 | 107 | ||
54 | @@ -760,6 +763,8 @@ | |||
55 | 760 | sprite_string = 'ppa-icon' | 763 | sprite_string = 'ppa-icon' |
56 | 761 | else: | 764 | else: |
57 | 762 | sprite_string = 'ppa-icon-inactive' | 765 | sprite_string = 'ppa-icon-inactive' |
58 | 766 | elif IArchive.providedBy(context): | ||
59 | 767 | sprite_string = 'distribution' | ||
60 | 763 | elif IBranch.providedBy(context): | 768 | elif IBranch.providedBy(context): |
61 | 764 | sprite_string = 'branch' | 769 | sprite_string = 'branch' |
62 | 765 | elif ISpecification.providedBy(context): | 770 | elif ISpecification.providedBy(context): |
63 | @@ -1847,8 +1852,8 @@ | |||
64 | 1847 | return {'author': self._context.message.owner.displayname} | 1852 | return {'author': self._context.message.owner.displayname} |
65 | 1848 | 1853 | ||
66 | 1849 | 1854 | ||
69 | 1850 | class PPAFormatterAPI(CustomizableFormatter): | 1855 | class ArchiveFormatterAPI(CustomizableFormatter): |
70 | 1851 | """Adapter providing fmt support for `IPPA` objects.""" | 1856 | """Adapter providing fmt support for `IArchive` objects.""" |
71 | 1852 | 1857 | ||
72 | 1853 | _link_summary_template = '%(display_name)s' | 1858 | _link_summary_template = '%(display_name)s' |
73 | 1854 | _link_permission = 'launchpad.View' | 1859 | _link_permission = 'launchpad.View' |
74 | @@ -1859,19 +1864,32 @@ | |||
75 | 1859 | final_traversable_names.update( | 1864 | final_traversable_names.update( |
76 | 1860 | CustomizableFormatter.final_traversable_names) | 1865 | CustomizableFormatter.final_traversable_names) |
77 | 1861 | 1866 | ||
78 | 1867 | def url(self, view_name=None, rootsite='mainsite'): | ||
79 | 1868 | """See `ObjectFormatterAPI`. | ||
80 | 1869 | |||
81 | 1870 | The default URL for a distribution main archive is the URL of the | ||
82 | 1871 | distribution. Other archive URLs are constructed as normal. | ||
83 | 1872 | """ | ||
84 | 1873 | if self._context.is_main: | ||
85 | 1874 | return queryAdapter( | ||
86 | 1875 | self._context.distribution, IPathAdapter, 'fmt').url( | ||
87 | 1876 | view_name, rootsite) | ||
88 | 1877 | else: | ||
89 | 1878 | return super(ArchiveFormatterAPI, self).url(view_name, rootsite) | ||
90 | 1879 | |||
91 | 1862 | def _link_summary_values(self): | 1880 | def _link_summary_values(self): |
92 | 1863 | """See CustomizableFormatter._link_summary_values.""" | 1881 | """See CustomizableFormatter._link_summary_values.""" |
93 | 1864 | return {'display_name': self._context.displayname} | 1882 | return {'display_name': self._context.displayname} |
94 | 1865 | 1883 | ||
95 | 1866 | def link(self, view_name): | 1884 | def link(self, view_name): |
97 | 1867 | """Return html including a link for the context PPA. | 1885 | """Return html including a link for the context archive. |
98 | 1868 | 1886 | ||
99 | 1869 | Render a link using CSS sprites for users with permission to view | 1887 | Render a link using CSS sprites for users with permission to view |
101 | 1870 | the PPA. | 1888 | the archive. |
102 | 1871 | 1889 | ||
103 | 1872 | Disabled PPAs are listed with sprites but not linkified. | 1890 | Disabled PPAs are listed with sprites but not linkified. |
104 | 1873 | 1891 | ||
106 | 1874 | Unaccessible private PPA are not rendered at all (empty string | 1892 | Inaccessible private PPAs are not rendered at all (empty string |
107 | 1875 | is returned). | 1893 | is returned). |
108 | 1876 | """ | 1894 | """ |
109 | 1877 | summary = self._make_link_summary() | 1895 | summary = self._make_link_summary() |
110 | @@ -1887,6 +1905,10 @@ | |||
111 | 1887 | 1905 | ||
112 | 1888 | def reference(self, view_name=None, rootsite=None): | 1906 | def reference(self, view_name=None, rootsite=None): |
113 | 1889 | """Return the text PPA reference for a PPA.""" | 1907 | """Return the text PPA reference for a PPA.""" |
114 | 1908 | if not IPPA.providedBy(self._context): | ||
115 | 1909 | raise NotImplementedError( | ||
116 | 1910 | "No reference implementation for non-PPA archive %r." % | ||
117 | 1911 | self._context) | ||
118 | 1890 | if not check_permission(self._reference_permission, self._context): | 1912 | if not check_permission(self._reference_permission, self._context): |
119 | 1891 | return '' | 1913 | return '' |
120 | 1892 | return self._reference_template % { | 1914 | return self._reference_template % { |
121 | 1893 | 1915 | ||
122 | === modified file 'lib/lp/app/doc/tales.txt' | |||
123 | --- lib/lp/app/doc/tales.txt 2013-01-24 05:50:23 +0000 | |||
124 | +++ lib/lp/app/doc/tales.txt 2013-04-17 11:10:37 +0000 | |||
125 | @@ -160,8 +160,8 @@ | |||
126 | 160 | ... name='pppa', private=True, owner=owner) | 160 | ... name='pppa', private=True, owner=owner) |
127 | 161 | 161 | ||
128 | 162 | >>> print test_tales("ppa/fmt:link", ppa=private_ppa) | 162 | >>> print test_tales("ppa/fmt:link", ppa=private_ppa) |
131 | 163 | <a href="/~joe/+archive/pppa" class="sprite ppa-icon private">PPA named pppa | 163 | <a href="/~joe/+archive/pppa" class="sprite ppa-icon private">PPA named |
132 | 164 | for Joe Smith</a> | 164 | pppa for Joe Smith</a> |
133 | 165 | 165 | ||
134 | 166 | >>> login(ANONYMOUS) | 166 | >>> login(ANONYMOUS) |
135 | 167 | 167 | ||
136 | @@ -180,6 +180,33 @@ | |||
137 | 180 | >>> print test_tales("ppa/fmt:reference", ppa=private_ppa) | 180 | >>> print test_tales("ppa/fmt:reference", ppa=private_ppa) |
138 | 181 | ppa:joe/pppa | 181 | ppa:joe/pppa |
139 | 182 | 182 | ||
140 | 183 | The same 'link' formatter works for distribution archives, with a different | ||
141 | 184 | sprite. The link target for main archives (primary, partner, and debug) is | ||
142 | 185 | the distribution rather than the archive, as the archives would just | ||
143 | 186 | redirect anyway. | ||
144 | 187 | |||
145 | 188 | >>> print test_tales("archive/fmt:link", archive=primary) | ||
146 | 189 | <a href="/ubuntu" class="sprite distribution">Primary Archive for Ubuntu | ||
147 | 190 | Linux</a> | ||
148 | 191 | |||
149 | 192 | >>> print test_tales("archive/fmt:link", archive=partner) | ||
150 | 193 | <a href="/ubuntu" class="sprite distribution">Partner Archive for Ubuntu | ||
151 | 194 | Linux</a> | ||
152 | 195 | |||
153 | 196 | >>> print test_tales("archive/fmt:link", archive=debug) | ||
154 | 197 | <a href="/ubuntu" class="sprite distribution">Ubuntu DEBUG archive</a> | ||
155 | 198 | |||
156 | 199 | >>> print test_tales("archive/fmt:link", archive=copy) | ||
157 | 200 | <a href="/ubuntu/+archive/rebuild" class="sprite distribution">Copy | ||
158 | 201 | archive rebuild for Mark Shuttleworth</a> | ||
159 | 202 | |||
160 | 203 | The 'reference' formatter is meaningless for non-PPA archives. | ||
161 | 204 | |||
162 | 205 | >>> test_tales("archive/fmt:reference", archive=primary) | ||
163 | 206 | Traceback (most recent call last): | ||
164 | 207 | ... | ||
165 | 208 | NotImplementedError: No reference implementation for non-PPA archive ... | ||
166 | 209 | |||
167 | 183 | We also have icons for builds which may have different dimensions. | 210 | We also have icons for builds which may have different dimensions. |
168 | 184 | 211 | ||
169 | 185 | >>> login('admin@canonical.com') | 212 | >>> login('admin@canonical.com') |
170 | 186 | 213 | ||
171 | === modified file 'lib/lp/soyuz/browser/queue.py' | |||
172 | --- lib/lp/soyuz/browser/queue.py 2013-01-08 05:45:26 +0000 | |||
173 | +++ lib/lp/soyuz/browser/queue.py 2013-04-17 11:10:37 +0000 | |||
174 | @@ -21,6 +21,7 @@ | |||
175 | 21 | UnexpectedFormData, | 21 | UnexpectedFormData, |
176 | 22 | ) | 22 | ) |
177 | 23 | from lp.registry.interfaces.person import IPersonSet | 23 | from lp.registry.interfaces.person import IPersonSet |
178 | 24 | from lp.registry.model.distribution import Distribution | ||
179 | 24 | from lp.services.database.bulk import ( | 25 | from lp.services.database.bulk import ( |
180 | 25 | load_referencing, | 26 | load_referencing, |
181 | 26 | load_related, | 27 | load_related, |
182 | @@ -196,11 +197,14 @@ | |||
183 | 196 | """Batch-load `PackageCopyJob`s and related information.""" | 197 | """Batch-load `PackageCopyJob`s and related information.""" |
184 | 197 | package_copy_jobs = load_related( | 198 | package_copy_jobs = load_related( |
185 | 198 | PackageCopyJob, uploads, ['package_copy_job_id']) | 199 | PackageCopyJob, uploads, ['package_copy_job_id']) |
187 | 199 | load_related(Archive, package_copy_jobs, ['source_archive_id']) | 200 | archives = load_related( |
188 | 201 | Archive, package_copy_jobs, ['source_archive_id']) | ||
189 | 202 | load_related(Distribution, archives, ['distributionID']) | ||
190 | 203 | person_ids = map(attrgetter('ownerID'), archives) | ||
191 | 200 | jobs = load_related(Job, package_copy_jobs, ['job_id']) | 204 | jobs = load_related(Job, package_copy_jobs, ['job_id']) |
193 | 201 | person_ids = map(attrgetter('requester_id'), jobs) | 205 | person_ids.extend(map(attrgetter('requester_id'), jobs)) |
194 | 202 | list(getUtility(IPersonSet).getPrecachedPersonsFromIDs( | 206 | list(getUtility(IPersonSet).getPrecachedPersonsFromIDs( |
196 | 203 | person_ids, need_validity=True)) | 207 | person_ids, need_validity=True, need_icon=True)) |
197 | 204 | 208 | ||
198 | 205 | def decoratedQueueBatch(self): | 209 | def decoratedQueueBatch(self): |
199 | 206 | """Return the current batch, converted to decorated objects. | 210 | """Return the current batch, converted to decorated objects. |
200 | 207 | 211 | ||
201 | === modified file 'lib/lp/soyuz/browser/tests/test_queue.py' | |||
202 | --- lib/lp/soyuz/browser/tests/test_queue.py 2013-01-31 02:02:37 +0000 | |||
203 | +++ lib/lp/soyuz/browser/tests/test_queue.py 2013-04-17 11:10:37 +0000 | |||
204 | @@ -6,6 +6,7 @@ | |||
205 | 6 | __metaclass__ = type | 6 | __metaclass__ = type |
206 | 7 | 7 | ||
207 | 8 | from lxml import html | 8 | from lxml import html |
208 | 9 | import soupmatchers | ||
209 | 9 | from storm.store import Store | 10 | from storm.store import Store |
210 | 10 | from testtools.matchers import Equals | 11 | from testtools.matchers import Equals |
211 | 11 | import transaction | 12 | import transaction |
212 | @@ -17,6 +18,7 @@ | |||
213 | 17 | from lp.archiveuploader.tests import datadir | 18 | from lp.archiveuploader.tests import datadir |
214 | 18 | from lp.registry.interfaces.pocket import PackagePublishingPocket | 19 | from lp.registry.interfaces.pocket import PackagePublishingPocket |
215 | 19 | from lp.services.webapp.escaping import html_escape | 20 | from lp.services.webapp.escaping import html_escape |
216 | 21 | from lp.services.webapp.publisher import canonical_url | ||
217 | 20 | from lp.services.webapp.servers import LaunchpadTestRequest | 22 | from lp.services.webapp.servers import LaunchpadTestRequest |
218 | 21 | from lp.soyuz.browser.queue import CompletePackageUpload | 23 | from lp.soyuz.browser.queue import CompletePackageUpload |
219 | 22 | from lp.soyuz.enums import PackageUploadStatus | 24 | from lp.soyuz.enums import PackageUploadStatus |
220 | @@ -360,8 +362,28 @@ | |||
221 | 360 | html_text = view() | 362 | html_text = view() |
222 | 361 | self.assertIn(upload.package_name, html_text) | 363 | self.assertIn(upload.package_name, html_text) |
223 | 362 | # The details section states the sync's origin and requester. | 364 | # The details section states the sync's origin and requester. |
224 | 365 | archive = upload.package_copy_job.source_archive | ||
225 | 366 | url = canonical_url(archive.distribution, path_only_if_possible=True) | ||
226 | 367 | self.assertThat(html_text, soupmatchers.HTMLContains( | ||
227 | 368 | soupmatchers.Tag( | ||
228 | 369 | "link", "a", text=archive.displayname, attrs={"href": url}), | ||
229 | 370 | )) | ||
230 | 363 | self.assertIn( | 371 | self.assertIn( |
232 | 364 | upload.package_copy_job.source_archive.displayname, html_text) | 372 | upload.package_copy_job.job.requester.displayname, html_text) |
233 | 373 | |||
234 | 374 | def test_view_renders_copy_upload_from_private_archive(self): | ||
235 | 375 | login(ADMIN_EMAIL) | ||
236 | 376 | p3a = self.factory.makeArchive(private=True) | ||
237 | 377 | upload = self.factory.makeCopyJobPackageUpload(source_archive=p3a) | ||
238 | 378 | queue_admin = self.factory.makeArchiveAdmin( | ||
239 | 379 | upload.distroseries.main_archive) | ||
240 | 380 | with person_logged_in(queue_admin): | ||
241 | 381 | view = self.makeView(upload.distroseries, queue_admin) | ||
242 | 382 | html_text = view() | ||
243 | 383 | self.assertIn(upload.package_name, html_text) | ||
244 | 384 | # The details section states the sync's origin and requester. | ||
245 | 385 | self.assertTextMatchesExpressionIgnoreWhitespace( | ||
246 | 386 | "Sync from <span>private archive</span>,", html_text) | ||
247 | 365 | self.assertIn( | 387 | self.assertIn( |
248 | 366 | upload.package_copy_job.job.requester.displayname, html_text) | 388 | upload.package_copy_job.job.requester.displayname, html_text) |
249 | 367 | 389 | ||
250 | @@ -379,6 +401,8 @@ | |||
251 | 379 | sprs[-1].addFile(dsc) | 401 | sprs[-1].addFile(dsc) |
252 | 380 | uploads.append(self.factory.makeCustomPackageUpload(distroseries)) | 402 | uploads.append(self.factory.makeCustomPackageUpload(distroseries)) |
253 | 381 | uploads.append(self.factory.makeCopyJobPackageUpload(distroseries)) | 403 | uploads.append(self.factory.makeCopyJobPackageUpload(distroseries)) |
254 | 404 | uploads.append(self.factory.makeCopyJobPackageUpload( | ||
255 | 405 | distroseries, source_archive=self.factory.makeArchive())) | ||
256 | 382 | self.factory.makePackageset( | 406 | self.factory.makePackageset( |
257 | 383 | packages=(sprs[0].sourcepackagename, sprs[2].sourcepackagename, | 407 | packages=(sprs[0].sourcepackagename, sprs[2].sourcepackagename, |
258 | 384 | sprs[4].sourcepackagename), | 408 | sprs[4].sourcepackagename), |
259 | @@ -398,7 +422,7 @@ | |||
260 | 398 | with StormStatementRecorder() as recorder: | 422 | with StormStatementRecorder() as recorder: |
261 | 399 | view = self.makeView(distroseries, queue_admin) | 423 | view = self.makeView(distroseries, queue_admin) |
262 | 400 | view() | 424 | view() |
264 | 401 | self.assertThat(recorder, HasQueryCount(Equals(54))) | 425 | self.assertThat(recorder, HasQueryCount(Equals(56))) |
265 | 402 | 426 | ||
266 | 403 | 427 | ||
267 | 404 | class TestCompletePackageUpload(TestCaseWithFactory): | 428 | class TestCompletePackageUpload(TestCaseWithFactory): |
268 | 405 | 429 | ||
269 | === modified file 'lib/lp/soyuz/configure.zcml' | |||
270 | --- lib/lp/soyuz/configure.zcml 2013-02-18 03:47:21 +0000 | |||
271 | +++ lib/lp/soyuz/configure.zcml 2013-04-17 11:10:37 +0000 | |||
272 | @@ -168,6 +168,7 @@ | |||
273 | 168 | custom_file_urls | 168 | custom_file_urls |
274 | 169 | customFileUrls | 169 | customFileUrls |
275 | 170 | getBinaryProperties | 170 | getBinaryProperties |
276 | 171 | copy_source_archive | ||
277 | 171 | getFileByName | 172 | getFileByName |
278 | 172 | date_created | 173 | date_created |
279 | 173 | sourcepackagerelease | 174 | sourcepackagerelease |
280 | 174 | 175 | ||
281 | === modified file 'lib/lp/soyuz/interfaces/queue.py' | |||
282 | --- lib/lp/soyuz/interfaces/queue.py 2013-01-08 01:10:35 +0000 | |||
283 | +++ lib/lp/soyuz/interfaces/queue.py 2013-04-17 11:10:37 +0000 | |||
284 | @@ -193,6 +193,14 @@ | |||
285 | 193 | readonly=True), | 193 | readonly=True), |
286 | 194 | ("devel", dict(exported=False)), exported=True) | 194 | ("devel", dict(exported=False)), exported=True) |
287 | 195 | 195 | ||
288 | 196 | copy_source_archive = exported( | ||
289 | 197 | Reference( | ||
290 | 198 | # Really IArchive, patched in _schema_circular_imports.py | ||
291 | 199 | schema=Interface, | ||
292 | 200 | description=_("The archive from which this package was copied, if " | ||
293 | 201 | "any."), | ||
294 | 202 | title=_("Copy source archive"), required=False, readonly=True)) | ||
295 | 203 | |||
296 | 196 | displayname = exported( | 204 | displayname = exported( |
297 | 197 | TextLine( | 205 | TextLine( |
298 | 198 | title=_("Generic displayname for a queue item"), readonly=True), | 206 | title=_("Generic displayname for a queue item"), readonly=True), |
299 | 199 | 207 | ||
300 | === modified file 'lib/lp/soyuz/model/queue.py' | |||
301 | --- lib/lp/soyuz/model/queue.py 2013-02-06 08:20:13 +0000 | |||
302 | +++ lib/lp/soyuz/model/queue.py 2013-04-17 11:10:37 +0000 | |||
303 | @@ -295,6 +295,14 @@ | |||
304 | 295 | }) | 295 | }) |
305 | 296 | return properties | 296 | return properties |
306 | 297 | 297 | ||
307 | 298 | @property | ||
308 | 299 | def copy_source_archive(self): | ||
309 | 300 | """See `IPackageUpload`.""" | ||
310 | 301 | if self.package_copy_job_id is not None: | ||
311 | 302 | return self.package_copy_job.source_archive | ||
312 | 303 | else: | ||
313 | 304 | return None | ||
314 | 305 | |||
315 | 298 | def getFileByName(self, filename): | 306 | def getFileByName(self, filename): |
316 | 299 | """See `IPackageUpload`.""" | 307 | """See `IPackageUpload`.""" |
317 | 300 | if (self.changesfile is not None and | 308 | if (self.changesfile is not None and |
318 | 301 | 309 | ||
319 | === modified file 'lib/lp/soyuz/templates/build-index.pt' | |||
320 | --- lib/lp/soyuz/templates/build-index.pt 2012-03-01 18:17:56 +0000 | |||
321 | +++ lib/lp/soyuz/templates/build-index.pt 2013-04-17 11:10:37 +0000 | |||
322 | @@ -86,14 +86,7 @@ | |||
323 | 86 | <dl> | 86 | <dl> |
324 | 87 | <dt>Archive:</dt> | 87 | <dt>Archive:</dt> |
325 | 88 | <dd> | 88 | <dd> |
334 | 89 | <span tal:condition="view/is_ppa" | 89 | <a tal:replace="structure context/archive/fmt:link" /> |
327 | 90 | tal:replace="structure context/archive/fmt:link" | ||
328 | 91 | >Celso PPA</span> | ||
329 | 92 | <a class="sprite distribution" | ||
330 | 93 | tal:condition="not: view/is_ppa" | ||
331 | 94 | tal:attributes="href context/archive/fmt:url" | ||
332 | 95 | tal:content="context/archive/displayname" | ||
333 | 96 | >Ubuntu Primary Archive</a> | ||
335 | 97 | </dd> | 90 | </dd> |
336 | 98 | </dl> | 91 | </dl> |
337 | 99 | <dl> | 92 | <dl> |
338 | 100 | 93 | ||
339 | === modified file 'lib/lp/soyuz/templates/distroseries-queue.pt' | |||
340 | --- lib/lp/soyuz/templates/distroseries-queue.pt 2012-11-15 01:41:14 +0000 | |||
341 | +++ lib/lp/soyuz/templates/distroseries-queue.pt 2013-04-17 11:10:37 +0000 | |||
342 | @@ -224,15 +224,15 @@ | |||
343 | 224 | <tr> | 224 | <tr> |
344 | 225 | <td /> | 225 | <td /> |
345 | 226 | <td tal:condition="view/availableActions" /> | 226 | <td tal:condition="view/availableActions" /> |
347 | 227 | <td colspan="7"> | 227 | <td colspan="7" |
348 | 228 | tal:define="pcj packageupload/package_copy_job; | ||
349 | 229 | visible pcj/source_archive/required:launchpad.View"> | ||
350 | 228 | Sync from | 230 | Sync from |
355 | 229 | <tal:archive | 231 | <a tal:condition="visible" |
356 | 230 | content="packageupload/package_copy_job/source_archive/displayname"> | 232 | tal:replace="structure pcj/source_archive/fmt:link" /> |
357 | 231 | Primary Archive for Ubuntu | 233 | <span tal:condition="not:visible">private archive</span>, |
354 | 232 | </tal:archive>, | ||
358 | 233 | requested by | 234 | requested by |
361 | 234 | <tal:requester | 235 | <tal:requester content="structure pcj/job/requester/fmt:link" /> |
360 | 235 | content="structure packageupload/package_copy_job/job/requester/fmt:link" /> | ||
362 | 236 | </td> | 236 | </td> |
363 | 237 | </tr> | 237 | </tr> |
364 | 238 | </tal:sync> | 238 | </tal:sync> |
365 | 239 | 239 | ||
366 | === modified file 'lib/lp/soyuz/tests/test_packageupload.py' | |||
367 | --- lib/lp/soyuz/tests/test_packageupload.py 2013-01-31 02:02:37 +0000 | |||
368 | +++ lib/lp/soyuz/tests/test_packageupload.py 2013-04-17 11:10:37 +0000 | |||
369 | @@ -902,6 +902,13 @@ | |||
370 | 902 | transaction.commit() | 902 | transaction.commit() |
371 | 903 | return upload, self.load(upload, person) | 903 | return upload, self.load(upload, person) |
372 | 904 | 904 | ||
373 | 905 | def makeCopyJobPackageUpload(self, person, **kwargs): | ||
374 | 906 | with person_logged_in(person): | ||
375 | 907 | upload = self.factory.makeCopyJobPackageUpload( | ||
376 | 908 | distroseries=self.distroseries, **kwargs) | ||
377 | 909 | transaction.commit() | ||
378 | 910 | return upload, self.load(upload, person) | ||
379 | 911 | |||
380 | 905 | def makeBinaryPackageUpload(self, person, binarypackagename=None, | 912 | def makeBinaryPackageUpload(self, person, binarypackagename=None, |
381 | 906 | component=None): | 913 | component=None): |
382 | 907 | with person_logged_in(person): | 914 | with person_logged_in(person): |
383 | @@ -1293,6 +1300,24 @@ | |||
384 | 1293 | } | 1300 | } |
385 | 1294 | self.assertEqual(expected_custom, ws_binaries[-1]) | 1301 | self.assertEqual(expected_custom, ws_binaries[-1]) |
386 | 1295 | 1302 | ||
387 | 1303 | def test_copy_info(self): | ||
388 | 1304 | # API clients can inspect properties of copies, including the source | ||
389 | 1305 | # archive. | ||
390 | 1306 | person = self.makeQueueAdmin([self.universe]) | ||
391 | 1307 | archive = self.factory.makeArchive() | ||
392 | 1308 | upload, ws_upload = self.makeCopyJobPackageUpload( | ||
393 | 1309 | person, sourcepackagename="hello", source_archive=archive) | ||
394 | 1310 | |||
395 | 1311 | self.assertFalse(ws_upload.contains_source) | ||
396 | 1312 | self.assertFalse(ws_upload.contains_build) | ||
397 | 1313 | self.assertTrue(ws_upload.contains_copy) | ||
398 | 1314 | self.assertEqual("hello", ws_upload.display_name) | ||
399 | 1315 | self.assertEqual("sync", ws_upload.display_arches) | ||
400 | 1316 | self.assertEqual("hello", ws_upload.package_name) | ||
401 | 1317 | with person_logged_in(person): | ||
402 | 1318 | archive_url = api_url(archive) | ||
403 | 1319 | self.assertEndsWith(ws_upload.copy_source_archive_link, archive_url) | ||
404 | 1320 | |||
405 | 1296 | def test_getPackageUploads_query_count(self): | 1321 | def test_getPackageUploads_query_count(self): |
406 | 1297 | person = self.makeQueueAdmin([self.universe]) | 1322 | person = self.makeQueueAdmin([self.universe]) |
407 | 1298 | uploads = [] | 1323 | uploads = [] |
408 | 1299 | 1324 | ||
409 | === modified file 'lib/lp/testing/factory.py' | |||
410 | --- lib/lp/testing/factory.py 2013-03-08 22:17:31 +0000 | |||
411 | +++ lib/lp/testing/factory.py 2013-04-17 11:10:37 +0000 | |||
412 | @@ -3460,12 +3460,13 @@ | |||
413 | 3460 | return upload | 3460 | return upload |
414 | 3461 | 3461 | ||
415 | 3462 | def makeCopyJobPackageUpload(self, distroseries=None, | 3462 | def makeCopyJobPackageUpload(self, distroseries=None, |
417 | 3463 | sourcepackagename=None, target_pocket=None): | 3463 | sourcepackagename=None, source_archive=None, |
418 | 3464 | target_pocket=None): | ||
419 | 3464 | """Make a `PackageUpload` with a `PackageCopyJob` attached.""" | 3465 | """Make a `PackageUpload` with a `PackageCopyJob` attached.""" |
420 | 3465 | if distroseries is None: | 3466 | if distroseries is None: |
421 | 3466 | distroseries = self.makeDistroSeries() | 3467 | distroseries = self.makeDistroSeries() |
422 | 3467 | spph = self.makeSourcePackagePublishingHistory( | 3468 | spph = self.makeSourcePackagePublishingHistory( |
424 | 3468 | sourcepackagename=sourcepackagename) | 3469 | archive=source_archive, sourcepackagename=sourcepackagename) |
425 | 3469 | spr = spph.sourcepackagerelease | 3470 | spr = spph.sourcepackagerelease |
426 | 3470 | job = self.makePlainPackageCopyJob( | 3471 | job = self.makePlainPackageCopyJob( |
427 | 3471 | package_name=spr.sourcepackagename.name, | 3472 | package_name=spr.sourcepackagename.name, |
428 | 3472 | 3473 | ||
429 | === modified file 'lib/lp/testing/tests/test_factory.py' | |||
430 | --- lib/lp/testing/tests/test_factory.py 2012-09-22 23:58:54 +0000 | |||
431 | +++ lib/lp/testing/tests/test_factory.py 2013-04-17 11:10:37 +0000 | |||
432 | @@ -1,4 +1,4 @@ | |||
434 | 1 | # Copyright 2010-2012 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2010-2013 Canonical Ltd. This software is licensed under the |
435 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
436 | 3 | 3 | ||
437 | 4 | """Tests for the Launchpad object factory.""" | 4 | """Tests for the Launchpad object factory.""" |
438 | @@ -833,11 +833,14 @@ | |||
439 | 833 | def test_makeCopyJobPackageUpload_passes_on_args(self): | 833 | def test_makeCopyJobPackageUpload_passes_on_args(self): |
440 | 834 | distroseries = self.factory.makeDistroSeries() | 834 | distroseries = self.factory.makeDistroSeries() |
441 | 835 | spn = self.factory.makeSourcePackageName() | 835 | spn = self.factory.makeSourcePackageName() |
442 | 836 | source_archive = self.factory.makeArchive() | ||
443 | 836 | pu = self.factory.makeCopyJobPackageUpload( | 837 | pu = self.factory.makeCopyJobPackageUpload( |
445 | 837 | distroseries=distroseries, sourcepackagename=spn) | 838 | distroseries=distroseries, sourcepackagename=spn, |
446 | 839 | source_archive=source_archive) | ||
447 | 838 | job = removeSecurityProxy(pu.package_copy_job) | 840 | job = removeSecurityProxy(pu.package_copy_job) |
448 | 839 | self.assertEqual(distroseries, pu.distroseries) | 841 | self.assertEqual(distroseries, pu.distroseries) |
449 | 840 | self.assertEqual(distroseries.distribution, pu.archive.distribution) | 842 | self.assertEqual(distroseries.distribution, pu.archive.distribution) |
450 | 843 | self.assertEqual(source_archive, job.source_archive) | ||
451 | 841 | self.assertEqual(distroseries, job.target_distroseries) | 844 | self.assertEqual(distroseries, job.target_distroseries) |
452 | 842 | self.assertEqual(spn.name, job.package_name) | 845 | self.assertEqual(spn.name, job.package_name) |
453 | 843 | 846 |
Did you consider just using fmt:link for the archive? It's probably not worth special casing PPAs, as non-primary archives have informative URLs that at worst just redirect to the distribution, still making the origin clearer.