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