Merge lp:~mwhudson/launchpad/filter-code-imports-by-type-bug-513182 into lp:launchpad

Proposed by Michael Hudson-Doyle
Status: Merged
Approved by: Gavin Panella
Approved revision: no longer in the source branch.
Merged at revision: not available
Proposed branch: lp:~mwhudson/launchpad/filter-code-imports-by-type-bug-513182
Merge into: lp:launchpad
Diff against target: 318 lines (+117/-53)
6 files modified
lib/lp/code/browser/codeimport.py (+19/-14)
lib/lp/code/doc/codeimport.txt (+35/-13)
lib/lp/code/interfaces/codeimport.py (+5/-6)
lib/lp/code/model/codeimport.py (+8/-6)
lib/lp/code/stories/codeimport/xx-codeimport-view.txt (+28/-12)
lib/lp/code/templates/codeimport-list.pt (+22/-2)
To merge this branch: bzr merge lp:~mwhudson/launchpad/filter-code-imports-by-type-bug-513182
Reviewer Review Type Date Requested Status
Gavin Panella (community) code Approve
Review via email: mp+20719@code.launchpad.net

Commit message

Allow filtering the +code-imports view by type and show more useful information in the table.

Description of the change

Hi,

This branch adds the option to filter the https://code.launchpad.dev/+code-imports view by VCS type as well as review status and shows more useful information in the table.

Screenshot at: http://people.canonical.com/~mwh/code-import-list.png

Cheers,
mwh

To post a comment you must log in.
Revision history for this message
Gavin Panella (allenap) wrote :
Download full text (10.7 KiB)

Hi Michael,

Nice feature :) A few tiny comments, but also a possible security hole
later on, so Needs Fixing.

Gavin.

> === modified file 'lib/lp/code/browser/codeimport.py'
> --- lib/lp/code/browser/codeimport.py 2010-01-20 03:19:44 +0000
> +++ lib/lp/code/browser/codeimport.py 2010-03-05 13:53:19 +0000
> @@ -67,7 +67,7 @@
> text = u'Code Import System'
>
>
> -class ReviewStatusDropdownWidget(LaunchpadDropdownWidget):
> +class DropdownWidgetWithAny(LaunchpadDropdownWidget):
> """A <select> widget with a more appropriate 'no value' message.
>
> By default `LaunchpadDropdownWidget` displays 'no value' when the
> @@ -88,9 +88,15 @@
> status_field = Choice(
> __name__='status', title=_("Review Status"),
> vocabulary=CodeImportReviewStatus, required=False)
> - self.status_widget = CustomWidgetFactory(ReviewStatusDropdownWidget)
> + self.status_widget = CustomWidgetFactory(DropdownWidgetWithAny)
> setUpWidget(self, 'status', status_field, IInputWidget)
>
> + type_field = Choice(

This is referred to as rcs_type later in initialize() and as an arg to
self.context.search(), so perhaps rename this to rcs_type_field?

> + __name__='type', title=_("Review Status"),

s/Review Status/Revision Control System/ ?

> + vocabulary=RevisionControlSystems, required=False)
> + self.type_widget = CustomWidgetFactory(DropdownWidgetWithAny)
> + setUpWidget(self, 'type', type_field, IInputWidget)
> +
> # status should be None if either (a) there were no query arguments
> # supplied, i.e. the user browsed directly to this page (this is when
> # hasValidInput returns False) or (b) the user chose 'Any' in the
> @@ -99,11 +105,12 @@
> status = None
> if self.status_widget.hasValidInput():
> status = self.status_widget.getInputValue()
> + # Similar for 'type'
> + rcs_type = None
> + if self.type_widget.hasValidInput():
> + rcs_type = self.type_widget.getInputValue()
>
> - if status is not None:
> - imports = self.context.search(review_status=status)
> - else:
> - imports = self.context.getAll()
> + imports = self.context.search(review_status=status, rcs_type=rcs_type)
>
> self.batchnav = BatchNavigator(imports, self.request)
>
>
> === modified file 'lib/lp/code/doc/codeimport.txt'
> --- lib/lp/code/doc/codeimport.txt 2010-01-12 03:46:21 +0000
> +++ lib/lp/code/doc/codeimport.txt 2010-03-05 13:53:19 +0000
> @@ -399,22 +399,15 @@
> Retrieving CodeImports
> ----------------------
>
> -You can retrieve all imports with the `getAll` method of ICodeImport.
> +You can retrive subsets of code imports with the `search` method of
> +ICodeImportSet. Passing no arguments returns all code imports.
>
> - >>> svn_import in code_import_set.getAll()
> + >>> svn_import in code_import_set.search()
> True
>
> -You can also retrive an import by id, which will be used to present the
> -object view for an import and also by branch, which will be used to
> -present the import's details on the page of the branch.
> -
> - ...

review: Needs Fixing (code)
Revision history for this message
Michael Hudson-Doyle (mwhudson) wrote :
Download full text (6.1 KiB)

Gavin Panella wrote:
> Review: Needs Fixing code
> Hi Michael,
>
> Nice feature :) A few tiny comments, but also a possible security hole
> later on, so Needs Fixing.

Hi Gavin, thanks for the review. I think I've addressed your comments
-- diff attached -- so please have another look.

>> === modified file 'lib/lp/code/browser/codeimport.py'
>> --- lib/lp/code/browser/codeimport.py 2010-01-20 03:19:44 +0000
>> +++ lib/lp/code/browser/codeimport.py 2010-03-05 13:53:19 +0000
>> @@ -67,7 +67,7 @@
>> text = u'Code Import System'
>>
>>
>> -class ReviewStatusDropdownWidget(LaunchpadDropdownWidget):
>> +class DropdownWidgetWithAny(LaunchpadDropdownWidget):
>> """A <select> widget with a more appropriate 'no value' message.
>>
>> By default `LaunchpadDropdownWidget` displays 'no value' when the
>> @@ -88,9 +88,15 @@
>> status_field = Choice(
>> __name__='status', title=_("Review Status"),
>> vocabulary=CodeImportReviewStatus, required=False)
>> - self.status_widget = CustomWidgetFactory(ReviewStatusDropdownWidget)
>> + self.status_widget = CustomWidgetFactory(DropdownWidgetWithAny)
>> setUpWidget(self, 'status', status_field, IInputWidget)
>>
>> + type_field = Choice(
>
> This is referred to as rcs_type later in initialize() and as an arg to
> self.context.search(), so perhaps rename this to rcs_type_field?
>
>> + __name__='type', title=_("Review Status"),
>
> s/Review Status/Revision Control System/ ?

Actually, I think it's much clearer to use copy_field instead of what I
was doing there for both fields, so I did that, and renamed everything
to be hopefully more consistent.

>> + vocabulary=RevisionControlSystems, required=False)
>> + self.type_widget = CustomWidgetFactory(DropdownWidgetWithAny)
>> + setUpWidget(self, 'type', type_field, IInputWidget)
>> +
>> # status should be None if either (a) there were no query arguments
>> # supplied, i.e. the user browsed directly to this page (this is when
>> # hasValidInput returns False) or (b) the user chose 'Any' in the
>> @@ -99,11 +105,12 @@
>> status = None
>> if self.status_widget.hasValidInput():
>> status = self.status_widget.getInputValue()
>> + # Similar for 'type'
>> + rcs_type = None
>> + if self.type_widget.hasValidInput():
>> + rcs_type = self.type_widget.getInputValue()
>>
>> - if status is not None:
>> - imports = self.context.search(review_status=status)
>> - else:
>> - imports = self.context.getAll()
>> + imports = self.context.search(review_status=status, rcs_type=rcs_type)
>>
>> self.batchnav = BatchNavigator(imports, self.request)
>>

>> === modified file 'lib/lp/code/stories/codeimport/xx-codeimport-view.txt'
>> --- lib/lp/code/stories/codeimport/xx-codeimport-view.txt 2009-12-07 08:57:49 +0000
>> +++ lib/lp/code/stories/codeimport/xx-codeimport-view.txt 2010-03-05 13:53:19 +0000
>> @@ -35,9 +35,9 @@
>> Filtering the code import list
>> ==============================
>>
>> -The code import listing is filterable, though o...

Read more...

1=== modified file 'lib/lp/code/browser/codeimport.py'
2--- lib/lp/code/browser/codeimport.py 2010-03-05 04:16:37 +0000
3+++ lib/lp/code/browser/codeimport.py 2010-03-08 01:15:46 +0000
4@@ -23,7 +23,6 @@
5 from zope.component import getUtility
6 from zope.formlib import form
7 from zope.interface import Interface
8-from zope.schema import Choice
9
10 from canonical.cachedproperty import cachedproperty
11 from canonical.launchpad import _
12@@ -85,32 +84,31 @@
13
14 def initialize(self):
15 """See `LaunchpadView.initialize`."""
16- status_field = Choice(
17- __name__='status', title=_("Review Status"),
18- vocabulary=CodeImportReviewStatus, required=False)
19- self.status_widget = CustomWidgetFactory(DropdownWidgetWithAny)
20- setUpWidget(self, 'status', status_field, IInputWidget)
21+ review_status_field = copy_field(
22+ ICodeImport['review_status'], required=False, default=None)
23+ self.review_status_widget = CustomWidgetFactory(DropdownWidgetWithAny)
24+ setUpWidget(self, 'review_status', review_status_field, IInputWidget)
25
26- type_field = Choice(
27- __name__='type', title=_("Review Status"),
28- vocabulary=RevisionControlSystems, required=False)
29- self.type_widget = CustomWidgetFactory(DropdownWidgetWithAny)
30- setUpWidget(self, 'type', type_field, IInputWidget)
31+ rcs_type_field = copy_field(
32+ ICodeImport['rcs_type'], required=False, default=None)
33+ self.rcs_type_widget = CustomWidgetFactory(DropdownWidgetWithAny)
34+ setUpWidget(self, 'rcs_type', rcs_type_field, IInputWidget)
35
36 # status should be None if either (a) there were no query arguments
37 # supplied, i.e. the user browsed directly to this page (this is when
38 # hasValidInput returns False) or (b) the user chose 'Any' in the
39 # status widget (this is when hasValidInput returns True but
40 # getInputValue returns None).
41- status = None
42- if self.status_widget.hasValidInput():
43- status = self.status_widget.getInputValue()
44+ review_status = None
45+ if self.review_status_widget.hasValidInput():
46+ review_status = self.review_status_widget.getInputValue()
47 # Similar for 'type'
48 rcs_type = None
49- if self.type_widget.hasValidInput():
50- rcs_type = self.type_widget.getInputValue()
51+ if self.rcs_type_widget.hasValidInput():
52+ rcs_type = self.rcs_type_widget.getInputValue()
53
54- imports = self.context.search(review_status=status, rcs_type=rcs_type)
55+ imports = self.context.search(
56+ review_status=review_status, rcs_type=rcs_type)
57
58 self.batchnav = BatchNavigator(imports, self.request)
59
60
61=== modified file 'lib/lp/code/stories/codeimport/xx-codeimport-view.txt'
62--- lib/lp/code/stories/codeimport/xx-codeimport-view.txt 2010-03-05 04:19:48 +0000
63+++ lib/lp/code/stories/codeimport/xx-codeimport-view.txt 2010-03-08 01:15:46 +0000
64@@ -43,7 +43,7 @@
65 internals a bit.
66
67 >>> browser.open('http://code.launchpad.dev/+code-imports')
68- >>> control = browser.getControl(name="field.status")
69+ >>> control = browser.getControl(name="field.review_status")
70 >>> control.displayValue
71 ['Any']
72 >>> control.displayValue = ["Invalid"]
73@@ -55,24 +55,30 @@
74 Of course selecting the "Any" filtering option ensures that all
75 imports appear again.
76
77- >>> browser.getControl(name="field.status").displayValue = ["Any"]
78+ >>> browser.getControl(name="field.review_status").displayValue = ["Any"]
79 >>> browser.getControl(name="submit").click()
80 >>> table = find_tag_by_id(browser.contents, 'code-import-listing')
81- >>> names = [extract_text(tr.td) for tr in table.tbody('tr')]
82- >>> for name in names:
83- ... print name
84- gnome-terminal/import
85- evolution/import
86+ >>> rows = [extract_text(tr) for tr in table('tr')]
87+ >>> for row in rows:
88+ ... print row
89+ Import Created Type Location Status
90+ gnome-terminal/import 2007-... Subversion via ... http://sv... Reviewed
91+ evolution/import 2007-... Concurrent Vers... :pserver:... Pending Review
92
93 We can also filter by type.
94
95- >>> browser.getControl(name="field.type").displayValue = ["Concurrent Versions System"]
96+ >>> control = browser.getControl(name="field.rcs_type")
97+ >>> control.displayValue
98+ ['Any']
99+ >>> browser.getControl(name="field.rcs_type").displayValue = [
100+ ... "Concurrent Versions System"]
101 >>> browser.getControl(name="submit").click()
102 >>> table = find_tag_by_id(browser.contents, 'code-import-listing')
103- >>> names = [extract_text(tr.td) for tr in table.tbody('tr')]
104- >>> for name in names:
105- ... print name
106- evolution/import
107+ >>> rows = [extract_text(tr) for tr in table('tr')]
108+ >>> for row in rows:
109+ ... print row
110+ Import Created Type Location Status
111+ evolution/import 2007-... Concurrent Vers... :pserver:... Pending Review
112
113 If we create a lot of imports, the listing view will be batched.
114
115
116=== modified file 'lib/lp/code/templates/codeimport-list.pt'
117--- lib/lp/code/templates/codeimport-list.pt 2010-03-05 04:16:37 +0000
118+++ lib/lp/code/templates/codeimport-list.pt 2010-03-08 01:15:46 +0000
119@@ -14,12 +14,12 @@
120 tal:define="codeimports view/batchnav/currentBatch">
121
122 <form action="" method="GET">
123- <select name="review_status" tal:replace="structure view/status_widget">
124+ <select name="review_status" tal:replace="structure view/review_status_widget">
125 <option label="">Any</option>
126 <option label="NEW">New</option>
127 <option label="REVIEWED">Reviewed</option>
128 </select>
129- <select name="rcs_type" tal:replace="structure view/type_widget">
130+ <select name="rcs_type" tal:replace="structure view/rcs_type_widget">
131 <option label="">Any</option>
132 <option label="GIT">Git</option>
133 <option label="BZR_SVN">Subversion</option>
134@@ -68,15 +68,15 @@
135 </a>
136 </td>
137
138- <td tal:content="structure codeimport/date_created/fmt:datetime">
139+ <td tal:content="codeimport/date_created/fmt:datetime">
140 some date
141 </td>
142
143- <td tal:content="structure codeimport/rcs_type/title">
144+ <td tal:content="codeimport/rcs_type/title">
145 some type
146 </td>
147
148- <td tal:content="structure codeimport/getImportDetailsForDisplay">
149+ <td tal:content="codeimport/getImportDetailsForDisplay">
150 some details
151 </td>
152
Revision history for this message
Gavin Panella (allenap) wrote :

This all looks great!

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/code/browser/codeimport.py'
2--- lib/lp/code/browser/codeimport.py 2010-03-05 03:46:53 +0000
3+++ lib/lp/code/browser/codeimport.py 2010-03-08 19:30:44 +0000
4@@ -23,7 +23,6 @@
5 from zope.component import getUtility
6 from zope.formlib import form
7 from zope.interface import Interface
8-from zope.schema import Choice
9
10 from canonical.cachedproperty import cachedproperty
11 from canonical.launchpad import _
12@@ -67,7 +66,7 @@
13 text = u'Code Import System'
14
15
16-class ReviewStatusDropdownWidget(LaunchpadDropdownWidget):
17+class DropdownWidgetWithAny(LaunchpadDropdownWidget):
18 """A <select> widget with a more appropriate 'no value' message.
19
20 By default `LaunchpadDropdownWidget` displays 'no value' when the
21@@ -85,25 +84,31 @@
22
23 def initialize(self):
24 """See `LaunchpadView.initialize`."""
25- status_field = Choice(
26- __name__='status', title=_("Review Status"),
27- vocabulary=CodeImportReviewStatus, required=False)
28- self.status_widget = CustomWidgetFactory(ReviewStatusDropdownWidget)
29- setUpWidget(self, 'status', status_field, IInputWidget)
30+ review_status_field = copy_field(
31+ ICodeImport['review_status'], required=False, default=None)
32+ self.review_status_widget = CustomWidgetFactory(DropdownWidgetWithAny)
33+ setUpWidget(self, 'review_status', review_status_field, IInputWidget)
34+
35+ rcs_type_field = copy_field(
36+ ICodeImport['rcs_type'], required=False, default=None)
37+ self.rcs_type_widget = CustomWidgetFactory(DropdownWidgetWithAny)
38+ setUpWidget(self, 'rcs_type', rcs_type_field, IInputWidget)
39
40 # status should be None if either (a) there were no query arguments
41 # supplied, i.e. the user browsed directly to this page (this is when
42 # hasValidInput returns False) or (b) the user chose 'Any' in the
43 # status widget (this is when hasValidInput returns True but
44 # getInputValue returns None).
45- status = None
46- if self.status_widget.hasValidInput():
47- status = self.status_widget.getInputValue()
48+ review_status = None
49+ if self.review_status_widget.hasValidInput():
50+ review_status = self.review_status_widget.getInputValue()
51+ # Similar for 'type'
52+ rcs_type = None
53+ if self.rcs_type_widget.hasValidInput():
54+ rcs_type = self.rcs_type_widget.getInputValue()
55
56- if status is not None:
57- imports = self.context.search(review_status=status)
58- else:
59- imports = self.context.getAll()
60+ imports = self.context.search(
61+ review_status=review_status, rcs_type=rcs_type)
62
63 self.batchnav = BatchNavigator(imports, self.request)
64
65
66=== modified file 'lib/lp/code/doc/codeimport.txt'
67--- lib/lp/code/doc/codeimport.txt 2010-01-12 03:46:21 +0000
68+++ lib/lp/code/doc/codeimport.txt 2010-03-08 19:30:44 +0000
69@@ -399,22 +399,15 @@
70 Retrieving CodeImports
71 ----------------------
72
73-You can retrieve all imports with the `getAll` method of ICodeImport.
74+You can retrive subsets of code imports with the `search` method of
75+ICodeImportSet. Passing no arguments returns all code imports.
76
77- >>> svn_import in code_import_set.getAll()
78+ >>> svn_import in code_import_set.search()
79 True
80
81-You can also retrive an import by id, which will be used to present the
82-object view for an import and also by branch, which will be used to
83-present the import's details on the page of the branch.
84-
85- >>> code_import_set.get(svn_import.id).url
86- u'svn://svn.example.com/trunk'
87- >>> code_import_set.getByBranch(cvs_import.branch).cvs_root
88- u':pserver:anonymous@cvs.example.com:/cvsroot'
89-
90-Finally, you can search for imports by review status. For instance,
91-there is a single sample CodeImport with the "REVIEWED" status:
92+You can filter the results by review status and by type. For
93+instance, there is a single sample CodeImport with the "REVIEWED"
94+status:
95
96 >>> reviewed_imports = list(code_import_set.search(
97 ... review_status=CodeImportReviewStatus.REVIEWED))
98@@ -423,6 +416,35 @@
99 >>> reviewed_imports[0].review_status.name
100 'REVIEWED'
101
102+And a single Git import.
103+
104+ >>> git_imports = list(code_import_set.search(
105+ ... rcs_type=RevisionControlSystems.GIT))
106+ >>> git_imports
107+ [<...CodeImport...>]
108+ >>> git_imports[0].rcs_type.name
109+ 'GIT'
110+
111+Passing both paramters is combined as "and".
112+
113+ >>> reviewed_git_imports = list(code_import_set.search(
114+ ... review_status=CodeImportReviewStatus.REVIEWED,
115+ ... rcs_type=RevisionControlSystems.GIT))
116+ >>> reviewed_git_imports
117+ [<...CodeImport...>]
118+ >>> reviewed_git_imports[0].rcs_type.name
119+ 'GIT'
120+ >>> reviewed_git_imports[0].review_status.name
121+ 'REVIEWED'
122+
123+You can also retrive an import by id and by branch, which will be used
124+to present the import's details on the page of the branch.
125+
126+ >>> code_import_set.get(svn_import.id).url
127+ u'svn://svn.example.com/trunk'
128+ >>> code_import_set.getByBranch(cvs_import.branch).cvs_root
129+ u':pserver:anonymous@cvs.example.com:/cvsroot'
130+
131 When you ask for an id that is not present ICodeImportSet.get() raises
132 canonical.launchpad.interfaces.NotFoundError, rather than some
133 internal database exception.
134
135=== modified file 'lib/lp/code/interfaces/codeimport.py'
136--- lib/lp/code/interfaces/codeimport.py 2010-01-12 02:07:38 +0000
137+++ lib/lp/code/interfaces/codeimport.py 2010-03-08 19:30:44 +0000
138@@ -191,9 +191,6 @@
139 cvs_root=None, cvs_module=None, review_status=None):
140 """Create a new CodeImport."""
141
142- def getAll():
143- """Return an iterable of all CodeImport objects."""
144-
145 def getActiveImports(text=None):
146 """Return an iterable of all 'active' CodeImport objects.
147
148@@ -222,9 +219,11 @@
149 def delete(id):
150 """Delete a CodeImport given its id."""
151
152- def search(review_status):
153- """Find the CodeImports of the given status.
154+ def search(review_status=None, rcs_type=None):
155+ """Find the CodeImports of the given status and type.
156
157 :param review_status: An entry from the `CodeImportReviewStatus`
158- schema.
159+ schema, or None, which signifies 'any status'.
160+ :param rcs_type: An entry from the `RevisionControlSystems`
161+ schema, or None, which signifies 'any type'.
162 """
163
164=== modified file 'lib/lp/code/model/codeimport.py'
165--- lib/lp/code/model/codeimport.py 2010-01-31 23:48:38 +0000
166+++ lib/lp/code/model/codeimport.py 2010-03-08 19:30:44 +0000
167@@ -31,6 +31,7 @@
168 from canonical.database.datetimecol import UtcDateTimeCol
169 from canonical.database.enumcol import EnumCol
170 from canonical.database.sqlbase import SQLBase, quote, sqlvalues
171+from canonical.launchpad.interfaces import IStore
172 from lp.code.model.codeimportjob import CodeImportJobWorkflow
173 from lp.registry.model.productseries import ProductSeries
174 from canonical.launchpad.webapp.interfaces import NotFoundError
175@@ -265,10 +266,6 @@
176 CodeImportJob.delete(code_import.import_job.id)
177 CodeImport.delete(code_import.id)
178
179- def getAll(self):
180- """See `ICodeImportSet`."""
181- return CodeImport.select()
182-
183 def getActiveImports(self, text=None):
184 """See `ICodeImportSet`."""
185 query = self.composeQueryString(text)
186@@ -334,6 +331,11 @@
187 """See `ICodeImportSet`."""
188 return CodeImport.selectOneBy(branch=branch)
189
190- def search(self, review_status):
191+ def search(self, review_status=None, rcs_type=None):
192 """See `ICodeImportSet`."""
193- return CodeImport.selectBy(review_status=review_status)
194+ clauses = []
195+ if review_status is not None:
196+ clauses.append(CodeImport.review_status == review_status)
197+ if rcs_type is not None:
198+ clauses.append(CodeImport.rcs_type == rcs_type)
199+ return IStore(CodeImport).find(CodeImport, *clauses)
200
201=== modified file 'lib/lp/code/stories/codeimport/xx-codeimport-view.txt'
202--- lib/lp/code/stories/codeimport/xx-codeimport-view.txt 2009-12-07 08:57:49 +0000
203+++ lib/lp/code/stories/codeimport/xx-codeimport-view.txt 2010-03-08 19:30:44 +0000
204@@ -35,15 +35,15 @@
205 Filtering the code import list
206 ==============================
207
208-The code import listing is filterable, though only on review status so
209-far. There are no invalid imports in the sample data, so if we filter
210-just on them we'll see the "no imports found" message. It is worth
211+The code import listing is filterable, on review status and type.
212+There are no invalid imports in the sample data, so if we filter just
213+on them we'll see the "no imports found" message. It is worth
214 ensuring that the control for filtering on review status reads "Any"
215 by default, as the code that ensures this is poking at Zope 3
216 internals a bit.
217
218 >>> browser.open('http://code.launchpad.dev/+code-imports')
219- >>> control = browser.getControl(name="field.status")
220+ >>> control = browser.getControl(name="field.review_status")
221 >>> control.displayValue
222 ['Any']
223 >>> control.displayValue = ["Invalid"]
224@@ -55,14 +55,30 @@
225 Of course selecting the "Any" filtering option ensures that all
226 imports appear again.
227
228- >>> browser.getControl(name="field.status").displayValue = ["Any"]
229- >>> browser.getControl(name="submit").click()
230- >>> table = find_tag_by_id(browser.contents, 'code-import-listing')
231- >>> names = [extract_text(tr.td) for tr in table.tbody('tr')]
232- >>> for name in names:
233- ... print name
234- gnome-terminal/import
235- evolution/import
236+ >>> browser.getControl(name="field.review_status").displayValue = ["Any"]
237+ >>> browser.getControl(name="submit").click()
238+ >>> table = find_tag_by_id(browser.contents, 'code-import-listing')
239+ >>> rows = [extract_text(tr) for tr in table('tr')]
240+ >>> for row in rows:
241+ ... print row
242+ Import Created Type Location Status
243+ gnome-terminal/import 2007-... Subversion via ... http://sv... Reviewed
244+ evolution/import 2007-... Concurrent Vers... :pserver:... Pending Review
245+
246+We can also filter by type.
247+
248+ >>> control = browser.getControl(name="field.rcs_type")
249+ >>> control.displayValue
250+ ['Any']
251+ >>> browser.getControl(name="field.rcs_type").displayValue = [
252+ ... "Concurrent Versions System"]
253+ >>> browser.getControl(name="submit").click()
254+ >>> table = find_tag_by_id(browser.contents, 'code-import-listing')
255+ >>> rows = [extract_text(tr) for tr in table('tr')]
256+ >>> for row in rows:
257+ ... print row
258+ Import Created Type Location Status
259+ evolution/import 2007-... Concurrent Vers... :pserver:... Pending Review
260
261 If we create a lot of imports, the listing view will be batched.
262
263
264=== modified file 'lib/lp/code/templates/codeimport-list.pt'
265--- lib/lp/code/templates/codeimport-list.pt 2009-08-18 00:19:52 +0000
266+++ lib/lp/code/templates/codeimport-list.pt 2010-03-08 19:30:44 +0000
267@@ -14,11 +14,17 @@
268 tal:define="codeimports view/batchnav/currentBatch">
269
270 <form action="" method="GET">
271- <select name="review_status" tal:replace="structure view/status_widget">
272+ <select name="review_status" tal:replace="structure view/review_status_widget">
273 <option label="">Any</option>
274 <option label="NEW">New</option>
275 <option label="REVIEWED">Reviewed</option>
276 </select>
277+ <select name="rcs_type" tal:replace="structure view/rcs_type_widget">
278+ <option label="">Any</option>
279+ <option label="GIT">Git</option>
280+ <option label="BZR_SVN">Subversion</option>
281+ <option label="SVN">Subversion (legacy)</option>
282+ </select>
283 <input type="submit" name="submit"/>
284 </form>
285
286@@ -38,6 +44,12 @@
287 Created
288 </th>
289 <th>
290+ Type
291+ </th>
292+ <th>
293+ Location
294+ </th>
295+ <th>
296 Status
297 </th>
298 </tr>
299@@ -56,10 +68,18 @@
300 </a>
301 </td>
302
303- <td tal:content="structure codeimport/date_created/fmt:datetime">
304+ <td tal:content="codeimport/date_created/fmt:datetime">
305 some date
306 </td>
307
308+ <td tal:content="codeimport/rcs_type/title">
309+ some type
310+ </td>
311+
312+ <td tal:content="codeimport/getImportDetailsForDisplay">
313+ some details
314+ </td>
315+
316 <td tal:content="codeimport/review_status/title">
317 status
318 </td>