Changeset 1524

Show
Ignore:
Timestamp:
15-02-11 15:05:23 (3 years ago)
Author:
robert@…
Message:

Refactored some of the querying stuff and built in 'check all' checkboxes

Location:
trunk
Files:
10 modified

Legend:

Unmodified
Added
Removed
  • trunk/grails-app/controllers/dbnp/query/AdvancedQueryController.groovy

    r1512 r1524  
    153153                } 
    154154 
    155                 // Determine the possible actions 
     155                // Determine the possible actions and build correct urls 
    156156                def actions = determineActions(s, selectedIds ); 
    157157 
     
    478478 
    479479                // First check whether a search with the same criteria is already present 
    480                 def previousSearch = retrieveSearchByCriteria( s.getCriteria() ); 
     480                def previousSearch = retrieveSearchByCriteria( s.getCriteria(), s.searchMode ); 
    481481 
    482482                def id 
     
    499499         * @return                      Search that has this criteria, or null if no such search is found. 
    500500         */ 
    501         protected Search retrieveSearchByCriteria( List criteria ) { 
     501        protected Search retrieveSearchByCriteria( List criteria, SearchMode searchMode = SearchMode.and ) { 
    502502                if( !session.queries ) 
    503503                        return null 
     
    510510                        def value = query.value; 
    511511 
    512                         if( value.criteria && value.criteria.containsAll( criteria ) && criteria.containsAll( value.criteria ) ) { 
     512                        if( value.searchMode == searchMode && value.criteria && value.criteria.containsAll( criteria ) && criteria.containsAll( value.criteria ) ) { 
    513513                                return value; 
    514514                        } 
  • trunk/grails-app/views/advancedQuery/list.gsp

    r1512 r1524  
    4040                <thead> 
    4141                        <tr> 
    42                                 <th class="nonsortable"></th> 
     42                                <th class="nonsortable"><input type="checkbox" id="checkAll" onClick="checkAllPaginated(this);" /></th> 
    4343                                <th>#</th> 
    4444                                <th>Type</th> 
     
    5252                <g:each in="${searches}" var="search"> 
    5353                        <tr> 
    54                                 <td><g:checkBox name="id" value="${search.id}" checked="${false}" /></td> 
     54                                <td><g:checkBox name="id" value="${search.id}" checked="${false}" onClick="updateCheckAll(this);" /></td> 
    5555                                <td>${search.id}</td> 
    5656                                <td>${search.entity}</td> 
  • trunk/grails-app/views/advancedQuery/results.gsp

    r1512 r1524  
    1818        <%  
    1919                def resultFields = search.getShowableResultFields(); 
    20                 def extraFields = resultFields[ search.getResults()[ 0 ].id ]?.keySet(); 
     20                def extraFields = search.getShowableResultFieldNames(resultFields); 
    2121        %> 
    2222        <table id="searchresults" class="paginate"> 
    2323                <thead> 
    2424                        <tr> 
    25                                 <th class="nonsortable"></th> 
     25                                <th class="nonsortable"><input type="checkbox" id="checkAll" onClick="checkAllPaginated(this);" /></th> 
    2626                                <th>Type</th> 
    2727                                <th>Id</th> 
     
    3939                                                also http://datatables.net/examples/api/form.html and advancedQueryResults.js 
    4040                                        */ %> 
    41                                         <g:checkBox name="id" value="${result.id}" checked="${false}" /> 
     41                                        <g:checkBox name="id" value="${result.id}" checked="${false}" onClick="updateCheckAll(this);" /> 
    4242                                </td>                    
    4343                                <td>${search.entity}</td> 
  • trunk/grails-app/views/advancedQuery/sampleresults.gsp

    r1512 r1524  
    1818        <%  
    1919                def resultFields = search.getShowableResultFields(); 
    20                 def extraFields = resultFields[ search.getResults()[ 0 ].id ]?.keySet(); 
     20                def extraFields = search.getShowableResultFieldNames(resultFields); 
    2121        %> 
    2222        <table id="searchresults" class="paginate"> 
    2323                <thead> 
    2424                <tr> 
    25                         <th class="nonsortable"></th>                    
     25                        <th class="nonsortable"><input type="checkbox" id="checkAll" onClick="checkAllPaginated(this);" /></th>                  
    2626                        <th>Name</th> 
    2727                        <th>Study</th> 
     
    4040                                                also http://datatables.net/examples/api/form.html and advancedQueryResults.js 
    4141                                        */ %> 
    42                                         <g:checkBox name="id" value="${sampleInstance.id}" checked="${false}" /> 
     42                                        <g:checkBox name="id" value="${sampleInstance.id}" checked="${false}" onClick="updateCheckAll(this);" /> 
    4343                                </td> 
    4444                                <td>${fieldValue(bean: sampleInstance, field: "name")}</td> 
  • trunk/grails-app/views/advancedQuery/studyresults.gsp

    r1512 r1524  
    1818        <%  
    1919                def resultFields = search.getShowableResultFields(); 
    20                 def extraFields = resultFields[ search.getResults()[ 0 ].id ]?.keySet(); 
     20                def extraFields = search.getShowableResultFieldNames(resultFields); 
    2121        %> 
    2222        <table id="searchresults" class="paginate"> 
    2323                <thead> 
    2424                <tr> 
    25                         <th class="nonsortable"></th> 
     25                        <th class="nonsortable"><input type="checkbox" id="checkAll" onClick="checkAllPaginated(this);" /></th> 
    2626                        <th>Title</th> 
    2727                        <th>Code</th> 
     
    4343                                                also http://datatables.net/examples/api/form.html and advancedQueryResults.js 
    4444                                        */ %> 
    45                                         <g:checkBox name="id" value="${studyInstance.id}" checked="${false}" /> 
     45                                        <g:checkBox name="id" value="${studyInstance.id}" checked="${false}" onClick="updateCheckAll(this);" /> 
    4646                                </td> 
    4747                                <td> 
  • trunk/src/groovy/dbnp/query/SampleSearch.groovy

    r1521 r1524  
    1515package dbnp.query 
    1616 
     17import groovy.lang.Closure; 
     18 
    1719import java.util.Map; 
    1820 
     
    2325class SampleSearch extends Search { 
    2426        private static final log = LogFactory.getLog(this); 
    25          
     27 
    2628        public SampleSearch() { 
    2729                super(); 
    28                                  
     30 
    2931                this.entity = "Sample"; 
    3032        } 
     
    118120 
    119121        /** 
    120         * Searches for samples based on the given criteria. Only one of the criteria have to be satisfied and 
    121         * criteria for the different entities are satisfied as follows: 
    122         * 
    123         *               Sample.title = 'abc' 
    124         *                               Samples are returned from studies with title 'abc' 
    125         * 
    126         *               Subject.species = 'human' 
    127         *                               Samples are returned from subjects with species = 'human' 
    128         * 
    129         *               Sample.name = 'sample 1' 
    130         *                               Samples are returned with name = 'sample 1' 
    131         * 
    132         *               Event.startTime = '0s' 
    133         *                               Samples are returned from subjects that have had an event with start time = '0s' 
    134         * 
    135         *               SamplingEvent.startTime = '0s' 
    136         *                               Samples are returned that have originated from a sampling event with start time = '0s' 
    137         * 
    138         *               Assay.module = 'metagenomics' 
    139         *                               Samples are returned that have been processed in an assay with module = metagenomics 
    140         * 
    141         * When searching for more than one criterion per entity, these are taken separately. Searching for 
    142         * 
    143         *               Subject.species = 'human' 
    144         *               Subject.name = 'Jan' 
    145         * 
    146         *  will result in all samples from a human subject or a subject named 'Jan'. Samples from a mouse subject 
    147         *  named 'Jan' or a human subject named 'Kees' will also satisfy the criteria. 
    148         * 
    149         */ 
    150    @Override 
    151    void executeOr() { 
    152            // We expect the sample criteria to be the most discriminative, and discard 
    153            // the most samples. (e.g. by searching on sample title of sample type). For 
    154            // that reason we first look through the list of studies. However, when the 
    155            // user didn't enter any sample criteria, this will be an extra step, but doesn't 
    156            // cost much time to process. 
    157            def samples = [] 
    158            def allSamples = Sample.list().findAll { it.parent?.canRead( this.user ) }.toList(); 
    159  
    160            // If no criteria are found, return all samples 
    161            if( !criteria || criteria.size() == 0 ) { 
    162                    results = allSamples 
    163                    return; 
    164            } 
    165             
    166            samples = ( samples + filterSamplesOnStudyCriteria( allSamples - samples ) ).unique(); 
    167            samples = ( samples + filterOnSubjectCriteria( allSamples - samples ) ).unique(); 
    168            samples = ( samples + filterOnSampleCriteria( allSamples - samples ) ).unique(); 
    169            samples = ( samples + filterOnEventCriteria( allSamples - samples ) ).unique(); 
    170            samples = ( samples + filterOnSamplingEventCriteria( allSamples - samples ) ).unique(); 
    171            samples = ( samples + filterOnAssayCriteria( allSamples - samples ) ).unique(); 
    172             
    173            samples = ( samples + filterOnModuleCriteria( allSamples - samples ) ).unique(); 
    174             
    175            // Save matches 
    176            results = samples; 
    177    } 
    178          
     122         * Searches for samples based on the given criteria. Only one of the criteria have to be satisfied and 
     123         * criteria for the different entities are satisfied as follows: 
     124         * 
     125         *              Sample.title = 'abc' 
     126         *                              Samples are returned from studies with title 'abc' 
     127         * 
     128         *              Subject.species = 'human' 
     129         *                              Samples are returned from subjects with species = 'human' 
     130         * 
     131         *              Sample.name = 'sample 1' 
     132         *                              Samples are returned with name = 'sample 1' 
     133         * 
     134         *              Event.startTime = '0s' 
     135         *                              Samples are returned from subjects that have had an event with start time = '0s' 
     136         * 
     137         *              SamplingEvent.startTime = '0s' 
     138         *                              Samples are returned that have originated from a sampling event with start time = '0s' 
     139         * 
     140         *              Assay.module = 'metagenomics' 
     141         *                              Samples are returned that have been processed in an assay with module = metagenomics 
     142         * 
     143         * When searching for more than one criterion per entity, these are taken separately. Searching for 
     144         * 
     145         *              Subject.species = 'human' 
     146         *              Subject.name = 'Jan' 
     147         * 
     148         *  will result in all samples from a human subject or a subject named 'Jan'. Samples from a mouse subject 
     149         *  named 'Jan' or a human subject named 'Kees' will also satisfy the criteria. 
     150         * 
     151         */ 
     152        @Override 
     153        void executeOr() { 
     154                // We expect the sample criteria to be the most discriminative, and discard 
     155                // the most samples. (e.g. by searching on sample title of sample type). For 
     156                // that reason we first look through the list of studies. However, when the 
     157                // user didn't enter any sample criteria, this will be an extra step, but doesn't 
     158                // cost much time to process. 
     159                def samples = [] 
     160                def allSamples = Sample.list().findAll { it.parent?.canRead( this.user ) }.toList(); 
     161 
     162                // If no criteria are found, return all samples 
     163                if( !criteria || criteria.size() == 0 ) { 
     164                        results = allSamples 
     165                        return; 
     166                } 
     167 
     168                samples = ( samples + filterOnStudyCriteria( allSamples - samples ) ).unique(); 
     169                samples = ( samples + filterOnSubjectCriteria( allSamples - samples ) ).unique(); 
     170                samples = ( samples + filterOnSampleCriteria( allSamples - samples ) ).unique(); 
     171                samples = ( samples + filterOnEventCriteria( allSamples - samples ) ).unique(); 
     172                samples = ( samples + filterOnSamplingEventCriteria( allSamples - samples ) ).unique(); 
     173                samples = ( samples + filterOnAssayCriteria( allSamples - samples ) ).unique(); 
     174 
     175                samples = ( samples + filterOnModuleCriteria( allSamples - samples ) ).unique(); 
     176 
     177                // Save matches 
     178                results = samples; 
     179        } 
     180 
     181        /** 
     182         * Returns a closure for the given entitytype that determines the value for a criterion 
     183         * on the given object. The closure receives two parameters: the object and a criterion. 
     184         * 
     185         * For example: 
     186         *              For a study search, the object given is a study. How to determine the value for that study of 
     187         *              the criterion field of type sample? This is done by returning the field values for all 
     188         *              samples in the study 
     189         *                      { study, criterion -> return study.samples?.collect { criterion.getFieldValue( it ); } } 
     190         * @return 
     191         */ 
     192        protected Closure valueCallback( String entity ) { 
     193                switch( entity ) { 
     194                        case "Study": 
     195                                return { sample, criterion -> return criterion.getFieldValue( sample.parent ) } 
     196                        case "Subject": 
     197                                return { sample, criterion -> return criterion.getFieldValue( sample.parentSubject ); } 
     198                        case "Sample": 
     199                                return { sample, criterion -> return criterion.getFieldValue( sample ) } 
     200                        case "Event": 
     201                                return { sample, criterion -> 
     202                                        if( !sample || !sample.parentEventGroup || !sample.parentEventGroup.events || sample.parentEventGroup.events.size() == 0 ) 
     203                                                return null 
     204 
     205                                        return criterion.getFieldValue( sample.parentEventGroup.events.toList() ); 
     206                                } 
     207                        case "SamplingEvent": 
     208                                return { sample, criterion -> return criterion.getFieldValue( sample.parentEvent ); } 
     209                        case "Assay": 
     210                                return { sample, criterion -> 
     211                                        println "Find value for " + sample + " and " + criterion 
     212                                        def sampleAssays = Assay.findByParent( sample.parent ).findAll { it.samples?.contains( sample ) }; 
     213                                        if( sampleAssays && sampleAssays.size() > 0 ) 
     214                                                return sampleAssays.collect { criterion.getFieldValue( it ) } 
     215                                        else 
     216                                                return null 
     217                                } 
     218                        default: 
     219                                return super.valueCallback( entity ); 
     220                } 
     221        } 
     222 
    179223        /** 
    180224         * Filters the given list of studies on the study criteria 
     
    185229                return filterOnTemplateEntityCriteria(studies, "Study", { study, criterion -> return criterion.getFieldValue( study ) }) 
    186230        } 
    187          
    188         /** 
    189         * Filters the given list of samples on the sample criteria 
    190         * @param samples        Original list of samples 
    191         * @return                       List with all samples that match the Study-criteria 
    192         */ 
    193    protected List filterSamplesOnStudyCriteria( List samples ) { 
    194            return filterOnTemplateEntityCriteria(samples, "Study", { study, criterion ->  
    195                    return criterion.getFieldValue( study.parent )  
    196            }) 
    197    } 
    198  
    199  
    200         /** 
    201          * Filters the given list of samples on the subject criteria 
    202          * @param samples       Original list of samples 
    203          * @return                      List with all samples that match the Subject-criteria 
    204          */ 
    205         protected List filterOnSubjectCriteria( List samples ) { 
    206                 return filterOnTemplateEntityCriteria(samples, "Subject", { sample, criterion -> 
    207                         return criterion.getFieldValue( sample.parentSubject ); 
    208                 }) 
    209         } 
    210  
    211         /** 
    212          * Filters the given list of samples on the sample criteria 
    213          * @param samples       Original list of samples 
    214          * @return                      List with all samples that match the sample-criteria 
    215          */ 
    216         protected List filterOnSampleCriteria( List samples ) { 
    217                 return filterOnTemplateEntityCriteria(samples, "Sample", { sample, criterion -> 
    218                         return criterion.getFieldValue( sample ); 
    219                 }) 
    220         } 
    221  
    222         /** 
    223          * Filters the given list of samples on the event criteria 
    224          * @param samples       Original list of samples 
    225          * @return                      List with all samples that match the event-criteria 
    226          */ 
    227         protected List filterOnEventCriteria( List samples ) { 
    228                 return filterOnTemplateEntityCriteria(samples, "Event", { sample, criterion -> 
    229                         if( !sample || !sample.parentEventGroup || !sample.parentEventGroup.events || sample.parentEventGroup.events.size() == 0 ) 
    230                                 return null 
    231  
    232                         return criterion.getFieldValue( sample.parentEventGroup.events.toList() ); 
    233                 }) 
    234         } 
    235  
    236         /** 
    237          * Filters the given list of samples on the sampling event criteria 
    238          * @param samples       Original list of samples 
    239          * @return                      List with all samples that match the event-criteria 
    240          */ 
    241         protected List filterOnSamplingEventCriteria( List samples ) { 
    242                 return filterOnTemplateEntityCriteria(samples, "SamplingEvent", { sample, criterion -> 
    243                         return criterion.getFieldValue( sample.parentEvent ); 
    244                 }) 
    245         } 
    246231 
    247232        /** 
     
    250235         * @return                      List with all samples that match the assay-criteria 
    251236         */ 
     237        @Override 
    252238        protected List filterOnAssayCriteria( List samples ) { 
    253239                if( !samples?.size() ) 
     
    257243                // all assays that match the criteria 
    258244                def criteria = getEntityCriteria( 'Assay' ); 
    259                  
     245 
    260246                if( getEntityCriteria( 'Assay' ).size() == 0 ) { 
    261247                        if( searchMode == SearchMode.and ) 
     
    292278                        return false; 
    293279                } 
    294                  
    295                 // Save sample data for later use 
    296                 println samples 
    297                 println "Find values for " + matchingSamples + " and " + criteria 
    298                 saveResultFields( matchingSamples, criteria, { sample, criterion -> 
    299                         println "Find value for " + sample + " and " + criterion 
    300                         def sampleAssays = Assay.findByParent( sample.parent ).findAll { it.samples?.contains( sample ) }; 
    301                         if( sampleAssays && sampleAssays.size() > 0 ) 
    302                                 return sampleAssays.collect { criterion.getFieldValue( it ) } 
    303                         else 
    304                                 return null 
    305                 }); 
    306          
     280 
    307281                return matchingSamples; 
    308282        } 
  • trunk/src/groovy/dbnp/query/Search.groovy

    r1501 r1524  
    3131 
    3232/** 
    33 * Available boolean operators for searches 
    34 * @author robert 
    35 * 
    36 */ 
     33 * Available boolean operators for searches 
     34 * @author robert 
     35 * 
     36 */ 
    3737enum SearchMode { 
    38    and, or 
     38        and, or 
    3939} 
    4040 
    4141class Search { 
     42        /** 
     43         * User that is performing this search. This has impact on the search results returned. 
     44         */ 
    4245        public SecUser user; 
     46 
     47        /** 
     48         * Date of execution of this search 
     49         */ 
    4350        public Date executionDate; 
    44         public int id;  // Is only used when this query is saved in session 
    45  
     51 
     52        /** 
     53         * Public identifier of this search. Is only used when this query is saved in session 
     54         */ 
     55        public int id; 
     56 
     57        /** 
     58         * Human readable entity name of the entities that can be found using this search 
     59         */ 
    4660        public String entity; 
     61 
     62        /** 
     63         * Mode to search: OR or AND. 
     64         * @see SearchMode 
     65         */ 
    4766        public SearchMode searchMode = SearchMode.and 
    48          
     67 
    4968        protected List criteria; 
    5069        protected List results; 
    5170        protected Map resultFields = [:]; 
    5271 
     72        /** 
     73         * Returns a list of Criteria 
     74         */ 
    5375        public List getCriteria() { return criteria; } 
     76 
     77        /** 
     78         * Sets a new list of criteria 
     79         * @param c     List with criteria objects 
     80         */ 
    5481        public void setCriteria( List c ) { criteria = c; } 
     82 
     83        /** 
     84         * Adds a criterion to this query 
     85         * @param c     Criterion 
     86         */ 
    5587        public void addCriterion( Criterion c ) { 
    56                 if( criteria )  
     88                if( criteria ) 
    5789                        criteria << c; 
    5890                else 
     
    6092        } 
    6193 
     94        /** 
     95         * Retrieves the results found using this query. The result is empty is  
     96         * the query has not been executed yet. 
     97         */ 
    6298        public List getResults() { return results; } 
    63         public void setResults( List r ) { results = r; } 
     99 
     100        /** 
     101         * Returns the results found using this query, filtered by a list of ids. 
     102         * @param selectedIds   List with ids of the entities you want to return. 
     103         * @return      A list with only those results for which the id is in the selectedIds 
     104         */ 
    64105        public List filterResults( List selectedIds ) { 
    65106                if( !selectedIds || !results ) 
    66107                        return results 
    67                          
     108 
    68109                return results.findAll { 
    69110                        selectedIds.contains( it.id ) 
    70111                } 
    71112        } 
    72          
    73          
     113 
     114        /** 
     115         * Returns a list of fields for the results of this query. The fields returned are those 
     116         * fields that the query searched for.  
     117         */ 
    74118        public Map getResultFields() { return resultFields; } 
    75         public void setResultFields( Map r ) { resultFields = r; } 
    76  
     119 
     120        /** 
     121         * Constructor of this search object. Sets the user field to the  
     122         * currently logged in user 
     123         * @see #user 
     124         */ 
    77125        public Search() { 
    78126                def ctx = ApplicationHolder.getApplication().getMainContext(); 
     
    85133                        this.user = null 
    86134        } 
    87          
     135 
    88136        /** 
    89137         * Returns the number of results found by this search 
     
    113161        public void execute() { 
    114162                this.executionDate = new Date(); 
    115                  
     163 
    116164                switch( searchMode ) { 
    117165                        case SearchMode.and: 
     
    122170                                break; 
    123171                } 
    124         } 
    125          
     172 
     173                // Save the value of this results for later use 
     174                saveResultFields(); 
     175        } 
     176 
    126177        /** 
    127178         * Executes an inclusive (AND) search based on the given criteria. Should be filled in by 
     
    129180         */ 
    130181        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    } 
     182 
     183        } 
     184 
     185        /** 
     186         * Executes an exclusive (OR) search based on the given criteria. Should be filled in by 
     187         * subclasses searching for a specific entity 
     188         */ 
     189        public void executeOr() { 
     190 
     191        } 
     192 
     193        /************************************************************************ 
     194         *  
     195         * These methods are used in querying and should be overridden by subclasses 
     196         * in order to provide custom searching 
     197         *  
     198         */ 
     199 
     200        /** 
     201         * Returns a closure for the given entitytype that determines the value for a criterion 
     202         * on the given object. The closure receives two parameters: the object and a criterion. 
     203         *  
     204         * For example: when searching for studies, the object given to the closure is a Study.  
     205         * Also, when searching for samples, the object given is a Sample. When you have the criterion 
     206         * 
     207         *      sample.name equals 'sample 1' 
     208         *  
     209         * and searching for samples, it is easy to determine the value of the object for this criterion: 
     210         *       
     211         *      object.getFieldValue( "name" ) 
     212         *  
     213         *  
     214         * However, when searching for samples with the criterion 
     215         *  
     216         *      study.title contains 'nbic' 
     217         *  
     218         * this determination is more complex: 
     219         *  
     220         *      object.parent.getFieldValue( "title" ) 
     221         *  
     222         *  
     223         * The other way around, when searching for studies with  
     224         *  
     225         *      sample.name equals 'sample 1' 
     226         *  
     227         * the value of the 'sample.name' property is a list: 
     228         *  
     229         *      object.samples*.getFieldValue( "name" ) 
     230         *   
     231         * The other search methods will handle the list and see whether any of the values  
     232         * matches the criterion. 
     233         *  
     234         * NB. The Criterion object has a convenience method to retrieve the field value on a 
     235         * specific (TemplateEntity) object: getFieldValue. This method also handles  
     236         * non-existing fields and casts the value to the correct type. 
     237         * 
     238         * This method should be overridden by all searches 
     239         *  
     240         * @see Criterion.getFieldValue() 
     241         * 
     242         * @return      Closure having 2 parameters: object and criterion 
     243         */ 
     244        protected Closure valueCallback( String entity ) { 
     245                switch( entity ) { 
     246                        case "Study": 
     247                        case "Subject": 
     248                        case "Sample": 
     249                        case "Event": 
     250                        case "SamplingEvent": 
     251                        case "Assay": 
     252                                return { object, criterion -> return criterion.getFieldValue( object ); } 
     253                        default: 
     254                                return null; 
     255                } 
     256        } 
     257 
     258        /***************************************************** 
     259         *  
     260         * The other methods are helper functions for the execution of queries in subclasses 
     261         *  
     262         *****************************************************/ 
    141263 
    142264        /** 
     
    148270                return criteria?.findAll { it.entity == entity } 
    149271        } 
    150          
     272 
    151273        /** 
    152274         * Filters a list with entities, based on the given criteria and a closure to check whether a criterion is matched 
     
    167289                                return [] 
    168290                } 
    169                  
     291 
    170292                return entities.findAll { entity -> 
    171293                        if( searchMode == SearchMode.and ) { 
     
    186308                } 
    187309        } 
    188          
     310 
    189311        /** 
    190312         * Prepares a value from a template entity for comparison, by giving it a correct type 
     
    197319                if( value == null ) 
    198320                        return value 
    199                          
     321 
    200322                switch (type) { 
    201323                        case TemplateFieldType.DATE: 
     
    210332                                                return new RelTime( value ); 
    211333                                        } else if( value.toString().isNumber() ) { 
    212                                                 return new RelTime( Long.parseLong( value.toString() ) )  
     334                                                return new RelTime( Long.parseLong( value.toString() ) ) 
    213335                                        } else { 
    214336                                                return new RelTime( value ); 
     
    250372                } 
    251373 
    252         }        
    253          
     374        } 
     375 
    254376        /** 
    255377         * Filters the given list of studies on the study criteria 
     
    261383        protected List filterOnTemplateEntityCriteria( List studies, String entityName, Closure valueCallback ) { 
    262384                def criteria = getEntityCriteria( entityName ); 
    263                  
     385 
    264386                def checkCallback = { study, criterion -> 
    265387                        def value = valueCallback( study, criterion ); 
    266                          
     388 
    267389                        if( value == null ) { 
    268390                                return false 
     
    276398                } 
    277399 
    278                 // Save the value of this entity for later use 
    279                 saveResultFields( studies, criteria, valueCallback ); 
    280                  
    281400                return filterEntityList( studies, criteria, checkCallback); 
    282401        } 
    283402 
    284403        /** 
    285         * Filters the given list of entities on the module criteria 
    286         * @param entities       Original list of entities. Entities should expose a giveUUID() method to give the token. 
    287         * @return                       List with all entities that match the module criteria 
    288         */ 
     404         * Filters the given list of studies on the study criteria 
     405         * @param studies       Original list of studies 
     406         * @return                      List with all studies that match the Study criteria 
     407         */ 
     408        protected List filterOnStudyCriteria( List studies ) { 
     409                def entity = "Study" 
     410                return filterOnTemplateEntityCriteria(studies, entity, valueCallback( entity ) ) 
     411        } 
     412 
     413        /** 
     414         * Filters the given list of studies on the subject criteria 
     415         * @param studies       Original list of studies 
     416         * @return                      List with all studies that match the Subject-criteria 
     417         */ 
     418        protected List filterOnSubjectCriteria( List studies ) { 
     419                def entity = "Subject" 
     420                return filterOnTemplateEntityCriteria(studies, entity, valueCallback( entity ) ) 
     421        } 
     422 
     423        /** 
     424         * Filters the given list of studies on the sample criteria 
     425         * @param studies       Original list of studies 
     426         * @return                      List with all studies that match the sample-criteria 
     427         */ 
     428        protected List filterOnSampleCriteria( List studies ) { 
     429                def entity = "Sample" 
     430                return filterOnTemplateEntityCriteria(studies, entity, valueCallback( entity ) ) 
     431        } 
     432 
     433        /** 
     434         * Filters the given list of studies on the event criteria 
     435         * @param studies       Original list of studies 
     436         * @return                      List with all studies that match the event-criteria 
     437         */ 
     438        protected List filterOnEventCriteria( List studies ) { 
     439                def entity = "Event" 
     440                return filterOnTemplateEntityCriteria(studies, entity, valueCallback( entity ) ) 
     441        } 
     442 
     443        /** 
     444         * Filters the given list of studies on the sampling event criteria 
     445         * @param studies       Original list of studies 
     446         * @return                      List with all studies that match the event-criteria 
     447         */ 
     448        protected List filterOnSamplingEventCriteria( List studies ) { 
     449                def entity = "SamplingEvent" 
     450                return filterOnTemplateEntityCriteria(studies, entity, valueCallback( entity ) ) 
     451        } 
     452 
     453        /** 
     454         * Filters the given list of studies on the assay criteria 
     455         * @param studies       Original list of studies 
     456         * @return                      List with all studies that match the assay-criteria 
     457         */ 
     458        protected List filterOnAssayCriteria( List studies ) { 
     459                def entity = "Assay" 
     460                return filterOnTemplateEntityCriteria(studies, entity, valueCallback( entity ) ) 
     461        } 
     462 
     463        /** 
     464         * Filters the given list of entities on the module criteria 
     465         * @param entities      Original list of entities. Entities should expose a giveUUID() method to give the token. 
     466         * @return                      List with all entities that match the module criteria 
     467         */ 
    289468        protected List filterOnModuleCriteria( List entities ) { 
    290469                // An empty list can't be filtered more than is has been now 
    291470                if( !entities || entities.size() == 0 ) 
    292471                        return []; 
    293                          
     472 
    294473                // Determine the moduleCommunicationService. Because this object 
    295474                // is mocked in the tests, it can't be converted to a ApplicationContext object 
    296475                def ctx = ApplicationHolder.getApplication().getMainContext(); 
    297476                def moduleCommunicationService = ctx.getBean("moduleCommunicationService"); 
     477 
     478                switch( searchMode ) { 
     479                        case SearchMode.and: 
     480                                // Loop through all modules and check whether criteria have been given 
     481                                // for that module 
     482                                AssayModule.list().each { module -> 
     483                                        // Remove 'module' from module name 
     484                                        def moduleName = module.name.replace( 'module', '' ).trim() 
     485                                        def moduleCriteria = getEntityCriteria( moduleName ); 
    298486                 
    299                 def allEntities = []  
    300                 if( searchMode == SearchMode.or ) { 
    301                         allEntities += entities; 
    302                         entities = []; 
    303                 }  
     487                                        if( moduleCriteria && moduleCriteria.size() > 0 ) { 
     488                                                def callUrl = moduleCriteriaUrl( module, entities, moduleCriteria ); 
     489                                                 
     490                                                try { 
     491                                                        def json = moduleCommunicationService.callModuleRestMethodJSON( module.url, callUrl ); 
     492                                                        Closure checkClosure = moduleCriterionClosure( json ); 
     493                                                        entities = filterEntityList( entities, moduleCriteria, checkClosure ); 
     494                                                } catch( Exception e ) { 
     495                                                        log.error( "Error while retrieving data from " + module.name + ": " + e.getMessage() ) 
     496                                                } 
     497                                        } 
     498                                } 
    304499                 
    305                 // Loop through all modules and check whether criteria have been given 
    306                 // for that module 
    307                 AssayModule.list().each { module -> 
    308                         // Remove 'module' from module name 
    309                         def moduleName = module.name.replace( 'module', '' ).trim() 
    310                         def moduleCriteria = getEntityCriteria( moduleName ); 
    311                          
    312                         if( moduleCriteria && moduleCriteria.size() > 0 ) { 
    313                                 // Retrieve the data from the module 
    314                                 def tokens = entities.collect { it.giveUUID() }.unique(); 
    315                                 def fields = moduleCriteria.collect { it.field }.unique(); 
     500                                return entities; 
     501                        case SearchMode.or: 
     502                                def resultingEntities = [] 
    316503                                 
    317                                 def callUrl = module.url + '/rest/getQueryableFieldData?entity=' + this.entity 
    318                                 tokens.sort().each { callUrl += "&tokens=" + it.encodeAsURL() } 
    319                                 fields.sort().each { callUrl += "&fields=" + it.encodeAsURL() } 
     504                                // Loop through all modules and check whether criteria have been given 
     505                                // for that module 
     506                                AssayModule.list().each { module -> 
     507                                        // Remove 'module' from module name 
     508                                        def moduleName = module.name.replace( 'module', '' ).trim() 
     509                                        def moduleCriteria = getEntityCriteria( moduleName ); 
     510                 
     511                                        if( moduleCriteria && moduleCriteria.size() > 0 ) { 
     512                                                def callUrl = moduleCriteriaUrl( module, entities, moduleCriteria ); 
     513                                                 
     514                                                try { 
     515                                                        def json = moduleCommunicationService.callModuleRestMethodJSON( module.url, callUrl ); 
     516                                                        Closure checkClosure = moduleCriterionClosure( json ); 
     517                                                         
     518                                                        resultingEntities += filterEntityList( entities, moduleCriteria, checkClosure ); 
     519                                                        resultingEntities = resultingEntities.unique(); 
     520                                                         
     521                                                } catch( Exception e ) { 
     522                                                        log.error( "Error while retrieving data from " + module.name + ": " + e.getMessage() ) 
     523                                                } 
     524                                        } 
     525                                } 
     526                 
     527                                println this.resultFields; 
    320528                                 
    321                                 try { 
    322                                         def json = moduleCommunicationService.callModuleRestMethodJSON( module.url, callUrl ); 
    323  
    324                                         Closure checkClosure = { entity, criterion -> 
    325                                                 // Find the value of the field in this sample. That value is still in the 
    326                                                 // JSON object 
    327                                                 def token = entity.giveUUID() 
    328                                                 if( !json[ token ] || json[ token ][ criterion.field ] == null ) 
    329                                                         return false; 
    330  
    331                                                 // Check whether a list or string is given 
    332                                                 def value = json[ token ][ criterion.field ]; 
    333                                                  
    334                                                 // Save the value of this entity for later use 
    335                                                 saveResultField( entity.id, criterion.entity + " " + criterion.field, value ) 
    336  
    337                                                 if( !( value instanceof Collection ) ) { 
    338                                                         value = [ value ]; 
    339                                                 } 
    340                                                  
    341                                                 // Convert numbers to a long or double in order to process them correctly 
    342                                                 def values = value.collect { val ->  
    343                                                         val = val.toString(); 
    344                                                         if( val.isLong() ) { 
    345                                                                 val = Long.parseLong( val ); 
    346                                                         } else if( val.isDouble() ) { 
    347                                                                 val = Double.parseDouble( val ); 
    348                                                         } 
    349                                                         return val; 
    350                                                 } 
    351  
    352                                                 // Loop through all values and match any 
    353                                                 for( val in values ) { 
    354                                                         if( criterion.match( val ) ) 
    355                                                                 return true; 
    356                                                 } 
    357                                                  
    358                                                 return false; 
     529                                return resultingEntities; 
     530                        default: 
     531                                return []; 
     532                } 
     533        } 
     534         
     535        /** 
     536         * Returns a closure for determining the value of a module field  
     537         * @param json 
     538         * @return 
     539         */ 
     540        protected Closure moduleCriterionClosure( def json ) { 
     541                return { entity, criterion -> 
     542                        // Find the value of the field in this sample. That value is still in the 
     543                        // JSON object 
     544                        def token = entity.giveUUID() 
     545                        if( !json[ token ] || json[ token ][ criterion.field ] == null ) 
     546                                return false; 
     547 
     548                        // Check whether a list or string is given 
     549                        def value = json[ token ][ criterion.field ]; 
     550 
     551                        // Save the value of this entity for later use 
     552                        saveResultField( entity.id, criterion.entity + " " + criterion.field, value ) 
     553 
     554                        if( !( value instanceof Collection ) ) { 
     555                                value = [ value ]; 
     556                        } 
     557 
     558                        // Convert numbers to a long or double in order to process them correctly 
     559                        def values = value.collect { val -> 
     560                                val = val.toString(); 
     561                                if( val.isLong() ) { 
     562                                        val = Long.parseLong( val ); 
     563                                } else if( val.isDouble() ) { 
     564                                        val = Double.parseDouble( val ); 
     565                                } 
     566                                return val; 
     567                        } 
     568 
     569                        // Loop through all values and match any 
     570                        for( val in values ) { 
     571                                if( criterion.match( val ) ) 
     572                                        return true; 
     573                        } 
     574 
     575                        return false; 
     576                } 
     577        } 
     578         
     579        protected String moduleCriteriaUrl( module, entities, moduleCriteria ) { 
     580                // Retrieve the data from the module 
     581                def tokens = entities.collect { it.giveUUID() }.unique(); 
     582                def fields = moduleCriteria.collect { it.field }.unique(); 
     583         
     584                def callUrl = module.url + '/rest/getQueryableFieldData?entity=' + this.entity 
     585                tokens.sort().each { callUrl += "&tokens=" + it.encodeAsURL() } 
     586                fields.sort().each { callUrl += "&fields=" + it.encodeAsURL() } 
     587 
     588                return callUrl; 
     589        } 
     590 
     591        /********************************************************************* 
     592         *  
     593         * These methods are used for saving information about the search results and showing the information later on. 
     594         *  
     595         *********************************************************************/ 
     596 
     597        /** 
     598         * Saves data about template entities to use later on. This data is copied to a special 
     599         * structure to make it compatible with data fetched from other modules.  
     600         * @see #saveResultField() 
     601         */ 
     602        protected void saveResultFields() { 
     603                if( !results || !criteria ) 
     604                        return 
     605 
     606                criteria.each { criterion -> 
     607                        if( criterion.field ) { 
     608                                def valueCallback = valueCallback( criterion.entity ); 
     609                                 
     610                                if( valueCallback != null ) { 
     611                                        def name = criterion.entity + ' ' + criterion.field 
     612         
     613                                        results.each { result -> 
     614                                                saveResultField( result.id, name, valueCallback( result, criterion ) ); 
    359615                                        } 
    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                                         }  
    368                                                                                  
    369                                 } catch( Exception e ) { 
    370                                         log.error( "Error while retrieving data from " + module.name + ": " + e.getMessage() ) 
    371                                 } 
    372                         } 
    373                 } 
    374                  
    375                 return entities; 
    376         } 
    377          
     616                                } 
     617                        } 
     618                } 
     619        } 
     620 
    378621        /** 
    379622         * Saves data about template entities to use later on. This data is copied to a special 
    380          * structure to make it compatible with data fetched from other modules. Ses also saveResultField() method 
     623         * structure to make it compatible with data fetched from other modules. 
    381624         * @param entities                      List of template entities to find data in 
    382625         * @param criteria                      Criteria to search for 
    383626         * @param valueCallback         Callback to retrieve a specific field from the entity 
     627         * @see #saveResultField() 
    384628         */ 
    385629        protected void saveResultFields( entities, criteria, valueCallback ) { 
     
    392636        } 
    393637 
    394          
     638 
    395639        /** 
    396640         * Saves a specific field of an object to use later on. Especially useful when looking up data from other modules. 
     
    402646                if( resultFields[ id ] == null ) 
    403647                        resultFields[ id ] = [:] 
    404                  
     648 
    405649                // Handle special cases 
    406650                if( value == null ) 
    407651                        value = ""; 
    408                  
     652 
    409653                if( value instanceof Collection ) { 
    410654                        value = value.findAll { it != null } 
    411655                } 
    412                  
     656 
    413657                resultFields[ id ][ fieldName ] = value; 
    414658        } 
    415          
     659 
    416660        /**  
    417661         * Removes all data from the result field map 
     
    420664                resultFields = [:] 
    421665        } 
    422          
     666 
    423667        /** 
    424668         * 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. 
     
    435679        } 
    436680         
     681        /** 
     682         * Returns the field names that are found in the map with showable result fields 
     683         *  
     684         * @param fields        Map with showable result fields 
     685         * @see getShowableResultFields 
     686         * @return 
     687         */ 
     688        public List getShowableResultFieldNames( fields ) { 
     689                return fields.values()*.keySet().flatten().unique(); 
     690        } 
     691 
    437692        public String toString() { 
    438693                return ( this.entity ? this.entity + " search" : "Search" ) + " " + this.id 
  • trunk/src/groovy/dbnp/query/StudySearch.groovy

    r1501 r1524  
    1414 */ 
    1515package dbnp.query 
     16 
     17import groovy.lang.Closure; 
    1618 
    1719import java.util.List; 
     
    144146 
    145147        /** 
    146          * Filters the given list of studies on the study criteria 
    147          * @param studies       Original list of studies 
    148          * @return                      List with all studies that match the Study criteria 
    149          */ 
    150         protected List filterOnStudyCriteria( List studies ) { 
    151                 return filterOnTemplateEntityCriteria(studies, "Study", { study, criterion -> return criterion.getFieldValue( study ) }) 
    152         } 
    153  
    154         /** 
    155          * Filters the given list of studies on the subject criteria 
    156          * @param studies       Original list of studies 
    157          * @return                      List with all studies that match the Subject-criteria 
    158          */ 
    159         protected List filterOnSubjectCriteria( List studies ) { 
    160                 return filterOnTemplateEntityCriteria(studies, "Subject", { study, criterion -> 
    161                         return study.subjects?.collect { criterion.getFieldValue( it ); } 
    162                 }) 
    163         } 
    164  
    165         /** 
    166          * Filters the given list of studies on the sample criteria 
    167          * @param studies       Original list of studies 
    168          * @return                      List with all studies that match the sample-criteria 
    169          */ 
    170         protected List filterOnSampleCriteria( List studies ) { 
    171                 return filterOnTemplateEntityCriteria(studies, "Sample", { study, criterion -> 
    172                         return study.samples?.collect { criterion.getFieldValue( it ); } 
    173                 }) 
    174         } 
    175  
    176         /** 
    177          * Filters the given list of studies on the event criteria 
    178          * @param studies       Original list of studies 
    179          * @return                      List with all studies that match the event-criteria 
    180          */ 
    181         protected List filterOnEventCriteria( List studies ) { 
    182                 return filterOnTemplateEntityCriteria(studies, "Event", { study, criterion -> 
    183                         return study.events?.collect { criterion.getFieldValue( it ); } 
    184                 }) 
    185         } 
    186  
    187         /** 
    188          * Filters the given list of studies on the sampling event criteria 
    189          * @param studies       Original list of studies 
    190          * @return                      List with all studies that match the event-criteria 
    191          */ 
    192         protected List filterOnSamplingEventCriteria( List studies ) { 
    193                 return filterOnTemplateEntityCriteria(studies, "SamplingEvent", { study, criterion -> 
    194                         return study.samplingEvents?.collect { criterion.getFieldValue( it ); } 
    195                 }) 
    196         } 
    197  
    198         /** 
    199          * Filters the given list of studies on the assay criteria 
    200          * @param studies       Original list of studies 
    201          * @return                      List with all studies that match the assay-criteria 
    202          */ 
    203         protected List filterOnAssayCriteria( List studies ) { 
    204                 return filterOnTemplateEntityCriteria(studies, "Assay", { study, criterion -> 
    205                         return study.assays?.collect { criterion.getFieldValue( it ); } 
    206                 }) 
    207         } 
     148        * Returns a closure for the given entitytype that determines the value for a criterion 
     149        * on the given object. The closure receives two parameters: the object and a criterion. 
     150        * 
     151        * This method should be implemented by all searches 
     152        * 
     153        * For example: 
     154        *               For a study search, the object given is a study. How to determine the value for that study of 
     155        *               the criterion field of type sample? This is done by returning the field values for all 
     156        *               samples in the study 
     157        *                       { study, criterion -> return study.samples?.collect { criterion.getFieldValue( it ); } } 
     158        * @return 
     159        */ 
     160   protected Closure valueCallback( String entity ) { 
     161           switch( entity ) { 
     162                   case "Study": 
     163                           return { study, criterion -> return criterion.getFieldValue( study ) } 
     164                   case "Subject": 
     165                           return { study, criterion -> return study.subjects?.collect { criterion.getFieldValue( it ); } } 
     166                   case "Sample": 
     167                           return { study, criterion -> return study.samples?.collect { criterion.getFieldValue( it ); } } 
     168                   case "Event": 
     169                           return { study, criterion -> return study.events?.collect { criterion.getFieldValue( it ); } } 
     170                   case "SamplingEvent": 
     171                           return { study, criterion -> return study.samplingEvents?.collect { criterion.getFieldValue( it ); } } 
     172                   case "Assay": 
     173                           return { study, criterion -> return study.assays?.collect { criterion.getFieldValue( it ); } } 
     174                   default: 
     175                           return super.valueCallback( entity ); 
     176           } 
     177   } 
    208178 
    209179        /** 
  • trunk/web-app/css/advancedQuery.css

    r1512 r1524  
    3131.searchoptions ul#criteria li { margin-left: 0; padding-left: 0; display: inline; } 
    3232 
     33input.transparent {  
     34        -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=30)"; 
     35        filter: alpha(opacity=30); 
     36        opacity: 0.3; 
     37        -moz-opacity: 0.3; 
     38        -khtml-opacity: 0.3; 
     39} 
     40         
    3341/** Options buttons **/ 
    3442.options { margin-top: 8px; } 
  • trunk/web-app/js/advancedQueryResults.js

    r1501 r1524  
     1function checkAllPaginated( input ) { 
     2        var paginatedTable = $(input).closest( '.paginate' ); 
     3        var dataTable = paginatedTable.closest( '.dataTables_wrapper' ); 
     4        var checkAll = $( '#checkAll', paginatedTable ); 
     5         
     6        var oTable = paginatedTable.dataTable(); 
     7        var inputs = $('input', oTable.fnGetNodes()) 
     8         
     9        // If any of the inputs is checked, uncheck all. Otherwise, check all 
     10        var check = false; 
     11         
     12        for(var i = 0; i < inputs.length; i++ ) { 
     13                if( !$(inputs[i]).attr( 'checked' ) ) { 
     14                        check = true; 
     15                        break; 
     16                } 
     17        } 
     18         
     19        inputs.each( function( idx, el ) { 
     20                $(el).attr( 'checked', check ); 
     21        }) 
     22         
     23        updateCheckAll( checkAll ); 
     24} 
     25 
     26function updateCheckAll( input ) { 
     27        var paginatedTable = $(input).closest( '.paginate' ); 
     28        var dataTable = paginatedTable.closest( '.dataTables_wrapper' ); 
     29         
     30        var checkAll = $( '#checkAll', paginatedTable ); 
     31         
     32        var oTable = paginatedTable.dataTable(); 
     33        var inputs = $('input', oTable.fnGetNodes()) 
     34         
     35        // Is none checked, are all checked or are some checked 
     36        var numChecked = 0 
     37        for(var i = 0; i < inputs.length; i++ ) { 
     38                if( $(inputs[i]).attr( 'checked' ) ) { 
     39                        numChecked++; 
     40                } 
     41        } 
     42         
     43        checkAll.attr( 'checked', numChecked > 0 ); 
     44         
     45        if( numChecked > 0 && numChecked < inputs.length - 1 ) { 
     46                checkAll.addClass( 'transparent' ); 
     47        } else { 
     48                checkAll.removeClass( 'transparent' ); 
     49        } 
     50} 
     51 
    152function submitForm( form, url ) { 
    253        if( form == undefined || !form )