Changeset 1501
- Timestamp:
- Feb 7, 2011, 4:07:54 PM (12 years ago)
- Location:
- trunk
- Files:
-
- 4 added
- 22 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/grails-app/conf/config-ci.properties
r1459 r1501 36 36 modules.metagenomics.url=http://ci.metagenomics.nmcdsp.org 37 37 38 # Number of seconds to keep rest results from modules in cache 39 modules.cacheDuration = 600 40 38 41 # default application users 39 42 authentication.users.admin.username=admin -
trunk/grails-app/conf/config-dbnpdemo.properties
r1459 r1501 36 36 modules.metagenomics.url=http://demo.metagenomics.dbnp.org 37 37 38 # Number of seconds to keep rest results from modules in cache 39 modules.cacheDuration = 600 40 38 41 # default application users 39 42 authentication.users.admin.username=admin -
trunk/grails-app/conf/config-dbnptest.properties
r1459 r1501 36 36 modules.metagenomics.url=http://test.metagenomics.dbnp.org 37 37 38 # Number of seconds to keep rest results from modules in cache 39 modules.cacheDuration = 600 40 38 41 # default application users 39 42 authentication.users.admin.username=admin -
trunk/grails-app/conf/config-development.properties
r1459 r1501 35 35 modules.metagenomics.url=http://localhost:8184/metagenomics 36 36 37 # Number of seconds to keep rest results from modules in cache 38 modules.cacheDuration = 0 39 37 40 # default application users 38 41 authentication.users.admin.username=admin -
trunk/grails-app/conf/config-nmcdsptest.properties
r1459 r1501 36 36 modules.metagenomics.url=http://test.metagenomics.nmcdsp.org 37 37 38 # Number of seconds to keep rest results from modules in cache 39 modules.cacheDuration = 600 40 38 41 # default application users 39 42 authentication.users.admin.username=admin -
trunk/grails-app/conf/config-production.properties
r1459 r1501 36 36 modules.metagenomics.url=http://metagenomics.nmcdsp.org 37 37 38 # Number of seconds to keep rest results from modules in cache 39 modules.cacheDuration = 600 40 38 41 # default application users 39 42 authentication.users.admin.username=admin -
trunk/grails-app/conf/config-test.properties
r1459 r1501 35 35 modules.metagenomics.url=http://localhost:8184/metagenomics 36 36 37 # Number of seconds to keep rest results from modules in cache 38 modules.cacheDuration = 600 39 37 40 # default application users 38 41 authentication.users.admin.username=admin -
trunk/grails-app/conf/config-www.properties
r1459 r1501 36 36 modules.metagenomics.url=http://metagenomics.nmcdsp.org 37 37 38 # Number of seconds to keep rest results from modules in cache 39 modules.cacheDuration = 600 40 38 41 # default application users 39 42 authentication.users.admin.username=admin -
trunk/grails-app/controllers/dbnp/query/AdvancedQueryController.groovy
r1482 r1501 1 1 package dbnp.query 2 2 3 import dbnp.modules.* 3 4 import org.dbnp.gdt.* … … 15 16 16 17 def entitiesToSearchFor = [ 'Study': 'Studies', 'Sample': 'Samples'] 17 18 18 19 /** 19 20 * Shows search screen … … 25 26 criteria = parseCriteria( params.criteria, false ) 26 27 } 27 [ entitiesToSearchFor: entitiesToSearchFor, searchableFields: getSearchableFields(), criteria: criteria]28 [searchModes: SearchMode.values(), entitiesToSearchFor: entitiesToSearchFor, searchableFields: getSearchableFields(), criteria: criteria] 28 29 } 29 30 … … 47 48 48 49 // Create a search object and let it do the searching 49 Search search ;50 Search search = determineSearch( params.entity ); 50 51 String view = determineView( params.entity ); 51 switch( params.entity ) { 52 case "Study": search = new StudySearch(); break; 53 case "Sample": search = new SampleSearch(); break; 54 55 // This exception will only be thrown if the entitiesToSearchFor contains more entities than 56 // mentioned in this switch structure. 57 default: throw new Exception( "Can't search for entities of type " + params.entity ); 58 } 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 59 63 search.execute( parseCriteria( params.criteria ) ); 60 64 61 65 // Save search in session 62 66 def queryId = saveSearch( search ); 63 render( view: view, model: [search: search, queryId: queryId ] );67 render( view: view, model: [search: search, queryId: queryId, actions: determineActions(search)] ); 64 68 } 65 69 … … 69 73 */ 70 74 def discard = { 71 Integer queryId72 try {73 queryId = params.id as Integer 74 } catch( Exception e) {75 def queryIds = params.list( 'id' ); 76 queryIds = queryIds.findAll { it.isInteger() }.collect { Integer.valueOf( it ) } 77 78 if( queryIds.size() == 0 ) { 75 79 flash.error = "Incorrect search ID given to discard" 76 80 redirect( action: "index" ); … … 78 82 } 79 83 80 discardSearch( queryId ); 81 flash.message = "Search has been discarded" 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 } 82 93 redirect( action: "list" ); 83 94 } … … 88 99 */ 89 100 def show = { 90 Integer queryId 91 try { 92 queryId = params.id as Integer 93 } catch( Exception e ) { 101 def queryId = params.int( 'id' ); 102 103 if( !queryId ) { 94 104 flash.error = "Incorrect search ID given to show" 95 105 redirect( action: "index" ); … … 107 117 // Determine which view to show 108 118 def view = determineView( s.entity ); 109 render( view: view, model: [search: s, queryId: queryId] ); 110 } 111 119 render( view: view, model: [search: s, queryId: queryId, actions: determineActions(s)] ); 120 } 121 122 /** 123 * Performs an action on specific searchResults 124 * @param queryId queryId of the search to show 125 * @param id list with the ids of the results to perform the action on 126 * @param actionName Name of the action to perform 127 */ 128 def performAction = { 129 def queryId = params.int( 'queryId' ); 130 def selectedIds = params.list( 'id' ).findAll { it.isLong() }.collect { Long.parseLong(it) } 131 def actionName = params.actionName; 132 def moduleName = params.moduleName; 133 134 if( !queryId ) { 135 flash.error = "Incorrect search ID given to show" 136 redirect( action: "index" ); 137 return 138 } 139 140 // Retrieve the search from session 141 Search s = retrieveSearch( queryId ); 142 if( !s ) { 143 flash.message = "Specified search could not be found" 144 redirect( action: "list" ); 145 return; 146 } 147 148 // Determine the possible actions 149 def actions = determineActions(s, selectedIds ); 150 151 // Find the right action to perform 152 def redirectUrl; 153 for( action in actions ) { 154 if( action.module == moduleName && action.name == actionName ) { 155 redirectUrl = action.url; 156 break; 157 } 158 } 159 160 if( !redirectUrl ) { 161 flash.error = "No valid action is given to perform"; 162 redirect( action: "show", id: queryId ); 163 return; 164 } 165 166 redirect( url: redirectUrl ); 167 } 168 112 169 /** 113 170 * Shows a list of searches that have been saved in session … … 124 181 [searches: searches] 125 182 } 126 183 127 184 /** 128 185 * Shows a search screen where the user can search within the results of another search … … 130 187 */ 131 188 def searchIn = { 132 Integer queryId133 try {134 queryId = params.id as Integer 135 } catch( Exception e) {189 def queryIds = params.list( 'id' ); 190 queryIds = queryIds.findAll { it.isInteger() }.collect { Integer.valueOf( it ) } 191 192 if( queryIds.size() == 0 ) { 136 193 flash.error = "Incorrect search ID given to show" 137 194 redirect( action: "index" ); … … 139 196 } 140 197 141 // Retrieve the search from session 142 Search s = retrieveSearch( queryId ); 143 if( !s ) { 144 flash.message = "Specified search could not be found" 198 // Retrieve the searches from session 199 def params = [:] 200 queryIds.eachWithIndex { queryId, idx -> 201 Search s = retrieveSearch( queryId ); 202 if( !s ) { 203 flash.message = "Specified search " + queryId + " could not be found" 204 return; 205 } else { 206 params[ "criteria." + idx + ".entityfield" ] = s.entity; 207 params[ "criteria." + idx + ".operator" ] = "in"; 208 params[ "criteria." + idx + ".value" ] = queryId; 209 } 210 } 211 212 redirect( action: "index", params: params) 213 } 214 215 /** 216 * Combines the results of multiple searches 217 * @param id queryIds of the searches to combine 218 */ 219 def combine = { 220 def queryIds = params.list( 'id' ); 221 queryIds = queryIds.findAll { it.isInteger() }.collect { Integer.valueOf( it ) } 222 223 if( queryIds.size() == 0 ) { 224 flash.error = "Incorrect search ID given to combine" 145 225 redirect( action: "index" ); 226 return 227 } 228 229 // First determine whether the types match 230 def searches = []; 231 def type = ""; 232 flash.error = ""; 233 queryIds.eachWithIndex { queryId, idx -> 234 Search s = retrieveSearch( queryId ); 235 if( !s ) { 236 return; 237 } 238 239 if( type ) { 240 if( type != s.entity ) { 241 flash.error = type + " and " + s.entity.toLowerCase() + " queries can't be combined. Selected queries of one type."; 242 return 243 } 244 } else { 245 type = s.entity 246 } 247 } 248 249 if( flash.error ) { 250 redirect( action: "list" ); 146 251 return; 147 252 } 148 253 149 redirect( action: "index", params: [ "criteria.0.entityfield": s.entity, "criteria.0.operator": "in", "criteria.0.value": queryId ]) 254 if( !type ) { 255 flash.error = "No correct query ids were given." 256 redirect( action: "list" ); 257 return; 258 } 259 260 // Retrieve the searches from session 261 Search combined = determineSearch( type ); 262 combined.searchMode = SearchMode.or; 263 264 queryIds.eachWithIndex { queryId, idx -> 265 Search s = retrieveSearch( queryId ); 266 if( s ) { 267 combined.addCriterion( new Criterion( entity: type, field: null, operator: Operator.insearch, value: s ) ); 268 } 269 } 270 271 // Execute search to combine the results 272 combined.execute(); 273 274 def queryId = saveSearch( combined ); 275 redirect( action: "show", id: queryId ); 150 276 } 151 277 … … 155 281 case "Sample": return "sampleresults"; break; 156 282 default: return "results"; break; 283 } 284 } 285 286 /** 287 * Returns the search object used for searching 288 */ 289 protected Search determineSearch( String entity ) { 290 switch( entity ) { 291 case "Study": return new StudySearch(); 292 case "Sample": return new SampleSearch(); 293 294 // This exception will only be thrown if the entitiesToSearchFor contains more entities than 295 // mentioned in this switch structure. 296 default: throw new Exception( "Can't search for entities of type " + entity ); 157 297 } 158 298 } … … 230 370 if( c.key ==~ /[0-9]+/ ) { 231 371 def formCriterion = c.value; 232 372 233 373 Criterion criterion = new Criterion(); 234 374 … … 251 391 continue; 252 392 } 253 393 254 394 // Special case of the 'in' operator 255 395 if( criterion.operator == Operator.insearch ) { … … 258 398 s = retrieveSearch( Integer.parseInt( formCriterion.value ) ); 259 399 } catch( Exception e ) {} 260 400 261 401 if( !s ) { 262 402 flash.error += "Can't search within previous query: query not found"; 263 403 continue; 264 404 } 265 405 266 406 if( parseSearchIds ) { 267 407 criterion.value = s … … 273 413 criterion.value = formCriterion.value; 274 414 } 275 415 276 416 list << criterion; 277 417 } … … 332 472 // First check whether a search with the same criteria is already present 333 473 def previousSearch = retrieveSearchByCriteria( s.getCriteria() ); 334 474 335 475 def id 336 476 if( previousSearch ) { … … 340 480 id = ( session.queries*.key.max() ?: 0 ) + 1; 341 481 } 342 482 343 483 s.id = id; 344 484 session.queries[ id ] = s; 345 485 346 println "On saveSearch: " + session.queries;347 486 return id; 348 487 } … … 356 495 if( !session.queries ) 357 496 return null 358 497 359 498 if( !criteria ) 360 499 return null 361 500 362 501 for( query in session.queries ) { 363 502 def key = query.key; … … 385 524 return null; 386 525 387 println "On retrieveSearch: " + session.queries;388 526 return (Search) session.queries[ id ] 389 527 } … … 402 540 session.queries.remove( id ); 403 541 404 println "On discardSearch: " + session.queries;405 542 if( !( sessionSearch instanceof Search ) ) 406 543 return null; … … 419 556 return session.queries*.value.toList() 420 557 } 558 559 /** 560 * Determine a list of actions that can be performed on specific entities 561 * @param entity Name of the entity that the actions could be performed on 562 * @param selectedIds List with ids of the selected items to perform an action on 563 * @return 564 */ 565 protected List determineActions( Search s, def selectedIds = null ) { 566 return gscfActions( s, selectedIds ) + moduleActions( s, selectedIds ); 567 } 568 569 /** 570 * Determine a list of actions that can be performed on specific entities by GSCF 571 * @param entity Name of the entity that the actions could be performed on 572 * @param selectedIds List with ids of the selected items to perform an action on 573 */ 574 protected List gscfActions(Search s, def selectedIds = null) { 575 switch(s.entity) { 576 case "Study": 577 def exportParams = [:] 578 s.filterResults(selectedIds).each { 579 exportParams[ it.code ] = it.id; 580 } 581 return [[ 582 module: "gscf", 583 name:"simpletox", 584 description: "Export as SimpleTox", 585 url: createLink( controller: "exporter", action: "export", params: exportParams ) 586 ]] 587 case "Sample": 588 return [] 589 default: 590 return []; 591 } 592 } 593 594 /** 595 * Determine a list of actions that can be performed on specific entities by other modules 596 * @param entity Name of the entity that the actions could be performed on 597 */ 598 protected List moduleActions(Search s, def selectedIds = null) { 599 def actions = [] 600 601 if( !s.getResults() || s.getResults().size() == 0 ) 602 return [] 603 604 // Loop through all modules and check which actions can be performed on the 605 AssayModule.list().each { module -> 606 // Remove 'module' from module name 607 def moduleName = module.name.replace( 'module', '' ).trim() 608 try { 609 def callUrl = module.url + "/rest/getPossibleActions?entity=" + s.entity 610 def json = moduleCommunicationService.callModuleRestMethodJSON( module.url, callUrl ); 611 612 // Check whether the entity is present in the return value 613 if( json[ s.entity ] ) { 614 json[ s.entity ].each { action -> 615 def url = module.url + "/action/" + action.name 616 url += "?entity=" + s.entity 617 url += "&" + s.filterResults(selectedIds).collect { "tokens=" + it.giveUUID() }.join( "&" ) 618 actions << [ 619 module: moduleName, 620 name: action.name, 621 description: action.description + " (" + moduleName + ")", 622 url: url 623 ]; 624 } 625 } 626 } catch( Exception e ) { 627 // Exception is thrown when the call to the module fails. No problems though. 628 log.error "Error while fetching possible actions from " + module.name + ": " + e.getMessage() 629 } 630 } 631 632 return actions; 633 } 634 421 635 } -
trunk/grails-app/services/dbnp/modules/ModuleCommunicationService.groovy
r1482 r1501 18 18 import grails.converters.* 19 19 import javax.servlet.http.HttpServletResponse 20 import org.codehaus.groovy.grails.commons.ConfigurationHolder 20 21 21 22 class ModuleCommunicationService implements Serializable { … … 33 34 * Number of seconds to save the data in cache 34 35 */ 35 def numberOfSecondsInCache = 10 * 60;36 def numberOfSecondsInCache = Integer.valueOf( ConfigurationHolder.config.modules.cacheDuration ) 36 37 37 38 /** … … 147 148 def user = authenticationService.getLoggedInUser(); 148 149 def userId = user ? user.id : -1; 149 150 151 println "Retrieve from cache: " + url 152 println "Seconds in cache: " + numberOfSecondsInCache 153 150 154 if( cache[ userId ] && cache[ userId ][ url ] && ( System.currentTimeMillis() - cache[ userId ][ url ][ "timestamp" ] ) < numberOfSecondsInCache * 1000 ) { 151 155 return cache[ userId ][ url ]; -
trunk/grails-app/views/advancedQuery/_criteria.gsp
r1482 r1501 1 <%@ page import="dbnp.query.*" %> 1 2 <ul id="criteria"> 2 <g:each in="${criteria}" var="criterion" >3 <g:each in="${criteria}" var="criterion" status="j"> 3 4 <li> 4 5 <span class="entityfield">${criterion.entityField()}</span> 5 6 <span class="operator">${criterion.operator}</span> 6 <span class="value">${criterion.value}</span> 7 <span class="value"> 8 <g:if test="${criterion.value != null && criterion.value instanceof Search}"> 9 <g:link action="show" id="${criterion.value.id}">${criterion.value}</g:link> 10 </g:if> 11 <g:else> 12 ${criterion.value} 13 </g:else> 14 </span> 15 <g:if test="${j < criteria.size() -1}"> 16 <g:if test="${search.searchMode == SearchMode.and}">and</g:if> 17 <g:if test="${search.searchMode == SearchMode.or}">or</g:if> 18 </g:if> 7 19 </li> 8 20 </g:each> -
trunk/grails-app/views/advancedQuery/_resultbuttons.gsp
r1482 r1501 1 <p >2 <g:link action="searchIn" id="${queryId}">Search within results</g:link><br />3 <g:link action="index">Search again</g:link><br />4 <g:link action="discard" id="${queryId}">Discard results</g:link><br />5 <g:link action="list">Previous searches</g:link>1 <p class="options"> 2 <g:link class="searchIn" action="searchIn" id="${queryId}">Search within results</g:link><br /> 3 <g:link class="search" action="index">Search again</g:link><br /> 4 <g:link class="discard" action="discard" id="${queryId}">Discard results</g:link><br /> 5 <g:link class="listPrevious" action="list">Previous searches</g:link> 6 6 </p> 7 <p class="options"> 8 <g:each in="${actions}" var="action"> 9 <a class="performAction ${action.name}" href="${action.url}" onClick="performAction( $('form#results'), '${action.name}', '${action.module}' ); return false;">${action.description}</a><br /> 10 </g:each> 11 </p> 12 <br clear="all"> -
trunk/grails-app/views/advancedQuery/index.gsp
r1487 r1501 14 14 <g:if test="${j > 0}">,</g:if> 15 15 { 16 label: "${entity.key.toString().encodeAsJavaScript()}.${field.toString().encodeAsJavaScript()} ${entity.key.toString().encodeAsJavaScript()} ${field.toString().encodeAsJavaScript()}", 17 show: "${(field[0].toUpperCase() + field[1..-1]).encodeAsJavaScript()}", 16 18 value: "${entity.key.toString().encodeAsJavaScript()}.${field.toString().encodeAsJavaScript()}", 17 show: "${(field[0].toUpperCase() + field[1..-1]).encodeAsJavaScript()}",18 label: "${entity.key.toString().encodeAsJavaScript()}.${field.toString().encodeAsJavaScript()}",19 19 entity: "${entity.key.toString().encodeAsJavaScript()}" 20 20 } … … 49 49 </div> 50 50 </g:if> 51 52 <a href="<g:createLink action="list" />">View previous queries</a>53 51 54 52 <form id="input_criteria"> … … 91 89 <g:form action="search" method="get"> 92 90 <label for="entity">Search for</label><g:select from="${entitiesToSearchFor}" optionKey="key" optionValue="value" name="entity" /><br /> 91 <label for="entity">Searchtype</label><g:select from="${searchModes}" name="operator" /><br /> 93 92 <label for="criteria">Criteria</label> 94 93 <ul id="criteria"> … … 99 98 </g:form> 100 99 </div> 101 100 <p class="options"> 101 <g:link class="listPrevious" action="list">Previous searches</g:link> 102 </p> 102 103 <br clear="all" /> 103 104 </body> -
trunk/grails-app/views/advancedQuery/list.gsp
r1482 r1501 6 6 <title>Previous queries</title> 7 7 <link rel="stylesheet" href="<g:resource dir="css" file="advancedQuery.css" />" type="text/css"/> 8 <g:javascript src="advancedQueryResults.js" /> 9 <script type="text/javascript"> 10 function searchWithinResults( form ) { 11 submitForm( form, '/advancedQuery/searchIn' ); 12 } 13 function discardResults( form ) { 14 submitForm( form, '/advancedQuery/discard' ); 15 } 16 function combineResults( form ) { 17 submitForm( form, '/advancedQuery/combine' ); 18 } 19 </script> 20 8 21 </head> 9 22 <body> … … 11 24 <h1>Previous queries</h1> 12 25 26 <g:if test="${flash.error}"> 27 <div class="errormessage"> 28 ${flash.error.toString().encodeAsHTML()} 29 </div> 30 </g:if> 31 <g:if test="${flash.message}"> 32 <div class="message"> 33 ${flash.message.toString().encodeAsHTML()} 34 </div> 35 </g:if> 36 13 37 <g:if test="${searches.size() > 0}"> 14 <table id="searchresults"> 38 <form id="searchform" method="post"> 39 <table id="searchresults" class="paginate"> 15 40 <thead> 16 41 <tr> 17 <th ></th>42 <th class="nonsortable"></th> 18 43 <th>#</th> 19 44 <th>Type</th> … … 21 46 <th># results</th> 22 47 <th>time</th> 23 <th ></th>24 <th ></th>48 <th class="nonsortable"></th> 49 <th class="nonsortable"></th> 25 50 </tr> 26 51 </thead> 27 52 <g:each in="${searches}" var="search"> 28 53 <tr> 29 <td><g:checkBox name=" queryId" value="${search.id}" checked="${false}" /></td>54 <td><g:checkBox name="id" value="${search.id}" checked="${false}" /></td> 30 55 <td>${search.id}</td> 31 56 <td>${search.entity}</td> … … 52 77 </g:each> 53 78 </table> 79 </form> 54 80 </g:if> 55 <p> 56 <g:link action="index">Search again</g:link> 81 82 <p class="options"> 83 <a href="#" class="combine" onClick="combineResults( $( '#searchform' ) ); return false;">Combine results</a><br /> 84 <a href="#" class="searchIn" onClick="searchWithinResults( $( '#searchform' ) ); return false;">Search within results</a><br /> 85 <g:link class="search" action="index">Search again</g:link><br /> 86 <a href="#" class="discard" onClick="discardResults( $( '#searchform' ) ); return false;">Discard results</a><br /> 57 87 </p> 88 58 89 </body> 59 90 </html> -
trunk/grails-app/views/advancedQuery/results.gsp
r1482 r1501 5 5 <title>Query results</title> 6 6 <link rel="stylesheet" href="<g:resource dir="css" file="advancedQuery.css" />" type="text/css"/> 7 <g:javascript src="advancedQueryResults.js" /> 7 8 </head> 8 9 <body> … … 13 14 Your search for: 14 15 </p> 15 <ul id="criteria"> 16 <g:each in="${search.getCriteria()}" var="criterion"> 17 <li> 18 <span class="entityfield">${criterion.entityField()}</span> 19 <span class="operator">${criterion.operator}</span> 20 <span class="value">${criterion.value}</span> 21 </li> 22 </g:each> 23 </ul> 16 <g:render template="criteria" model="[criteria: search.getCriteria()]" /> 24 17 <p> 25 18 resulted in ${search.getNumResults()} results. … … 31 24 def extraFields = resultFields[ search.getResults()[ 0 ].id ]?.keySet(); 32 25 %> 33 <table id="searchresults" >26 <table id="searchresults" class="paginate"> 34 27 <thead> 35 28 <tr> 29 <th class="nonsortable"></th> 36 30 <th>Type</th> 37 31 <th>Id</th> … … 43 37 <g:each in="${search.getResults()}" var="result"> 44 38 <tr> 39 <td width="3%"> 40 <% /* 41 The value of this checkbox will be moved to the form (under this table) with javascript. This 42 way the user can select items from multiple pages of the paginated result list correctly. See 43 also http://datatables.net/examples/api/form.html and advancedQueryResults.js 44 */ %> 45 <g:checkBox name="id" value="${result.id}" checked="${false}" /> 46 </td> 45 47 <td>${search.entity}</td> 46 48 <td>${result.id}</td> … … 50 52 def fieldValue = resultFields[ result.id ]?.get( fieldName ); 51 53 if( fieldValue ) { 52 if( fieldValue instanceof Collection ) 54 if( fieldValue instanceof Collection ) { 53 55 fieldValue = fieldValue.collect { it.toString() }.findAll { it }.join( ', ' ); 54 else56 } else { 55 57 fieldValue = fieldValue.toString(); 58 } 59 } else { 60 fieldValue = ""; 56 61 } 57 62 %> -
trunk/grails-app/views/advancedQuery/sampleresults.gsp
r1482 r1501 5 5 <title>Query results</title> 6 6 <link rel="stylesheet" href="<g:resource dir="css" file="advancedQuery.css" />" type="text/css"/> 7 <g:javascript src="advancedQueryResults.js" /> 7 8 </head> 8 9 <body> … … 26 27 <thead> 27 28 <tr> 29 <th class="nonsortable"></th> 30 <th>Name</th> 28 31 <th>Study</th> 29 <th>Name</th>30 32 <g:each in="${extraFields}" var="fieldName"> 31 33 <th>${fieldName}</th> … … 36 38 <g:each in="${search.getResults()}" var="sampleInstance" status="i"> 37 39 <tr class="${(i % 2) == 0 ? 'odd' : 'even'}"> 40 <td width="3%"> 41 <% /* 42 The value of this checkbox will be moved to the form (under this table) with javascript. This 43 way the user can select items from multiple pages of the paginated result list correctly. See 44 also http://datatables.net/examples/api/form.html and advancedQueryResults.js 45 */ %> 46 <g:checkBox name="id" value="${sampleInstance.id}" checked="${false}" /> 47 </td> 48 <td>${fieldValue(bean: sampleInstance, field: "name")}</td> 38 49 <td><g:link controller="study" action="show" id="${sampleInstance?.parent?.id}">${sampleInstance?.parent?.title}</g:link></td> 39 <td>${fieldValue(bean: sampleInstance, field: "name")}</td>40 50 <g:each in="${extraFields}" var="fieldName"> 41 51 <td> … … 43 53 def fieldValue = resultFields[ sampleInstance.id ]?.get( fieldName ); 44 54 if( fieldValue ) { 45 if( fieldValue instanceof Collection ) 55 if( fieldValue instanceof Collection ) { 46 56 fieldValue = fieldValue.collect { it.toString() }.findAll { it }.join( ', ' ); 47 else57 } else { 48 58 fieldValue = fieldValue.toString(); 59 } 60 } else { 61 fieldValue = ""; 49 62 } 50 63 %> … … 56 69 </tbody> 57 70 </table> 71 <g:render template="resultsform" /> 58 72 59 73 </g:if> -
trunk/grails-app/views/advancedQuery/studyresults.gsp
r1482 r1501 5 5 <title>Query results</title> 6 6 <link rel="stylesheet" href="<g:resource dir="css" file="advancedQuery.css" />" type="text/css"/> 7 <g:javascript src="advancedQueryResults.js" /> 7 8 </head> 8 9 <body> … … 22 23 def extraFields = resultFields[ search.getResults()[ 0 ].id ]?.keySet(); 23 24 %> 24 25 25 <table id="searchresults" class="paginate"> 26 26 <thead> 27 27 <tr> 28 <th colspan="2"></th> 28 <th class="nonsortable"></th> 29 <th>Title</th> 29 30 <th>Code</th> 30 <th>Title</th>31 31 <th>Subjects</th> 32 32 <th>Events</th> … … 40 40 <g:each in="${search.getResults()}" var="studyInstance" status="i"> 41 41 <tr class="${(i % 2) == 0 ? 'odd' : 'even'}"> 42 43 <td><g:link controller="study" action="show" id="${studyInstance?.id}"><img src='${fam.icon(name: 'application_form_magnify')}' border="0" alt="view study" /></g:link></td> 44 <td><g:if test="${studyInstance.canWrite(loggedInUser)}"><g:link class="edit" controller="studyWizard" params="[jump:'edit']" id="${studyInstance?.id}"><img src='${fam.icon(name: 'application_form_edit')}' border="0" alt="edit study" /></g:link></g:if><g:else><img src='${fam.icon(name: 'lock')}' border="0" alt="you have no write access to shis study" /></g:else> </td> 42 <td width="3%"> 43 <% /* 44 The value of this checkbox will be moved to the form (under this table) with javascript. This 45 way the user can select items from multiple pages of the paginated result list correctly. See 46 also http://datatables.net/examples/api/form.html and advancedQueryResults.js 47 */ %> 48 <g:checkBox name="id" value="${studyInstance.id}" checked="${false}" /> 49 </td> 50 <td> 51 <g:link controller="study" action="show" id="${studyInstance?.id}">${fieldValue(bean: studyInstance, field: "title")}</g:link> 52 53 </td> 45 54 <td>${fieldValue(bean: studyInstance, field: "code")}</td> 46 <td>47 ${fieldValue(bean: studyInstance, field: "title")}48 </td>49 55 <td> 50 56 <g:if test="${studyInstance.subjects.species.size()==0}"> … … 82 88 def fieldValue = resultFields[ studyInstance.id ]?.get( fieldName ); 83 89 if( fieldValue ) { 84 if( fieldValue instanceof Collection ) 90 if( fieldValue instanceof Collection ) { 85 91 fieldValue = fieldValue.collect { it.toString() }.findAll { it }.join( ', ' ); 86 else92 } else { 87 93 fieldValue = fieldValue.toString(); 94 } 95 } else { 96 fieldValue = ""; 88 97 } 89 98 %> … … 95 104 </tbody> 96 105 </table> 106 <g:render template="resultsform" /> 97 107 98 108 </g:if> -
trunk/grails-app/views/layouts/main.gsp
r1434 r1501 21 21 <script type="text/javascript" src="${resource(dir: 'js', file: 'topnav.js')}"></script> 22 22 <g:if env="development"><script type="text/javascript" src="${resource(dir: 'js', file: 'development.js')}"></script></g:if> 23 24 <!-- Scripts for pagination using dataTables --> 25 <link rel="stylesheet" href="${resource(dir: 'css/datatables', file: 'demo_table_jui.css')}"/> 26 <script type="text/javascript" src="${resource(dir: 'js', file: 'jquery.dataTables.min.js')}"></script> 27 <script type="text/javascript" src="${resource(dir: 'js', file: 'paginate.js')}"></script> 28 23 29 </head> 24 30 <body> -
trunk/src/groovy/dbnp/query/SampleSearch.groovy
r1482 r1501 62 62 */ 63 63 @Override 64 void execute() { 65 super.execute(); 66 64 void executeAnd() { 67 65 // If no criteria are found, return all samples 68 66 if( !criteria || criteria.size() == 0 ) { … … 80 78 def studies = Study.findAll().findAll { it.canRead( this.user ) }; 81 79 82 studies = filter OnStudyCriteria( studies );80 studies = filterStudiesOnStudyCriteria( studies ); 83 81 84 82 if( studies.size() == 0 ) { … … 116 114 117 115 /** 118 * Filters the given list of samples on the sample criteria 119 * @param samples Original list of samples 120 * @return List with all samples that match the Sample-criteria 121 */ 122 protected List filterOnStudyCriteria( List studies ) { 116 * Searches for samples based on the given criteria. Only one of the criteria have to be satisfied and 117 * criteria for the different entities are satisfied as follows: 118 * 119 * Sample.title = 'abc' 120 * Samples are returned from studies with title 'abc' 121 * 122 * Subject.species = 'human' 123 * Samples are returned from subjects with species = 'human' 124 * 125 * Sample.name = 'sample 1' 126 * Samples are returned with name = 'sample 1' 127 * 128 * Event.startTime = '0s' 129 * Samples are returned from subjects that have had an event with start time = '0s' 130 * 131 * SamplingEvent.startTime = '0s' 132 * Samples are returned that have originated from a sampling event with start time = '0s' 133 * 134 * Assay.module = 'metagenomics' 135 * Samples are returned that have been processed in an assay with module = metagenomics 136 * 137 * When searching for more than one criterion per entity, these are taken separately. Searching for 138 * 139 * Subject.species = 'human' 140 * Subject.name = 'Jan' 141 * 142 * will result in all samples from a human subject or a subject named 'Jan'. Samples from a mouse subject 143 * named 'Jan' or a human subject named 'Kees' will also satisfy the criteria. 144 * 145 */ 146 @Override 147 void executeOr() { 148 // We expect the sample criteria to be the most discriminative, and discard 149 // the most samples. (e.g. by searching on sample title of sample type). For 150 // that reason we first look through the list of studies. However, when the 151 // user didn't enter any sample criteria, this will be an extra step, but doesn't 152 // cost much time to process. 153 def samples = [] 154 def allSamples = Sample.list().findAll { it.parent?.canRead( this.user ) }.toList(); 155 156 // If no criteria are found, return all samples 157 if( !criteria || criteria.size() == 0 ) { 158 results = allSamples 159 return; 160 } 161 162 samples = ( samples + filterSamplesOnStudyCriteria( allSamples - samples ) ).unique(); 163 samples = ( samples + filterOnSubjectCriteria( allSamples - samples ) ).unique(); 164 samples = ( samples + filterOnSampleCriteria( allSamples - samples ) ).unique(); 165 samples = ( samples + filterOnEventCriteria( allSamples - samples ) ).unique(); 166 samples = ( samples + filterOnSamplingEventCriteria( allSamples - samples ) ).unique(); 167 samples = ( samples + filterOnAssayCriteria( allSamples - samples ) ).unique(); 168 169 samples = ( samples + filterOnModuleCriteria( allSamples - samples ) ).unique(); 170 171 // Save matches 172 results = samples; 173 } 174 175 /** 176 * Filters the given list of studies on the study criteria 177 * @param studies Original list of studies 178 * @return List with all samples that match the Study-criteria 179 */ 180 protected List filterStudiesOnStudyCriteria( List studies ) { 123 181 return filterOnTemplateEntityCriteria(studies, "Study", { study, criterion -> return criterion.getFieldValue( study ) }) 124 182 } 183 184 /** 185 * Filters the given list of samples on the sample criteria 186 * @param samples Original list of samples 187 * @return List with all samples that match the Study-criteria 188 */ 189 protected List filterSamplesOnStudyCriteria( List samples ) { 190 return filterOnTemplateEntityCriteria(samples, "Study", { study, criterion -> 191 return criterion.getFieldValue( study.parent ) 192 }) 193 } 194 125 195 126 196 /** … … 180 250 return []; 181 251 182 if( getEntityCriteria( 'Assay' ).size() == 0 )183 return samples184 185 252 // There is no sample.assays property, so we have to look for assays another way: just find 186 253 // all assays that match the criteria 187 254 def criteria = getEntityCriteria( 'Assay' ); 255 256 if( getEntityCriteria( 'Assay' ).size() == 0 ) { 257 if( searchMode == SearchMode.and ) 258 return samples 259 else if( searchMode == SearchMode.or ) 260 return []; 261 } 262 188 263 def assays = filterEntityList( Assay.list(), criteria, { assay, criterion -> 189 264 if( !assay ) 190 265 return false 191 266 192 return criterion.match One( assay );267 return criterion.matchEntity( assay ); 193 268 }); 194 269 … … 197 272 return []; 198 273 199 // Save sample data for later use200 saveResultFields( samples, criteria, { sample, criterion ->201 def sampleAssays = Assay.findByStudy( sample.parent ).findAll { it.samples?.contains( sample ) };202 if( sampleAssays && sampleAssays.size() > 0 )203 return sampleAssays.collect( criterion.getFieldValue( it ) )204 else205 return null206 });207 208 274 // Now filter the samples on whether they are attached to the filtered assays 209 returnsamples.findAll { sample ->275 def matchingSamples = samples.findAll { sample -> 210 276 if( !sample.parent ) 211 277 return false; … … 222 288 return false; 223 289 } 290 291 // Save sample data for later use 292 println samples 293 println "Find values for " + matchingSamples + " and " + criteria 294 saveResultFields( matchingSamples, criteria, { sample, criterion -> 295 println "Find value for " + sample + " and " + criterion 296 def sampleAssays = Assay.findByParent( sample.parent ).findAll { it.samples?.contains( sample ) }; 297 if( sampleAssays && sampleAssays.size() > 0 ) 298 return sampleAssays.collect { criterion.getFieldValue( it ) } 299 else 300 return null 301 }); 302 303 return matchingSamples; 224 304 } 225 305 -
trunk/src/groovy/dbnp/query/Search.groovy
r1487 r1501 30 30 import org.dbnp.gdt.* 31 31 32 /** 33 * Available boolean operators for searches 34 * @author robert 35 * 36 */ 37 enum SearchMode { 38 and, or 39 } 40 32 41 class Search { 33 public String entity;34 42 public SecUser user; 35 43 public Date executionDate; 36 44 public int id; // Is only used when this query is saved in session 37 45 46 public String entity; 47 public SearchMode searchMode = SearchMode.and 48 38 49 protected List criteria; 39 50 protected List results; … … 42 53 public List getCriteria() { return criteria; } 43 54 public void setCriteria( List c ) { criteria = c; } 55 public void addCriterion( Criterion c ) { 56 if( criteria ) 57 criteria << c; 58 else 59 criteria = [c]; 60 } 44 61 45 62 public List getResults() { return results; } 46 63 public void setResults( List r ) { results = r; } 64 public List filterResults( List selectedIds ) { 65 if( !selectedIds || !results ) 66 return results 67 68 return results.findAll { 69 selectedIds.contains( it.id ) 70 } 71 } 72 47 73 48 74 public Map getResultFields() { return resultFields; } … … 83 109 84 110 /** 85 * Executes a search based on the given criteria. Should be filled in by 86 * subclasses searching for a specific entity 111 * Executes a search based on the given criteria. 87 112 */ 88 113 public void execute() { 89 114 this.executionDate = new Date(); 90 } 115 116 switch( searchMode ) { 117 case SearchMode.and: 118 executeAnd(); 119 break; 120 case SearchMode.or: 121 executeOr(); 122 break; 123 } 124 } 125 126 /** 127 * Executes an inclusive (AND) search based on the given criteria. Should be filled in by 128 * subclasses searching for a specific entity 129 */ 130 public void executeAnd() { 131 132 } 133 134 /** 135 * Executes an exclusive (OR) search based on the given criteria. Should be filled in by 136 * subclasses searching for a specific entity 137 */ 138 public void executeOr() { 139 140 } 91 141 92 142 /** … … 112 162 protected List filterEntityList( List entities, List<Criterion> criteria, Closure check ) { 113 163 if( !entities || !criteria || criteria.size() == 0 ) { 114 return entities; 115 } 116 164 if( searchMode == SearchMode.and ) 165 return entities; 166 else if( searchMode == SearchMode.or ) 167 return [] 168 } 169 117 170 return entities.findAll { entity -> 118 for( criterion in criteria ) { 119 if( !check( entity, criterion ) ) { 120 return false; 121 } 171 if( searchMode == SearchMode.and ) { 172 for( criterion in criteria ) { 173 if( !check( entity, criterion ) ) { 174 return false; 175 } 176 } 177 return true; 178 } else if( searchMode == SearchMode.or ) { 179 for( criterion in criteria ) { 180 if( check( entity, criterion ) ) { 181 return true; 182 } 183 } 184 return false; 122 185 } 123 return true;124 186 } 125 187 } … … 133 195 */ 134 196 public static def prepare( def value, TemplateFieldType type ) { 197 if( value == null ) 198 return value 199 135 200 switch (type) { 136 201 case TemplateFieldType.DATE: … … 185 250 } 186 251 187 } 188 252 } 189 253 190 254 /** … … 201 265 def value = valueCallback( study, criterion ); 202 266 203 if( value == null ) 267 if( value == null ) { 204 268 return false 269 } 270 205 271 if( value instanceof Collection ) { 206 272 return criterion.matchAny( value ) … … 230 296 def ctx = ApplicationHolder.getApplication().getMainContext(); 231 297 def moduleCommunicationService = ctx.getBean("moduleCommunicationService"); 232 298 299 def allEntities = [] 300 if( searchMode == SearchMode.or ) { 301 allEntities += entities; 302 entities = []; 303 } 304 233 305 // Loop through all modules and check whether criteria have been given 234 306 // for that module … … 250 322 def json = moduleCommunicationService.callModuleRestMethodJSON( module.url, callUrl ); 251 323 252 // The data has been retrieved. Now walk through all criteria to filter the samples 253 entities = filterEntityList( entities, moduleCriteria, { entity, criterion -> 324 Closure checkClosure = { entity, criterion -> 254 325 // Find the value of the field in this sample. That value is still in the 255 326 // JSON object … … 268 339 } 269 340 270 // Loop through all values and match any 271 for( val in value ) { 272 // Convert numbers to a long or double in order to process them correctly 341 // Convert numbers to a long or double in order to process them correctly 342 def values = value.collect { val -> 273 343 val = val.toString(); 274 344 if( val.isLong() ) { … … 277 347 val = Double.parseDouble( val ); 278 348 } 279 349 return val; 350 } 351 352 // Loop through all values and match any 353 for( val in values ) { 280 354 if( criterion.match( val ) ) 281 355 return true; … … 283 357 284 358 return false; 285 }); 359 } 360 361 // The data has been retrieved. Now walk through all criteria to filter the samples 362 if( searchMode == SearchMode.and ) { 363 entities = filterEntityList( entities, moduleCriteria, checkClosure ); 364 } else if( searchMode == SearchMode.or ) { 365 entities += filterEntityList( allEntities - entities, moduleCriteria, checkClosure ); 366 entities = entities.unique(); 367 } 286 368 287 369 } catch( Exception e ) { … … 321 403 resultFields[ id ] = [:] 322 404 405 // Handle special cases 406 if( value == null ) 407 value = ""; 408 409 if( value instanceof Collection ) { 410 value = value.findAll { it != null } 411 } 412 323 413 resultFields[ id ][ fieldName ] = value; 324 414 } -
trunk/src/groovy/dbnp/query/StudySearch.groovy
r1487 r1501 24 24 class StudySearch extends Search { 25 25 private static final log = LogFactory.getLog(this); 26 26 27 27 public StudySearch() { 28 28 super(); … … 63 63 */ 64 64 @Override 65 void execute() { 66 super.execute(); 67 65 void executeAnd() { 68 66 def studies = Study.list().findAll { it.canRead( this.user ) }; 69 67 … … 81 79 studies = filterOnSamplingEventCriteria( studies ); 82 80 studies = filterOnAssayCriteria( studies ); 81 82 studies = filterOnModuleCriteria( studies ); 83 84 // Save matches 85 results = studies; 86 } 87 88 /** 89 * Searches for studies based on the given criteria. Only one criteria have to be satisfied and 90 * criteria for the different entities are satisfied as follows: 91 * 92 * Study.title = 'abc' 93 * The returned study will have title 'abc' 94 * 95 * Subject.species = 'human' 96 * The returned study will have one or more subjects with species = 'human' 97 * 98 * Sample.name = 'sample 1' 99 * The returned study will have one or more samples with name = 'sample 1' 100 * 101 * Event.startTime = '0s' 102 * The returned study will have one or more events with start time = '0s' 103 * 104 * Assay.module = 'metagenomics' 105 * The returned study will have one or more assays with module = 'metagenomics' 106 * 107 * When searching the system doesn't look at the connections between different entities. This means, 108 * the system doesn't look for human subjects having a sample with name 'sample 1'. The sample 1 might 109 * as well belong to a mouse subject and still the study satisfies the criteria. 110 * 111 * When searching for more than one criterion per entity, these are taken separately. Searching for 112 * 113 * Subject.species = 'human' 114 * Subject.name = 'Jan' 115 * 116 * will result in all studies having a human subject or a subject named 'Jan'. Studies with only a 117 * mouse subject named 'Jan' or a human subject named 'Kees' will satisfy the criteria. 118 * 119 */ 120 @Override 121 void executeOr() { 122 def allStudies = Study.list().findAll { it.canRead( this.user ) }; 123 124 // If no criteria are found, return all studies 125 if( !criteria || criteria.size() == 0 ) { 126 results = allStudies; 127 return; 128 } 129 130 // Perform filters 131 def studies = [] 132 studies = ( studies + filterOnStudyCriteria( allStudies - studies ) ).unique(); 133 studies = ( studies + filterOnSubjectCriteria( allStudies - studies ) ).unique(); 134 studies = ( studies + filterOnSampleCriteria( allStudies - studies ) ).unique(); 135 studies = ( studies + filterOnEventCriteria( allStudies - studies ) ).unique(); 136 studies = ( studies + filterOnSamplingEventCriteria( allStudies - studies ) ).unique(); 137 studies = ( studies + filterOnAssayCriteria( allStudies - studies ) ).unique(); 83 138 84 studies = filterOnModuleCriteria( studies);139 studies = ( studies + filterOnModuleCriteria( allStudies - studies ) ).unique(); 85 140 86 141 // Save matches -
trunk/web-app/css/advancedQuery.css
r1482 r1501 23 23 24 24 .ui-menu-item .entity { color: #666; font-style: italic; } 25 26 .options { float: left; width: 300px; } 27 .options a { 28 font-size: 10px; 29 font-weight: bold; 30 margin-left: 3px; 31 margin-right: 3px; 32 padding-top: 2px; 33 padding-bottom: 2px; 34 line-height: 20px; 35 padding-left: 28px; 36 } 37 38 .options a.performAction { 39 background: transparent url(../plugins/famfamfam-1.0.1/images/icons/brick_go.png) 5px 50% no-repeat; 40 } 41 .options a.excel { 42 background-image: url(../plugins/famfamfam-1.0.1/images/icons/page_excel.png); 43 } 44 .options a.searchIn { 45 background: transparent url(../plugins/famfamfam-1.0.1/images/icons/arrow_branch.png) 5px 50% no-repeat; 46 } 47 .options a.search { 48 background: transparent url(../plugins/famfamfam-1.0.1/images/icons/arrow_undo.png) 5px 50% no-repeat; 49 } 50 .options a.discard { 51 background: transparent url(../plugins/famfamfam-1.0.1/images/icons/basket_remove.png) 5px 50% no-repeat; 52 } 53 .options a.listPrevious { 54 background: transparent url(../plugins/famfamfam-1.0.1/images/icons/basket.png) 5px 50% no-repeat; 55 } 56 .options a.combine { 57 background: transparent url(../plugins/famfamfam-1.0.1/images/icons/arrow_join.png) 5px 50% no-repeat; 58 }
Note: See TracChangeset
for help on using the changeset viewer.