Changeset 1482
- Timestamp:
- Feb 2, 2011, 4:40:22 PM (12 years ago)
- Location:
- trunk
- Files:
-
- 4 added
- 16 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/grails-app/controllers/RestController.groovy
r1440 r1482 525 525 526 526 def user = AuthenticationService.getRemotelyLoggedInUser( params.consumer, params.token ); 527 render( ['isOwner': study.isOwner(user), 'canRead': study.canRead(user), 'canWrite': study.canWrite(user)] as JSON ) 527 def auth = ['isOwner': study.isOwner(user), 'canRead': study.canRead(user), 'canWrite': study.canWrite(user)]; 528 log.trace "Authorization for study " + study.title + " and user " + user.username + ": " + auth 529 render auth as JSON; 528 530 } else { 529 531 response.sendError(400) -
trunk/grails-app/controllers/dbnp/authentication/LogoutController.groovy
r1430 r1482 16 16 } 17 17 // TODO put any pre-logout code here 18 19 // Remove all queries from session 20 session.queries = []; 18 21 } 19 22 … … 30 33 redirect uri: SpringSecurityUtils.securityConfig.logout.filterProcessesUrl // '/j_spring_security_logout' 31 34 } 35 36 // Remove all queries from session 37 session.queries = []; 32 38 } 33 39 } -
trunk/grails-app/controllers/dbnp/query/AdvancedQueryController.groovy
r1458 r1482 12 12 class AdvancedQueryController { 13 13 def moduleCommunicationService; 14 def authenticationService 15 16 def entitiesToSearchFor = [ 'Study': 'Studies', 'Sample': 'Samples'] 14 17 15 def entitiesToSearchFor = [ 'Study': 'Studies', 'Sample': 'Samples'] 16 def index = { 17 [entitiesToSearchFor: entitiesToSearchFor, searchableFields: getSearchableFields()] 18 } 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 [entitiesToSearchFor: entitiesToSearchFor, searchableFields: getSearchableFields(), criteria: criteria] 28 } 19 29 20 30 /** … … 38 48 // Create a search object and let it do the searching 39 49 Search search; 40 String view ;50 String view = determineView( params.entity ); 41 51 switch( params.entity ) { 42 case "Study": search = new StudySearch(); view = "studyresults";break;43 case "Sample": search = new SampleSearch(); view = "sampleresults";break;44 45 // This exception will only be thrown if the entitiesToSearchFor contains more entities than 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 46 56 // mentioned in this switch structure. 47 default: throw new Exception( "Can't search for entities of type " + params.entity ); 48 } 49 57 default: throw new Exception( "Can't search for entities of type " + params.entity ); 58 } 50 59 search.execute( parseCriteria( params.criteria ) ); 51 52 render( view: view, model: [search: search] ); 60 61 // Save search in session 62 def queryId = saveSearch( search ); 63 render( view: view, model: [search: search, queryId: queryId] ); 64 } 65 66 /** 67 * Removes a specified search from session 68 * @param id queryId of the search to discard 69 */ 70 def discard = { 71 Integer queryId 72 try { 73 queryId = params.id as Integer 74 } catch( Exception e ) { 75 flash.error = "Incorrect search ID given to discard" 76 redirect( action: "index" ); 77 return 78 } 79 80 discardSearch( queryId ); 81 flash.message = "Search has been discarded" 82 redirect( action: "list" ); 83 } 84 85 /** 86 * Shows a specified search from session 87 * @param id queryId of the search to show 88 */ 89 def show = { 90 Integer queryId 91 try { 92 queryId = params.id as Integer 93 } catch( Exception e ) { 94 flash.error = "Incorrect search ID given to show" 95 redirect( action: "index" ); 96 return 97 } 98 99 // Retrieve the search from session 100 Search s = retrieveSearch( queryId ); 101 if( !s ) { 102 flash.message = "Specified search could not be found" 103 redirect( action: "index" ); 104 return; 105 } 106 107 // Determine which view to show 108 def view = determineView( s.entity ); 109 render( view: view, model: [search: s, queryId: queryId] ); 110 } 111 112 /** 113 * Shows a list of searches that have been saved in session 114 * @param id queryId of the search to show 115 */ 116 def list = { 117 def searches = listSearches(); 118 119 if( !searches || searches.size() == 0 ) { 120 flash.message = "No previous searches found"; 121 redirect( action: "index" ); 122 return; 123 } 124 [searches: searches] 53 125 } 54 126 127 /** 128 * Shows a search screen where the user can search within the results of another search 129 * @param id queryId of the search to search in 130 */ 131 def searchIn = { 132 Integer queryId 133 try { 134 queryId = params.id as Integer 135 } catch( Exception e ) { 136 flash.error = "Incorrect search ID given to show" 137 redirect( action: "index" ); 138 return 139 } 140 141 // Retrieve the search from session 142 Search s = retrieveSearch( queryId ); 143 if( !s ) { 144 flash.message = "Specified search could not be found" 145 redirect( action: "index" ); 146 return; 147 } 148 149 redirect( action: "index", params: [ "criteria.0.entityfield": s.entity, "criteria.0.operator": "in", "criteria.0.value": queryId ]) 150 } 151 152 protected String determineView( String entity ) { 153 switch( entity ) { 154 case "Study": return "studyresults"; break; 155 case "Sample": return "sampleresults"; break; 156 default: return "results"; break; 157 } 158 } 159 55 160 /** 56 161 * Returns a map of entities with the names of the fields the user can search on … … 59 164 protected def getSearchableFields() { 60 165 def fields = [:]; 61 166 62 167 // Retrieve all local search fields 63 168 getEntities().each { 64 169 def entity = getEntity( 'dbnp.studycapturing.' + it ); 65 170 66 171 if( entity ) { 67 172 def domainFields = entity.giveDomainFields(); 68 173 def templateFields = TemplateField.findAllByEntity( entity ) 69 174 70 175 def fieldNames = ( domainFields + templateFields ).collect { it.name }.unique() + 'Template' 71 176 72 177 fields[ it ] = fieldNames.sort { a, b -> a[0].toUpperCase() + a[1..-1] <=> b[0].toUpperCase() + b[1..-1] }; 73 178 } 74 179 } 75 180 76 181 // Loop through all modules and check which fields are searchable 77 182 // Right now, we just combine the results for different entities … … 81 186 def json = moduleCommunicationService.callModuleRestMethodJSON( module.url, callUrl ); 82 187 def moduleFields = []; 83 entitiesToSearchFor.each { entity -> 188 entitiesToSearchFor.each { entity -> 84 189 if( json[ entity.key ] ) { 85 190 json[ entity.key ].each { field -> … … 88 193 } 89 194 } 90 195 91 196 // Remove 'module' from module name 92 197 def moduleName = module.name.replace( 'module', '' ).trim() 93 198 94 199 fields[ moduleName ] = moduleFields.unique(); 95 200 } catch( Exception e ) { … … 97 202 } 98 203 } 99 204 100 205 return fields; 101 206 } 102 207 103 208 /** 104 209 * Parses the criteria from the query form given by the user … … 115 220 * 1.field: d 116 221 * ] 117 * 118 * @return List with Criterion objects119 */ 120 protected List parseCriteria( def c) {222 * @param parseSearchIds Determines whether searches are returned instead of their ids 223 * @return List with Criterion objects 224 */ 225 protected List parseCriteria( def formCriteria, def parseSearchIds = true ) { 121 226 ArrayList list = []; 122 227 flash.error = ""; 123 228 // Loop through all keys of c and remove the non-numeric ones 124 c.each { 125 if( it.key ==~ /[0-9]+/ ) { 126 def formCriterion = it.value; 229 for( c in formCriteria ) { 230 if( c.key ==~ /[0-9]+/ ) { 231 def formCriterion = c.value; 232 127 233 Criterion criterion = new Criterion(); 128 234 129 235 // Split entity and field 130 236 def field = formCriterion.entityfield?.split( /\./ ); 131 132 237 if( field.size() > 1 ) { 133 238 criterion.entity = field[0].toString(); 134 239 criterion.field = field[1].toString(); 135 240 } else { 136 criterion.entity = null; 137 criterion.field = field; 241 criterion.entity = field[0]; 242 criterion.field = null; 243 } 244 245 // Convert operator string to Operator-enum field 246 try { 247 criterion.operator = Criterion.parseOperator( formCriterion.operator ); 248 } catch( Exception e) { 249 println "Operator " + formCriterion.operator + " could not be parsed: " + e.getMessage(); 250 flash.error += "Criterion could not be used: operator " + formCriterion.operator + " is not valid.<br />\n"; 251 continue; 138 252 } 139 253 140 // Convert operator string to Operator-enum field 141 switch( formCriterion.operator ) { 142 case ">=": criterion.operator = Operator.gte; break; 143 case ">": criterion.operator = Operator.gt; break; 144 case "<": criterion.operator = Operator.lte; break; 145 case "<=": criterion.operator = Operator.lt; break; 146 case "contains": criterion.operator = Operator.contains; break; 147 case "equals": criterion.operator = Operator.equals; break; 254 // Special case of the 'in' operator 255 if( criterion.operator == Operator.insearch ) { 256 Search s 257 try { 258 s = retrieveSearch( Integer.parseInt( formCriterion.value ) ); 259 } catch( Exception e ) {} 260 261 if( !s ) { 262 flash.error += "Can't search within previous query: query not found"; 263 continue; 264 } 265 266 if( parseSearchIds ) { 267 criterion.value = s 268 } else { 269 criterion.value = s.id 270 } 271 } else { 272 // Copy value 273 criterion.value = formCriterion.value; 148 274 } 149 275 150 // Copy value151 criterion.value = formCriterion.value;152 153 276 list << criterion; 154 277 } 155 278 } 156 279 157 280 return list; 158 281 } 159 282 160 283 /** 161 284 * Returns all entities for which criteria can be entered … … 165 288 return [ 'Study', 'Subject', 'Sample', 'Event', 'SamplingEvent', 'Assay' ] 166 289 } 167 168 /** 169 * Creates an object of the given entity. 170 * 171 * @return False if the entity is not a subclass of TemplateEntity 172 */ 173 protected def getEntity( entityName ) { 174 // Find the templates 175 def entity 176 try { 177 entity = Class.forName(entityName, true, this.getClass().getClassLoader()) 178 179 // succes, is entity an instance of TemplateEntity? 180 if (entity.superclass =~ /TemplateEntity$/ || entity.superclass.superclass =~ /TemplateEntity$/) { 181 return entity; 182 } else { 183 return false; 184 } 185 } catch( ClassNotFoundException e ) { 186 log.error "Class " + entityName + " not found: " + e.getMessage() 187 return null; 188 } 189 190 } 191 290 291 /** 292 * Creates an object of the given entity. 293 * 294 * @return False if the entity is not a subclass of TemplateEntity 295 */ 296 protected def getEntity( entityName ) { 297 // Find the templates 298 def entity 299 try { 300 entity = Class.forName(entityName, true, this.getClass().getClassLoader()) 301 302 // succes, is entity an instance of TemplateEntity? 303 if (entity.superclass =~ /TemplateEntity$/ || entity.superclass.superclass =~ /TemplateEntity$/) { 304 return entity; 305 } else { 306 return false; 307 } 308 } catch( ClassNotFoundException e ) { 309 log.error "Class " + entityName + " not found: " + e.getMessage() 310 return null; 311 } 312 313 } 314 315 316 /*************************************************************************** 317 * 318 * Methods for saving results in session 319 * 320 ***************************************************************************/ 321 322 /** 323 * Saves the given search in session. Any search with the same criteria will be overwritten 324 * 325 * @param s Search to save 326 * @return Id of the search for later reference 327 */ 328 protected int saveSearch( Search s ) { 329 if( !session.queries ) 330 session.queries = [:] 331 332 // First check whether a search with the same criteria is already present 333 def previousSearch = retrieveSearchByCriteria( s.getCriteria() ); 334 335 def id 336 if( previousSearch ) { 337 id = previousSearch.id; 338 } else { 339 // Determine unique id 340 id = ( session.queries*.key.max() ?: 0 ) + 1; 341 } 342 343 s.id = id; 344 session.queries[ id ] = s; 345 346 println "On saveSearch: " + session.queries; 347 return id; 348 } 349 350 /** 351 * Retrieves a search from session with the same criteria as given 352 * @param criteria List of criteria to search for 353 * @return Search that has this criteria, or null if no such search is found. 354 */ 355 protected Search retrieveSearchByCriteria( List criteria ) { 356 if( !session.queries ) 357 return null 358 359 if( !criteria ) 360 return null 361 362 for( query in session.queries ) { 363 def key = query.key; 364 def value = query.value; 365 366 if( value.criteria && value.criteria.containsAll( criteria ) && criteria.containsAll( value.criteria ) ) { 367 return value; 368 } 369 } 370 371 return null; 372 } 373 374 375 /** 376 * Retrieves a search from session 377 * @param id Id of the search 378 * @return Search that belongs to this ID or null if no search is found 379 */ 380 protected Search retrieveSearch( int id ) { 381 if( !session.queries || !session.queries[ id ] ) 382 return null 383 384 if( !( session.queries[ id ] instanceof Search ) ) 385 return null; 386 387 println "On retrieveSearch: " + session.queries; 388 return (Search) session.queries[ id ] 389 } 390 391 /** 392 * Removes a search from session 393 * @param id Id of the search 394 * @return Search that belonged to this ID or null if no search is found 395 */ 396 protected Search discardSearch( int id ) { 397 if( !session.queries || !session.queries[ id ] ) 398 return null 399 400 def sessionSearch = session.queries[ id ]; 401 402 session.queries.remove( id ); 403 404 println "On discardSearch: " + session.queries; 405 if( !( sessionSearch instanceof Search ) ) 406 return null; 407 408 return (Search) sessionSearch 409 } 410 411 /** 412 * Retrieves a list of searches from session 413 * @return List of searches from session 414 */ 415 protected List listSearches() { 416 if( !session.queries ) 417 return [] 418 419 return session.queries*.value.toList() 420 } 192 421 } -
trunk/grails-app/domain/dbnp/studycapturing/Sample.groovy
r1457 r1482 129 129 return name 130 130 } 131 132 /** 133 * Basic equals method to check whether objects are equals, by comparing the ids 134 * @param o Object to compare with 135 * @return True iff the id of the given Sample is equal to the id of this Sample 136 */ 137 public boolean equals( Object o ) { 138 if( o == null ) 139 return false; 140 141 if( !( o instanceof Sample ) ) 142 return false 143 144 Sample s = (Sample) o; 145 146 return this.id == s.id 147 } 131 148 132 149 /** -
trunk/grails-app/domain/dbnp/studycapturing/Study.groovy
r1457 r1482 522 522 return this.studyUUID; 523 523 } 524 525 /** 526 * Basic equals method to check whether objects are equals, by comparing the ids 527 * @param o Object to compare with 528 * @return True iff the id of the given Study is equal to the id of this Study 529 */ 530 public boolean equals( Object o ) { 531 if( o == null ) 532 return false; 533 534 if( !( o instanceof Study ) ) 535 return false 536 537 Study s = (Study) o; 538 539 return this.id == s.id 540 } 524 541 525 542 // Send messages to modules about changes in this study -
trunk/grails-app/services/dbnp/modules/ModuleCommunicationService.groovy
r1458 r1482 48 48 * @return 49 49 */ 50 def invalidateStudy( Studystudy ) {50 def invalidateStudy( def study ) { 51 51 moduleNotificationService.invalidateStudy( study ); 52 52 } … … 113 113 try { 114 114 def textResponse = url.toURL().getText() 115 println"GSCF call to " + consumer + " URL: " + url116 println"GSCF response: " + textResponse115 log.trace "GSCF call to " + consumer + " URL: " + url 116 log.trace "GSCF response: " + textResponse 117 117 restResponse = JSON.parse( textResponse ) 118 118 } catch (Exception e) { -
trunk/grails-app/views/advancedQuery/index.gsp
r1430 r1482 6 6 <link rel="stylesheet" href="<g:resource dir="css" file="advancedQuery.css" />" type="text/css"/> 7 7 <g:javascript src="advancedQuery.js" /> 8 <script type="text/javascript"> 9 // Make a list of fields to search in 10 var queryableFields = [ 11 <g:set var="j" value="${0}" /> 12 <g:each in="${searchableFields}" var="entity"> 13 <g:each in="${entity.value}" var="field"> 14 <g:if test="${j > 0}">,</g:if> 15 { 16 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 entity: "${entity.key.toString().encodeAsJavaScript()}" 20 } 21 <g:set var="j" value="1" /> 22 </g:each> 23 </g:each> 24 ]; 25 26 <g:if test="${criteria && criteria.size() > 0}"> 27 // Show given criteria 28 $(function() { 29 <g:each in="${criteria}" var="criterion"> 30 showCriterium("${criterion.entityField().encodeAsJavaScript()}", "${criterion.value.toString().encodeAsJavaScript()}", "${criterion.operator.toString().encodeAsJavaScript()}"); 31 </g:each> 32 showHideNoCriteriaItem(); 33 }); 34 </g:if> 35 </script> 8 36 </head> 9 37 <body> … … 11 39 <h1>Query database</h1> 12 40 41 <g:if test="${flash.error}"> 42 <div class="error"> 43 ${flash.error.toString().encodeAsHTML()} 44 </div> 45 </g:if> 46 <g:if test="${flash.message}"> 47 <div class="message"> 48 ${flash.message.toString().encodeAsHTML()} 49 </div> 50 </g:if> 51 52 <a href="<g:createLink action="list" />">View previous queries</a> 53 13 54 <form id="input_criteria"> 14 55 <h2>Add criterium</h2> 15 <p >56 <p class="explanation"> 16 57 N.B. Comparing numerical values is done without taking into 17 58 account the units. E.g. a weight of 1 kg equals 1 grams. 18 59 </p> 19 60 <label for="field">Field</label> 20 <select name="field" >61 <select name="field" id="queryFieldSelect"> 21 62 <option value=""></option> 22 63 <g:each in="${searchableFields}" var="entity"> … … 29 70 </optgroup> 30 71 </g:each> 31 </select> 72 </select> 32 73 33 74 <label for="value">Comparison</label> -
trunk/grails-app/views/advancedQuery/results.gsp
r1430 r1482 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="advancedQuery.js" />8 7 </head> 9 8 <body> … … 17 16 <g:each in="${search.getCriteria()}" var="criterion"> 18 17 <li> 19 <span class="entityfield">${criterion.entity }.${criterion.field}</span>18 <span class="entityfield">${criterion.entityField()}</span> 20 19 <span class="operator">${criterion.operator}</span> 21 20 <span class="value">${criterion.value}</span> … … 28 27 29 28 <g:if test="${search.getNumResults() > 0}"> 30 29 <% 30 def resultFields = search.getShowableResultFields(); 31 def extraFields = resultFields[ search.getResults()[ 0 ].id ]?.keySet(); 32 %> 31 33 <table id="searchresults"> 32 34 <thead> … … 34 36 <th>Type</th> 35 37 <th>Id</th> 38 <g:each in="${extraFields}" var="fieldName"> 39 <th>${fieldName}</th> 40 </g:each> 36 41 </tr> 37 42 </thead> … … 40 45 <td>${search.entity}</td> 41 46 <td>${result.id}</td> 47 <g:each in="${extraFields}" var="fieldName"> 48 <td> 49 <% 50 def fieldValue = resultFields[ result.id ]?.get( fieldName ); 51 if( fieldValue ) { 52 if( fieldValue instanceof Collection ) 53 fieldValue = fieldValue.collect { it.toString() }.findAll { it }.join( ', ' ); 54 else 55 fieldValue = fieldValue.toString(); 56 } 57 %> 58 ${fieldValue} 59 </td> 60 </g:each> 61 42 62 </tr> 43 63 </g:each> 44 64 </table> 45 65 </g:if> 46 <p> 47 <g:link action="index">Search again</g:link> 48 </p> 66 <g:render template="resultbuttons" model="[queryId: queryId]" /> 67 49 68 </body> 50 69 </html> -
trunk/grails-app/views/advancedQuery/sampleresults.gsp
r1458 r1482 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="advancedQuery.js" />8 7 </head> 9 8 <body> … … 12 11 13 12 <p> 14 Your search for s tudies with:13 Your search for samples with: 15 14 </p> 16 <ul id="criteria"> 17 <g:each in="${search.getCriteria()}" var="criterion"> 18 <li> 19 <span class="entityfield">${criterion.entity}.${criterion.field}</span> 20 <span class="operator">${criterion.operator}</span> 21 <span class="value">${criterion.value}</span> 22 </li> 23 </g:each> 24 </ul> 15 <g:render template="criteria" model="[criteria: search.getCriteria()]" /> 25 16 <p> 26 17 resulted in ${search.getNumResults()} <g:if test="${search.getNumResults() == 1}">sample</g:if><g:else>samples</g:else>. 27 18 </p> 28 19 29 30 20 <g:if test="${search.getNumResults() > 0}"> 31 21 <% 22 def resultFields = search.getShowableResultFields(); 23 def extraFields = resultFields[ search.getResults()[ 0 ].id ]?.keySet(); 24 %> 32 25 <table id="searchresults" class="paginate"> 33 26 <thead> … … 35 28 <th>Study</th> 36 29 <th>Name</th> 30 <g:each in="${extraFields}" var="fieldName"> 31 <th>${fieldName}</th> 32 </g:each> 37 33 </tr> 38 34 </thead> … … 40 36 <g:each in="${search.getResults()}" var="sampleInstance" status="i"> 41 37 <tr class="${(i % 2) == 0 ? 'odd' : 'even'}"> 42 43 38 <td><g:link controller="study" action="show" id="${sampleInstance?.parent?.id}">${sampleInstance?.parent?.title}</g:link></td> 44 39 <td>${fieldValue(bean: sampleInstance, field: "name")}</td> 40 <g:each in="${extraFields}" var="fieldName"> 41 <td> 42 <% 43 def fieldValue = resultFields[ sampleInstance.id ]?.get( fieldName ); 44 if( fieldValue ) { 45 if( fieldValue instanceof Collection ) 46 fieldValue = fieldValue.collect { it.toString() }.findAll { it }.join( ', ' ); 47 else 48 fieldValue = fieldValue.toString(); 49 } 50 %> 51 ${fieldValue} 52 </td> 53 </g:each> 45 54 </tr> 46 55 </g:each> … … 49 58 50 59 </g:if> 51 <p> 52 <g:link action="index">Search again</g:link> 53 </p> 60 <g:render template="resultbuttons" model="[queryId: queryId]" /> 54 61 </body> 55 62 </html> -
trunk/grails-app/views/advancedQuery/studyresults.gsp
r1458 r1482 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="advancedQuery.js" />8 7 </head> 9 8 <body> … … 14 13 Your search for studies with: 15 14 </p> 16 <ul id="criteria"> 17 <g:each in="${search.getCriteria()}" var="criterion"> 18 <li> 19 <span class="entityfield">${criterion.entity}.${criterion.field}</span> 20 <span class="operator">${criterion.operator}</span> 21 <span class="value">${criterion.value}</span> 22 </li> 23 </g:each> 24 </ul> 15 <g:render template="criteria" model="[criteria: search.getCriteria()]" /> 25 16 <p> 26 17 resulted in ${search.getNumResults()} <g:if test="${search.getNumResults() == 1}">study</g:if><g:else>studies</g:else>. 27 18 </p> 28 19 <g:if test="${search.getNumResults() > 0}"> 20 <% 21 def resultFields = search.getShowableResultFields(); 22 def extraFields = resultFields[ search.getResults()[ 0 ].id ]?.keySet(); 23 %> 29 24 30 25 <table id="searchresults" class="paginate"> … … 37 32 <th>Events</th> 38 33 <th>Assays</th> 34 <g:each in="${extraFields}" var="fieldName"> 35 <th>${fieldName}</th> 36 </g:each> 39 37 </tr> 40 38 </thead> … … 79 77 </g:else> 80 78 </td> 81 79 <g:each in="${extraFields}" var="fieldName"> 80 <td> 81 <% 82 def fieldValue = resultFields[ studyInstance.id ]?.get( fieldName ); 83 if( fieldValue ) { 84 if( fieldValue instanceof Collection ) 85 fieldValue = fieldValue.collect { it.toString() }.findAll { it }.join( ', ' ); 86 else 87 fieldValue = fieldValue.toString(); 88 } 89 %> 90 ${fieldValue} 91 </td> 92 </g:each> 82 93 </tr> 83 94 </g:each> … … 86 97 87 98 </g:if> 88 <p> 89 <g:link action="index">Search again</g:link> 90 </p> 99 <g:render template="resultbuttons" model="[queryId: queryId]" /> 91 100 </body> 92 101 </html> -
trunk/src/groovy/dbnp/query/Criterion.groovy
r1458 r1482 3 3 import java.text.SimpleDateFormat 4 4 import org.dbnp.gdt.* 5 import org.apache.commons.logging.LogFactory; 5 6 6 7 /** … … 10 11 */ 11 12 enum Operator { 12 equals, contains, gte, gt, lte, lt 13 equals( "=" ), contains( "contains" ), gte( ">="), gt( ">" ), lte( "<=" ), lt( "<" ), insearch( "in" ) 14 Operator(String name) { this.name = name } 15 private final String name; 16 public String toString() { return name } 13 17 } 14 18 … … 19 23 */ 20 24 class Criterion { 25 private static final log = LogFactory.getLog(this); 21 26 public String entity 22 27 public String field … … 25 30 26 31 /** 32 * Retrieves a combination of the entity and field 33 * @return 34 */ 35 public String entityField() { 36 return entity.toString() + ( field ? "." + field.toString() : "" ); 37 } 38 39 /** 27 40 * Retrieves the correct value for this criterion in the given object (with template) 28 41 * 29 42 * @param entity Entity to check for value. Should be a child of template entity 30 * @param criterion Criterion to match on31 * @return Value of the given field or null if the field doesn't exist43 * @param criterion Criterion to match on 44 * @return Value of the given field or null if the field doesn't exist 32 45 */ 33 46 public def getFieldValue( TemplateEntity entity ) { … … 37 50 try { 38 51 def fieldValue 39 if( field == "Template" ) { 52 if( !field ) { 53 fieldValue = entity 54 } else if( field == "Template" ) { 40 55 fieldValue = entity.template?.name 41 56 } else { … … 138 153 if( fieldValue == null ) 139 154 return false; 140 155 156 // in-search criteria have to be handled separately 157 if( this.operator == Operator.insearch ) { 158 return this.value?.getResults()?.contains( fieldValue ); 159 } 160 161 // Other criteria are handled based on the class of the value given. 141 162 def classname = fieldValue.class.getName(); 142 163 classname = classname[classname.lastIndexOf( '.' ) + 1..-1].toLowerCase(); 143 144 println "Match " + fieldValue + " of class " + classname + " with " + this145 164 146 165 try { … … 185 204 return fieldValue <= criterionValue; 186 205 case Operator.contains: 187 return fieldValue.contains( criterionValue ); 206 // Contains operator can only be used on string values 207 return fieldValue.toString().contains( criterionValue.toString() ); 188 208 case Operator.equals: 189 209 default: … … 295 315 } 296 316 } 317 318 public static Operator parseOperator( String name ) throws Exception { 319 switch( name.trim() ) { 320 case "=": 321 case "equals": return Operator.equals; 322 case "contains": return Operator.contains; 323 case ">=": 324 case "gte": return Operator.gte; 325 case ">": 326 case "gt": return Operator.gt; 327 case "<=": 328 case "lte": return Operator.lte; 329 case "<": 330 case "lt": return Operator.lt; 331 case "in": return Operator.insearch; 332 default: 333 throw new Exception( "Operator not found" ); 334 } 335 } 297 336 298 337 public String toString() { 299 return "[Criterion " + entity + "." + field + " " + operator + " " + value + "]"; 338 return "[Criterion " + entityField() + " " + operator + " " + value + "]"; 339 } 340 341 public boolean equals( Object o ) { 342 if( o == null ) 343 return false; 344 345 if( !( o instanceof Criterion ) ) 346 return false; 347 348 Criterion otherCriterion = (Criterion) o; 349 return this.entity == otherCriterion.entity && 350 this.field == otherCriterion.field && 351 this.operator == otherCriterion.operator && 352 this.value == otherCriterion.value; 300 353 } 301 354 } -
trunk/src/groovy/dbnp/query/SampleSearch.groovy
r1458 r1482 15 15 package dbnp.query 16 16 17 import java.util.Map; 18 17 19 import dbnp.studycapturing.* 18 20 import org.dbnp.gdt.* 21 import org.apache.commons.logging.LogFactory; 19 22 20 23 class SampleSearch extends Search { 24 private static final log = LogFactory.getLog(this); 21 25 22 26 public SampleSearch() { 27 super(); 28 23 29 this.entity = "Sample"; 24 30 } … … 57 63 @Override 58 64 void execute() { 59 // TODO: check for authorization for these studies?65 super.execute(); 60 66 61 67 // If no criteria are found, return all samples 62 68 if( !criteria || criteria.size() == 0 ) { 63 results = Sample.list() ;69 results = Sample.list().findAll { it.parent?.canRead( this.user ) }; 64 70 return; 65 71 } … … 72 78 def samples = [] 73 79 if( getEntityCriteria( 'Study' ).size() > 0 ) { 74 def studies = Study.findAll() ;75 80 def studies = Study.findAll().findAll { it.canRead( this.user ) }; 81 76 82 studies = filterOnStudyCriteria( studies ); 77 83 78 84 if( studies.size() == 0 ) { 79 85 results = []; 80 86 return; 81 87 } 82 88 83 89 def c = Sample.createCriteria() 84 90 samples = c.list { 85 91 'in'( 'parent', studies ) 86 92 } 93 94 // Save data about the resulting studies in the 95 // result fields array. The data that is now in the array 96 // is saved based on the study id, not based on the sample id 97 clearResultFields(); 98 saveResultFields( samples, getEntityCriteria( "Study" ), { sample, criterion -> 99 return criterion.getFieldValue( sample.parent ); 100 }); 87 101 } else { 88 samples = Sample.findAll() 102 samples = Sample.findAll().findAll { it.parent?.canRead( this.user ) } 89 103 } 90 104 … … 94 108 samples = filterOnSamplingEventCriteria( samples ); 95 109 samples = filterOnAssayCriteria( samples ); 96 110 97 111 samples = filterOnModuleCriteria( samples ); 98 112 99 113 // Save matches 100 114 results = samples; … … 168 182 if( getEntityCriteria( 'Assay' ).size() == 0 ) 169 183 return samples 170 184 171 185 // There is no sample.assays property, so we have to look for assays another way: just find 172 186 // all assays that match the criteria … … 178 192 return criterion.matchOne( assay ); 179 193 }); 180 194 181 195 // If no assays match these criteria, then no samples will match either 182 196 if( assays.size() == 0 ) 183 197 return []; 184 198 185 199 // Save sample data for later use 186 saveResultFields( samples, criteria, { sample, criterion -> 187 188 if( sampleAssays && sampleAssays.size() > 0 )189 190 else 200 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 else 191 205 return null 192 206 }); 193 207 194 208 // Now filter the samples on whether they are attached to the filtered assays 195 209 return samples.findAll { sample -> 196 210 if( !sample.parent ) 197 211 return false; 198 212 199 213 def studyAssays = assays.findAll { it.parent.equals( sample.parent ); } 200 201 // See if this sample is present in any of the matching assays. If so, 214 215 // See if this sample is present in any of the matching assays. If so, 202 216 // this sample matches the criteria 203 217 for( def assay in studyAssays ) { 204 if( assay.samples?.contains( sample ) ) 218 if( assay.samples?.contains( sample ) ) 205 219 return true; 206 220 } 207 221 208 222 return false; 209 223 } 210 224 } 225 226 /** 227 * Returns the saved field data that could be shown on screen. This means, the data 228 * is filtered to show only data of the query results. Also, the study title and sample 229 * name are filtered out, in order to be able to show all data on the screen without 230 * checking further 231 * 232 * @return Map with the entity id as a key, and a field-value map as value 233 */ 234 public Map getShowableResultFields() { 235 Map showableFields = super.getShowableResultFields() 236 showableFields.each { sampleElement -> 237 sampleElement.value = sampleElement.value.findAll { fieldElement -> 238 fieldElement.key != "Study title" && fieldElement.key != "Sample name" 239 } 240 } 241 } 211 242 } -
trunk/src/groovy/dbnp/query/Search.groovy
r1478 r1482 16 16 package dbnp.query 17 17 18 import dbnp.authentication.SecUser 18 19 import groovy.lang.Closure; 19 20 … … 22 23 23 24 import org.springframework.context.ApplicationContext 25 import org.springframework.web.context.request.RequestContextHolder; 24 26 import org.codehaus.groovy.grails.commons.ApplicationHolder; 25 27 … … 28 30 class Search { 29 31 public String entity; 32 public SecUser user; 33 public Date executionDate; 34 public int id; // Is only used when this query is saved in session 30 35 31 36 protected List criteria; … … 42 47 public void setResultFields( Map r ) { resultFields = r; } 43 48 49 <<<<<<< .mine 50 public Search() { 51 ApplicationContext ctx = (ApplicationContext)ApplicationHolder.getApplication().getMainContext(); 52 def authenticationService = ctx.getBean("authenticationService"); 53 def sessionUser = authenticationService.getLoggedInUser(); 54 55 if( sessionUser ) 56 this.user = sessionUser; 57 else 58 this.user = null 59 } 60 61 ======= 62 >>>>>>> .r1481 44 63 /** 45 64 * Returns the number of results found by this search … … 68 87 * subclasses searching for a specific entity 69 88 */ 70 public void execute() {} 89 public void execute() { 90 this.executionDate = new Date(); 91 } 71 92 72 93 /** … … 146 167 return Boolean.valueOf( value ) 147 168 } catch( Exception e ) { 148 println e.getMessage();149 169 return value.toString(); 150 170 } … … 178 198 protected List filterOnTemplateEntityCriteria( List studies, String entityName, Closure valueCallback ) { 179 199 def criteria = getEntityCriteria( entityName ); 200 180 201 def checkCallback = { study, criterion -> 181 202 def value = valueCallback( study, criterion ); … … 192 213 // Save the value of this entity for later use 193 214 saveResultFields( studies, criteria, valueCallback ); 194 215 195 216 return filterEntityList( studies, criteria, checkCallback); 196 217 } … … 207 228 208 229 // Determine the moduleCommunicationService 209 def ctx = ApplicationHolder.getApplication().getMainContext();230 def ctx = (ApplicationContext)ApplicationHolder.getApplication().getMainContext(); 210 231 def moduleCommunicationService = ctx.getBean("moduleCommunicationService"); 211 232 … … 218 239 219 240 if( moduleCriteria && moduleCriteria.size() > 0 ) { 220 println "Filter " + entities.size() + " entities on " + module.name + " criteria: " + moduleCriteria.size();221 222 241 // Retrieve the data from the module 223 242 def tokens = entities.collect { it.giveUUID() }.unique(); … … 243 262 244 263 // Save the value of this entity for later use 245 saveResultField( entity.id, criterion. field, value )264 saveResultField( entity.id, criterion.entity + " " + criterion.field, value ) 246 265 247 266 if( !( value instanceof Collection ) ) { … … 267 286 268 287 } catch( Exception e ) { 269 println( "Error while retrieving data from " + module.name + ": " + e.getMessage() )288 log.error( "Error while retrieving data from " + module.name + ": " + e.getMessage() ) 270 289 } 271 290 } … … 285 304 for( criterion in criteria ) { 286 305 for( entity in entities ) { 287 saveResultField( entity.id, criterion.field, valueCallback( entity, criterion ) ) 306 if( criterion.field ) 307 saveResultField( entity.id, criterion.entity + ' ' + criterion.field, valueCallback( entity, criterion ) ) 288 308 } 289 309 } … … 303 323 resultFields[ id ][ fieldName ] = value; 304 324 } 325 326 /** 327 * Removes all data from the result field map 328 */ 329 protected void clearResultFields() { 330 resultFields = [:] 331 } 332 333 /** 334 * Returns the saved field data that could be shown on screen. This means, the data is filtered to show only data of the query results. 335 * 336 * Subclasses could filter out the fields they don't want to show on the result screen (e.g. because they are shown regardless of the 337 * query.) 338 * @return Map with the entity id as a key, and a field-value map as value 339 */ 340 public Map getShowableResultFields() { 341 def resultIds = getResults()*.id; 342 return getResultFields().findAll { 343 resultIds.contains( it.key ) 344 } 345 } 346 347 public String toString() { 348 return ( this.entity ? this.entity + " search" : "Search" ) + " " + this.id 349 } 305 350 } -
trunk/src/groovy/dbnp/query/StudySearch.groovy
r1458 r1482 16 16 17 17 import java.util.List; 18 import java.util.Map; 18 19 19 20 import dbnp.studycapturing.* 20 21 import org.dbnp.gdt.* 22 import org.apache.commons.logging.LogFactory; 21 23 22 24 class StudySearch extends Search { 25 private static final log = LogFactory.getLog(this); 26 23 27 public StudySearch() { 28 super(); 24 29 this.entity = "Study"; 25 30 } … … 59 64 @Override 60 65 void execute() { 61 // TODO: check for authorization for these studies? 62 def studies = Study.list(); 66 super.execute(); 63 67 68 def studies = Study.list().findAll { it.canRead( this.user ) }; 69 64 70 // If no criteria are found, return all studies 65 71 if( !criteria || criteria.size() == 0 ) { … … 75 81 studies = filterOnSamplingEventCriteria( studies ); 76 82 studies = filterOnAssayCriteria( studies ); 77 83 78 84 studies = filterOnModuleCriteria( studies ); 79 85 … … 81 87 results = studies; 82 88 } 83 89 84 90 /** 85 91 * Filters the given list of studies on the study criteria … … 97 103 */ 98 104 protected List filterOnSubjectCriteria( List studies ) { 99 return filterOnTemplateEntityCriteria(studies, "Subject", { study, criterion -> 105 return filterOnTemplateEntityCriteria(studies, "Subject", { study, criterion -> 100 106 return study.subjects?.collect { criterion.getFieldValue( it ); } 101 107 }) … … 119 125 */ 120 126 protected List filterOnEventCriteria( List studies ) { 121 return filterOnTemplateEntityCriteria(studies, "Event", { study, criterion -> 127 return filterOnTemplateEntityCriteria(studies, "Event", { study, criterion -> 122 128 return study.events?.collect { criterion.getFieldValue( it ); } 123 129 }) 124 130 } 125 131 126 132 /** 127 * Filters the given list of studies on the sampling event criteria128 * @param studies Original list of studies129 * @return List with all studies that match the event-criteria130 */131 132 return filterOnTemplateEntityCriteria(studies, "SamplingEvent", { study, criterion -> 133 * Filters the given list of studies on the sampling event criteria 134 * @param studies Original list of studies 135 * @return List with all studies that match the event-criteria 136 */ 137 protected List filterOnSamplingEventCriteria( List studies ) { 138 return filterOnTemplateEntityCriteria(studies, "SamplingEvent", { study, criterion -> 133 139 return study.samplingEvents?.collect { criterion.getFieldValue( it ); } 134 140 }) 135 136 141 } 142 137 143 /** 138 144 * Filters the given list of studies on the assay criteria … … 145 151 }) 146 152 } 153 154 /** 155 * Returns the saved field data that could be shown on screen. This means, the data 156 * is filtered to show only data of the query results. Also, the study title and sample 157 * name are filtered out, in order to be able to show all data on the screen without 158 * checking further 159 * 160 * @return Map with the entity id as a key, and a field-value map as value 161 */ 162 public Map getShowableResultFields() { 163 Map showableFields = super.getShowableResultFields() 164 showableFields.each { sampleElement -> 165 sampleElement.value = sampleElement.value.findAll { fieldElement -> 166 fieldElement.key != "Study title" && fieldElement.key != "Subject species" 167 } 168 } 169 return showableFields 170 } 147 171 } -
trunk/web-app/css/advancedQuery.css
r1424 r1482 1 label { display: inline-block; zoom: 1; *display: inline; width: 110px; margin-top: 5px;}1 label { display: inline-block; zoom: 1; *display: inline; width: 110px; margin-top: 10px; } 2 2 3 3 #searchForm ul#criteria { margin-left: 110px; padding-left: 0px; margin-top: -19px; list-style-type: none; } 4 4 #searchForm ul#criteria li { margin: 2px 0; padding-left: 0; } 5 #searchForm ul#criteria li span { display: inline-block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-right: 5px; } 5 #searchForm ul#criteria li span, 6 #searchForm ul#criteria li a { display: inline-block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-right: 5px; } 6 7 #searchForm ul#criteria li .entityfield { width: 200px; } 7 8 #searchForm ul#criteria li .operator { width: 100px; } 8 9 #searchForm ul#criteria li .value { width: 240px; } 9 10 11 #searchForm ul#criteria li.emptyList { color: #666; } 10 12 #searchForm ul#criteria li.emptyList:hover { cursor: default; } 11 13 … … 14 16 15 17 #input_criteria { display: block; float: right; width: 260px; border: 1px solid #666; padding: 10px; } 16 #input_criteria h2 { margin-top: 2px; margin-bottom: 8px; }18 #input_criteria h2 { margin-top: 2px; margin-bottom: 8px; font-weight: bold; } 17 19 #input_criteria label { width: 80px; margin-top: 8px; } 18 20 #input_criteria input.text, #input_criteria select { width: 165px; } 19 21 #input_criteria input.button { margin-top: 8px; } 22 #input_criteria .explanation { font-size: 10px; } 23 24 .ui-menu-item .entity { color: #666; font-style: italic; } -
trunk/web-app/css/default_style.css
r1448 r1482 196 196 } 197 197 198 #content .error { 199 border: 1px solid #f99; /* #006dba; */ 200 margin-bottom: 10px; 201 margin-top: 10px; 202 203 background: #ffe0e0 url(../plugins/famfamfam-1.0.1/images/icons/error.png) 10px 10px no-repeat; 204 padding: 10px 10px 10px 33px; 205 206 } 207 198 208 /** END :: content **/ 199 209 /** START :: footer **/
Note: See TracChangeset
for help on using the changeset viewer.