Merge lp:~adeuring/launchpad/bug-201015-bug-branch-search into lp:launchpad/db-devel

Proposed by Abel Deuring
Status: Merged
Merged at revision: not available
Proposed branch: lp:~adeuring/launchpad/bug-201015-bug-branch-search
Merge into: lp:launchpad/db-devel
Diff against target: 647 lines (+273/-96)
9 files modified
lib/canonical/launchpad/icing/style.css (+11/-1)
lib/lp/bugs/browser/bugtask.py (+14/-4)
lib/lp/bugs/doc/bugtask-search.txt (+27/-1)
lib/lp/bugs/interfaces/bugtarget.py (+1/-1)
lib/lp/bugs/interfaces/bugtask.py (+32/-2)
lib/lp/bugs/model/bugtarget.py (+1/-1)
lib/lp/bugs/model/bugtask.py (+22/-7)
lib/lp/bugs/stories/bugtask-searches/xx-filter-by-linked-branches.txt (+64/-0)
lib/lp/bugs/templates/bugtask-macros-tableview.pt (+101/-79)
To merge this branch: bzr merge lp:~adeuring/launchpad/bug-201015-bug-branch-search
Reviewer Review Type Date Requested Status
Curtis Hovey (community) Approve
Review via email: mp+19699@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Abel Deuring (adeuring) wrote :

This branch is a fix of bug 201015: "Cannot search for bugs with Bazaar branches"

implementation details:

- added an enumeration BugBranchSearch to lp.bugs.interfaces.bugtask
- added a new constructor parameter and a new property "linked_branches" to class BugTaskSearchParams
- code in BugTaskSet.search() to generate an SQL clause for the new search option.
- added a Choice field with BugBranchSearch as a vocabulary to the interface class for the data of the "advanced bug search" form
- told the browser class for the "advanced bug search" to use a radio button widget for the new input filed "search for bugs having [no] linked branches"
- tests of BugTaskSet.search() (via IProduct.searchTasks()) for the new search option
- a short story test for the "presentation" of the new search option

UI note: I wanted to place the new radio buttons close to the existing input field "Show only bugs with patches available", since the new option has a similar purpose. This makes the right column of the table "other options" much longer that the left column, which is aesthetically not very appealing. I considered to move the options "Show only bugs associated with a CVE" and "Show only bugs affecting me" into the left column, but did not do this, because myself would be a bit confused by the changed layout, i.e., I would need a few extra seconds to find the options in their slightly changed location, when doing a bug search...

./bin/test -t bugtask-search.txt
./bin/test -t xx-filter-by-linked-branches.txt

no lint

Revision history for this message
Curtis Hovey (sinzui) wrote :
Download full text (4.2 KiB)

Hi Abel.

I am sorry you had to touch this page and that I am the reviewer; this
page has irked me for a long time because it does not look consistent
with the rest of Launchpad. Instead of asking you to fix this page, I
will propose code and text fixes that I think are easy to make.

There are some general problems that are never being addresses when
we update this page. I really want to take the time to fix the general
problem with this branch, and you get to close some extra bugs too:

    * Hard to scan. There is not enough white-space in the layout and
      all the text is the same weight and size. Information is lost
      like a tree in a forest.
    * Labels/legends do not match their field content.
    * We have added to this page without ever asking if we need
      to rearrange to the order of the sections to match what users
      want to use.

Your screenshot illustrated these three problems. Notice that the
Tags any/all radio buttons look like there are subordinate to
Show only bugs affecting me. I also question why some checkbox text are
bold and others are normal.

What follows is my analysis and proposed fix: you can see a see a picture
and patch at:
    http://people.canonical.com/~curtis/bug-search.png
    http://people.canonical.com/~curtis/bug-search.patch

My assumption was that the primary problem with this form is that
it is crafted rather than generated. After investigating how I could
make this better, I know understand this issue is really one of
markup+css neglect. All pages that use <legend> in a collapsible field
except this one. They are used to add an expansible section to a form.
This page is intended to be long with all sections visible. We do not
have css rules for this page!

    * In style.css, replace
        legend {font-weight: bold;}

      with
        .long td {
            padding-right: 1em;
            }
        .long fieldset {
            margin-top: 1em;
            }
        .long legend {
            color: #666;
            font-weight: bold;
            font-size: 123.1%;
            }

     * In bugtask-macros-tableview.pt, update the form class in
       <metal:advanced_search_form define-macro="advanced_search_form"> from
          <form class="lesser" name="search" method="get" action="">

       to
          <form class="long" name="search" method="get" action="">

We do not want to use the .lesser class because this page is not showing
a long listing, and it is forcing all the text to be the same small font.
We are replacing the css rules for this page with rules that create space
between the sections and make each section distinct.

As for the legend text and sections:

    * Series-specific => Milestones, components, and tags
      * Remove the width="40%" rules from the <td> and the remove the
        empty <td>.
      * Move tags below milestones and components
      * Use <td span="2"> so that the layout can fit the space:
        Move the help to the right of the input,
        Move the radio button below the field that controls them.

    * Other => Bug relationships
      * Move the "Show ..." group of option to the first column,
        and make your addition to the bottom
 ...

Read more...

review: Needs Fixing (ui)
Revision history for this message
Abel Deuring (adeuring) wrote :
Download full text (6.3 KiB)

Hi Curtis,

thanks for the improvements of the search page!

On 19.02.2010 19:06, Curtis Hovey wrote:
> Review: Needs Fixing ui
> Hi Abel.
>
> I am sorry you had to touch this page and that I am the reviewer; this
> page has irked me for a long time because it does not look consistent
> with the rest of Launchpad. Instead of asking you to fix this page, I
> will propose code and text fixes that I think are easy to make.
>
> There are some general problems that are never being addresses when
> we update this page. I really want to take the time to fix the general
> problem with this branch, and you get to close some extra bugs too:
>
> * Hard to scan. There is not enough white-space in the layout and
> all the text is the same weight and size. Information is lost
> like a tree in a forest.
> * Labels/legends do not match their field content.
> * We have added to this page without ever asking if we need
> to rearrange to the order of the sections to match what users
> want to use.
>
> Your screenshot illustrated these three problems. Notice that the
> Tags any/all radio buttons look like there are subordinate to
> Show only bugs affecting me. I also question why some checkbox text are
> bold and others are normal.
>
> What follows is my analysis and proposed fix: you can see a see a picture
> and patch at:
> http://people.canonical.com/~curtis/bug-search.png
> http://people.canonical.com/~curtis/bug-search.patch

I applied your patch.

>
> My assumption was that the primary problem with this form is that
> it is crafted rather than generated. After investigating how I could
> make this better, I know understand this issue is really one of
> markup+css neglect. All pages that use <legend> in a collapsible field
> except this one. They are used to add an expansible section to a form.
> This page is intended to be long with all sections visible. We do not
> have css rules for this page!
>
> * In style.css, replace
> legend {font-weight: bold;}
>
> with
> .long td {
> padding-right: 1em;
> }
> .long fieldset {
> margin-top: 1em;
> }
> .long legend {
> color: #666;
> font-weight: bold;
> font-size: 123.1%;
> }
>
> * In bugtask-macros-tableview.pt, update the form class in
> <metal:advanced_search_form define-macro="advanced_search_form"> from
> <form class="lesser" name="search" method="get" action="">
>
> to
> <form class="long" name="search" method="get" action="">
>
> We do not want to use the .lesser class because this page is not showing
> a long listing, and it is forcing all the text to be the same small font.
> We are replacing the css rules for this page with rules that create space
> between the sections and make each section distinct.
>
> As for the legend text and sections:
>
> * Series-specific => Milestones, components, and tags
> * Remove the width="40%" rules from the <td> and the remove the
> empty <td>.
> * Move tags below milestones and components
> * Use <td span="2"> so...

Read more...

Revision history for this message
Abel Deuring (adeuring) wrote :

A screenshot of the the searhc page: http://people.canonical.com/~adeuring/branchsearch.png

Revision history for this message
Curtis Hovey (sinzui) wrote :

Hi Abel

  review: approve

On Tue, 2010-02-23 at 09:54 +0000, Abel Deuring wrote:
> Hi Curtis,
...
> I added two checkboxes "Show bugs with[out] branches" below "Show only
> bugs with patches available".
>
> We had on Friday a short discussion on IRC if searching for bugs without
> branches is important enough. I think it is: We want to improve the
> relations with upstream project, and people working on upstream projects
> can find the most autoritative answer to what code is used in Ubuntu in
> Bazaar branches. On the other hand, a link between a branch and bug must
> be manually created by an Ubuntu developer, and this is quite easy to
> forget (I am a good example that this can happen quite often ;) So,
> being able to get a list of bugs in the states "fix commited" and "fix
> released" but having no linked branches allows Ubuntu developers to add
> possibly missing links betwwen branches and bugs, thus making live
> easier for upstream.
>
> Also, we discussed if radio button should be used to select the options
> "search for bugs with/without branches/search all bugs". Deryck proposed
> to use checkboxes instead, and while I would prefer radio buttons,
> because they describe more precisely the three possible options,
> checkboxes are of course more consistent with the other interfaces
> elements of the page.

Yes, a radio button is correct in this case. Your screenshot of two
mutually exclusive checkboxes selected shows we are encouraging users to
ask the impossible. I think the layout was the underlying reason to
change the control. This layout is much nicer and the position of the
two radio buttons will make the intent clear.

--
__Curtis C. Hovey_________
http://launchpad.net/

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/canonical/launchpad/icing/style.css'
--- lib/canonical/launchpad/icing/style.css 2010-02-21 20:46:26 +0000
+++ lib/canonical/launchpad/icing/style.css 2010-02-25 08:53:48 +0000
@@ -84,7 +84,17 @@
84 outline: 1px #666;84 outline: 1px #666;
85}85}
86label {white-space: nowrap;}86label {white-space: nowrap;}
87legend {font-weight: bold;}87.long td {
88 padding-right: 1em;
89 }
90.long fieldset {
91 margin-top: 1em;
92 }
93.long legend {
94 color: #666;
95 font-weight: bold;
96 font-size: 123.1%;
97 }
88.listbox { /* a scrolling list of checkboxes or radio buttons */98.listbox { /* a scrolling list of checkboxes or radio buttons */
89 border: 1px solid #8cacbb;99 border: 1px solid #8cacbb;
90 display: inline-block;100 display: inline-block;
91101
=== modified file 'lib/lp/bugs/browser/bugtask.py'
--- lib/lp/bugs/browser/bugtask.py 2010-02-20 13:16:38 +0000
+++ lib/lp/bugs/browser/bugtask.py 2010-02-25 08:53:48 +0000
@@ -103,8 +103,9 @@
103 BugNominationStatus, IBugNominationSet)103 BugNominationStatus, IBugNominationSet)
104from lp.bugs.interfaces.bug import IBug, IBugSet104from lp.bugs.interfaces.bug import IBug, IBugSet
105from lp.bugs.interfaces.bugtask import (105from lp.bugs.interfaces.bugtask import (
106 BugTagsSearchCombinator, BugTaskImportance, BugTaskSearchParams,106 BugBranchSearch, BugTagsSearchCombinator, BugTaskImportance,
107 BugTaskStatus, BugTaskStatusSearchDisplay, IBugTask, IBugTaskSearch,107 BugTaskSearchParams, BugTaskStatus, BugTaskStatusSearchDisplay,
108 DEFAULT_SEARCH_BUGTASK_STATUSES_FOR_DISPLAY, IBugTask, IBugTaskSearch,
108 IBugTaskSet, ICreateQuestionFromBugTaskForm, IDistroBugTask,109 IBugTaskSet, ICreateQuestionFromBugTaskForm, IDistroBugTask,
109 IDistroSeriesBugTask, IFrontPageBugTaskSearch,110 IDistroSeriesBugTask, IFrontPageBugTaskSearch,
110 INominationsReviewTableBatchNavigator, INullBugTask, IPersonBugTaskSearch,111 INominationsReviewTableBatchNavigator, INullBugTask, IPersonBugTaskSearch,
@@ -2324,6 +2325,16 @@
2324 if has_patch:2325 if has_patch:
2325 data["attachmenttype"] = BugAttachmentType.PATCH2326 data["attachmenttype"] = BugAttachmentType.PATCH
23262327
2328 has_branches = data.get('has_branches', True)
2329 has_no_branches = data.get('has_no_branches', True)
2330 if has_branches and not has_no_branches:
2331 data['linked_branches'] = BugBranchSearch.BUGS_WITH_BRANCHES
2332 elif not has_branches and has_no_branches:
2333 data['linked_branches'] = (
2334 BugBranchSearch.BUGS_WITHOUT_BRANCHES)
2335 else:
2336 data['linked_branches'] = BugBranchSearch.ALL
2337
2327 # Filter appropriately if the user wants to restrict the2338 # Filter appropriately if the user wants to restrict the
2328 # search to only bugs with no package information.2339 # search to only bugs with no package information.
2329 has_no_package = data.pop("has_no_package", False)2340 has_no_package = data.pop("has_no_package", False)
@@ -2501,14 +2512,13 @@
2501 dict(2512 dict(
2502 value=term.token, title=term.title or term.token,2513 value=term.token, title=term.title or term.token,
2503 checked=term.value in default_values))2514 checked=term.value in default_values))
2504
2505 return helpers.shortlist(widget_values, longest_expected=10)2515 return helpers.shortlist(widget_values, longest_expected=10)
25062516
2507 def getStatusWidgetValues(self):2517 def getStatusWidgetValues(self):
2508 """Return data used to render the status checkboxes."""2518 """Return data used to render the status checkboxes."""
2509 return self.getWidgetValues(2519 return self.getWidgetValues(
2510 vocabulary=BugTaskStatusSearchDisplay,2520 vocabulary=BugTaskStatusSearchDisplay,
2511 default_values=UNRESOLVED_BUGTASK_STATUSES)2521 default_values=DEFAULT_SEARCH_BUGTASK_STATUSES_FOR_DISPLAY)
25122522
2513 def getImportanceWidgetValues(self):2523 def getImportanceWidgetValues(self):
2514 """Return data used to render the Importance checkboxes."""2524 """Return data used to render the Importance checkboxes."""
25152525
=== modified file 'lib/lp/bugs/doc/bugtask-search.txt'
--- lib/lp/bugs/doc/bugtask-search.txt 2010-02-11 05:08:47 +0000
+++ lib/lp/bugs/doc/bugtask-search.txt 2010-02-25 08:53:48 +0000
@@ -962,10 +962,36 @@
962 >>> print reduce(962 >>> print reduce(
963 ... lambda x, y: x and y,963 ... lambda x, y: x and y,
964 ... [task.bug.isUserAffected(foo_bar)964 ... [task.bug.isUserAffected(foo_bar)
965 ... for task in firefox.searchTasks(search_params)]) 965 ... for task in firefox.searchTasks(search_params)])
966 True966 True
967967
968968
969== Searching for bugs linked to branches ==
970
971We can search for bugs having branches linked to them.
972
973 >>> from lp.bugs.interfaces.bugtask import BugBranchSearch
974 >>> search_params = BugTaskSearchParams(
975 ... user=None, linked_branches=BugBranchSearch.BUGS_WITH_BRANCHES)
976 >>> for task in firefox.searchTasks(search_params):
977 ... print task.bug.id, task.bug.linked_branches.count()
978 4 2
979 5 1
980
981Similary, we can search for bugs that do not have any linked branches.
982
983 >>> from lp.bugs.interfaces.bugtask import BugBranchSearch
984 >>> search_params = BugTaskSearchParams(
985 ... user=None, linked_branches=BugBranchSearch.BUGS_WITHOUT_BRANCHES)
986 >>> for task in firefox.searchTasks(search_params):
987 ... print task.bug.id, task.bug.linked_branches.count()
988 1 0
989 6 0
990 18 0
991 20 0
992 21 0
993
994
969== Ordering search results ==995== Ordering search results ==
970996
971The result returned by bugtask searches can come sorted by a specified order997The result returned by bugtask searches can come sorted by a specified order
972998
=== modified file 'lib/lp/bugs/interfaces/bugtarget.py'
--- lib/lp/bugs/interfaces/bugtarget.py 2010-01-21 22:43:19 +0000
+++ lib/lp/bugs/interfaces/bugtarget.py 2010-02-25 08:53:48 +0000
@@ -179,7 +179,7 @@
179 hardware_owner_is_bug_reporter=None,179 hardware_owner_is_bug_reporter=None,
180 hardware_owner_is_affected_by_bug=False,180 hardware_owner_is_affected_by_bug=False,
181 hardware_owner_is_subscribed_to_bug=False,181 hardware_owner_is_subscribed_to_bug=False,
182 hardware_is_linked_to_bug=False):182 hardware_is_linked_to_bug=False, linked_branches=None):
183 """Search the IBugTasks reported on this entity.183 """Search the IBugTasks reported on this entity.
184184
185 :search_params: a BugTaskSearchParams object185 :search_params: a BugTaskSearchParams object
186186
=== modified file 'lib/lp/bugs/interfaces/bugtask.py'
--- lib/lp/bugs/interfaces/bugtask.py 2010-02-08 17:00:55 +0000
+++ lib/lp/bugs/interfaces/bugtask.py 2010-02-25 08:53:48 +0000
@@ -9,6 +9,7 @@
99
10__all__ = [10__all__ = [
11 'BUG_SUPERVISOR_BUGTASK_STATUSES',11 'BUG_SUPERVISOR_BUGTASK_STATUSES',
12 'BugBranchSearch',
12 'BugTagsSearchCombinator',13 'BugTagsSearchCombinator',
13 'BugTaskImportance',14 'BugTaskImportance',
14 'BugTaskSearchParams',15 'BugTaskSearchParams',
@@ -16,6 +17,7 @@
16 'BugTaskStatusSearch',17 'BugTaskStatusSearch',
17 'BugTaskStatusSearchDisplay',18 'BugTaskStatusSearchDisplay',
18 'ConjoinedBugTaskEditError',19 'ConjoinedBugTaskEditError',
20 'DEFAULT_SEARCH_BUGTASK_STATUSES_FOR_DISPLAY',
19 'IAddBugTaskForm',21 'IAddBugTaskForm',
20 'IAddBugTaskWithProductCreationForm',22 'IAddBugTaskWithProductCreationForm',
21 'IBugTask',23 'IBugTask',
@@ -273,6 +275,20 @@
273 use_template(BugTaskStatusSearch, exclude=('INCOMPLETE'))275 use_template(BugTaskStatusSearch, exclude=('INCOMPLETE'))
274276
275277
278class BugBranchSearch(EnumeratedType):
279 """Bug branch search option.
280
281 The possible values to search for bugs having branches attached
282 or not having branches attched.
283 """
284
285 ALL = Item("Show all bugs")
286
287 BUGS_WITH_BRANCHES = Item("Show only Bugs with linked Branches")
288
289 BUGS_WITHOUT_BRANCHES = Item("Show only Bugs without linked Branches")
290
291
276# XXX: Brad Bollenbach 2005-12-02 bugs=5320:292# XXX: Brad Bollenbach 2005-12-02 bugs=5320:
277# In theory, INCOMPLETE belongs in UNRESOLVED_BUGTASK_STATUSES, but the293# In theory, INCOMPLETE belongs in UNRESOLVED_BUGTASK_STATUSES, but the
278# semantics of our current reports would break if it were added to the294# semantics of our current reports would break if it were added to the
@@ -309,6 +325,11 @@
309 BugTaskStatusSearch.INPROGRESS,325 BugTaskStatusSearch.INPROGRESS,
310 BugTaskStatusSearch.FIXCOMMITTED)326 BugTaskStatusSearch.FIXCOMMITTED)
311327
328DEFAULT_SEARCH_BUGTASK_STATUSES_FOR_DISPLAY = [
329 BugTaskStatusSearchDisplay.items.mapping[item.value]
330 for item in DEFAULT_SEARCH_BUGTASK_STATUSES
331 ]
332
312class ConjoinedBugTaskEditError(Exception):333class ConjoinedBugTaskEditError(Exception):
313 """An error raised when trying to modify a conjoined bugtask."""334 """An error raised when trying to modify a conjoined bugtask."""
314335
@@ -799,6 +820,12 @@
799 required=False)820 required=False)
800 affects_me = Bool(821 affects_me = Bool(
801 title=_('Show only bugs affecting me'), required=False)822 title=_('Show only bugs affecting me'), required=False)
823 has_branches = Bool(
824 title=_('Show only bugs with linked branches'), required=False,
825 default=True)
826 has_no_branches = Bool(
827 title=_('Show only bugs without linked branches'), required=False,
828 default=True)
802829
803830
804class IBugTaskSearch(IBugTaskSearchBase):831class IBugTaskSearch(IBugTaskSearchBase):
@@ -989,7 +1016,8 @@
989 hardware_owner_is_bug_reporter=None,1016 hardware_owner_is_bug_reporter=None,
990 hardware_owner_is_affected_by_bug=False,1017 hardware_owner_is_affected_by_bug=False,
991 hardware_owner_is_subscribed_to_bug=False,1018 hardware_owner_is_subscribed_to_bug=False,
992 hardware_is_linked_to_bug=False1019 hardware_is_linked_to_bug=False,
1020 linked_branches=None
993 ):1021 ):
9941022
995 self.bug = bug1023 self.bug = bug
@@ -1033,6 +1061,7 @@
1033 self.hardware_owner_is_subscribed_to_bug = (1061 self.hardware_owner_is_subscribed_to_bug = (
1034 hardware_owner_is_subscribed_to_bug)1062 hardware_owner_is_subscribed_to_bug)
1035 self.hardware_is_linked_to_bug = hardware_is_linked_to_bug1063 self.hardware_is_linked_to_bug = hardware_is_linked_to_bug
1064 self.linked_branches = linked_branches
10361065
1037 def setProduct(self, product):1066 def setProduct(self, product):
1038 """Set the upstream context on which to filter the search."""1067 """Set the upstream context on which to filter the search."""
@@ -1105,7 +1134,7 @@
1105 hardware_owner_is_bug_reporter=None,1134 hardware_owner_is_bug_reporter=None,
1106 hardware_owner_is_affected_by_bug=False,1135 hardware_owner_is_affected_by_bug=False,
1107 hardware_owner_is_subscribed_to_bug=False,1136 hardware_owner_is_subscribed_to_bug=False,
1108 hardware_is_linked_to_bug=False):1137 hardware_is_linked_to_bug=False, linked_branches=None):
1109 """Create and return a new instance using the parameter list."""1138 """Create and return a new instance using the parameter list."""
1110 search_params = cls(user=user, orderby=order_by)1139 search_params = cls(user=user, orderby=order_by)
11111140
@@ -1172,6 +1201,7 @@
1172 hardware_owner_is_subscribed_to_bug)1201 hardware_owner_is_subscribed_to_bug)
1173 search_params.hardware_is_linked_to_bug = (1202 search_params.hardware_is_linked_to_bug = (
1174 hardware_is_linked_to_bug)1203 hardware_is_linked_to_bug)
1204 search_params.linked_branches=linked_branches
11751205
1176 return search_params1206 return search_params
11771207
11781208
=== modified file 'lib/lp/bugs/model/bugtarget.py'
--- lib/lp/bugs/model/bugtarget.py 2010-01-25 20:04:20 +0000
+++ lib/lp/bugs/model/bugtarget.py 2010-02-25 08:53:48 +0000
@@ -57,7 +57,7 @@
57 hardware_owner_is_bug_reporter=None,57 hardware_owner_is_bug_reporter=None,
58 hardware_owner_is_affected_by_bug=False,58 hardware_owner_is_affected_by_bug=False,
59 hardware_owner_is_subscribed_to_bug=False,59 hardware_owner_is_subscribed_to_bug=False,
60 hardware_is_linked_to_bug=False):60 hardware_is_linked_to_bug=False, linked_branches=None):
61 """See `IHasBugs`."""61 """See `IHasBugs`."""
62 if status is None:62 if status is None:
63 # If no statuses are supplied, default to the63 # If no statuses are supplied, default to the
6464
=== modified file 'lib/lp/bugs/model/bugtask.py'
--- lib/lp/bugs/model/bugtask.py 2010-02-20 13:16:38 +0000
+++ lib/lp/bugs/model/bugtask.py 2010-02-25 08:53:48 +0000
@@ -55,13 +55,13 @@
55from lp.bugs.interfaces.bugattachment import BugAttachmentType55from lp.bugs.interfaces.bugattachment import BugAttachmentType
56from lp.bugs.interfaces.bugnomination import BugNominationStatus56from lp.bugs.interfaces.bugnomination import BugNominationStatus
57from lp.bugs.interfaces.bugtask import (57from lp.bugs.interfaces.bugtask import (
58 BUG_SUPERVISOR_BUGTASK_STATUSES, BugTaskImportance, BugTaskSearchParams,58 BUG_SUPERVISOR_BUGTASK_STATUSES, BugBranchSearch, BugTaskImportance,
59 BugTaskStatus, BugTaskStatusSearch, ConjoinedBugTaskEditError, IBugTask,59 BugTaskSearchParams, BugTaskStatus, BugTaskStatusSearch,
60 IBugTaskDelta, IBugTaskSet, IDistroBugTask, IDistroSeriesBugTask,60 ConjoinedBugTaskEditError, IBugTask, IBugTaskDelta, IBugTaskSet,
61 INullBugTask, IProductSeriesBugTask, IUpstreamBugTask, IllegalTarget,61 IDistroBugTask, IDistroSeriesBugTask, INullBugTask, IProductSeriesBugTask,
62 RESOLVED_BUGTASK_STATUSES, UNRESOLVED_BUGTASK_STATUSES,62 IUpstreamBugTask, IllegalTarget, RESOLVED_BUGTASK_STATUSES,
63 UserCannotEditBugTaskImportance, UserCannotEditBugTaskMilestone,63 UNRESOLVED_BUGTASK_STATUSES, UserCannotEditBugTaskImportance,
64 UserCannotEditBugTaskStatus)64 UserCannotEditBugTaskMilestone, UserCannotEditBugTaskStatus)
65from lp.bugs.model.bugsubscription import BugSubscription65from lp.bugs.model.bugsubscription import BugSubscription
66from lp.registry.interfaces.distribution import (66from lp.registry.interfaces.distribution import (
67 IDistribution, IDistributionSet)67 IDistribution, IDistributionSet)
@@ -1671,6 +1671,21 @@
1671 if hw_clause is not None:1671 if hw_clause is not None:
1672 extra_clauses.append(hw_clause)1672 extra_clauses.append(hw_clause)
16731673
1674 if params.linked_branches == BugBranchSearch.BUGS_WITH_BRANCHES:
1675 extra_clauses.append(
1676 """EXISTS (
1677 SELECT id FROM BugBranch WHERE BugBranch.bug=Bug.id)
1678 """)
1679 elif params.linked_branches == BugBranchSearch.BUGS_WITHOUT_BRANCHES:
1680 extra_clauses.append(
1681 """NOT EXISTS (
1682 SELECT id FROM BugBranch WHERE BugBranch.bug=Bug.id)
1683 """)
1684 else:
1685 # If no branch specific search restriction is specified,
1686 # we don't need to add any clause.
1687 pass
1688
1674 orderby_arg = self._processOrderBy(params)1689 orderby_arg = self._processOrderBy(params)
16751690
1676 query = " AND ".join(extra_clauses)1691 query = " AND ".join(extra_clauses)
16771692
=== added file 'lib/lp/bugs/stories/bugtask-searches/xx-filter-by-linked-branches.txt'
--- lib/lp/bugs/stories/bugtask-searches/xx-filter-by-linked-branches.txt 1970-01-01 00:00:00 +0000
+++ lib/lp/bugs/stories/bugtask-searches/xx-filter-by-linked-branches.txt 2010-02-25 08:53:48 +0000
@@ -0,0 +1,64 @@
1= Searching for bugs with linked branches =
2
3Using the "advanced search" form, we can limit a bug task search
4to bugs that are linked to branches or to bugs that are not linked
5to any branches. Normally, both options are turned on.
6
7 >>> anon_browser.open(
8 ... 'http://bugs.launchpad.dev/firefox/+bugs?advanced=1')
9 >>> anon_browser.getControl(
10 ... 'Show only bugs with linked branches').selected
11 True
12 >>> anon_browser.getControl(
13 ... 'Show only bugs without linked branches').selected
14 True
15
16In this case all bugs are returned.
17
18 >>> from lp.bugs.tests.bug import print_bugtasks
19 >>> anon_browser.getControl('Search', index=1).click()
20 >>> print_bugtasks(anon_browser.contents)
21 5 Firefox install instructions should be complete Critical New
22 4 Reflow problems with complex page layouts Medium New
23 1 Firefox does not support SVG Low New
24
25When we uncheck 'Show only bugs without linked branches', only bugs with
26linkes branches are returned.
27
28 >>> anon_browser.open(
29 ... 'http://bugs.launchpad.dev/firefox/+bugs?advanced=1')
30 >>> without_branches = anon_browser.getControl(
31 ... 'Show only bugs without linked branches')
32 >>> without_branches.selected = False
33 >>> anon_browser.getControl('Search', index=1).click()
34 >>> print_bugtasks(anon_browser.contents)
35 5 Firefox install instructions should be complete Critical New
36 4 Reflow problems with complex page layouts Medium New
37
38Similary, we can search for branches that don't have linked branches, if
39we uncheck 'Show only bugs with linked branches'.
40
41 >>> anon_browser.open(
42 ... 'http://bugs.launchpad.dev/firefox/+bugs?advanced=1')
43 >>> with_branches = anon_browser.getControl(
44 ... 'Show only bugs with linked branches')
45 >>> with_branches.selected = False
46 >>> anon_browser.getControl('Search', index=1).click()
47 >>> print_bugtasks(anon_browser.contents)
48 1 Firefox does not support SVG Low New
49
50If we uncheck both fields, all bugs are returned.
51
52 >>> anon_browser.open(
53 ... 'http://bugs.launchpad.dev/firefox/+bugs?advanced=1')
54 >>> with_branches = anon_browser.getControl(
55 ... 'Show only bugs with linked branches')
56 >>> with_branches.selected = False
57 >>> without_branches = anon_browser.getControl(
58 ... 'Show only bugs without linked branches')
59 >>> without_branches.selected = False
60 >>> anon_browser.getControl('Search', index=1).click()
61 >>> print_bugtasks(anon_browser.contents)
62 5 Firefox install instructions should be complete Critical New
63 4 Reflow problems with complex page layouts Medium New
64 1 Firefox does not support SVG Low New
065
=== modified file 'lib/lp/bugs/templates/bugtask-macros-tableview.pt'
--- lib/lp/bugs/templates/bugtask-macros-tableview.pt 2010-01-21 16:47:24 +0000
+++ lib/lp/bugs/templates/bugtask-macros-tableview.pt 2010-02-25 08:53:48 +0000
@@ -236,7 +236,7 @@
236236
237<metal:advanced_search_form define-macro="advanced_search_form">237<metal:advanced_search_form define-macro="advanced_search_form">
238238
239 <form class="lesser" name="search" method="get" action="">239 <form class="long" name="search" method="get" action="">
240 <span tal:condition="view/current_package|nothing">240 <span tal:condition="view/current_package|nothing">
241 <input type="hidden" name="field.distribution"241 <input type="hidden" name="field.distribution"
242 tal:attributes="value view/current_package/distribution/name" />242 tal:attributes="value view/current_package/distribution/name" />
@@ -260,13 +260,13 @@
260 <div tal:repeat="widget_value view/getStatusWidgetValues">260 <div tal:repeat="widget_value view/getStatusWidgetValues">
261 <tal:checkbox261 <tal:checkbox
262 define="widget_id string:status.${widget_value/title}">262 define="widget_id string:status.${widget_value/title}">
263
264 <input name="field.status:list"263 <input name="field.status:list"
265 type="checkbox"264 type="checkbox"
266 tal:attributes="value widget_value/value;265 tal:attributes="value widget_value/value;
267 checked widget_value/checked;266 checked widget_value/checked;
268 id widget_id"/>267 id widget_id"/>
269 <label tal:content="widget_value/title"268 <label style="font-weight: normal"
269 tal:content="widget_value/title"
270 tal:attributes="for widget_id">270 tal:attributes="for widget_id">
271 Unconfirmed271 Unconfirmed
272 </label>272 </label>
@@ -285,7 +285,8 @@
285 tal:attributes="value widget_value/value;285 tal:attributes="value widget_value/value;
286 checked widget_value/checked;286 checked widget_value/checked;
287 id widget_id"/>287 id widget_id"/>
288 <label tal:content="widget_value/title"288 <label style="font-weight: normal"
289 tal:content="widget_value/title"
289 tal:attributes="for widget_id">Critical</label>290 tal:attributes="for widget_id">Critical</label>
290291
291 </tal:checkbox>292 </tal:checkbox>
@@ -315,14 +316,16 @@
315 value="any"316 value="any"
316 id="any"317 id="any"
317 checked="checked" />318 checked="checked" />
318 <label for="any">Doesn&#8217;t matter</label><br />319 <label style="font-weight: normal"
320 for="any">Doesn&#8217;t matter</label><br />
319321
320 <input322 <input
321 type="radio"323 type="radio"
322 name="assignee_option"324 name="assignee_option"
323 value="none"325 value="none"
324 id="none" />326 id="none" />
325 <label for="none"> Nobody</label><br />327 <label style="font-weight: normal"
328 for="none"> Nobody</label><br />
326 <div class="field"329 <div class="field"
327 tal:define="error330 tal:define="error
328 python:view.getFieldError('assignee')">331 python:view.getFieldError('assignee')">
@@ -463,10 +466,10 @@
463466
464 </fieldset>467 </fieldset>
465 <fieldset tal:define="show_component_widget view/shouldShowComponentWidget">468 <fieldset tal:define="show_component_widget view/shouldShowComponentWidget">
466 <legend>Series-specific</legend>469 <legend>Milestones, components, and tags</legend>
467 <table>470 <table>
468 <tr>471 <tr>
469 <td width="40%">472 <td>
470 <table tal:define="widget_values view/getMilestoneWidgetValues">473 <table tal:define="widget_values view/getMilestoneWidgetValues">
471 <tr>474 <tr>
472 <td><label for="field.milestone">Target milestone:</label></td>475 <td><label for="field.milestone">Target milestone:</label></td>
@@ -481,7 +484,8 @@
481 tal:attributes="value widget_value/value;484 tal:attributes="value widget_value/value;
482 checked widget_value/checked;485 checked widget_value/checked;
483 id widget_id" />486 id widget_id" />
484 <label tal:content="widget_value/title"487 <label style="font-weight: normal"
488 tal:content="widget_value/title"
485 tal:attributes="for widget_id">489 tal:attributes="for widget_id">
486 dapper490 dapper
487 </label>491 </label>
@@ -493,7 +497,7 @@
493 </tr>497 </tr>
494 </table>498 </table>
495 </td>499 </td>
496 <td tal:condition="show_component_widget" width="40%">500 <td tal:condition="show_component_widget">
497 <table>501 <table>
498 <tr>502 <tr>
499 <td><label for="field.component">Component:</label></td>503 <td><label for="field.component">Component:</label></td>
@@ -505,57 +509,6 @@
505 </tr>509 </tr>
506 </table>510 </table>
507 </td>511 </td>
508 <td>&nbsp;</td>
509 </tr>
510 </table>
511 </fieldset>
512
513 <fieldset tal:condition="view/shouldShowUpstreamStatusBox">
514 <legend>Upstream status</legend>
515
516 <table>
517 <tr>
518 <td style="white-space: nowrap"
519 tal:content="structure view/widgets/status_upstream" />
520 </tr>
521 </table>
522
523 </fieldset>
524
525 <fieldset>
526 <legend>Other</legend>
527 <table>
528 <tr>
529 <td style="white-space: nowrap">
530 <span tal:content="structure view/widgets/omit_dupes" />
531 <label for="field.omit_dupes">Hide duplicate bugs</label>
532 </td>
533 <td style="white-space: nowrap">
534 <span tal:content="structure view/widgets/has_patch" />
535 <label for="field.has_patch">
536 Show only bugs with patches available
537 </label>
538 </td>
539 </tr>
540 <tr>
541 <td>
542 </td>
543 <td style="white-space: nowrap">
544 <input tal:replace="structure view/widgets/has_cve" />
545 <label tal:attributes="for view/widgets/has_cve/name">
546 Show only bugs associated with a CVE
547 </label>
548 </td>
549 </tr>
550 <tr>
551 <td>
552 </td>
553 <td style="white-space: nowrap">
554 <input tal:replace="structure view/widgets/affects_me" />
555 <label tal:attributes="for view/widgets/affects_me/name">
556 Show only bugs affecting me
557 </label>
558 </td>
559 </tr>512 </tr>
560 <tr>513 <tr>
561 <tal:XXX condition="nothing">514 <tal:XXX condition="nothing">
@@ -567,55 +520,124 @@
567 # FormLayout lands we should be able to use the520 # FormLayout lands we should be able to use the
568 # standard macro instead.521 # standard macro instead.
569 </tal:XXX>522 </tal:XXX>
570 <td tal:define="error view/widgets/tag/error">523 <td span="2"
524 tal:define="error view/widgets/tag/error">
571 <div tal:attributes="class python:error and 'error' or ''">525 <div tal:attributes="class python:error and 'error' or ''">
572 <label tal:attributes="for view/widgets/tag/name"526 <label tal:attributes="for view/widgets/tag/name"
573 tal:content="structure view/widgets/tag/label">527 tal:content="structure view/widgets/tag/label">
574 Tag528 Tag
575 </label>: <input tal:replace="structure view/widgets/tag" />529 </label>: <input tal:replace="structure view/widgets/tag" />
530 <a href="/+help/tag-search.html"
531 target="help">Tag search help</a>
576 <div532 <div
577 tal:condition="error"533 tal:condition="error"
578 class="message"534 class="message"
579 tal:content="structure error"535 tal:content="structure error"
580 >An error message.</div>536 >An error message.</div>
581 </div>537 </div>
582 <p><a href="/+help/tag-search.html"538
583 target="help">Tag search help</a></p>
584 </td>
585 <td>
586 <div condition="view/shouldShowTagsCombinatorWidget"539 <div condition="view/shouldShowTagsCombinatorWidget"
587 class="value">540 class="value">
588 <label>541 <label style="font-weight: normal">
589 <input id="field.tags_combinator.0"542 <input id="field.tags_combinator.0"
590 name="field.tags_combinator"543 name="field.tags_combinator"
591 value="ANY" checked="checked"544 value="ANY" checked="checked"
592 class="radioType" type="radio" />&nbsp;Any545 class="radioType" type="radio" />&nbsp;Any
593 </label>546 </label>
594 <br />547 <br />
595 <label>548 <label style="font-weight: normal">
596 <input id="field.tags_combinator.1"549 <input id="field.tags_combinator.1"
597 name="field.tags_combinator"550 name="field.tags_combinator"
598 value="ALL"551 value="ALL"
599 class="radioType" type="radio" />&nbsp;All552 class="radioType" type="radio" />&nbsp;All
600 </label>553 </label>
601 </div>554 </div>
602 </td>555 </td>
603 </tr>556 </tr>
557 </table>
558 </fieldset>
559
560 <fieldset tal:condition="view/shouldShowUpstreamStatusBox">
561 <legend>Upstream status</legend>
562
563 <table>
604 <tr>564 <tr>
605 <td tal:condition="view/shouldShowNoPackageWidget">565 <td style="white-space: nowrap"
606 <span tal:content="structure view/widgets/has_no_package" />566 tal:content="structure view/widgets/status_upstream" />
607 <label for="field.has_no_package">
608 Hide bugs with packages specified
609 </label>
610 </td>
611 <td style="white-space: nowrap">
612 </td>
613 </tr>567 </tr>
614
615 </table>568 </table>
616569
617 </fieldset>570 </fieldset>
618 <div><input type="submit" name="search" value="Search" /></div>571
572 <fieldset>
573 <legend>Bug relationships</legend>
574 <table>
575 <tr>
576 <td style="white-space: nowrap">
577 <input tal:replace="structure view/widgets/has_cve" />
578 <label style="font-weight: normal"
579 tal:attributes="for view/widgets/has_cve/name">
580 Show only bugs associated with a CVE
581 </label>
582 </td>
583 <td style="white-space: nowrap">
584 <span tal:content="structure view/widgets/omit_dupes" />
585 <label style="font-weight: normal"
586 for="field.omit_dupes">Hide duplicate bugs</label>
587 </td>
588 </tr>
589
590 <tr>
591 <td style="white-space: nowrap">
592 <input tal:replace="structure view/widgets/affects_me" />
593 <label style="font-weight: normal"
594 tal:attributes="for view/widgets/affects_me/name">
595 Show only bugs affecting me
596 </label>
597 </td>
598 <td style="white-space: nowrap"
599 tal:condition="view/shouldShowNoPackageWidget">
600 <span tal:content="structure view/widgets/has_no_package" />
601 <label style="font-weight: normal" for="field.has_no_package">
602 Hide bugs with packages specified
603 </label>
604 </td>
605 </tr>
606
607 <tr>
608 <td style="white-space: nowrap">
609 <span tal:content="structure view/widgets/has_patch" />
610 <label style="font-weight: normal" for="field.has_patch">
611 Show only bugs with patches available
612 </label>
613 </td>
614 <td>
615 </td>
616 </tr>
617 <tr>
618 <td style="white-space: nowrap">
619 <span tal:content="structure view/widgets/has_branches" />
620 <label style="font-weight: normal" for="field.has_branches">
621 Show only bugs with linked branches
622 </label>
623 </td>
624 <td>
625 </td>
626 </tr>
627 <tr>
628 <td style="white-space: nowrap">
629 <span tal:content="structure view/widgets/has_no_branches" />
630 <label style="font-weight: normal" for="field.has_no_branches">
631 Show only bugs without linked branches
632 </label>
633 </td>
634 <td>
635 </td>
636 </tr>
637 </table>
638
639 </fieldset>
640 <div style="margin-top: 1em;"><input type="submit" name="search" value="Search" /></div>
619 </form>641 </form>
620</metal:advanced_search_form>642</metal:advanced_search_form>
621</tal:root>643</tal:root>

Subscribers

People subscribed via source and target branches

to status/vote changes: