Merge lp:~michael.nelson/launchpad/429263-no-value-option into lp:launchpad

Proposed by Michael Nelson
Status: Merged
Approved by: Graham Binns
Approved revision: no longer in the source branch.
Merged at revision: not available
Proposed branch: lp:~michael.nelson/launchpad/429263-no-value-option
Merge into: lp:launchpad
Diff against target: None lines
To merge this branch: bzr merge lp:~michael.nelson/launchpad/429263-no-value-option
Reviewer Review Type Date Requested Status
Graham Binns (community) Approve
Review via email: mp+11768@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Michael Nelson (michael.nelson) wrote :

= Summary =
This branch is a fix for bug 429263.

Since updating the filtering options for pkgs on ppas to use more
zope.formlib stuff, we can no-longer explicitly declare the null-option,
but need to rely on zope's built-in null option for choices (which gets
added irrespectively for non-required choices).

== Proposed fix ==

Remove our explicit 'any' option for status and series filters and
instead rely on zope's built-in None option.

== Pre-implementation notes ==

Discussed briefly with Celso to see how we had handled this in other
situations.

== Implementation details ==

I ended up with two near identical properties - selected_status_filter
and selected_series_filter - hence drying up with the
getSelectedFilterValue() method.

== Tests ==

bin/test -vv -t archive-views -t stories/ppa

== Demo and Q/A ==

There should no longer be a '(no value)' option at:

Local demo, play with filtering at:
https://launchpad.dev/~cprov/+archive/ppa
https://launchpad.dev/~cprov/+archive/ppa/+packages
https://launchpad.dev/~cprov/+archive/ppa/+copy-packages
https://launchpad.dev/~cprov/+archive/ppa/+delete-packages

On edge: similar sub-urls from:

https://edge.launchpad.net/~cprov/+archive/ppa

= Launchpad lint =

Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.

Linting changed files:
  lib/lp/soyuz/stories/ppa/xx-ubuntu-ppas.txt
  lib/lp/soyuz/browser/tests/archive-views.txt
  lib/lp/soyuz/stories/ppa/xx-delete-packages.txt
  lib/lp/soyuz/stories/ppa/xx-copy-packages.txt
  lib/lp/soyuz/stories/ppa/xx-ppa-packages.txt
  lib/lp/soyuz/browser/archive.py

--
Michael

Revision history for this message
Graham Binns (gmb) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/soyuz/browser/archive.py'
--- lib/lp/soyuz/browser/archive.py 2009-09-11 17:55:23 +0000
+++ lib/lp/soyuz/browser/archive.py 2009-09-15 08:25:32 +0000
@@ -577,7 +577,7 @@
577 this factory can only be used in a class where the context is577 this factory can only be used in a class where the context is
578 an IArchive.578 an IArchive.
579 """579 """
580 series_terms = [SimpleTerm(None, token='any', title='Any Series')]580 series_terms = []
581 for distroseries in context.series_with_sources:581 for distroseries in context.series_with_sources:
582 series_terms.append(582 series_terms.append(
583 SimpleTerm(distroseries, token=distroseries.name,583 SimpleTerm(distroseries, token=distroseries.name,
@@ -585,6 +585,16 @@
585 return SimpleVocabulary(series_terms)585 return SimpleVocabulary(series_terms)
586586
587587
588class SeriesFilterWidget(LaunchpadDropdownWidget):
589 """Redefining default display value as 'Any series'."""
590 _messageNoValue = _("any", "Any series")
591
592
593class StatusFilterWidget(LaunchpadDropdownWidget):
594 """Redefining default display value as 'Any status'."""
595 _messageNoValue = _("any", "Any status")
596
597
588class IPPAPackageFilter(Interface):598class IPPAPackageFilter(Interface):
589 """The interface used as the schema for the package filtering form."""599 """The interface used as the schema for the package filtering form."""
590 name_filter = TextLine(600 name_filter = TextLine(
@@ -596,7 +606,6 @@
596 status_filter = Choice(vocabulary=SimpleVocabulary((606 status_filter = Choice(vocabulary=SimpleVocabulary((
597 SimpleTerm(active_publishing_status, 'published', 'Published'),607 SimpleTerm(active_publishing_status, 'published', 'Published'),
598 SimpleTerm(inactive_publishing_status, 'superseded', 'Superseded'),608 SimpleTerm(inactive_publishing_status, 'superseded', 'Superseded'),
599 SimpleTerm(None, 'any', 'Any status')
600 )), required=False)609 )), required=False)
601610
602611
@@ -604,6 +613,8 @@
604 """A Form view for filtering and batching source packages."""613 """A Form view for filtering and batching source packages."""
605614
606 schema = IPPAPackageFilter615 schema = IPPAPackageFilter
616 custom_widget('series_filter', SeriesFilterWidget)
617 custom_widget('status_filter', StatusFilterWidget)
607618
608 # By default this view will not display the sources with selectable619 # By default this view will not display the sources with selectable
609 # checkboxes, but subclasses can override as needed.620 # checkboxes, but subclasses can override as needed.
@@ -625,65 +636,52 @@
625 else:636 else:
626 return None637 return None
627638
628 @cachedproperty639 def getSelectedFilterValue(self, filter_name):
629 def selected_status_filter(self):640 """Return the selected filter or the default, given a filter name.
630 """Return the selected status filter or the default."""641
631 requested_status_filter = self.request.query_string_params.get(642 This is needed because zope's form library does not consider
632 'field.status_filter')643 query string params (GET params) during a post request.
633644 """
634 # If the request included a status filter, try to use it:645 field_name = 'field.' + filter_name
635 selected_status_filter = None646 requested_filter = self.request.query_string_params.get(field_name)
636 if requested_status_filter is not None:647
637 selected_status_filter = (648 # If an empty filter was specified, then it's explicitly
638 self.widgets['status_filter'].vocabulary.getTermByToken(649 # been set to empty - so we use None.
639 requested_status_filter[0]))650 if requested_filter == ['']:
640651 return None
641 # If the request didn't include a status, or it was invalid, use652
642 # the default:653 # If the requested filter is none, then we use the default.
643 if selected_status_filter is None:654 default_filter_attr = 'default_' + filter_name
644 selected_status_filter = self.default_status_filter655 if requested_filter is None:
645656 return getattr(self, default_filter_attr)
646 return selected_status_filter657
658 # If the request included a filter, try to use it - if it's
659 # invalid we use the default instead.
660 vocab = self.widgets[filter_name].vocabulary
661 if vocab.by_token.has_key(requested_filter[0]):
662 return vocab.getTermByToken(requested_filter[0]).value
663 else:
664 return getattr(self, default_filter_attr)
647665
648 @property666 @property
649 def plain_status_filter_widget(self):667 def plain_status_filter_widget(self):
650 """Render a <select> control with no <div>s around it."""668 """Render a <select> control with no <div>s around it."""
651 return self.widgets['status_filter'].renderValue(669 return self.widgets['status_filter'].renderValue(
652 self.selected_status_filter.value)670 self.getSelectedFilterValue('status_filter'))
653
654 @property
655 def selected_series_filter(self):
656 """Return the currently selected filter or None."""
657 requested_series_filter = self.request.query_string_params.get(
658 'field.series_filter')
659
660 # If the request included a series filter, try to use it:
661 selected_series_filter = None
662 if requested_series_filter is not None:
663 series_vocabulary = self.widgets['series_filter'].vocabulary
664 selected_series_filter = series_vocabulary.getTermByToken(
665 requested_series_filter[0])
666
667 # If the request didn't include a series, or it was invalid, use
668 # the default:
669 if selected_series_filter is None:
670 selected_series_filter = self.default_series_filter
671
672 return selected_series_filter
673671
674 @property672 @property
675 def plain_series_filter_widget(self):673 def plain_series_filter_widget(self):
676 """Render a <select> control with no <div>s around it."""674 """Render a <select> control with no <div>s around it."""
677 return self.widgets['series_filter'].renderValue(675 return self.widgets['series_filter'].renderValue(
678 self.selected_series_filter.value)676 self.getSelectedFilterValue('series_filter'))
679677
680 @property678 @property
681 def filtered_sources(self):679 def filtered_sources(self):
682 """Return the source results for display after filtering."""680 """Return the source results for display after filtering."""
683 return self.context.getPublishedSources(681 return self.context.getPublishedSources(
684 name=self.specified_name_filter,682 name=self.specified_name_filter,
685 status=self.selected_status_filter.value,683 status=self.getSelectedFilterValue('status_filter'),
686 distroseries=self.selected_series_filter.value)684 distroseries=self.getSelectedFilterValue('series_filter'))
687685
688 @property686 @property
689 def default_status_filter(self):687 def default_status_filter(self):
@@ -692,7 +690,7 @@
692 Subclasses of ArchiveViewBase can override this when required.690 Subclasses of ArchiveViewBase can override this when required.
693 """691 """
694 return self.widgets['status_filter'].vocabulary.getTermByToken(692 return self.widgets['status_filter'].vocabulary.getTermByToken(
695 'published')693 'published').value
696694
697 @property695 @property
698 def default_series_filter(self):696 def default_series_filter(self):
@@ -700,7 +698,7 @@
700698
701 Subclasses of ArchiveViewBase can override this when required.699 Subclasses of ArchiveViewBase can override this when required.
702 """700 """
703 return self.widgets['series_filter'].vocabulary.getTermByToken('any')701 return None
704702
705 @cachedproperty703 @cachedproperty
706 def batchnav(self):704 def batchnav(self):
@@ -769,10 +767,10 @@
769 for term in vocabulary:767 for term in vocabulary:
770 if (term.value is not None and768 if (term.value is not None and
771 term.value.version == version_number):769 term.value.version == version_number):
772 return term770 return term.value
773771
774 # Otherwise we default to 'any'772 # Otherwise we default to 'any'
775 return vocabulary.getTermByToken('any')773 return None
776774
777 @property775 @property
778 def archive_description_html(self):776 def archive_description_html(self):
@@ -990,7 +988,7 @@
990 @property988 @property
991 def default_status_filter(self):989 def default_status_filter(self):
992 """Present records in any status by default."""990 """Present records in any status by default."""
993 return self.widgets['status_filter'].vocabulary.getTermByToken('any')991 return None
994992
995 @cachedproperty993 @cachedproperty
996 def filtered_sources(self):994 def filtered_sources(self):
@@ -998,14 +996,11 @@
998996
999 This overrides ArchiveViewBase.filtered_sources to use a997 This overrides ArchiveViewBase.filtered_sources to use a
1000 different method on the context specific to deletion records.998 different method on the context specific to deletion records.
1001
1002 It expects 'self.selected_status_filter' and
1003 'self.selected_series_filter' to be set.
1004 """999 """
1005 return self.context.getSourcesForDeletion(1000 return self.context.getSourcesForDeletion(
1006 name=self.specified_name_filter,1001 name=self.specified_name_filter,
1007 status=self.selected_status_filter.value,1002 status=self.getSelectedFilterValue('status_filter'),
1008 distroseries=self.selected_series_filter.value)1003 distroseries=self.getSelectedFilterValue('series_filter'))
10091004
1010 @cachedproperty1005 @cachedproperty
1011 def has_sources(self):1006 def has_sources(self):
@@ -1093,7 +1088,7 @@
1093 def default_status_filter(self):1088 def default_status_filter(self):
1094 """Present published records by default."""1089 """Present published records by default."""
1095 return self.widgets['status_filter'].vocabulary.getTermByToken(1090 return self.widgets['status_filter'].vocabulary.getTermByToken(
1096 'published')1091 'published').value
10971092
1098 def setUpFields(self):1093 def setUpFields(self):
1099 """Override `ArchiveSourceSelectionFormView`.1094 """Override `ArchiveSourceSelectionFormView`.
11001095
=== modified file 'lib/lp/soyuz/browser/tests/archive-views.txt'
--- lib/lp/soyuz/browser/tests/archive-views.txt 2009-09-11 17:55:23 +0000
+++ lib/lp/soyuz/browser/tests/archive-views.txt 2009-09-15 08:25:32 +0000
@@ -90,14 +90,12 @@
90 ... print term.title90 ... print term.title
91 Published91 Published
92 Superseded92 Superseded
93 Any status
9493
95An ArchiveView inherits the series-filter widget for filtering packages94An ArchiveView inherits the series-filter widget for filtering packages
96by series.95by series.
9796
98 >>> for term in ppa_archive_view.widgets['series_filter'].vocabulary:97 >>> for term in ppa_archive_view.widgets['series_filter'].vocabulary:
99 ... print term.title98 ... print term.title
100 Any Series
101 Breezy Badger Autotest99 Breezy Badger Autotest
102 Warty100 Warty
103101
@@ -406,8 +404,8 @@
406The archive index view overrides the default series filter to use the404The archive index view overrides the default series filter to use the
407distroseries from the browser's user-agent, when applicable.405distroseries from the browser's user-agent, when applicable.
408406
409 >>> print view.default_series_filter.token407 >>> print view.default_series_filter
410 any408 None
411409
412 >>> view_warty = create_view(410 >>> view_warty = create_view(
413 ... cprov.archive, name="+index",411 ... cprov.archive, name="+index",
@@ -416,8 +414,19 @@
416 ... 'Gecko/2009042523 Ubuntu/4.10 (whatever) '414 ... 'Gecko/2009042523 Ubuntu/4.10 (whatever) '
417 ... 'Firefox/3.0.10')415 ... 'Firefox/3.0.10')
418 >>> view_warty.initialize()416 >>> view_warty.initialize()
419 >>> print view_warty.default_series_filter.token417 >>> print view_warty.default_series_filter.name
420 warty418 warty
419
420The archive index view also inherits the getSelectedFilterValue() method
421which can be used to find the currently selected value for both filters.
422
423 >>> print view_warty.getSelectedFilterValue('series_filter').name
424 warty
425
426 >>> for status in view_warty.getSelectedFilterValue('status_filter'):
427 ... print status.name
428 PENDING
429 PUBLISHED
421430
422431
423== ArchivePackageView ==432== ArchivePackageView ==
@@ -558,7 +567,6 @@
558 ... form={567 ... form={
559 ... 'field.actions.delete': 'Delete Packages',568 ... 'field.actions.delete': 'Delete Packages',
560 ... 'field.name_filter': '',569 ... 'field.name_filter': '',
561 ... 'field.status_filter': 'any',
562 ... 'field.deletion_comment': 'Go away',570 ... 'field.deletion_comment': 'Go away',
563 ... 'field.selected_sources': ['27', '28', '29'],571 ... 'field.selected_sources': ['27', '28', '29'],
564 ... 'field.selected_sources-empty-marker': 1,572 ... 'field.selected_sources-empty-marker': 1,
@@ -579,7 +587,6 @@
579 ... form={587 ... form={
580 ... 'field.actions.delete': 'Delete Packages',588 ... 'field.actions.delete': 'Delete Packages',
581 ... 'field.name_filter': '',589 ... 'field.name_filter': '',
582 ... 'field.status_filter': 'any',
583 ... 'field.deletion_comment': 'Go away',590 ... 'field.deletion_comment': 'Go away',
584 ... 'field.selected_sources': ['27', '28', '29'],591 ... 'field.selected_sources': ['27', '28', '29'],
585 ... 'field.selected_sources-empty-marker': 1,592 ... 'field.selected_sources-empty-marker': 1,
@@ -1009,7 +1016,7 @@
10091016
1010 >>> view = create_initialized_view(1017 >>> view = create_initialized_view(
1011 ... cprov.archive, name="+copy-packages",1018 ... cprov.archive, name="+copy-packages",
1012 ... query_string="field.status_filter=any")1019 ... query_string="field.status_filter=")
10131020
1014 >>> [pub.status.name for pub in view.batched_sources]1021 >>> [pub.status.name for pub in view.batched_sources]
1015 ['DELETED', 'DELETED', 'DELETED']1022 ['DELETED', 'DELETED', 'DELETED']
@@ -1289,7 +1296,6 @@
1289 >>> view = create_initialized_view(1296 >>> view = create_initialized_view(
1290 ... cprov.archive, name="+copy-packages",1297 ... cprov.archive, name="+copy-packages",
1291 ... form={1298 ... form={
1292 ... 'field.status_filter': 'any',
1293 ... 'field.selected_sources': [str(private_source.id)],1299 ... 'field.selected_sources': [str(private_source.id)],
1294 ... 'field.destination_archive': 'ubuntu-team/ppa',1300 ... 'field.destination_archive': 'ubuntu-team/ppa',
1295 ... 'field.destination_series': '',1301 ... 'field.destination_series': '',
12961302
=== modified file 'lib/lp/soyuz/stories/ppa/xx-copy-packages.txt'
--- lib/lp/soyuz/stories/ppa/xx-copy-packages.txt 2009-09-12 06:49:56 +0000
+++ lib/lp/soyuz/stories/ppa/xx-copy-packages.txt 2009-09-15 08:00:26 +0000
@@ -559,7 +559,7 @@
559really deleted.559really deleted.
560560
561 >>> jblack_browser.getLink('Cancel').click()561 >>> jblack_browser.getLink('Cancel').click()
562 >>> jblack_browser.getControl(name='field.status_filter').value = ['any']562 >>> jblack_browser.getControl(name='field.status_filter').value = ['']
563 >>> jblack_browser.getControl('Filter', index=0).click()563 >>> jblack_browser.getControl('Filter', index=0).click()
564 >>> print_ppa_packages(jblack_browser.contents)564 >>> print_ppa_packages(jblack_browser.contents)
565 Source Published Status Series Section Build565 Source Published Status Series Section Build
@@ -587,7 +587,7 @@
587Packages in other status can be browsed by adjusting the status587Packages in other status can be browsed by adjusting the status
588filter dropdown box.588filter dropdown box.
589589
590 >>> jblack_browser.getControl(name='field.status_filter').value = ['any']590 >>> jblack_browser.getControl(name='field.status_filter').value = ['']
591 >>> jblack_browser.getControl("Filter").click()591 >>> jblack_browser.getControl("Filter").click()
592 >>> print_ppa_packages(jblack_browser.contents)592 >>> print_ppa_packages(jblack_browser.contents)
593 Source Published Status Series Section Build593 Source Published Status Series Section Build
@@ -831,7 +831,7 @@
831 >>> jblack_browser.open(831 >>> jblack_browser.open(
832 ... 'http://launchpad.dev/~jblack/+archive/ppa/+packages')832 ... 'http://launchpad.dev/~jblack/+archive/ppa/+packages')
833 >>> jblack_browser.getLink('Copy packages').click()833 >>> jblack_browser.getLink('Copy packages').click()
834 >>> jblack_browser.getControl(name='field.status_filter').value = ['any']834 >>> jblack_browser.getControl(name='field.status_filter').value = ['']
835 >>> jblack_browser.getControl("Filter").click()835 >>> jblack_browser.getControl("Filter").click()
836836
837 >>> print_ppa_packages(jblack_browser.contents)837 >>> print_ppa_packages(jblack_browser.contents)
@@ -886,7 +886,7 @@
886 >>> jblack_browser.open(886 >>> jblack_browser.open(
887 ... 'http://launchpad.dev/~jblack/+archive/ppa/+packages')887 ... 'http://launchpad.dev/~jblack/+archive/ppa/+packages')
888 >>> jblack_browser.getLink('Copy packages').click()888 >>> jblack_browser.getLink('Copy packages').click()
889 >>> jblack_browser.getControl(name='field.status_filter').value = ['any']889 >>> jblack_browser.getControl(name='field.status_filter').value = ['']
890 >>> jblack_browser.getControl("Filter").click()890 >>> jblack_browser.getControl("Filter").click()
891891
892 >>> deleted_pub_id = getPPAPubIDsFor(892 >>> deleted_pub_id = getPPAPubIDsFor(
893893
=== modified file 'lib/lp/soyuz/stories/ppa/xx-delete-packages.txt'
--- lib/lp/soyuz/stories/ppa/xx-delete-packages.txt 2009-09-12 01:57:22 +0000
+++ lib/lp/soyuz/stories/ppa/xx-delete-packages.txt 2009-09-15 08:06:17 +0000
@@ -211,7 +211,7 @@
211 cdrkit - 1.0 2007-07-09 Deleted Breezy-autotest Editors i386211 cdrkit - 1.0 2007-07-09 Deleted Breezy-autotest Editors i386
212212
213 >>> cprov_browser.getControl(213 >>> cprov_browser.getControl(
214 ... name='field.status_filter').value = ['any']214 ... name='field.status_filter').value = ['']
215 >>> cprov_browser.getControl('Filter', index=0).click()215 >>> cprov_browser.getControl('Filter', index=0).click()
216 >>> print_ppa_packages(cprov_browser.contents)216 >>> print_ppa_packages(cprov_browser.contents)
217 Source Published Status Series Section Build217 Source Published Status Series Section Build
@@ -274,7 +274,7 @@
274Any user can see that all packages present in Celso's PPA are deleted.274Any user can see that all packages present in Celso's PPA are deleted.
275275
276 >>> cprov_browser.getControl(276 >>> cprov_browser.getControl(
277 ... name='field.status_filter').value = ['any']277 ... name='field.status_filter').value = ['']
278 >>> cprov_browser.getControl('Filter', index=0).click()278 >>> cprov_browser.getControl('Filter', index=0).click()
279 >>> print_ppa_packages(cprov_browser.contents)279 >>> print_ppa_packages(cprov_browser.contents)
280 Source Published Status Series Section Build280 Source Published Status Series Section Build
@@ -421,7 +421,7 @@
421filter is 'Any Status', but the user can choose another.421filter is 'Any Status', but the user can choose another.
422422
423 >>> print user_browser.getControl(name='field.status_filter').value423 >>> print user_browser.getControl(name='field.status_filter').value
424 ['any']424 ['']
425425
426When the user selects 'Published' filter and update the results, no426When the user selects 'Published' filter and update the results, no
427records are presented.427records are presented.
428428
=== modified file 'lib/lp/soyuz/stories/ppa/xx-ppa-packages.txt'
--- lib/lp/soyuz/stories/ppa/xx-ppa-packages.txt 2009-09-12 06:49:56 +0000
+++ lib/lp/soyuz/stories/ppa/xx-ppa-packages.txt 2009-09-15 08:06:17 +0000
@@ -273,7 +273,7 @@
273The 'Any Status' filter is also available, so the user can search over273The 'Any Status' filter is also available, so the user can search over
274any package ever published in the context PPA.274any package ever published in the context PPA.
275275
276 >>> anon_browser.getControl(name='field.status_filter').value = ['any']276 >>> anon_browser.getControl(name='field.status_filter').value = ['']
277 >>> anon_browser.getControl('Filter', index=0).click()277 >>> anon_browser.getControl('Filter', index=0).click()
278 >>> print_archive_package_rows(anon_browser.contents)278 >>> print_archive_package_rows(anon_browser.contents)
279 Source Published Status Series Section Build279 Source Published Status Series Section Build
280280
=== modified file 'lib/lp/soyuz/stories/ppa/xx-ubuntu-ppas.txt'
--- lib/lp/soyuz/stories/ppa/xx-ubuntu-ppas.txt 2009-09-12 02:09:47 +0000
+++ lib/lp/soyuz/stories/ppa/xx-ubuntu-ppas.txt 2009-09-15 08:06:17 +0000
@@ -393,12 +393,12 @@
393393
394== Filtering an archive ==394== Filtering an archive ==
395395
396The default series filter is 'any' which means that by default the396The default series filter is '' which means that by default the
397results will include packages from any distro series. A user can397results will include packages from any distro series. A user can
398explicitly set the 'Any Series' filter and get the same result:398explicitly set the 'Any Series' filter and get the same result:
399399
400 >>> anon_browser.getControl(name='field.series_filter').value = (400 >>> anon_browser.getControl(name='field.series_filter').value = (
401 ... ['any'])401 ... [''])
402 >>> anon_browser.getControl('Filter', index=0).click()402 >>> anon_browser.getControl('Filter', index=0).click()
403 >>> print_archive_package_rows(anon_browser.contents)403 >>> print_archive_package_rows(anon_browser.contents)
404 Package Version Uploaded by404 Package Version Uploaded by