1 | package dbnp.query |
---|
2 | |
---|
3 | import dbnp.modules.* |
---|
4 | import org.dbnp.gdt.* |
---|
5 | |
---|
6 | // TODO: Make use of the searchable-plugin or Lucene possibilities instead of querying the database directly |
---|
7 | |
---|
8 | /** |
---|
9 | * Basic web interface for searching within studies |
---|
10 | * |
---|
11 | * @author Robert Horlings (robert@isdat.nl) |
---|
12 | */ |
---|
13 | class AdvancedQueryController { |
---|
14 | def moduleCommunicationService; |
---|
15 | def authenticationService |
---|
16 | |
---|
17 | def entitiesToSearchFor = [ 'Study': 'Studies', 'Sample': 'Samples', 'Assay': 'Assays'] |
---|
18 | |
---|
19 | /** |
---|
20 | * Shows search screen |
---|
21 | */ |
---|
22 | def index = { |
---|
23 | // Check whether criteria have been given before |
---|
24 | def criteria = []; |
---|
25 | if( params.criteria ) { |
---|
26 | criteria = parseCriteria( params.criteria, false ) |
---|
27 | } |
---|
28 | [searchModes: SearchMode.values(), entitiesToSearchFor: entitiesToSearchFor, searchableFields: getSearchableFields(), criteria: criteria] |
---|
29 | } |
---|
30 | |
---|
31 | /** |
---|
32 | * Searches for studies or samples based on the user parameters. |
---|
33 | * |
---|
34 | * @param entity The entity to search for ( 'Study' or 'Sample' ) |
---|
35 | * @param criteria HashMap with the values being hashmaps with field, operator and value. |
---|
36 | * [ 0: [ field: 'Study.name', operator: 'equals', value: 'term' ], 1: [..], .. ] |
---|
37 | */ |
---|
38 | def search = { |
---|
39 | if( !params.criteria ) { |
---|
40 | flash.error = "No criteria given to search for. Please try again."; |
---|
41 | redirect( action: 'index' ) |
---|
42 | } |
---|
43 | |
---|
44 | if( !params.entity || !entitiesToSearchFor*.key.contains( params.entity ) ) { |
---|
45 | flash.error = "No or incorrect entity given to search for. Please try again."; |
---|
46 | redirect( action: 'index', params: [ criteria: parseCriteria( params.criteria ) ] ) |
---|
47 | } |
---|
48 | |
---|
49 | // Create a search object and let it do the searching |
---|
50 | Search search = determineSearch( params.entity ); |
---|
51 | String view = determineView( params.entity ); |
---|
52 | |
---|
53 | // Choose between AND and OR search. Default is given by the Search class itself. |
---|
54 | switch( params.operator?.toString()?.toLowerCase() ) { |
---|
55 | case "or": |
---|
56 | search.searchMode = SearchMode.or; |
---|
57 | break; |
---|
58 | case "and": |
---|
59 | search.searchMode = SearchMode.and; |
---|
60 | break; |
---|
61 | } |
---|
62 | |
---|
63 | search.execute( parseCriteria( params.criteria ) ); |
---|
64 | |
---|
65 | // Save search in session |
---|
66 | def queryId = saveSearch( search ); |
---|
67 | render( view: view, model: [search: search, queryId: queryId, actions: determineActions(search)] ); |
---|
68 | } |
---|
69 | |
---|
70 | /** |
---|
71 | * Removes a specified search from session |
---|
72 | * @param id queryId of the search to discard |
---|
73 | */ |
---|
74 | def discard = { |
---|
75 | def queryIds = params.list( 'id' ); |
---|
76 | queryIds = queryIds.findAll { it.isInteger() }.collect { Integer.valueOf( it ) } |
---|
77 | |
---|
78 | if( queryIds.size() == 0 ) { |
---|
79 | flash.error = "Incorrect search ID given to discard" |
---|
80 | redirect( action: "index" ); |
---|
81 | return |
---|
82 | } |
---|
83 | |
---|
84 | queryIds.each { queryId -> |
---|
85 | discardSearch( queryId ); |
---|
86 | } |
---|
87 | |
---|
88 | if( queryIds.size() > 1 ) { |
---|
89 | flash.message = "Searches have been discarded" |
---|
90 | } else { |
---|
91 | flash.message = "Search has been discarded" |
---|
92 | } |
---|
93 | redirect( action: "list" ); |
---|
94 | } |
---|
95 | |
---|
96 | /** |
---|
97 | * Shows a specified search from session |
---|
98 | * @param id queryId of the search to show |
---|
99 | */ |
---|
100 | def show = { |
---|
101 | def queryId = params.int( 'id' ); |
---|
102 | |
---|
103 | if( !queryId ) { |
---|
104 | flash.error = "Incorrect search ID given to show" |
---|
105 | redirect( action: "index" ); |
---|
106 | return |
---|
107 | } |
---|
108 | |
---|
109 | // Retrieve the search from session |
---|
110 | Search s = retrieveSearch( queryId ); |
---|
111 | if( !s ) { |
---|
112 | flash.message = "Specified search could not be found" |
---|
113 | redirect( action: "index" ); |
---|
114 | return; |
---|
115 | } |
---|
116 | |
---|
117 | // Attach all objects to the current hibernate thread, because the |
---|
118 | // object might be attached to an old thread, since the results are |
---|
119 | // saved in session |
---|
120 | s.getResults().each { |
---|
121 | it.attach(); |
---|
122 | } |
---|
123 | |
---|
124 | // Determine which view to show |
---|
125 | def view = determineView( s.entity ); |
---|
126 | render( view: view, model: [search: s, queryId: queryId, actions: determineActions(s)] ); |
---|
127 | } |
---|
128 | |
---|
129 | /** |
---|
130 | * Performs an action on specific searchResults |
---|
131 | * @param queryId queryId of the search to show |
---|
132 | * @param id list with the ids of the results to perform the action on |
---|
133 | * @param actionName Name of the action to perform |
---|
134 | */ |
---|
135 | def performAction = { |
---|
136 | def queryId = params.int( 'queryId' ); |
---|
137 | def selectedIds = params.list( 'id' ).findAll { it.isLong() }.collect { Long.parseLong(it) } |
---|
138 | def actionName = params.actionName; |
---|
139 | def moduleName = params.moduleName; |
---|
140 | |
---|
141 | if( !queryId ) { |
---|
142 | flash.error = "Incorrect search ID given to show" |
---|
143 | redirect( action: "index" ); |
---|
144 | return |
---|
145 | } |
---|
146 | |
---|
147 | // Retrieve the search from session |
---|
148 | Search s = retrieveSearch( queryId ); |
---|
149 | if( !s ) { |
---|
150 | flash.message = "Specified search could not be found" |
---|
151 | redirect( action: "list" ); |
---|
152 | return; |
---|
153 | } |
---|
154 | |
---|
155 | // Determine the possible actions and build correct urls |
---|
156 | def actions = determineActions(s, selectedIds ); |
---|
157 | |
---|
158 | // Find the right action to perform |
---|
159 | def redirectUrl; |
---|
160 | for( action in actions ) { |
---|
161 | if( action.module == moduleName && action.name == actionName ) { |
---|
162 | redirectUrl = action.url; |
---|
163 | break; |
---|
164 | } |
---|
165 | } |
---|
166 | |
---|
167 | if( !redirectUrl ) { |
---|
168 | flash.error = "No valid action is given to perform"; |
---|
169 | redirect( action: "show", id: queryId ); |
---|
170 | return; |
---|
171 | } |
---|
172 | |
---|
173 | redirect( url: redirectUrl ); |
---|
174 | } |
---|
175 | |
---|
176 | /** |
---|
177 | * Shows a list of searches that have been saved in session |
---|
178 | * @param id queryId of the search to show |
---|
179 | */ |
---|
180 | def list = { |
---|
181 | def searches = listSearches(); |
---|
182 | |
---|
183 | if( !searches || searches.size() == 0 ) { |
---|
184 | flash.message = "No previous searches found"; |
---|
185 | redirect( action: "index" ); |
---|
186 | return; |
---|
187 | } |
---|
188 | [searches: searches] |
---|
189 | } |
---|
190 | |
---|
191 | /** |
---|
192 | * Shows a search screen where the user can search within the results of another search |
---|
193 | * @param id queryId of the search to search in |
---|
194 | */ |
---|
195 | def searchIn = { |
---|
196 | def queryIds = params.list( 'id' ); |
---|
197 | queryIds = queryIds.findAll { it.isInteger() }.collect { Integer.valueOf( it ) } |
---|
198 | |
---|
199 | if( queryIds.size() == 0 ) { |
---|
200 | flash.error = "Incorrect search ID given to search in" |
---|
201 | redirect( action: "list" ); |
---|
202 | return |
---|
203 | } |
---|
204 | |
---|
205 | // Retrieve the searches from session |
---|
206 | def params = [:] |
---|
207 | queryIds.eachWithIndex { queryId, idx -> |
---|
208 | Search s = retrieveSearch( queryId ); |
---|
209 | if( !s ) { |
---|
210 | flash.message = "Specified search " + queryId + " could not be found" |
---|
211 | return; |
---|
212 | } else { |
---|
213 | params[ "criteria." + idx + ".entityfield" ] = s.entity; |
---|
214 | params[ "criteria." + idx + ".operator" ] = "in"; |
---|
215 | params[ "criteria." + idx + ".value" ] = queryId; |
---|
216 | } |
---|
217 | } |
---|
218 | |
---|
219 | redirect( action: "index", params: params) |
---|
220 | } |
---|
221 | |
---|
222 | /** |
---|
223 | * Combines the results of multiple searches |
---|
224 | * @param id queryIds of the searches to combine |
---|
225 | */ |
---|
226 | def combine = { |
---|
227 | def queryIds = params.list( 'id' ); |
---|
228 | queryIds = queryIds.findAll { it.isInteger() }.collect { Integer.valueOf( it ) } |
---|
229 | |
---|
230 | if( queryIds.size() == 0 ) { |
---|
231 | flash.error = "Incorrect search ID given to combine" |
---|
232 | redirect( action: "index" ); |
---|
233 | return |
---|
234 | } |
---|
235 | |
---|
236 | // First determine whether the types match |
---|
237 | def searches = []; |
---|
238 | def type = ""; |
---|
239 | flash.error = ""; |
---|
240 | queryIds.eachWithIndex { queryId, idx -> |
---|
241 | Search s = retrieveSearch( queryId ); |
---|
242 | if( !s ) { |
---|
243 | return; |
---|
244 | } |
---|
245 | |
---|
246 | if( type ) { |
---|
247 | if( type != s.entity ) { |
---|
248 | flash.error = type + " and " + s.entity.toLowerCase() + " queries can't be combined. Selected queries of one type."; |
---|
249 | return |
---|
250 | } |
---|
251 | } else { |
---|
252 | type = s.entity |
---|
253 | } |
---|
254 | } |
---|
255 | |
---|
256 | if( flash.error ) { |
---|
257 | redirect( action: "list" ); |
---|
258 | return; |
---|
259 | } |
---|
260 | |
---|
261 | if( !type ) { |
---|
262 | flash.error = "No correct query ids were given." |
---|
263 | redirect( action: "list" ); |
---|
264 | return; |
---|
265 | } |
---|
266 | |
---|
267 | // Retrieve the searches from session |
---|
268 | Search combined = determineSearch( type ); |
---|
269 | combined.searchMode = SearchMode.or; |
---|
270 | |
---|
271 | queryIds.eachWithIndex { queryId, idx -> |
---|
272 | Search s = retrieveSearch( queryId ); |
---|
273 | if( s ) { |
---|
274 | combined.addCriterion( new Criterion( entity: type, field: null, operator: Operator.insearch, value: s ) ); |
---|
275 | } |
---|
276 | } |
---|
277 | |
---|
278 | // Execute search to combine the results |
---|
279 | combined.execute(); |
---|
280 | |
---|
281 | def queryId = saveSearch( combined ); |
---|
282 | redirect( action: "show", id: queryId ); |
---|
283 | } |
---|
284 | |
---|
285 | protected String determineView( String entity ) { |
---|
286 | switch( entity ) { |
---|
287 | case "Study": return "studyresults"; break; |
---|
288 | case "Sample": return "sampleresults"; break; |
---|
289 | case "Assay": return "assayresults"; break; |
---|
290 | default: return "results"; break; |
---|
291 | } |
---|
292 | } |
---|
293 | |
---|
294 | /** |
---|
295 | * Returns the search object used for searching |
---|
296 | */ |
---|
297 | protected Search determineSearch( String entity ) { |
---|
298 | switch( entity ) { |
---|
299 | case "Study": return new StudySearch(); |
---|
300 | case "Sample": return new SampleSearch(); |
---|
301 | case "Assay": return new AssaySearch(); |
---|
302 | |
---|
303 | // This exception will only be thrown if the entitiesToSearchFor contains more entities than |
---|
304 | // mentioned in this switch structure. |
---|
305 | default: throw new Exception( "Can't search for entities of type " + entity ); |
---|
306 | } |
---|
307 | } |
---|
308 | |
---|
309 | /** |
---|
310 | * Returns a map of entities with the names of the fields the user can search on |
---|
311 | * @return |
---|
312 | */ |
---|
313 | protected def getSearchableFields() { |
---|
314 | def fields = [:]; |
---|
315 | |
---|
316 | // Retrieve all local search fields |
---|
317 | getEntities().each { |
---|
318 | def entity = getEntity( 'dbnp.studycapturing.' + it ); |
---|
319 | |
---|
320 | if( entity ) { |
---|
321 | def domainFields = entity.giveDomainFields(); |
---|
322 | def templateFields = TemplateField.findAllByEntity( entity ) |
---|
323 | |
---|
324 | def fieldNames = ( domainFields + templateFields ).collect { it.name }.unique() + 'Template' |
---|
325 | |
---|
326 | fields[ it ] = fieldNames.sort { a, b -> a[0].toUpperCase() + a[1..-1] <=> b[0].toUpperCase() + b[1..-1] }; |
---|
327 | } |
---|
328 | } |
---|
329 | |
---|
330 | // Loop through all modules and check which fields are searchable |
---|
331 | // Right now, we just combine the results for different entities |
---|
332 | AssayModule.list().each { module -> |
---|
333 | def callUrl = module.url + '/rest/getQueryableFields' |
---|
334 | try { |
---|
335 | def json = moduleCommunicationService.callModuleMethod( module.url, callUrl ); |
---|
336 | def moduleFields = []; |
---|
337 | entitiesToSearchFor.each { entity -> |
---|
338 | if( json[ entity.key ] ) { |
---|
339 | json[ entity.key ].each { field -> |
---|
340 | moduleFields << field.toString(); |
---|
341 | } |
---|
342 | } |
---|
343 | } |
---|
344 | |
---|
345 | // Remove 'module' from module name |
---|
346 | def moduleName = module.name.replace( 'module', '' ).trim() |
---|
347 | |
---|
348 | fields[ moduleName ] = moduleFields.unique(); |
---|
349 | } catch( Exception e ) { |
---|
350 | log.error( "Error while retrieving queryable fields from " + module.name + ": " + e.getMessage() ) |
---|
351 | } |
---|
352 | } |
---|
353 | |
---|
354 | return fields; |
---|
355 | } |
---|
356 | |
---|
357 | /** |
---|
358 | * Parses the criteria from the query form given by the user |
---|
359 | * @param c Data from the input form and had a form like |
---|
360 | * |
---|
361 | * [ |
---|
362 | * 0: [entityfield:a.b, operator: b, value: c], |
---|
363 | * 0.entityfield: a.b, |
---|
364 | * 0.operator: b, |
---|
365 | * 0.field: c |
---|
366 | * 1: [entityfield:f.q, operator: e, value: d], |
---|
367 | * 1.entityfield: f.q, |
---|
368 | * 1.operator: e, |
---|
369 | * 1.field: d |
---|
370 | * ] |
---|
371 | * @param parseSearchIds Determines whether searches are returned instead of their ids |
---|
372 | * @return List with Criterion objects |
---|
373 | */ |
---|
374 | protected List parseCriteria( def formCriteria, def parseSearchIds = true ) { |
---|
375 | ArrayList list = []; |
---|
376 | flash.error = ""; |
---|
377 | // Loop through all keys of c and remove the non-numeric ones |
---|
378 | for( c in formCriteria ) { |
---|
379 | if( c.key ==~ /[0-9]+/ && c.value.entityfield ) { |
---|
380 | def formCriterion = c.value; |
---|
381 | |
---|
382 | Criterion criterion = new Criterion(); |
---|
383 | |
---|
384 | // Split entity and field |
---|
385 | def field = formCriterion.entityfield?.split( /\./ ); |
---|
386 | if( field.size() > 1 ) { |
---|
387 | criterion.entity = field[0].toString(); |
---|
388 | criterion.field = field[1].toString(); |
---|
389 | } else { |
---|
390 | criterion.entity = field[0]; |
---|
391 | criterion.field = null; |
---|
392 | } |
---|
393 | |
---|
394 | // Convert operator string to Operator-enum field |
---|
395 | try { |
---|
396 | criterion.operator = Criterion.parseOperator( formCriterion.operator ); |
---|
397 | } catch( Exception e) { |
---|
398 | log.debug "Operator " + formCriterion.operator + " could not be parsed: " + e.getMessage(); |
---|
399 | flash.error += "Criterion could not be used: operator " + formCriterion.operator + " is not valid.<br />\n"; |
---|
400 | continue; |
---|
401 | } |
---|
402 | |
---|
403 | // Special case of the 'in' operator |
---|
404 | if( criterion.operator == Operator.insearch ) { |
---|
405 | Search s |
---|
406 | try { |
---|
407 | s = retrieveSearch( Integer.parseInt( formCriterion.value ) ); |
---|
408 | } catch( Exception e ) {} |
---|
409 | |
---|
410 | if( !s ) { |
---|
411 | flash.error += "Can't search within previous query: query not found"; |
---|
412 | continue; |
---|
413 | } |
---|
414 | |
---|
415 | if( parseSearchIds ) { |
---|
416 | criterion.value = s |
---|
417 | } else { |
---|
418 | criterion.value = s.id |
---|
419 | } |
---|
420 | } else { |
---|
421 | // Copy value |
---|
422 | criterion.value = formCriterion.value; |
---|
423 | } |
---|
424 | |
---|
425 | list << criterion; |
---|
426 | } |
---|
427 | } |
---|
428 | |
---|
429 | return list; |
---|
430 | } |
---|
431 | |
---|
432 | /** |
---|
433 | * Returns all entities for which criteria can be entered |
---|
434 | * @return |
---|
435 | */ |
---|
436 | protected def getEntities() { |
---|
437 | return [ 'Study', 'Subject', 'Sample', 'Event', 'SamplingEvent', 'Assay' ] |
---|
438 | } |
---|
439 | |
---|
440 | /** |
---|
441 | * Creates an object of the given entity. |
---|
442 | * |
---|
443 | * @return False if the entity is not a subclass of TemplateEntity |
---|
444 | */ |
---|
445 | protected def getEntity( entityName ) { |
---|
446 | // Find the templates |
---|
447 | def entity |
---|
448 | try { |
---|
449 | entity = Class.forName(entityName, true, this.getClass().getClassLoader()) |
---|
450 | |
---|
451 | // succes, is entity an instance of TemplateEntity? |
---|
452 | if (entity.superclass =~ /TemplateEntity$/ || entity.superclass.superclass =~ /TemplateEntity$/) { |
---|
453 | return entity; |
---|
454 | } else { |
---|
455 | return false; |
---|
456 | } |
---|
457 | } catch( ClassNotFoundException e ) { |
---|
458 | log.error "Class " + entityName + " not found: " + e.getMessage() |
---|
459 | return null; |
---|
460 | } |
---|
461 | |
---|
462 | } |
---|
463 | |
---|
464 | |
---|
465 | /*************************************************************************** |
---|
466 | * |
---|
467 | * Methods for saving results in session |
---|
468 | * |
---|
469 | ***************************************************************************/ |
---|
470 | |
---|
471 | /** |
---|
472 | * Saves the given search in session. Any search with the same criteria will be overwritten |
---|
473 | * |
---|
474 | * @param s Search to save |
---|
475 | * @return Id of the search for later reference |
---|
476 | */ |
---|
477 | protected int saveSearch( Search s ) { |
---|
478 | if( !session.queries ) |
---|
479 | session.queries = [:] |
---|
480 | |
---|
481 | // First check whether a search with the same criteria is already present |
---|
482 | def previousSearch = retrieveSearch( s ); |
---|
483 | |
---|
484 | def id |
---|
485 | if( previousSearch ) { |
---|
486 | id = previousSearch.id; |
---|
487 | } else { |
---|
488 | // Determine unique id |
---|
489 | id = ( session.queries*.key.max() ?: 0 ) + 1; |
---|
490 | } |
---|
491 | |
---|
492 | s.id = id; |
---|
493 | session.queries[ id ] = s; |
---|
494 | |
---|
495 | return id; |
---|
496 | } |
---|
497 | |
---|
498 | /** |
---|
499 | * Retrieves a search from session with the same criteria as given |
---|
500 | * @param s Search that is used as an example to search for |
---|
501 | * @return Search that has this criteria, or null if no such search is found. |
---|
502 | */ |
---|
503 | protected Search retrieveSearch( Search s ) { |
---|
504 | if( !session.queries ) |
---|
505 | return null |
---|
506 | |
---|
507 | for( query in session.queries ) { |
---|
508 | def value = query.value; |
---|
509 | |
---|
510 | if( s.equals( value ) ) |
---|
511 | return value |
---|
512 | } |
---|
513 | |
---|
514 | return null; |
---|
515 | } |
---|
516 | |
---|
517 | |
---|
518 | /** |
---|
519 | * Retrieves a search from session |
---|
520 | * @param id Id of the search |
---|
521 | * @return Search that belongs to this ID or null if no search is found |
---|
522 | */ |
---|
523 | protected Search retrieveSearch( int id ) { |
---|
524 | if( !session.queries || !session.queries[ id ] ) |
---|
525 | return null |
---|
526 | |
---|
527 | if( !( session.queries[ id ] instanceof Search ) ) |
---|
528 | return null; |
---|
529 | |
---|
530 | return (Search) session.queries[ id ] |
---|
531 | } |
---|
532 | |
---|
533 | /** |
---|
534 | * Removes a search from session |
---|
535 | * @param id Id of the search |
---|
536 | * @return Search that belonged to this ID or null if no search is found |
---|
537 | */ |
---|
538 | protected Search discardSearch( int id ) { |
---|
539 | if( !session.queries || !session.queries[ id ] ) |
---|
540 | return null |
---|
541 | |
---|
542 | def sessionSearch = session.queries[ id ]; |
---|
543 | |
---|
544 | session.queries.remove( id ); |
---|
545 | |
---|
546 | if( !( sessionSearch instanceof Search ) ) |
---|
547 | return null; |
---|
548 | |
---|
549 | return (Search) sessionSearch |
---|
550 | } |
---|
551 | |
---|
552 | /** |
---|
553 | * Retrieves a list of searches from session |
---|
554 | * @return List of searches from session |
---|
555 | */ |
---|
556 | protected List listSearches() { |
---|
557 | if( !session.queries ) |
---|
558 | return [] |
---|
559 | |
---|
560 | return session.queries*.value.toList() |
---|
561 | } |
---|
562 | |
---|
563 | /** |
---|
564 | * Determine a list of actions that can be performed on specific entities |
---|
565 | * @param entity Name of the entity that the actions could be performed on |
---|
566 | * @param selectedIds List with ids of the selected items to perform an action on |
---|
567 | * @return |
---|
568 | */ |
---|
569 | protected List determineActions( Search s, def selectedIds = null ) { |
---|
570 | return gscfActions( s, selectedIds ) + moduleActions( s, selectedIds ); |
---|
571 | } |
---|
572 | |
---|
573 | /** |
---|
574 | * Determine a list of actions that can be performed on specific entities by GSCF |
---|
575 | * @param entity Name of the entity that the actions could be performed on |
---|
576 | * @param selectedIds List with ids of the selected items to perform an action on |
---|
577 | */ |
---|
578 | protected List gscfActions(Search s, def selectedIds = null) { |
---|
579 | switch(s.entity) { |
---|
580 | case "Study": |
---|
581 | def ids = [] |
---|
582 | s.filterResults(selectedIds).each { |
---|
583 | ids << it.id |
---|
584 | } |
---|
585 | |
---|
586 | return [[ |
---|
587 | module: "gscf", |
---|
588 | name:"simpletox", |
---|
589 | description: "Export as SimpleTox", |
---|
590 | url: createLink( controller: "exporter", action: "export", params: [ 'ids' : ids ] ) |
---|
591 | ]] |
---|
592 | case "Sample": |
---|
593 | return [] |
---|
594 | default: |
---|
595 | return []; |
---|
596 | } |
---|
597 | } |
---|
598 | |
---|
599 | /** |
---|
600 | * Determine a list of actions that can be performed on specific entities by other modules |
---|
601 | * @param entity Name of the entity that the actions could be performed on |
---|
602 | */ |
---|
603 | protected List moduleActions(Search s, def selectedIds = null) { |
---|
604 | def actions = [] |
---|
605 | |
---|
606 | if( !s.getResults() || s.getResults().size() == 0 ) |
---|
607 | return [] |
---|
608 | |
---|
609 | // Loop through all modules and check which actions can be performed on the |
---|
610 | AssayModule.list().each { module -> |
---|
611 | // Remove 'module' from module name |
---|
612 | def moduleName = module.name.replace( 'module', '' ).trim() |
---|
613 | try { |
---|
614 | def callUrl = module.url + "/rest/getPossibleActions?entity=" + s.entity |
---|
615 | def json = moduleCommunicationService.callModuleRestMethodJSON( module.url, callUrl ); |
---|
616 | |
---|
617 | // Check whether the entity is present in the return value |
---|
618 | if( json[ s.entity ] ) { |
---|
619 | json[ s.entity ].each { action -> |
---|
620 | def url = action.url ?: module.url + "/action/" + action.name |
---|
621 | |
---|
622 | if( url.find( /\?/ ) ) |
---|
623 | url += "&" |
---|
624 | else |
---|
625 | url += "?" |
---|
626 | |
---|
627 | url += "entity=" + s.entity |
---|
628 | url += "&" + s.filterResults(selectedIds).collect { "tokens=" + it.giveUUID() }.join( "&" ) |
---|
629 | actions << [ |
---|
630 | module: moduleName, |
---|
631 | name: action.name, |
---|
632 | description: action.description + " (" + moduleName + ")", |
---|
633 | url: url |
---|
634 | ]; |
---|
635 | } |
---|
636 | } |
---|
637 | } catch( Exception e ) { |
---|
638 | // Exception is thrown when the call to the module fails. No problems though. |
---|
639 | log.error "Error while fetching possible actions from " + module.name + ": " + e.getMessage() |
---|
640 | } |
---|
641 | } |
---|
642 | |
---|
643 | return actions; |
---|
644 | } |
---|
645 | |
---|
646 | } |
---|