Changeset 1501

Show
Ignore:
Timestamp:
07-02-11 16:07:54 (3 years ago)
Author:
robert@…
Message:

- Number of seconds for the rest controller to keep data in cache is now a configuration option
- After searching, it is possible to choose which action to perform on the search results.

Location:
trunk
Files:
4 added
22 modified

Legend:

Unmodified
Added
Removed
  • trunk/grails-app/conf/config-ci.properties

    r1459 r1501  
    3636modules.metagenomics.url=http://ci.metagenomics.nmcdsp.org 
    3737 
     38# Number of seconds to keep rest results from modules in cache 
     39modules.cacheDuration = 600 
     40 
    3841# default application users 
    3942authentication.users.admin.username=admin 
  • trunk/grails-app/conf/config-dbnpdemo.properties

    r1459 r1501  
    3636modules.metagenomics.url=http://demo.metagenomics.dbnp.org 
    3737 
     38# Number of seconds to keep rest results from modules in cache 
     39modules.cacheDuration = 600 
     40 
    3841# default application users 
    3942authentication.users.admin.username=admin 
  • trunk/grails-app/conf/config-dbnptest.properties

    r1459 r1501  
    3636modules.metagenomics.url=http://test.metagenomics.dbnp.org 
    3737 
     38# Number of seconds to keep rest results from modules in cache 
     39modules.cacheDuration = 600 
     40 
    3841# default application users 
    3942authentication.users.admin.username=admin 
  • trunk/grails-app/conf/config-development.properties

    r1459 r1501  
    3535modules.metagenomics.url=http://localhost:8184/metagenomics 
    3636 
     37# Number of seconds to keep rest results from modules in cache 
     38modules.cacheDuration = 0 
     39 
    3740# default application users 
    3841authentication.users.admin.username=admin 
  • trunk/grails-app/conf/config-nmcdsptest.properties

    r1459 r1501  
    3636modules.metagenomics.url=http://test.metagenomics.nmcdsp.org 
    3737 
     38# Number of seconds to keep rest results from modules in cache 
     39modules.cacheDuration = 600 
     40 
    3841# default application users 
    3942authentication.users.admin.username=admin 
  • trunk/grails-app/conf/config-production.properties

    r1459 r1501  
    3636modules.metagenomics.url=http://metagenomics.nmcdsp.org 
    3737 
     38# Number of seconds to keep rest results from modules in cache 
     39modules.cacheDuration = 600 
     40 
    3841# default application users 
    3942authentication.users.admin.username=admin 
  • trunk/grails-app/conf/config-test.properties

    r1459 r1501  
    3535modules.metagenomics.url=http://localhost:8184/metagenomics 
    3636 
     37# Number of seconds to keep rest results from modules in cache 
     38modules.cacheDuration = 600 
     39 
    3740# default application users 
    3841authentication.users.admin.username=admin 
  • trunk/grails-app/conf/config-www.properties

    r1459 r1501  
    3636modules.metagenomics.url=http://metagenomics.nmcdsp.org 
    3737 
     38# Number of seconds to keep rest results from modules in cache 
     39modules.cacheDuration = 600 
     40 
    3841# default application users 
    3942authentication.users.admin.username=admin 
  • trunk/grails-app/controllers/dbnp/query/AdvancedQueryController.groovy

    r1482 r1501  
    11package dbnp.query 
     2 
    23import dbnp.modules.* 
    34import org.dbnp.gdt.* 
     
    1516 
    1617        def entitiesToSearchFor = [ 'Study': 'Studies', 'Sample': 'Samples'] 
    17          
     18 
    1819        /** 
    1920         * Shows search screen 
     
    2526                        criteria = parseCriteria( params.criteria, false ) 
    2627                } 
    27                 [entitiesToSearchFor: entitiesToSearchFor, searchableFields: getSearchableFields(), criteria: criteria] 
     28                [searchModes: SearchMode.values(), entitiesToSearchFor: entitiesToSearchFor, searchableFields: getSearchableFields(), criteria: criteria] 
    2829        } 
    2930 
     
    4748 
    4849                // Create a search object and let it do the searching 
    49                 Search search; 
     50                Search search = determineSearch( params.entity ); 
    5051                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 
    5963                search.execute( parseCriteria( params.criteria ) ); 
    6064 
    6165                // Save search in session 
    6266                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)] ); 
    6468        } 
    6569 
     
    6973         */ 
    7074        def discard = { 
    71                 Integer queryId 
    72                 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 ) { 
    7579                        flash.error = "Incorrect search ID given to discard" 
    7680                        redirect( action: "index" ); 
     
    7882                } 
    7983 
    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                } 
    8293                redirect( action: "list" ); 
    8394        } 
     
    8899         */ 
    89100        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 ) { 
    94104                        flash.error = "Incorrect search ID given to show" 
    95105                        redirect( action: "index" ); 
     
    107117                // Determine which view to show 
    108118                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         
    112169        /** 
    113170         * Shows a list of searches that have been saved in session 
     
    124181                [searches: searches] 
    125182        } 
    126          
     183 
    127184        /** 
    128185         * Shows a search screen where the user can search within the results of another search 
     
    130187         */ 
    131188        def searchIn = { 
    132                 Integer queryId 
    133                 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 ) { 
    136193                        flash.error = "Incorrect search ID given to show" 
    137194                        redirect( action: "index" ); 
     
    139196                } 
    140197 
    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" 
    145225                        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" ); 
    146251                        return; 
    147252                } 
    148253 
    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 ); 
    150276        } 
    151277 
     
    155281                        case "Sample":  return "sampleresults"; break; 
    156282                        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 ); 
    157297                } 
    158298        } 
     
    230370                        if( c.key ==~ /[0-9]+/ ) { 
    231371                                def formCriterion = c.value; 
    232                                  
     372 
    233373                                Criterion criterion = new Criterion(); 
    234374 
     
    251391                                        continue; 
    252392                                } 
    253                                  
     393 
    254394                                // Special case of the 'in' operator 
    255395                                if( criterion.operator == Operator.insearch ) { 
     
    258398                                                s = retrieveSearch( Integer.parseInt( formCriterion.value ) ); 
    259399                                        } catch( Exception e ) {} 
    260                                          
     400 
    261401                                        if( !s ) { 
    262402                                                flash.error += "Can't search within previous query: query not found"; 
    263403                                                continue; 
    264404                                        } 
    265                                          
     405 
    266406                                        if( parseSearchIds ) { 
    267407                                                criterion.value = s 
     
    273413                                        criterion.value = formCriterion.value; 
    274414                                } 
    275                                  
     415 
    276416                                list << criterion; 
    277417                        } 
     
    332472                // First check whether a search with the same criteria is already present 
    333473                def previousSearch = retrieveSearchByCriteria( s.getCriteria() ); 
    334                  
     474 
    335475                def id 
    336476                if( previousSearch ) { 
     
    340480                        id = ( session.queries*.key.max() ?: 0 ) + 1; 
    341481                } 
    342                  
     482 
    343483                s.id = id; 
    344484                session.queries[ id ] = s; 
    345485 
    346                 println "On saveSearch: " + session.queries; 
    347486                return id; 
    348487        } 
     
    356495                if( !session.queries ) 
    357496                        return null 
    358                  
     497 
    359498                if( !criteria ) 
    360499                        return null 
    361                          
     500 
    362501                for( query in session.queries ) { 
    363502                        def key = query.key; 
     
    385524                        return null; 
    386525 
    387                 println "On retrieveSearch: " + session.queries; 
    388526                return (Search) session.queries[ id ] 
    389527        } 
     
    402540                session.queries.remove( id ); 
    403541 
    404                 println "On discardSearch: " + session.queries; 
    405542                if( !( sessionSearch instanceof Search ) ) 
    406543                        return null; 
     
    419556                return session.queries*.value.toList() 
    420557        } 
     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 
    421635} 
  • trunk/grails-app/services/dbnp/modules/ModuleCommunicationService.groovy

    r1482 r1501  
    1818import grails.converters.* 
    1919import javax.servlet.http.HttpServletResponse 
     20import org.codehaus.groovy.grails.commons.ConfigurationHolder 
    2021 
    2122class ModuleCommunicationService implements Serializable { 
     
    3334         * Number of seconds to save the data in cache 
    3435         */ 
    35         def numberOfSecondsInCache = 10 * 60; 
     36        def numberOfSecondsInCache = Integer.valueOf( ConfigurationHolder.config.modules.cacheDuration ) 
    3637 
    3738        /** 
     
    147148                def user = authenticationService.getLoggedInUser(); 
    148149                def userId = user ? user.id : -1; 
    149  
     150                 
     151                println "Retrieve from cache: " + url  
     152                println "Seconds in cache: " + numberOfSecondsInCache 
     153                 
    150154                if( cache[ userId ] && cache[ userId ][ url ] && ( System.currentTimeMillis() - cache[ userId ][ url ][ "timestamp" ] ) < numberOfSecondsInCache * 1000 ) { 
    151155                        return cache[ userId ][ url ]; 
  • trunk/grails-app/views/advancedQuery/_criteria.gsp

    r1482 r1501  
     1<%@ page import="dbnp.query.*" %> 
    12<ul id="criteria"> 
    2         <g:each in="${criteria}" var="criterion"> 
     3        <g:each in="${criteria}" var="criterion" status="j"> 
    34                <li> 
    45                        <span class="entityfield">${criterion.entityField()}</span> 
    56                        <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> 
    719                </li> 
    820        </g:each> 
  • trunk/grails-app/views/advancedQuery/index.gsp

    r1487 r1501  
    1414                                        <g:if test="${j > 0}">,</g:if> 
    1515                                        { 
     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()}", 
    1618                                                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()}", 
    1919                                                entity: "${entity.key.toString().encodeAsJavaScript()}" 
    2020                                        } 
     
    4949        </div> 
    5050</g:if> 
    51  
    52 <a href="<g:createLink action="list" />">View previous queries</a> 
    5351 
    5452<form id="input_criteria"> 
     
    9189        <g:form action="search" method="get"> 
    9290                <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 /> 
    9392                <label for="criteria">Criteria</label> 
    9493                <ul id="criteria"> 
     
    9998        </g:form> 
    10099</div> 
    101  
     100<p class="options"> 
     101        <g:link class="listPrevious" action="list">Previous searches</g:link> 
     102</p> 
    102103<br  clear="all" /> 
    103104</body> 
  • trunk/grails-app/views/advancedQuery/list.gsp

    r1482 r1501  
    66        <title>Previous queries</title> 
    77        <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         
    821</head> 
    922<body> 
     
    1124<h1>Previous queries</h1> 
    1225 
     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 
    1337<g:if test="${searches.size() > 0}"> 
    14         <table id="searchresults"> 
     38        <form id="searchform" method="post"> 
     39        <table id="searchresults" class="paginate"> 
    1540                <thead> 
    1641                        <tr> 
    17                                 <th></th> 
     42                                <th class="nonsortable"></th> 
    1843                                <th>#</th> 
    1944                                <th>Type</th> 
     
    2146                                <th># results</th> 
    2247                                <th>time</th> 
    23                                 <th></th> 
    24                                 <th></th> 
     48                                <th class="nonsortable"></th> 
     49                                <th class="nonsortable"></th> 
    2550                        </tr> 
    2651                </thead> 
    2752                <g:each in="${searches}" var="search"> 
    2853                        <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> 
    3055                                <td>${search.id}</td> 
    3156                                <td>${search.entity}</td> 
     
    5277                </g:each> 
    5378        </table> 
     79        </form>  
    5480</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 /> 
    5787</p> 
     88 
    5889</body> 
    5990</html> 
  • 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> 
    66</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/results.gsp

    r1482 r1501  
    55        <title>Query results</title> 
    66        <link rel="stylesheet" href="<g:resource dir="css" file="advancedQuery.css" />" type="text/css"/> 
     7        <g:javascript src="advancedQueryResults.js" /> 
    78</head> 
    89<body> 
     
    1314        Your search for: 
    1415</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()]" /> 
    2417<p>  
    2518        resulted in ${search.getNumResults()} results. 
     
    3124                def extraFields = resultFields[ search.getResults()[ 0 ].id ]?.keySet(); 
    3225        %> 
    33         <table id="searchresults"> 
     26        <table id="searchresults" class="paginate"> 
    3427                <thead> 
    3528                        <tr> 
     29                                <th class="nonsortable"></th> 
    3630                                <th>Type</th> 
    3731                                <th>Id</th> 
     
    4337                <g:each in="${search.getResults()}" var="result"> 
    4438                        <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>                    
    4547                                <td>${search.entity}</td> 
    4648                                <td>${result.id}</td> 
     
    5052                                                        def fieldValue = resultFields[ result.id ]?.get( fieldName ); 
    5153                                                        if( fieldValue ) {  
    52                                                                 if( fieldValue instanceof Collection ) 
     54                                                                if( fieldValue instanceof Collection ) { 
    5355                                                                        fieldValue = fieldValue.collect { it.toString() }.findAll { it }.join( ', ' ); 
    54                                                                 else 
     56                                                                } else { 
    5557                                                                        fieldValue = fieldValue.toString(); 
     58                                                                } 
     59                                                        } else { 
     60                                                                fieldValue = ""; 
    5661                                                        } 
    5762                                                %> 
  • trunk/grails-app/views/advancedQuery/sampleresults.gsp

    r1482 r1501  
    55        <title>Query results</title> 
    66        <link rel="stylesheet" href="<g:resource dir="css" file="advancedQuery.css" />" type="text/css"/> 
     7        <g:javascript src="advancedQueryResults.js" /> 
    78</head> 
    89<body> 
     
    2627                <thead> 
    2728                <tr> 
     29                        <th class="nonsortable"></th>                    
     30                        <th>Name</th> 
    2831                        <th>Study</th> 
    29                         <th>Name</th> 
    3032                        <g:each in="${extraFields}" var="fieldName"> 
    3133                                <th>${fieldName}</th> 
     
    3638                <g:each in="${search.getResults()}" var="sampleInstance" status="i"> 
    3739                        <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> 
    3849                                <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> 
    4050                                <g:each in="${extraFields}" var="fieldName"> 
    4151                                        <td> 
     
    4353                                                        def fieldValue = resultFields[ sampleInstance.id ]?.get( fieldName ); 
    4454                                                        if( fieldValue ) {  
    45                                                                 if( fieldValue instanceof Collection ) 
     55                                                                if( fieldValue instanceof Collection ) { 
    4656                                                                        fieldValue = fieldValue.collect { it.toString() }.findAll { it }.join( ', ' ); 
    47                                                                 else 
     57                                                                } else { 
    4858                                                                        fieldValue = fieldValue.toString(); 
     59                                                                } 
     60                                                        } else { 
     61                                                                fieldValue = ""; 
    4962                                                        } 
    5063                                                %> 
     
    5669                </tbody> 
    5770        </table> 
     71        <g:render template="resultsform" /> 
    5872 
    5973</g:if> 
  • trunk/grails-app/views/advancedQuery/studyresults.gsp

    r1482 r1501  
    55        <title>Query results</title> 
    66        <link rel="stylesheet" href="<g:resource dir="css" file="advancedQuery.css" />" type="text/css"/> 
     7        <g:javascript src="advancedQueryResults.js" /> 
    78</head> 
    89<body> 
     
    2223                def extraFields = resultFields[ search.getResults()[ 0 ].id ]?.keySet(); 
    2324        %> 
    24  
    2525        <table id="searchresults" class="paginate"> 
    2626                <thead> 
    2727                <tr> 
    28                         <th colspan="2"></th> 
     28                        <th class="nonsortable"></th> 
     29                        <th>Title</th> 
    2930                        <th>Code</th> 
    30                         <th>Title</th> 
    3131                        <th>Subjects</th> 
    3232                        <th>Events</th> 
     
    4040                <g:each in="${search.getResults()}" var="studyInstance" status="i"> 
    4141                        <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> 
    4554                                <td>${fieldValue(bean: studyInstance, field: "code")}</td> 
    46                                 <td> 
    47                                         ${fieldValue(bean: studyInstance, field: "title")} 
    48                                 </td> 
    4955                                <td> 
    5056                                        <g:if test="${studyInstance.subjects.species.size()==0}"> 
     
    8288                                                        def fieldValue = resultFields[ studyInstance.id ]?.get( fieldName ); 
    8389                                                        if( fieldValue ) {  
    84                                                                 if( fieldValue instanceof Collection ) 
     90                                                                if( fieldValue instanceof Collection ) { 
    8591                                                                        fieldValue = fieldValue.collect { it.toString() }.findAll { it }.join( ', ' ); 
    86                                                                 else 
     92                                                                } else { 
    8793                                                                        fieldValue = fieldValue.toString(); 
     94                                                                } 
     95                                                        } else { 
     96                                                                fieldValue = ""; 
    8897                                                        } 
    8998                                                %> 
     
    95104                </tbody> 
    96105        </table> 
     106        <g:render template="resultsform" /> 
    97107 
    98108</g:if> 
  • trunk/grails-app/views/layouts/main.gsp

    r1434 r1501  
    2121        <script type="text/javascript" src="${resource(dir: 'js', file: 'topnav.js')}"></script> 
    2222        <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 
    2329</head> 
    2430<body> 
  • trunk/src/groovy/dbnp/query/SampleSearch.groovy

    r1482 r1501  
    6262         */ 
    6363        @Override 
    64         void execute() { 
    65                 super.execute(); 
    66  
     64        void executeAnd() { 
    6765                // If no criteria are found, return all samples 
    6866                if( !criteria || criteria.size() == 0 ) { 
     
    8078                        def studies = Study.findAll().findAll { it.canRead( this.user ) }; 
    8179 
    82                         studies = filterOnStudyCriteria( studies ); 
     80                        studies = filterStudiesOnStudyCriteria( studies ); 
    8381 
    8482                        if( studies.size() == 0 ) { 
     
    116114 
    117115        /** 
    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 ) { 
    123181                return filterOnTemplateEntityCriteria(studies, "Study", { study, criterion -> return criterion.getFieldValue( study ) }) 
    124182        } 
     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 
    125195 
    126196        /** 
     
    180250                        return []; 
    181251 
    182                 if( getEntityCriteria( 'Assay' ).size() == 0 ) 
    183                         return samples 
    184  
    185252                // There is no sample.assays property, so we have to look for assays another way: just find 
    186253                // all assays that match the criteria 
    187254                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 
    188263                def assays = filterEntityList( Assay.list(), criteria, { assay, criterion -> 
    189264                        if( !assay ) 
    190265                                return false 
    191266 
    192                         return criterion.matchOne( assay ); 
     267                        return criterion.matchEntity( assay ); 
    193268                }); 
    194269 
     
    197272                        return []; 
    198273 
    199                 // Save sample data for later use 
    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 
    205                                 return null 
    206                 }); 
    207  
    208274                // Now filter the samples on whether they are attached to the filtered assays 
    209                 return samples.findAll { sample -> 
     275                def matchingSamples = samples.findAll { sample -> 
    210276                        if( !sample.parent ) 
    211277                                return false; 
     
    222288                        return false; 
    223289                } 
     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; 
    224304        } 
    225305 
  • trunk/src/groovy/dbnp/query/Search.groovy

    r1487 r1501  
    3030import org.dbnp.gdt.* 
    3131 
     32/** 
     33* Available boolean operators for searches 
     34* @author robert 
     35* 
     36*/ 
     37enum SearchMode { 
     38   and, or 
     39} 
     40 
    3241class Search { 
    33         public String entity; 
    3442        public SecUser user; 
    3543        public Date executionDate; 
    3644        public int id;  // Is only used when this query is saved in session 
    3745 
     46        public String entity; 
     47        public SearchMode searchMode = SearchMode.and 
     48         
    3849        protected List criteria; 
    3950        protected List results; 
     
    4253        public List getCriteria() { return criteria; } 
    4354        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        } 
    4461 
    4562        public List getResults() { return results; } 
    4663        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         
    4773         
    4874        public Map getResultFields() { return resultFields; } 
     
    83109 
    84110        /** 
    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.  
    87112         */ 
    88113        public void execute() { 
    89114                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   } 
    91141 
    92142        /** 
     
    112162        protected List filterEntityList( List entities, List<Criterion> criteria, Closure check ) { 
    113163                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                 
    117170                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; 
    122185                        } 
    123                         return true; 
    124186                } 
    125187        } 
     
    133195         */ 
    134196        public static def prepare( def value, TemplateFieldType type ) { 
     197                if( value == null ) 
     198                        return value 
     199                         
    135200                switch (type) { 
    136201                        case TemplateFieldType.DATE: 
     
    185250                } 
    186251 
    187         } 
    188          
     252        }        
    189253         
    190254        /** 
     
    201265                        def value = valueCallback( study, criterion ); 
    202266                         
    203                         if( value == null ) 
     267                        if( value == null ) { 
    204268                                return false 
     269                        } 
     270 
    205271                        if( value instanceof Collection ) { 
    206272                                return criterion.matchAny( value ) 
     
    230296                def ctx = ApplicationHolder.getApplication().getMainContext(); 
    231297                def moduleCommunicationService = ctx.getBean("moduleCommunicationService"); 
    232                          
     298                 
     299                def allEntities = []  
     300                if( searchMode == SearchMode.or ) { 
     301                        allEntities += entities; 
     302                        entities = []; 
     303                }  
     304                 
    233305                // Loop through all modules and check whether criteria have been given 
    234306                // for that module 
     
    250322                                        def json = moduleCommunicationService.callModuleRestMethodJSON( module.url, callUrl ); 
    251323 
    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 -> 
    254325                                                // Find the value of the field in this sample. That value is still in the 
    255326                                                // JSON object 
     
    268339                                                } 
    269340                                                 
    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 ->  
    273343                                                        val = val.toString(); 
    274344                                                        if( val.isLong() ) { 
     
    277347                                                                val = Double.parseDouble( val ); 
    278348                                                        } 
    279                                                          
     349                                                        return val; 
     350                                                } 
     351 
     352                                                // Loop through all values and match any 
     353                                                for( val in values ) { 
    280354                                                        if( criterion.match( val ) ) 
    281355                                                                return true; 
     
    283357                                                 
    284358                                                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                                        }  
    286368                                                                                 
    287369                                } catch( Exception e ) { 
     
    321403                        resultFields[ id ] = [:] 
    322404                 
     405                // Handle special cases 
     406                if( value == null ) 
     407                        value = ""; 
     408                 
     409                if( value instanceof Collection ) { 
     410                        value = value.findAll { it != null } 
     411                } 
     412                 
    323413                resultFields[ id ][ fieldName ] = value; 
    324414        } 
  • trunk/src/groovy/dbnp/query/StudySearch.groovy

    r1487 r1501  
    2424class StudySearch extends Search { 
    2525        private static final log = LogFactory.getLog(this); 
    26          
     26 
    2727        public StudySearch() { 
    2828                super(); 
     
    6363         */ 
    6464        @Override 
    65         void execute() { 
    66                 super.execute(); 
    67  
     65        void executeAnd() { 
    6866                def studies = Study.list().findAll { it.canRead( this.user ) }; 
    6967 
     
    8179                studies = filterOnSamplingEventCriteria( studies ); 
    8280                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(); 
    83138                 
    84                 studies = filterOnModuleCriteria( studies ); 
     139                studies = ( studies + filterOnModuleCriteria( allStudies - studies ) ).unique(); 
    85140                 
    86141                // Save matches 
  • trunk/web-app/css/advancedQuery.css

    r1482 r1501  
    2323 
    2424.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}