Merge lp:~michael.nelson/launchpad/496862-ppa-installable-binaries into lp:launchpad
- 496862-ppa-installable-binaries
- Merge into devel
Status: | Rejected |
---|---|
Rejected by: | Michael Nelson |
Proposed branch: | lp:~michael.nelson/launchpad/496862-ppa-installable-binaries |
Merge into: | lp:launchpad |
Diff against target: |
675 lines (+240/-89) 10 files modified
lib/lp/soyuz/browser/archive.py (+79/-40) lib/lp/soyuz/browser/tests/archive-views.txt (+51/-37) lib/lp/soyuz/browser/tests/test_archive_view.py (+59/-0) lib/lp/soyuz/doc/archive.txt (+14/-4) lib/lp/soyuz/interfaces/archive.py (+7/-0) lib/lp/soyuz/model/archive.py (+23/-1) lib/lp/soyuz/templates/archive-copy-packages.pt (+1/-1) lib/lp/soyuz/templates/archive-delete-packages.pt (+1/-1) lib/lp/soyuz/templates/archive-index.pt (+1/-1) lib/lp/soyuz/templates/archive-macros.pt (+4/-4) |
To merge this branch: | bzr merge lp:~michael.nelson/launchpad/496862-ppa-installable-binaries |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jonathan Lange (community) | Approve | ||
Review via email: mp+21623@code.launchpad.net |
Commit message
Description of the change
This branch is the first of two to address bug 496862.
It adds an ArchiveArchFactory to create a vocabulary dynamically depending on the binaries published in the archive.
The ArchiveSourcePa
Finally, the ArchiveView (for the PPA index page) is updated to present binary packages.
The next branch will switch the PPA index ui to present the binary packages.
To test:
bin/test -vv -t archive-views.txt -t archive.txt -t TestArchiveView
Jonathan Lange (jml) wrote : | # |
Jonathan Lange (jml) wrote : | # |
On Thu, Mar 18, 2010 at 12:02 PM, Jonathan Lange <email address hidden> wrote:
...
>> + def make_binaries_
>
> Methods should be camelCase :( I hate that rule.
>
Per our recent discussion, this is comment is correct and the method
needs to change...
>> + def test_batched_
>
> test_batchedPac
>
But this comment is not. The test can remain the same.
>> + def test_batched_
>
> testBatchedPack
>
Likewise here.
jml
Michael Nelson (michael.nelson) wrote : | # |
On Thu, Mar 18, 2010 at 12:03 PM, Jonathan Lange <email address hidden> wrote:
> On Thu, Mar 18, 2010 at 9:24 AM, Michael Nelson
...
>> === modified file 'lib/lp/
>> --- lib/lp/
>> +++ lib/lp/
>> @@ -589,6 +589,26 @@
>> return SimpleVocabular
>>
>>
>> +class ArchiveArchVoca
>> + """A factory for generating vocabularies of an archive's arches in
>> + a given distroseries."""
>> +
>> + implements(
>> +
>> + def __call__(self, context):
>> + """Return a vocabulary created dynamically from the context archive.
>> +
>> + :param context: The archive used to generating vocabulary.
>> + """
>> + arch_terms = []
>> + for arch in context.
>> + term = SimpleTerm(
>> + arch, token=arch.
>> + title=arch.
>> + arch_terms.
>> + return SimpleVocabular
>> +
>
> Why is this a class and not a function? Is the implements() bit really
> important?
>
Yep, but I used the zope.interface.
and it worked well :)
>> === added file 'lib/lp/
>> --- lib/lp/
>> +++ lib/lp/
>> @@ -0,0 +1,59 @@
>> +# Copyright 2010 Canonical Ltd. This software is licensed under the
>> +# GNU Affero General Public License version 3 (see the file LICENSE).
>> +
>> +"""Test the main PPA (user) view functionality."""
>> +
>> +__metaclass__ = type
>> +
>> +import unittest
>> +
>> +from canonical.
>> +from canonical.testing import LaunchpadFuncti
>> +from lp.soyuz.
>> +from lp.testing import TestCaseWithFactory
>> +from lp.testing.views import create_
>> +
>> +
>> +class TestArchiveView
>> +
>> + layer = LaunchpadFuncti
>> +
>
> You need the librarian?
Yes, publishing test data with the SoyuzTestPublisher adds librarian
files for the tests.
>
>> + def setUp(self):
>> + """Create a ppa with some publishings for different arches
>> + and in different distroseries."""
>
> Closing triple quotes on newline please.
Done.
>
>> + def make_binaries_
>
> Methods should be camelCase :( I hate that rule.
Done.
>
>> + def test_batched_
>
> test_batchedPac
Not done, as your subsequent email.
>
>> + def test_batched_
>
> testBatchedPack
And again.
>
>> === modified file 'lib/lp/
>> --- lib/lp/
>> +++ lib/lp/
>> @@ -47,6 +47,7 @@
>> from canonical.
>> from lp.registry.
>> ...
1 | === modified file 'lib/lp/soyuz/browser/archive.py' | |||
2 | --- lib/lp/soyuz/browser/archive.py 2010-03-18 09:08:28 +0000 | |||
3 | +++ lib/lp/soyuz/browser/archive.py 2010-03-18 17:01:59 +0000 | |||
4 | @@ -33,7 +33,7 @@ | |||
5 | 33 | from zope.app.form.browser import TextAreaWidget | 33 | from zope.app.form.browser import TextAreaWidget |
6 | 34 | from zope.component import getUtility | 34 | from zope.component import getUtility |
7 | 35 | from zope.formlib import form | 35 | from zope.formlib import form |
9 | 36 | from zope.interface import implements, Interface | 36 | from zope.interface import directlyProvides, implements, Interface |
10 | 37 | from zope.security.proxy import removeSecurityProxy | 37 | from zope.security.proxy import removeSecurityProxy |
11 | 38 | from zope.schema import Choice, List, TextLine | 38 | from zope.schema import Choice, List, TextLine |
12 | 39 | from zope.schema.interfaces import IContextSourceBinder | 39 | from zope.schema.interfaces import IContextSourceBinder |
13 | @@ -568,45 +568,22 @@ | |||
14 | 568 | return list(copy_requests) | 568 | return list(copy_requests) |
15 | 569 | 569 | ||
16 | 570 | 570 | ||
56 | 571 | class ArchiveSeriesVocabularyFactory: | 571 | def archive_series_vocabulary_factory(context): |
57 | 572 | """A factory for generating vocabularies of an archive's series.""" | 572 | """Return a vocabulary created dynamically from the context archive. |
58 | 573 | 573 | ||
59 | 574 | implements(IContextSourceBinder) | 574 | :param context: The context used to generate the vocabulary. This |
60 | 575 | 575 | is passed automatically by the zope machinery. Therefore | |
61 | 576 | def __call__(self, context): | 576 | this factory can only be used in a class where the context is |
62 | 577 | """Return a vocabulary created dynamically from the context archive. | 577 | an IArchive. |
63 | 578 | 578 | """ | |
64 | 579 | :param context: The context used to generate the vocabulary. This | 579 | series_terms = [] |
65 | 580 | is passed automatically by the zope machinery. Therefore | 580 | for distroseries in context.series_with_sources: |
66 | 581 | this factory can only be used in a class where the context is | 581 | series_terms.append( |
67 | 582 | an IArchive. | 582 | SimpleTerm(distroseries, token=distroseries.name, |
68 | 583 | """ | 583 | title=distroseries.displayname)) |
69 | 584 | series_terms = [] | 584 | return SimpleVocabulary(series_terms) |
70 | 585 | for distroseries in context.series_with_sources: | 585 | |
71 | 586 | series_terms.append( | 586 | directlyProvides(archive_series_vocabulary_factory, IContextSourceBinder) |
33 | 587 | SimpleTerm(distroseries, token=distroseries.name, | ||
34 | 588 | title=distroseries.displayname)) | ||
35 | 589 | return SimpleVocabulary(series_terms) | ||
36 | 590 | |||
37 | 591 | |||
38 | 592 | class ArchiveArchVocabularyFactory: | ||
39 | 593 | """A factory for generating vocabularies of an archive's arches in | ||
40 | 594 | a given distroseries.""" | ||
41 | 595 | |||
42 | 596 | implements(IContextSourceBinder) | ||
43 | 597 | |||
44 | 598 | def __call__(self, context): | ||
45 | 599 | """Return a vocabulary created dynamically from the context archive. | ||
46 | 600 | |||
47 | 601 | :param context: The archive used to generating vocabulary. | ||
48 | 602 | """ | ||
49 | 603 | arch_terms = [] | ||
50 | 604 | for arch in context.arches_with_binaries: | ||
51 | 605 | term = SimpleTerm( | ||
52 | 606 | arch, token=arch.architecturetag, | ||
53 | 607 | title=arch.displayname) | ||
54 | 608 | arch_terms.append(term) | ||
55 | 609 | return SimpleVocabulary(arch_terms) | ||
72 | 610 | 587 | ||
73 | 611 | 588 | ||
74 | 612 | class SeriesFilterWidget(LaunchpadDropdownWidget): | 589 | class SeriesFilterWidget(LaunchpadDropdownWidget): |
75 | @@ -625,7 +602,7 @@ | |||
76 | 625 | title=_("Package name contains"), required=False) | 602 | title=_("Package name contains"), required=False) |
77 | 626 | 603 | ||
78 | 627 | series_filter = Choice( | 604 | series_filter = Choice( |
80 | 628 | source=ArchiveSeriesVocabularyFactory(), required=False) | 605 | source=archive_series_vocabulary_factory, required=False) |
81 | 629 | 606 | ||
82 | 630 | status_filter = Choice(vocabulary=SimpleVocabulary(( | 607 | status_filter = Choice(vocabulary=SimpleVocabulary(( |
83 | 631 | SimpleTerm(active_publishing_status, 'published', 'Published'), | 608 | SimpleTerm(active_publishing_status, 'published', 'Published'), |
84 | @@ -633,10 +610,26 @@ | |||
85 | 633 | )), required=False) | 610 | )), required=False) |
86 | 634 | 611 | ||
87 | 635 | 612 | ||
88 | 613 | def archive_arch_vocabulary_factory(context): | ||
89 | 614 | """Return a vocabulary created dynamically from the context archive. | ||
90 | 615 | |||
91 | 616 | :param context: The archive used to generating vocabulary. | ||
92 | 617 | """ | ||
93 | 618 | arch_terms = [] | ||
94 | 619 | for arch in context.arches_with_binaries: | ||
95 | 620 | term = SimpleTerm( | ||
96 | 621 | arch, token=arch.architecturetag, | ||
97 | 622 | title=arch.displayname) | ||
98 | 623 | arch_terms.append(term) | ||
99 | 624 | return SimpleVocabulary(arch_terms) | ||
100 | 625 | |||
101 | 626 | directlyProvides(archive_arch_vocabulary_factory, IContextSourceBinder) | ||
102 | 627 | |||
103 | 628 | |||
104 | 636 | class IArchiveBinaryPackageFilter(IPPAPackageFilter): | 629 | class IArchiveBinaryPackageFilter(IPPAPackageFilter): |
105 | 637 | """The interface used for filtering binary packages.""" | 630 | """The interface used for filtering binary packages.""" |
106 | 638 | arch_filter = Choice( | 631 | arch_filter = Choice( |
108 | 639 | source=ArchiveArchVocabularyFactory(), required=False) | 632 | source=archive_arch_vocabulary_factory, required=False) |
109 | 640 | 633 | ||
110 | 641 | 634 | ||
111 | 642 | class ArchivePackagesViewBase(ArchiveViewBase, LaunchpadFormView): | 635 | class ArchivePackagesViewBase(ArchiveViewBase, LaunchpadFormView): |
112 | 643 | 636 | ||
113 | === modified file 'lib/lp/soyuz/browser/tests/test_archive_view.py' | |||
114 | --- lib/lp/soyuz/browser/tests/test_archive_view.py 2010-03-18 09:06:00 +0000 | |||
115 | +++ lib/lp/soyuz/browser/tests/test_archive_view.py 2010-03-18 17:14:42 +0000 | |||
116 | @@ -29,7 +29,7 @@ | |||
117 | 29 | 29 | ||
118 | 30 | test_publisher.getPubBinaries(archive=self.ppa) | 30 | test_publisher.getPubBinaries(archive=self.ppa) |
119 | 31 | 31 | ||
121 | 32 | def make_binaries_readable(self, binaries): | 32 | def makeBinariesReadable(self, binaries): |
122 | 33 | """Return a string representation of the binaries.""" | 33 | """Return a string representation of the binaries.""" |
123 | 34 | return ", ".join([ | 34 | return ", ".join([ |
124 | 35 | "%s in %s" % ( | 35 | "%s in %s" % ( |
125 | @@ -45,7 +45,7 @@ | |||
126 | 45 | self.assertEqual( | 45 | self.assertEqual( |
127 | 46 | "foo-bin in ubuntutest Breezy Badger Autotest i386, " | 46 | "foo-bin in ubuntutest Breezy Badger Autotest i386, " |
128 | 47 | "foo-bin in ubuntutest Breezy Badger Autotest hppa", | 47 | "foo-bin in ubuntutest Breezy Badger Autotest hppa", |
130 | 48 | self.make_binaries_readable(view.batched_packages)) | 48 | self.makeBinariesReadable(view.batched_packages)) |
131 | 49 | 49 | ||
132 | 50 | def test_batched_packages_filter_arch(self): | 50 | def test_batched_packages_filter_arch(self): |
133 | 51 | view = create_initialized_view( | 51 | view = create_initialized_view( |
134 | @@ -53,7 +53,7 @@ | |||
135 | 53 | query_string="field.arch_filter=i386") | 53 | query_string="field.arch_filter=i386") |
136 | 54 | self.assertEqual( | 54 | self.assertEqual( |
137 | 55 | "foo-bin in ubuntutest Breezy Badger Autotest i386", | 55 | "foo-bin in ubuntutest Breezy Badger Autotest i386", |
139 | 56 | self.make_binaries_readable(view.batched_packages)) | 56 | self.makeBinariesReadable(view.batched_packages)) |
140 | 57 | 57 | ||
141 | 58 | def test_suite(): | 58 | def test_suite(): |
142 | 59 | return unittest.TestLoader().loadTestsFromName(__name__) | 59 | return unittest.TestLoader().loadTestsFromName(__name__) |
- 10544. By Michael Nelson
-
Refactored dynamic vocabularies to functions rather than classes.
- 10545. By Michael Nelson
-
test style method renames.
Unmerged revisions
- 10545. By Michael Nelson
-
test style method renames.
- 10544. By Michael Nelson
-
Refactored dynamic vocabularies to functions rather than classes.
- 10543. By Michael Nelson
-
Removed branch todos as they're done.
- 10542. By Michael Nelson
-
Removed lint
- 10541. By Michael Nelson
-
Added unit tests for ArchiveView.
batched_ packages. - 10540. By Michael Nelson
-
Added filtered binary packages to the ArchiveView.
- 10539. By Michael Nelson
-
Adds IArchive.
arches_ with_binaries. - 10538. By Michael Nelson
-
Added failing test for IArchive.
arches_ with_binaries. - 10537. By Michael Nelson
-
A few more s/sources/packages renaming.
- 10536. By Michael Nelson
-
Initial renaming to re-use ArchivePackages
ViewBase for both source and binary
package listings.
Preview Diff
1 | === modified file 'lib/lp/soyuz/browser/archive.py' | |||
2 | --- lib/lp/soyuz/browser/archive.py 2010-03-10 12:50:18 +0000 | |||
3 | +++ lib/lp/soyuz/browser/archive.py 2010-03-22 11:43:06 +0000 | |||
4 | @@ -33,7 +33,7 @@ | |||
5 | 33 | from zope.app.form.browser import TextAreaWidget | 33 | from zope.app.form.browser import TextAreaWidget |
6 | 34 | from zope.component import getUtility | 34 | from zope.component import getUtility |
7 | 35 | from zope.formlib import form | 35 | from zope.formlib import form |
9 | 36 | from zope.interface import implements, Interface | 36 | from zope.interface import directlyProvides, implements, Interface |
10 | 37 | from zope.security.proxy import removeSecurityProxy | 37 | from zope.security.proxy import removeSecurityProxy |
11 | 38 | from zope.schema import Choice, List, TextLine | 38 | from zope.schema import Choice, List, TextLine |
12 | 39 | from zope.schema.interfaces import IContextSourceBinder | 39 | from zope.schema.interfaces import IContextSourceBinder |
13 | @@ -568,25 +568,22 @@ | |||
14 | 568 | return list(copy_requests) | 568 | return list(copy_requests) |
15 | 569 | 569 | ||
16 | 570 | 570 | ||
36 | 571 | class ArchiveSeriesVocabularyFactory: | 571 | def archive_series_vocabulary_factory(context): |
37 | 572 | """A factory for generating vocabularies of an archive's series.""" | 572 | """Return a vocabulary created dynamically from the context archive. |
38 | 573 | 573 | ||
39 | 574 | implements(IContextSourceBinder) | 574 | :param context: The context used to generate the vocabulary. This |
40 | 575 | 575 | is passed automatically by the zope machinery. Therefore | |
41 | 576 | def __call__(self, context): | 576 | this factory can only be used in a class where the context is |
42 | 577 | """Return a vocabulary created dynamically from the context archive. | 577 | an IArchive. |
43 | 578 | 578 | """ | |
44 | 579 | :param context: The context used to generate the vocabulary. This | 579 | series_terms = [] |
45 | 580 | is passed automatically by the zope machinery. Therefore | 580 | for distroseries in context.series_with_sources: |
46 | 581 | this factory can only be used in a class where the context is | 581 | series_terms.append( |
47 | 582 | an IArchive. | 582 | SimpleTerm(distroseries, token=distroseries.name, |
48 | 583 | """ | 583 | title=distroseries.displayname)) |
49 | 584 | series_terms = [] | 584 | return SimpleVocabulary(series_terms) |
50 | 585 | for distroseries in context.series_with_sources: | 585 | |
51 | 586 | series_terms.append( | 586 | directlyProvides(archive_series_vocabulary_factory, IContextSourceBinder) |
33 | 587 | SimpleTerm(distroseries, token=distroseries.name, | ||
34 | 588 | title=distroseries.displayname)) | ||
35 | 589 | return SimpleVocabulary(series_terms) | ||
52 | 590 | 587 | ||
53 | 591 | 588 | ||
54 | 592 | class SeriesFilterWidget(LaunchpadDropdownWidget): | 589 | class SeriesFilterWidget(LaunchpadDropdownWidget): |
55 | @@ -605,7 +602,7 @@ | |||
56 | 605 | title=_("Package name contains"), required=False) | 602 | title=_("Package name contains"), required=False) |
57 | 606 | 603 | ||
58 | 607 | series_filter = Choice( | 604 | series_filter = Choice( |
60 | 608 | source=ArchiveSeriesVocabularyFactory(), required=False) | 605 | source=archive_series_vocabulary_factory, required=False) |
61 | 609 | 606 | ||
62 | 610 | status_filter = Choice(vocabulary=SimpleVocabulary(( | 607 | status_filter = Choice(vocabulary=SimpleVocabulary(( |
63 | 611 | SimpleTerm(active_publishing_status, 'published', 'Published'), | 608 | SimpleTerm(active_publishing_status, 'published', 'Published'), |
64 | @@ -613,8 +610,30 @@ | |||
65 | 613 | )), required=False) | 610 | )), required=False) |
66 | 614 | 611 | ||
67 | 615 | 612 | ||
70 | 616 | class ArchiveSourcePackageListViewBase(ArchiveViewBase, LaunchpadFormView): | 613 | def archive_arch_vocabulary_factory(context): |
71 | 617 | """A Form view for filtering and batching source packages.""" | 614 | """Return a vocabulary created dynamically from the context archive. |
72 | 615 | |||
73 | 616 | :param context: The archive used to generating vocabulary. | ||
74 | 617 | """ | ||
75 | 618 | arch_terms = [] | ||
76 | 619 | for arch in context.arches_with_binaries: | ||
77 | 620 | term = SimpleTerm( | ||
78 | 621 | arch, token=arch.architecturetag, | ||
79 | 622 | title=arch.displayname) | ||
80 | 623 | arch_terms.append(term) | ||
81 | 624 | return SimpleVocabulary(arch_terms) | ||
82 | 625 | |||
83 | 626 | directlyProvides(archive_arch_vocabulary_factory, IContextSourceBinder) | ||
84 | 627 | |||
85 | 628 | |||
86 | 629 | class IArchiveBinaryPackageFilter(IPPAPackageFilter): | ||
87 | 630 | """The interface used for filtering binary packages.""" | ||
88 | 631 | arch_filter = Choice( | ||
89 | 632 | source=archive_arch_vocabulary_factory, required=False) | ||
90 | 633 | |||
91 | 634 | |||
92 | 635 | class ArchivePackagesViewBase(ArchiveViewBase, LaunchpadFormView): | ||
93 | 636 | """A form view for filtering and batching source or binary packages.""" | ||
94 | 618 | 637 | ||
95 | 619 | schema = IPPAPackageFilter | 638 | schema = IPPAPackageFilter |
96 | 620 | custom_widget('series_filter', SeriesFilterWidget) | 639 | custom_widget('series_filter', SeriesFilterWidget) |
97 | @@ -622,7 +641,7 @@ | |||
98 | 622 | 641 | ||
99 | 623 | # By default this view will not display the sources with selectable | 642 | # By default this view will not display the sources with selectable |
100 | 624 | # checkboxes, but subclasses can override as needed. | 643 | # checkboxes, but subclasses can override as needed. |
102 | 625 | selectable_sources = False | 644 | selectable_packages = False |
103 | 626 | 645 | ||
104 | 627 | @cachedproperty | 646 | @cachedproperty |
105 | 628 | def series_with_sources(self): | 647 | def series_with_sources(self): |
106 | @@ -680,7 +699,7 @@ | |||
107 | 680 | self.getSelectedFilterValue('series_filter')) | 699 | self.getSelectedFilterValue('series_filter')) |
108 | 681 | 700 | ||
109 | 682 | @property | 701 | @property |
111 | 683 | def filtered_sources(self): | 702 | def filtered_packages(self): |
112 | 684 | """Return the source results for display after filtering.""" | 703 | """Return the source results for display after filtering.""" |
113 | 685 | return self.context.getPublishedSources( | 704 | return self.context.getPublishedSources( |
114 | 686 | name=self.specified_name_filter, | 705 | name=self.specified_name_filter, |
115 | @@ -707,31 +726,33 @@ | |||
116 | 707 | @cachedproperty | 726 | @cachedproperty |
117 | 708 | def batchnav(self): | 727 | def batchnav(self): |
118 | 709 | """Return a batch navigator of the filtered sources.""" | 728 | """Return a batch navigator of the filtered sources.""" |
120 | 710 | return BatchNavigator(self.filtered_sources, self.request) | 729 | return BatchNavigator(self.filtered_packages, self.request) |
121 | 711 | 730 | ||
122 | 712 | @cachedproperty | 731 | @cachedproperty |
124 | 713 | def batched_sources(self): | 732 | def batched_packages(self): |
125 | 714 | """Return the current batch of archive source publications.""" | 733 | """Return the current batch of archive source publications.""" |
126 | 715 | results = list(self.batchnav.currentBatch()) | 734 | results = list(self.batchnav.currentBatch()) |
127 | 716 | return ArchiveSourcePublications(results) | 735 | return ArchiveSourcePublications(results) |
128 | 717 | 736 | ||
129 | 718 | @cachedproperty | 737 | @cachedproperty |
131 | 719 | def has_sources_for_display(self): | 738 | def has_packages_for_display(self): |
132 | 720 | """Whether or not the PPA has any source packages for display. | 739 | """Whether or not the PPA has any source packages for display. |
133 | 721 | 740 | ||
134 | 722 | This is after any filtering or overriding of the sources() method. | 741 | This is after any filtering or overriding of the sources() method. |
135 | 723 | """ | 742 | """ |
136 | 724 | # XXX cprov 20080708 bug=246200: use bool() when it gets fixed | 743 | # XXX cprov 20080708 bug=246200: use bool() when it gets fixed |
137 | 725 | # in storm. | 744 | # in storm. |
142 | 726 | return self.filtered_sources.count() > 0 | 745 | return self.filtered_packages.count() > 0 |
143 | 727 | 746 | ||
144 | 728 | 747 | ||
145 | 729 | class ArchiveView(ArchiveSourcePackageListViewBase): | 748 | class ArchiveView(ArchivePackagesViewBase): |
146 | 730 | """Default Archive view class. | 749 | """Default Archive view class. |
147 | 731 | 750 | ||
148 | 732 | Implements useful actions and collects useful sets for the page template. | 751 | Implements useful actions and collects useful sets for the page template. |
149 | 733 | """ | 752 | """ |
150 | 734 | 753 | ||
151 | 754 | schema = IArchiveBinaryPackageFilter | ||
152 | 755 | |||
153 | 735 | __used_for__ = IArchive | 756 | __used_for__ = IArchive |
154 | 736 | implements(IArchiveIndexActionsMenu) | 757 | implements(IArchiveIndexActionsMenu) |
155 | 737 | 758 | ||
156 | @@ -744,6 +765,19 @@ | |||
157 | 744 | super(ArchiveView, self).initialize() | 765 | super(ArchiveView, self).initialize() |
158 | 745 | 766 | ||
159 | 746 | @property | 767 | @property |
160 | 768 | def filtered_packages(self): | ||
161 | 769 | """Return the binary results for display after filtering.""" | ||
162 | 770 | return self.context.getAllPublishedBinaries( | ||
163 | 771 | name=self.specified_name_filter, | ||
164 | 772 | status=self.getSelectedFilterValue('status_filter'), | ||
165 | 773 | distroarchseries=self.getSelectedFilterValue('arch_filter')) | ||
166 | 774 | |||
167 | 775 | @cachedproperty | ||
168 | 776 | def batched_packages(self): | ||
169 | 777 | """Return the current batch of archive binary publications.""" | ||
170 | 778 | return self.batchnav.currentBatch() | ||
171 | 779 | |||
172 | 780 | @property | ||
173 | 747 | def displayname_edit_widget(self): | 781 | def displayname_edit_widget(self): |
174 | 748 | widget = TextLineEditorWidget( | 782 | widget = TextLineEditorWidget( |
175 | 749 | self.context, 'displayname', | 783 | self.context, 'displayname', |
176 | @@ -777,6 +811,11 @@ | |||
177 | 777 | return None | 811 | return None |
178 | 778 | 812 | ||
179 | 779 | @property | 813 | @property |
180 | 814 | def default_arch_filter(self): | ||
181 | 815 | """Return the architecture identified by the user-agent.""" | ||
182 | 816 | return None | ||
183 | 817 | |||
184 | 818 | @property | ||
185 | 780 | def archive_description_html(self): | 819 | def archive_description_html(self): |
186 | 781 | """The archive's description as HTML.""" | 820 | """The archive's description as HTML.""" |
187 | 782 | formatter = FormattersAPI | 821 | formatter = FormattersAPI |
188 | @@ -889,7 +928,7 @@ | |||
189 | 889 | } | 928 | } |
190 | 890 | 929 | ||
191 | 891 | 930 | ||
193 | 892 | class ArchivePackagesView(ArchiveSourcePackageListViewBase): | 931 | class ArchivePackagesView(ArchivePackagesViewBase): |
194 | 893 | """Detailed packages view for an archive.""" | 932 | """Detailed packages view for an archive.""" |
195 | 894 | implements(IArchivePackagesActionMenu) | 933 | implements(IArchivePackagesActionMenu) |
196 | 895 | 934 | ||
197 | @@ -915,12 +954,12 @@ | |||
198 | 915 | return self.context.is_copy | 954 | return self.context.is_copy |
199 | 916 | 955 | ||
200 | 917 | 956 | ||
202 | 918 | class ArchiveSourceSelectionFormView(ArchiveSourcePackageListViewBase): | 957 | class ArchiveSourceSelectionFormView(ArchivePackagesViewBase): |
203 | 919 | """Base class to implement a source selection widget for PPAs.""" | 958 | """Base class to implement a source selection widget for PPAs.""" |
204 | 920 | 959 | ||
205 | 921 | custom_widget('selected_sources', LabeledMultiCheckBoxWidget) | 960 | custom_widget('selected_sources', LabeledMultiCheckBoxWidget) |
206 | 922 | 961 | ||
208 | 923 | selectable_sources = True | 962 | selectable_packages = True |
209 | 924 | 963 | ||
210 | 925 | def setNextURL(self): | 964 | def setNextURL(self): |
211 | 926 | """Set self.next_url based on current context. | 965 | """Set self.next_url based on current context. |
212 | @@ -938,7 +977,7 @@ | |||
213 | 938 | """ | 977 | """ |
214 | 939 | # To create the selected sources field, we need to define a | 978 | # To create the selected sources field, we need to define a |
215 | 940 | # vocabulary based on the currently selected sources (using self | 979 | # vocabulary based on the currently selected sources (using self |
217 | 941 | # batched_sources) but this itself requires the current values of | 980 | # batched_packages) but this itself requires the current values of |
218 | 942 | # the filtering widgets. So we setup the widgets, then add the | 981 | # the filtering widgets. So we setup the widgets, then add the |
219 | 943 | # extra field and create its widget too. | 982 | # extra field and create its widget too. |
220 | 944 | super(ArchiveSourceSelectionFormView, self).setUpWidgets() | 983 | super(ArchiveSourceSelectionFormView, self).setUpWidgets() |
221 | @@ -955,7 +994,7 @@ | |||
222 | 955 | 994 | ||
223 | 956 | Ensure focus is only set if there are sources actually presented. | 995 | Ensure focus is only set if there are sources actually presented. |
224 | 957 | """ | 996 | """ |
226 | 958 | if not self.has_sources_for_display: | 997 | if not self.has_packages_for_display: |
227 | 959 | return '' | 998 | return '' |
228 | 960 | return LaunchpadFormView.focusedElementScript(self) | 999 | return LaunchpadFormView.focusedElementScript(self) |
229 | 961 | 1000 | ||
230 | @@ -968,7 +1007,7 @@ | |||
231 | 968 | """ | 1007 | """ |
232 | 969 | terms = [] | 1008 | terms = [] |
233 | 970 | 1009 | ||
235 | 971 | for pub in self.batched_sources: | 1010 | for pub in self.batched_packages: |
236 | 972 | terms.append(SimpleTerm(pub, str(pub.id), pub.displayname)) | 1011 | terms.append(SimpleTerm(pub, str(pub.id), pub.displayname)) |
237 | 973 | return form.Fields( | 1012 | return form.Fields( |
238 | 974 | List(__name__='selected_sources', | 1013 | List(__name__='selected_sources', |
239 | @@ -1010,10 +1049,10 @@ | |||
240 | 1010 | return None | 1049 | return None |
241 | 1011 | 1050 | ||
242 | 1012 | @cachedproperty | 1051 | @cachedproperty |
244 | 1013 | def filtered_sources(self): | 1052 | def filtered_packages(self): |
245 | 1014 | """Return the filtered results of publishing records for deletion. | 1053 | """Return the filtered results of publishing records for deletion. |
246 | 1015 | 1054 | ||
248 | 1016 | This overrides ArchiveViewBase.filtered_sources to use a | 1055 | This overrides ArchiveViewBase.filtered_packages to use a |
249 | 1017 | different method on the context specific to deletion records. | 1056 | different method on the context specific to deletion records. |
250 | 1018 | """ | 1057 | """ |
251 | 1019 | return self.context.getSourcesForDeletion( | 1058 | return self.context.getSourcesForDeletion( |
252 | 1020 | 1059 | ||
253 | === modified file 'lib/lp/soyuz/browser/tests/archive-views.txt' | |||
254 | --- lib/lp/soyuz/browser/tests/archive-views.txt 2010-03-06 04:57:40 +0000 | |||
255 | +++ lib/lp/soyuz/browser/tests/archive-views.txt 2010-03-22 11:43:06 +0000 | |||
256 | @@ -165,27 +165,17 @@ | |||
257 | 165 | used_css_class: green | 165 | used_css_class: green |
258 | 166 | used_percentage: 0.92 | 166 | used_percentage: 0.92 |
259 | 167 | 167 | ||
281 | 168 | An ArchiveView provides a batched_sources property that can be used | 168 | An ArchiveView provides a batched_packages property that can be used |
282 | 169 | to get the current batch of publishing records for an archive: | 169 | to get the current batch of installable binaries published for an archive: |
283 | 170 | 170 | ||
284 | 171 | >>> for publishing in ppa_archive_view.batched_sources: | 171 | >>> for pub in ppa_archive_view.batched_packages: |
285 | 172 | ... print publishing.source_package_name | 172 | ... print "%s in %s" % ( |
286 | 173 | cdrkit | 173 | ... pub.binary_package_name, |
287 | 174 | iceweasel | 174 | ... pub.distroarchseries.displayname) |
288 | 175 | pmount | 175 | mozilla-firefox in Ubuntu Warty hppa |
289 | 176 | 176 | mozilla-firefox in Ubuntu Warty i386 | |
290 | 177 | The batched_sources property will also be filtered by distroseries when | 177 | pmount in Ubuntu Warty hppa |
291 | 178 | appropriate: | 178 | pmount in Ubuntu Warty i386 |
271 | 179 | |||
272 | 180 | >>> filtered_view = create_initialized_view( | ||
273 | 181 | ... cprov.archive, | ||
274 | 182 | ... name="+index", | ||
275 | 183 | ... method='GET', | ||
276 | 184 | ... query_string='field.series_filter=warty') | ||
277 | 185 | >>> for publishing in filtered_view.batched_sources: | ||
278 | 186 | ... print publishing.source_package_name | ||
279 | 187 | iceweasel | ||
280 | 188 | pmount | ||
292 | 189 | 179 | ||
293 | 190 | The context archive dependencies access is also encapsulated in | 180 | The context archive dependencies access is also encapsulated in |
294 | 191 | `ArchiveView` with the following aspects: | 181 | `ArchiveView` with the following aspects: |
295 | @@ -312,7 +302,8 @@ | |||
296 | 312 | 302 | ||
297 | 313 | >>> from canonical.database.constants import UTC_NOW | 303 | >>> from canonical.database.constants import UTC_NOW |
298 | 314 | >>> login('foo.bar@canonical.com') | 304 | >>> login('foo.bar@canonical.com') |
300 | 315 | >>> view.filtered_sources[1].datepublished = UTC_NOW | 305 | >>> iceweasel = view.context.getPublishedSources(name="iceweasel")[0] |
301 | 306 | >>> iceweasel.datepublished = UTC_NOW | ||
302 | 316 | >>> login(ANONYMOUS) | 307 | >>> login(ANONYMOUS) |
303 | 317 | >>> print_latest_updates(view.latest_updates) | 308 | >>> print_latest_updates(view.latest_updates) |
304 | 318 | iceweasel - Successfully built | 309 | iceweasel - Successfully built |
305 | @@ -333,9 +324,10 @@ | |||
306 | 333 | >>> import pytz | 324 | >>> import pytz |
307 | 334 | >>> thirtyone_days_ago = datetime.now(tz=pytz.UTC) - timedelta(31) | 325 | >>> thirtyone_days_ago = datetime.now(tz=pytz.UTC) - timedelta(31) |
308 | 335 | >>> login('foo.bar@canonical.com') | 326 | >>> login('foo.bar@canonical.com') |
312 | 336 | >>> view.filtered_sources[0].datecreated = UTC_NOW | 327 | >>> sources = view.context.getPublishedSources() |
313 | 337 | >>> view.filtered_sources[1].datecreated = UTC_NOW | 328 | >>> sources[0].datecreated = UTC_NOW |
314 | 338 | >>> view.filtered_sources[2].datecreated = thirtyone_days_ago | 329 | >>> sources[1].datecreated = UTC_NOW |
315 | 330 | >>> sources[2].datecreated = thirtyone_days_ago | ||
316 | 339 | >>> login(ANONYMOUS) | 331 | >>> login(ANONYMOUS) |
317 | 340 | 332 | ||
318 | 341 | >>> view.num_updates_over_last_days() | 333 | >>> view.num_updates_over_last_days() |
319 | @@ -358,7 +350,7 @@ | |||
320 | 358 | >>> from lp.buildmaster.interfaces.buildbase import BuildStatus | 350 | >>> from lp.buildmaster.interfaces.buildbase import BuildStatus |
321 | 359 | >>> from lp.soyuz.interfaces.build import IBuildSet | 351 | >>> from lp.soyuz.interfaces.build import IBuildSet |
322 | 360 | >>> warty_hppa = getUtility(IDistributionSet)['ubuntu']['warty']['hppa'] | 352 | >>> warty_hppa = getUtility(IDistributionSet)['ubuntu']['warty']['hppa'] |
324 | 361 | >>> source = view.filtered_sources[0] | 353 | >>> source = sources[0] |
325 | 362 | >>> ignore = source.sourcepackagerelease.createBuild( | 354 | >>> ignore = source.sourcepackagerelease.createBuild( |
326 | 363 | ... distroarchseries=warty_hppa, archive=view.context, | 355 | ... distroarchseries=warty_hppa, archive=view.context, |
327 | 364 | ... pocket=source.pocket) | 356 | ... pocket=source.pocket) |
328 | @@ -487,6 +479,28 @@ | |||
329 | 487 | corresponding properties such as archive_url, build_counters etc. | 479 | corresponding properties such as archive_url, build_counters etc. |
330 | 488 | (see ArchiveView above). | 480 | (see ArchiveView above). |
331 | 489 | 481 | ||
332 | 482 | An ArchiveView provides a batched_packages property that can be used | ||
333 | 483 | to get the current batch of publishing records for an archive: | ||
334 | 484 | |||
335 | 485 | >>> for publishing in ppa_archive_view.batched_packages: | ||
336 | 486 | ... print publishing.source_package_name | ||
337 | 487 | cdrkit | ||
338 | 488 | iceweasel | ||
339 | 489 | pmount | ||
340 | 490 | |||
341 | 491 | The batched_packages property will also be filtered by distroseries when | ||
342 | 492 | appropriate: | ||
343 | 493 | |||
344 | 494 | >>> filtered_view = create_initialized_view( | ||
345 | 495 | ... cprov.archive, | ||
346 | 496 | ... name="+packages", | ||
347 | 497 | ... method='GET', | ||
348 | 498 | ... query_string='field.series_filter=warty') | ||
349 | 499 | >>> for publishing in filtered_view.batched_packages: | ||
350 | 500 | ... print publishing.source_package_name | ||
351 | 501 | iceweasel | ||
352 | 502 | pmount | ||
353 | 503 | |||
354 | 490 | Additionally, ArchivePackageView can display a string representation | 504 | Additionally, ArchivePackageView can display a string representation |
355 | 491 | of the series supported by this archive. | 505 | of the series supported by this archive. |
356 | 492 | 506 | ||
357 | @@ -530,10 +544,10 @@ | |||
358 | 530 | We query the available PUBLISHED sources and use them to build the | 544 | We query the available PUBLISHED sources and use them to build the |
359 | 531 | 'selected_sources' widget. | 545 | 'selected_sources' widget. |
360 | 532 | 546 | ||
362 | 533 | >>> [pub.id for pub in view.batched_sources] | 547 | >>> [pub.id for pub in view.batched_packages] |
363 | 534 | [27, 28, 29] | 548 | [27, 28, 29] |
364 | 535 | 549 | ||
366 | 536 | >>> view.has_sources_for_display | 550 | >>> view.has_packages_for_display |
367 | 537 | True | 551 | True |
368 | 538 | 552 | ||
369 | 539 | >>> len(view.widgets.get('selected_sources').vocabulary) | 553 | >>> len(view.widgets.get('selected_sources').vocabulary) |
370 | @@ -543,7 +557,7 @@ | |||
371 | 543 | the user can refine the available options presented. By default all | 557 | the user can refine the available options presented. By default all |
372 | 544 | available sources are presented with empty filter. | 558 | available sources are presented with empty filter. |
373 | 545 | 559 | ||
375 | 546 | >>> for pub in view.batched_sources: | 560 | >>> for pub in view.batched_packages: |
376 | 547 | ... print pub.displayname | 561 | ... print pub.displayname |
377 | 548 | cdrkit 1.0 in breezy-autotest | 562 | cdrkit 1.0 in breezy-autotest |
378 | 549 | iceweasel 1.0 in warty | 563 | iceweasel 1.0 in warty |
379 | @@ -556,7 +570,7 @@ | |||
380 | 556 | ... cprov.archive, name="+delete-packages", | 570 | ... cprov.archive, name="+delete-packages", |
381 | 557 | ... query_string="field.name_filter=pmount") | 571 | ... query_string="field.name_filter=pmount") |
382 | 558 | 572 | ||
384 | 559 | >>> for pub in view.batched_sources: | 573 | >>> for pub in view.batched_packages: |
385 | 560 | ... print pub.displayname | 574 | ... print pub.displayname |
386 | 561 | pmount 0.1-1 in warty | 575 | pmount 0.1-1 in warty |
387 | 562 | 576 | ||
388 | @@ -568,7 +582,7 @@ | |||
389 | 568 | ... cprov.archive, name="+delete-packages", | 582 | ... cprov.archive, name="+delete-packages", |
390 | 569 | ... query_string="field.name_filter=%C3%A7") | 583 | ... query_string="field.name_filter=%C3%A7") |
391 | 570 | 584 | ||
393 | 571 | >>> len(list(view.batched_sources)) | 585 | >>> len(list(view.batched_packages)) |
394 | 572 | 0 | 586 | 0 |
395 | 573 | 587 | ||
396 | 574 | Similarly, the sources can be filtered by series: | 588 | Similarly, the sources can be filtered by series: |
397 | @@ -577,7 +591,7 @@ | |||
398 | 577 | ... cprov.archive, name="+delete-packages", | 591 | ... cprov.archive, name="+delete-packages", |
399 | 578 | ... query_string="field.series_filter=warty") | 592 | ... query_string="field.series_filter=warty") |
400 | 579 | 593 | ||
402 | 580 | >>> for pub in view.batched_sources: | 594 | >>> for pub in view.batched_packages: |
403 | 581 | ... print pub.displayname | 595 | ... print pub.displayname |
404 | 582 | iceweasel 1.0 in warty | 596 | iceweasel 1.0 in warty |
405 | 583 | pmount 0.1-1 in warty | 597 | pmount 0.1-1 in warty |
406 | @@ -589,7 +603,7 @@ | |||
407 | 589 | ... query_string="field.series_filter=warty", | 603 | ... query_string="field.series_filter=warty", |
408 | 590 | ... form={'batch': '1', 'start': '1'}) | 604 | ... form={'batch': '1', 'start': '1'}) |
409 | 591 | 605 | ||
411 | 592 | >>> for pub in view.batched_sources: | 606 | >>> for pub in view.batched_packages: |
412 | 593 | ... print pub.displayname | 607 | ... print pub.displayname |
413 | 594 | pmount 0.1-1 in warty | 608 | pmount 0.1-1 in warty |
414 | 595 | 609 | ||
415 | @@ -606,7 +620,7 @@ | |||
416 | 606 | ... 'field.selected_sources-empty-marker': 1, | 620 | ... 'field.selected_sources-empty-marker': 1, |
417 | 607 | ... }) | 621 | ... }) |
418 | 608 | 622 | ||
420 | 609 | >>> view.has_sources_for_display | 623 | >>> view.has_packages_for_display |
421 | 610 | False | 624 | False |
422 | 611 | 625 | ||
423 | 612 | >>> import transaction | 626 | >>> import transaction |
424 | @@ -626,7 +640,7 @@ | |||
425 | 626 | ... 'field.selected_sources-empty-marker': 1, | 640 | ... 'field.selected_sources-empty-marker': 1, |
426 | 627 | ... }) | 641 | ... }) |
427 | 628 | 642 | ||
429 | 629 | >>> view.has_sources_for_display | 643 | >>> view.has_packages_for_display |
430 | 630 | False | 644 | False |
431 | 631 | 645 | ||
432 | 632 | >>> len(view.errors) | 646 | >>> len(view.errors) |
433 | @@ -1036,7 +1050,7 @@ | |||
434 | 1036 | different default status filter (only published sources are presented | 1050 | different default status filter (only published sources are presented |
435 | 1037 | by default). | 1051 | by default). |
436 | 1038 | 1052 | ||
438 | 1039 | >>> view.has_sources_for_display | 1053 | >>> view.has_packages_for_display |
439 | 1040 | False | 1054 | False |
440 | 1041 | 1055 | ||
441 | 1042 | In this case, the template can use the has_sources | 1056 | In this case, the template can use the has_sources |
442 | @@ -1054,7 +1068,7 @@ | |||
443 | 1054 | ... cprov.archive, name="+copy-packages", | 1068 | ... cprov.archive, name="+copy-packages", |
444 | 1055 | ... query_string="field.status_filter=") | 1069 | ... query_string="field.status_filter=") |
445 | 1056 | 1070 | ||
447 | 1057 | >>> [pub.status.name for pub in view.batched_sources] | 1071 | >>> [pub.status.name for pub in view.batched_packages] |
448 | 1058 | ['DELETED', 'DELETED', 'DELETED'] | 1072 | ['DELETED', 'DELETED', 'DELETED'] |
449 | 1059 | 1073 | ||
450 | 1060 | This view contains three properties. The first is a list of the PPAs | 1074 | This view contains three properties. The first is a list of the PPAs |
451 | 1061 | 1075 | ||
452 | === added file 'lib/lp/soyuz/browser/tests/test_archive_view.py' | |||
453 | --- lib/lp/soyuz/browser/tests/test_archive_view.py 1970-01-01 00:00:00 +0000 | |||
454 | +++ lib/lp/soyuz/browser/tests/test_archive_view.py 2010-03-22 11:43:06 +0000 | |||
455 | @@ -0,0 +1,59 @@ | |||
456 | 1 | # Copyright 2010 Canonical Ltd. This software is licensed under the | ||
457 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | ||
458 | 3 | |||
459 | 4 | """Test the main PPA (user) view functionality.""" | ||
460 | 5 | |||
461 | 6 | __metaclass__ = type | ||
462 | 7 | |||
463 | 8 | import unittest | ||
464 | 9 | |||
465 | 10 | from canonical.launchpad.ftests import login | ||
466 | 11 | from canonical.testing import LaunchpadFunctionalLayer | ||
467 | 12 | from lp.soyuz.tests.test_publishing import SoyuzTestPublisher | ||
468 | 13 | from lp.testing import TestCaseWithFactory | ||
469 | 14 | from lp.testing.views import create_initialized_view | ||
470 | 15 | |||
471 | 16 | |||
472 | 17 | class TestArchiveView(TestCaseWithFactory): | ||
473 | 18 | |||
474 | 19 | layer = LaunchpadFunctionalLayer | ||
475 | 20 | |||
476 | 21 | def setUp(self): | ||
477 | 22 | """Create a ppa with some publishings for different arches | ||
478 | 23 | and in different distroseries.""" | ||
479 | 24 | super(TestArchiveView, self).setUp() | ||
480 | 25 | self.ppa = self.factory.makeArchive() | ||
481 | 26 | login('admin@canonical.com') | ||
482 | 27 | test_publisher = SoyuzTestPublisher() | ||
483 | 28 | test_publisher.prepareBreezyAutotest() | ||
484 | 29 | |||
485 | 30 | test_publisher.getPubBinaries(archive=self.ppa) | ||
486 | 31 | |||
487 | 32 | def makeBinariesReadable(self, binaries): | ||
488 | 33 | """Return a string representation of the binaries.""" | ||
489 | 34 | return ", ".join([ | ||
490 | 35 | "%s in %s" % ( | ||
491 | 36 | bin.binary_package_name, | ||
492 | 37 | bin.distroarchseries.displayname | ||
493 | 38 | ) for bin in binaries]) | ||
494 | 39 | |||
495 | 40 | def test_batched_packages(self): | ||
496 | 41 | # The batched_packages property of ArchiveView returns | ||
497 | 42 | # a batch of binary publishings. | ||
498 | 43 | view = create_initialized_view( | ||
499 | 44 | self.ppa, name="+index", method="GET") | ||
500 | 45 | self.assertEqual( | ||
501 | 46 | "foo-bin in ubuntutest Breezy Badger Autotest i386, " | ||
502 | 47 | "foo-bin in ubuntutest Breezy Badger Autotest hppa", | ||
503 | 48 | self.makeBinariesReadable(view.batched_packages)) | ||
504 | 49 | |||
505 | 50 | def test_batched_packages_filter_arch(self): | ||
506 | 51 | view = create_initialized_view( | ||
507 | 52 | self.ppa, name="+index", method="GET", | ||
508 | 53 | query_string="field.arch_filter=i386") | ||
509 | 54 | self.assertEqual( | ||
510 | 55 | "foo-bin in ubuntutest Breezy Badger Autotest i386", | ||
511 | 56 | self.makeBinariesReadable(view.batched_packages)) | ||
512 | 57 | |||
513 | 58 | def test_suite(): | ||
514 | 59 | return unittest.TestLoader().loadTestsFromName(__name__) | ||
515 | 0 | 60 | ||
516 | === modified file 'lib/lp/soyuz/doc/archive.txt' | |||
517 | --- lib/lp/soyuz/doc/archive.txt 2010-03-13 00:32:40 +0000 | |||
518 | +++ lib/lp/soyuz/doc/archive.txt 2010-03-22 11:43:06 +0000 | |||
519 | @@ -158,10 +158,20 @@ | |||
520 | 158 | >>> print cprov_archive.archive_url | 158 | >>> print cprov_archive.archive_url |
521 | 159 | http://ppa.launchpad.dev/cprov/ppa/ubuntu | 159 | http://ppa.launchpad.dev/cprov/ppa/ubuntu |
522 | 160 | 160 | ||
527 | 161 | Inquire what Distribution Series this archive has published sources to: | 161 | We can find out to which Distribution Series this archive has published |
528 | 162 | 162 | sources: | |
529 | 163 | >>> [s.name for s in cprov_archive.series_with_sources] | 163 | |
530 | 164 | [u'breezy-autotest', u'warty'] | 164 | >>> for series in cprov_archive.series_with_sources: |
531 | 165 | ... print series.name | ||
532 | 166 | breezy-autotest | ||
533 | 167 | warty | ||
534 | 168 | |||
535 | 169 | Or the DistroArchSeries to which this archive has published binaries: | ||
536 | 170 | |||
537 | 171 | >>> for arch in cprov_archive.arches_with_binaries: | ||
538 | 172 | ... print arch.architecturetag | ||
539 | 173 | hppa | ||
540 | 174 | i386 | ||
541 | 165 | 175 | ||
542 | 166 | 'purpose' is a read-only attribute, it can't and shouldn't be modified | 176 | 'purpose' is a read-only attribute, it can't and shouldn't be modified |
543 | 167 | once a IArchive is created. Changing those values would affect the way | 177 | once a IArchive is created. Changing those values would affect the way |
544 | 168 | 178 | ||
545 | === modified file 'lib/lp/soyuz/interfaces/archive.py' | |||
546 | --- lib/lp/soyuz/interfaces/archive.py 2010-03-17 16:45:38 +0000 | |||
547 | +++ lib/lp/soyuz/interfaces/archive.py 2010-03-22 11:43:06 +0000 | |||
548 | @@ -47,6 +47,7 @@ | |||
549 | 47 | from canonical.launchpad.interfaces.launchpad import IPrivacy | 47 | from canonical.launchpad.interfaces.launchpad import IPrivacy |
550 | 48 | from lp.registry.interfaces.role import IHasOwner | 48 | from lp.registry.interfaces.role import IHasOwner |
551 | 49 | from lp.soyuz.interfaces.buildrecords import IHasBuildRecords | 49 | from lp.soyuz.interfaces.buildrecords import IHasBuildRecords |
552 | 50 | from lp.soyuz.interfaces.distroarchseries import IDistroArchSeries | ||
553 | 50 | from lp.registry.interfaces.gpg import IGPGKey | 51 | from lp.registry.interfaces.gpg import IGPGKey |
554 | 51 | from lp.registry.interfaces.person import IPerson | 52 | from lp.registry.interfaces.person import IPerson |
555 | 52 | from canonical.launchpad.validators.name import name_validator | 53 | from canonical.launchpad.validators.name import name_validator |
556 | @@ -223,6 +224,12 @@ | |||
557 | 223 | 224 | ||
558 | 224 | series_with_sources = Attribute( | 225 | series_with_sources = Attribute( |
559 | 225 | "DistroSeries to which this archive has published sources") | 226 | "DistroSeries to which this archive has published sources") |
560 | 227 | arches_with_binaries = CollectionField( | ||
561 | 228 | title=_( | ||
562 | 229 | "DistroArchSeries to which this archive has published " | ||
563 | 230 | "binaries."), | ||
564 | 231 | value_type=Reference(schema=IDistroArchSeries), | ||
565 | 232 | readonly=True) | ||
566 | 226 | number_of_sources = Attribute( | 233 | number_of_sources = Attribute( |
567 | 227 | 'The number of sources published in the context archive.') | 234 | 'The number of sources published in the context archive.') |
568 | 228 | number_of_binaries = Attribute( | 235 | number_of_binaries = Attribute( |
569 | 229 | 236 | ||
570 | === modified file 'lib/lp/soyuz/model/archive.py' | |||
571 | --- lib/lp/soyuz/model/archive.py 2010-03-17 04:05:42 +0000 | |||
572 | +++ lib/lp/soyuz/model/archive.py 2010-03-22 11:43:06 +0000 | |||
573 | @@ -205,7 +205,7 @@ | |||
574 | 205 | for (family, archive_arch) in restricted_families: | 205 | for (family, archive_arch) in restricted_families: |
575 | 206 | if family == arm: | 206 | if family == arm: |
576 | 207 | return (archive_arch is not None) | 207 | return (archive_arch is not None) |
578 | 208 | # ARM doesn't exist or isn't restricted. Either way, there is no | 208 | # ARM doesn't exist or isn't restricted. Either way, there is no |
579 | 209 | # need for an explicit association. | 209 | # need for an explicit association. |
580 | 210 | return False | 210 | return False |
581 | 211 | 211 | ||
582 | @@ -286,6 +286,28 @@ | |||
583 | 286 | distro_series, key=lambda a: Version(a.version), reverse=True) | 286 | distro_series, key=lambda a: Version(a.version), reverse=True) |
584 | 287 | 287 | ||
585 | 288 | @property | 288 | @property |
586 | 289 | def arches_with_binaries(self): | ||
587 | 290 | """See `IArchive`.""" | ||
588 | 291 | store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR) | ||
589 | 292 | |||
590 | 293 | # Import DistroArchSeries here to avoid circular imports. | ||
591 | 294 | from lp.soyuz.model.distroarchseries import DistroArchSeries | ||
592 | 295 | |||
593 | 296 | distro_arch_series = store.find( | ||
594 | 297 | DistroArchSeries, | ||
595 | 298 | (BinaryPackagePublishingHistory.distroarchseries == | ||
596 | 299 | DistroArchSeries.id), | ||
597 | 300 | BinaryPackagePublishingHistory.archive == self, | ||
598 | 301 | BinaryPackagePublishingHistory.status.is_in( | ||
599 | 302 | active_publishing_status)) | ||
600 | 303 | |||
601 | 304 | # Ensure the ordering is the same as presented by | ||
602 | 305 | # DistroSeries.enabled_architectures. | ||
603 | 306 | distro_arch_series.config(distinct=True) | ||
604 | 307 | return distro_arch_series.order_by( | ||
605 | 308 | DistroArchSeries.architecturetag) | ||
606 | 309 | |||
607 | 310 | @property | ||
608 | 289 | def dependencies(self): | 311 | def dependencies(self): |
609 | 290 | query = """ | 312 | query = """ |
610 | 291 | ArchiveDependency.dependency = Archive.id AND | 313 | ArchiveDependency.dependency = Archive.id AND |
611 | 292 | 314 | ||
612 | === modified file 'lib/lp/soyuz/templates/archive-copy-packages.pt' | |||
613 | --- lib/lp/soyuz/templates/archive-copy-packages.pt 2009-09-10 11:53:18 +0000 | |||
614 | +++ lib/lp/soyuz/templates/archive-copy-packages.pt 2010-03-22 11:43:06 +0000 | |||
615 | @@ -68,7 +68,7 @@ | |||
616 | 68 | </tal:selectable_sources_end> | 68 | </tal:selectable_sources_end> |
617 | 69 | 69 | ||
618 | 70 | <table style="margin-top: 1em;" | 70 | <table style="margin-top: 1em;" |
620 | 71 | tal:condition="view/has_sources_for_display"> | 71 | tal:condition="view/has_packages_for_display"> |
621 | 72 | 72 | ||
622 | 73 | <tal:archive | 73 | <tal:archive |
623 | 74 | define="widget nocall:view/widgets/destination_archive|nothing" | 74 | define="widget nocall:view/widgets/destination_archive|nothing" |
624 | 75 | 75 | ||
625 | === modified file 'lib/lp/soyuz/templates/archive-delete-packages.pt' | |||
626 | --- lib/lp/soyuz/templates/archive-delete-packages.pt 2009-09-09 17:54:13 +0000 | |||
627 | +++ lib/lp/soyuz/templates/archive-delete-packages.pt 2010-03-22 11:43:06 +0000 | |||
628 | @@ -52,7 +52,7 @@ | |||
629 | 52 | tal:content="structure error">Error message</div> | 52 | tal:content="structure error">Error message</div> |
630 | 53 | </tal:selectable_sources_end> | 53 | </tal:selectable_sources_end> |
631 | 54 | 54 | ||
633 | 55 | <tal:actions condition="view/has_sources_for_display" | 55 | <tal:actions condition="view/has_packages_for_display" |
634 | 56 | define="widget nocall:view/widgets/deletion_comment; | 56 | define="widget nocall:view/widgets/deletion_comment; |
635 | 57 | field_name widget/context/__name__; | 57 | field_name widget/context/__name__; |
636 | 58 | error python:view.getFieldError(field_name);"> | 58 | error python:view.getFieldError(field_name);"> |
637 | 59 | 59 | ||
638 | === modified file 'lib/lp/soyuz/templates/archive-index.pt' | |||
639 | --- lib/lp/soyuz/templates/archive-index.pt 2010-02-25 10:47:50 +0000 | |||
640 | +++ lib/lp/soyuz/templates/archive-index.pt 2010-03-22 11:43:06 +0000 | |||
641 | @@ -242,7 +242,7 @@ | |||
642 | 242 | </form> | 242 | </form> |
643 | 243 | </h2> | 243 | </h2> |
644 | 244 | 244 | ||
646 | 245 | <div style="padding-top: 1em;" tal:define="batch view/batched_sources"> | 245 | <div style="padding-top: 1em;" tal:define="batch view/batched_packages"> |
647 | 246 | 246 | ||
648 | 247 | <tal:navigation_top | 247 | <tal:navigation_top |
649 | 248 | replace="structure view/batchnav/@@+navigation-links-upper" /> | 248 | replace="structure view/batchnav/@@+navigation-links-upper" /> |
650 | 249 | 249 | ||
651 | === modified file 'lib/lp/soyuz/templates/archive-macros.pt' | |||
652 | --- lib/lp/soyuz/templates/archive-macros.pt 2009-12-03 18:33:22 +0000 | |||
653 | +++ lib/lp/soyuz/templates/archive-macros.pt 2010-03-22 11:43:06 +0000 | |||
654 | @@ -154,7 +154,7 @@ | |||
655 | 154 | archive. | 154 | archive. |
656 | 155 | </tal:comment> | 155 | </tal:comment> |
657 | 156 | 156 | ||
659 | 157 | <div style="padding-top: 1em;" tal:define="batch view/batched_sources"> | 157 | <div style="padding-top: 1em;" tal:define="batch view/batched_packages"> |
660 | 158 | 158 | ||
661 | 159 | <tal:navigation_top | 159 | <tal:navigation_top |
662 | 160 | replace="structure view/batchnav/@@+navigation-links-upper" /> | 160 | replace="structure view/batchnav/@@+navigation-links-upper" /> |
663 | @@ -175,9 +175,9 @@ | |||
664 | 175 | <tbody> | 175 | <tbody> |
665 | 176 | <tal:non_selectable repeat="publishing batch" | 176 | <tal:non_selectable repeat="publishing batch" |
666 | 177 | replace="structure publishing/@@+listing-archive-detailed" | 177 | replace="structure publishing/@@+listing-archive-detailed" |
670 | 178 | condition="not:view/selectable_sources"> | 178 | condition="not:view/selectable_packages"> |
671 | 179 | </tal:non_selectable> | 179 | </tal:non_selectable> |
672 | 180 | <tal:selectable condition="view/selectable_sources"> | 180 | <tal:selectable condition="view/selectable_packages"> |
673 | 181 | <tal:each_term define="widget nocall:view/widgets/selected_sources" | 181 | <tal:each_term define="widget nocall:view/widgets/selected_sources" |
674 | 182 | repeat="term widget/vocabulary"> | 182 | repeat="term widget/vocabulary"> |
675 | 183 | <tal:block | 183 | <tal:block |
On Thu, Mar 18, 2010 at 9:24 AM, Michael Nelson ckageListViewBa se class is refactored to ArchivePackages ViewBase so that it can be re-used for both source and binary packages.
<email address hidden> wrote:
> Michael Nelson has proposed merging lp:~michael.nelson/launchpad/496862-ppa-installable-binaries into lp:launchpad/devel.
>
> Requested reviews:
> Canonical Launchpad Engineering (launchpad)
>
>
> This branch is the first of two to address bug 496862.
>
> It adds an ArchiveArchFactory to create a vocabulary dynamically depending on the binaries published in the archive.
>
> The ArchiveSourcePa
>
> Finally, the ArchiveView (for the PPA index page) is updated to present binary packages.
>
> The next branch will switch the PPA index ui to present the binary packages.
>
> To test:
> bin/test -vv -t archive-views.txt -t archive.txt -t TestArchiveView
>
Good patch. I like the tests and the renames.
Some minor issues with method names and docstring formatting and a
couple of questions.
jml
> -- /code.launchpad .net/~michael. nelson/ launchpad/ 496862- ppa-installable -binaries/ +merge/ 21623 soyuz/browser/ archive. py' soyuz/browser/ archive. py 2010-03-10 12:50:18 +0000 soyuz/browser/ archive. py 2010-03-18 09:24:24 +0000 y(series_ terms) bularyFactory: IContextSourceB inder) arches_ with_binaries: architecturetag , displayname) append( term) y(arch_ terms)
> https:/
> Your team Launchpad code reviewers from Canonical is subscribed to branch lp:launchpad/devel.
>
> === modified file 'lib/lp/
> --- lib/lp/
> +++ lib/lp/
> @@ -589,6 +589,26 @@
> return SimpleVocabular
>
>
> +class ArchiveArchVoca
> + """A factory for generating vocabularies of an archive's arches in
> + a given distroseries."""
> +
> + implements(
> +
> + def __call__(self, context):
> + """Return a vocabulary created dynamically from the context archive.
> +
> + :param context: The archive used to generating vocabulary.
> + """
> + arch_terms = []
> + for arch in context.
> + term = SimpleTerm(
> + arch, token=arch.
> + title=arch.
> + arch_terms.
> + return SimpleVocabular
> +
Why is this a class and not a function? Is the implements() bit really
important?
> === added file 'lib/lp/ soyuz/browser/ tests/test_ archive_ view.py' soyuz/browser/ tests/test_ archive_ view.py 1970-01-01 00:00:00 +0000 soyuz/browser/ tests/test_ archive_ view.py 2010-03-18 09:24:24 +0000 launchpad. ftests import login onalLayer tests.test_ publishing import SoyuzTestPublisher initialized_ view (TestCaseWithFa ctory): onalLayer
> --- lib/lp/
> +++ lib/lp/
> @@ -0,0 +1,59 @@
> +# Copyright 2010 Canonical Ltd. This software is licensed under the
> +# GNU Affero General Public License version 3 (see the file LICENSE).
> +
> +"""Test the main PPA (user) view functionality."""
> +
> +__metaclass__ = type
> +
> +import unittest
> +
> +from canonical.
> +from canonical.testing import LaunchpadFuncti
> +from lp.soyuz.
> +from lp.testing import TestCaseWithFactory
> +from lp.testing.views import create_
> +
> +
> +class TestArchiveView
> +
> + layer = LaunchpadFuncti
> +
You need the librarian?
> + def setUp(self)...