Skip to content

Commit e636710

Browse files
committed
Add support for @verbose=2 to a GET on a collection object. Using this
will return the label prop for each item returned by the get. Also added an example of using this to doc/rest.txt.
1 parent 9d212bb commit e636710

File tree

2 files changed

+115
-6
lines changed

2 files changed

+115
-6
lines changed

doc/rest.txt

Lines changed: 95 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,19 @@ This endpoint supports pagination. With the attributes @page_size and
6363
items are displayed at once. The @page_index (which defaults to 1 if not
6464
given) specifies which page number of @page_size items is displayed. If
6565
no @page_size is specified, all items are returned.
66+
67+
Adding the query parameter @verbose=2 to the GET will include the label
68+
property in addition to id and link for the items. This is useful as
69+
documented below in "Searches and selection".
70+
6671
In addition this method supports searching. Search parameters are names
6772
of properties of the given class, e.g., ``status`` for ``issue``. Links
6873
and Multilinks can be specified numerically or symbolically, e.g.,
6974
searching for issues in status ``closed`` can be achieved by searching
7075
for ``status=closed`` or ``status=3`` (provided the ``closed`` status
71-
has ID 3). Note that searching for strings (e.g. the issue title)
72-
performs a case-insensitive substring search, so searching for
73-
title=Something will find all issues with "Something" or "someThing",
76+
has ID 3). Note that searching for strings (e.g. the issue title, or a
77+
keyword name) performs a case-insensitive substring search, so searching
78+
for title=Something will find all issues with "Something" or "someThing",
7479
etc. in the title. There is currently no way to perform an exact string
7580
match.
7681

@@ -176,3 +181,90 @@ Retire/Restore::
176181
>>> r = s.patch(u + 'issue/42', data = d, headers = h)
177182
>>> print(r.json())
178183

184+
Searches and selection
185+
======================
186+
187+
One difficult interface issue is selection of items from a long list.
188+
Using multi-item selects requires loading a lot of data (e.g. consider
189+
a selection tool to select one or more issues as in the classic
190+
superseder field).
191+
192+
This can be made easier using javascript selection tools like select2,
193+
selectize.js, chosen etc. These tools can query a remote data provider
194+
to get a list of items for the user to select from.
195+
196+
Consider a multi-select box for the superseder property. Using
197+
selectize.js (and jquery) code similar to:
198+
199+
$('#superseder').selectize({
200+
valueField: 'id',
201+
labelField: 'title',
202+
searchField: 'title', ...
203+
load: function(query, callback) {
204+
if (!query.length) return callback();
205+
$.ajax({
206+
url: '.../rest/data/issue?@verbose=2&title='
207+
+ encodeURIComponent(query),
208+
type: 'GET',
209+
error: function() {callback();},
210+
success: function(res) {
211+
callback(res.data.collection);}
212+
213+
Sets up a box that a user can type the word "request" into. Then
214+
selectize.js will use that word to generate an ajax request with the
215+
url: .../rest/data/issue?@verbose=2&title=request
216+
217+
This will return data like:
218+
219+
{
220+
"data": {
221+
"@total_size": 440,
222+
"collection": [
223+
{
224+
"link": ".../rest/data/issue/8",
225+
"id": "8",
226+
"title": "Request for Power plugs"
227+
},
228+
{
229+
"link": ".../rest/data/issue/27",
230+
"id": "27",
231+
"title": "Request for foo"
232+
},
233+
234+
selectize.js will look at these objects (as passed to
235+
callback(res.data.collection)) and create a select list from the each
236+
object showing the user the labelField (title) for each object and
237+
associating each title with the corresponding valueField (id). The
238+
example above has 440 issues returned from a total of 2000
239+
issues. Only 440 had the word "request" somewhere in the title greatly
240+
reducing the amount of data that needed to be transferred.
241+
242+
Similar things can be set up to search a large list of keywords using
243+
244+
.../rest/data/keyword?@verbose=2&name=some
245+
246+
which would return: "some keyword" "awesome" "somebody" making
247+
selections for links and multilinks much easier.
248+
249+
Hopefully future enhancements will allow get on a collection to
250+
include other fields. Why do we want this? Selectize.js can set up
251+
option groups (optgroups) in the select pulldown. So by including
252+
status in the returned data:
253+
254+
{
255+
"link": ".../rest/data/issue/27",
256+
"id": "27",
257+
"title": "Request for foo",
258+
'status": "open"
259+
},
260+
261+
a select widget like:
262+
263+
=== New ===
264+
A request
265+
=== Open ===
266+
Request for bar
267+
Request for foo
268+
269+
etc. can be generated. Also depending on the javascript library, other
270+
fields can be used for subsearch and sorting.

roundup/rest.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -490,8 +490,11 @@ def get_collection(self, class_name, input):
490490
"""
491491
if class_name not in self.db.classes:
492492
raise NotFound('Class %s not found' % class_name)
493+
494+
uid = self.db.getuid()
495+
493496
if not self.db.security.hasPermission(
494-
'View', self.db.getuid(), class_name
497+
'View', uid, class_name
495498
):
496499
raise Unauthorised('Permission to view %s denied' % class_name)
497500

@@ -504,14 +507,16 @@ def get_collection(self, class_name, input):
504507
'size': None,
505508
'index': 1 # setting just size starts at page 1
506509
}
507-
uid = self.db.getuid()
510+
verbose = 1
508511
for form_field in input.value:
509512
key = form_field.name
510513
value = form_field.value
511514
if key.startswith("@page_"): # serve the paging purpose
512515
key = key[6:]
513516
value = int(value)
514517
page[key] = value
518+
elif key == "@verbose":
519+
verbose = int (value)
515520
else: # serve the filter purpose
516521
prop = class_obj.getprops()[key]
517522
# We drop properties without search permission silently
@@ -543,9 +548,21 @@ def get_collection(self, class_name, input):
543548
{'id': item_id, 'link': class_path + item_id}
544549
for item_id in obj_list
545550
if self.db.security.hasPermission(
546-
'View', self.db.getuid(), class_name, itemid=item_id
551+
'View', uid, class_name, itemid=item_id
547552
)
548553
]
554+
555+
# add verbose elements. First identifying label.
556+
if verbose > 1:
557+
label = class_obj.labelprop()
558+
for obj in result['collection']:
559+
id = obj['id']
560+
if self.db.security.hasPermission(
561+
'View', uid, class_name, property=label,
562+
itemid=id
563+
):
564+
obj[label] = class_obj.get(id, label)
565+
549566
result_len = len(result['collection'])
550567

551568
# pagination - page_index from 1...N

0 commit comments

Comments
 (0)