Changeset 2009
- Timestamp:
- Sep 8, 2011, 3:18:25 PM (12 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/grails-app/controllers/dbnp/visualization/VisualizeController.groovy
r2002 r2009 24 24 def moduleCommunicationService 25 25 def infoMessage = "" 26 final int categoricalData= 027 final int numericalData= 126 final int CATEGORICALDATA = 0 27 final int NUMERICALDATA = 1 28 28 29 29 /** … … 39 39 } 40 40 41 def getFields = { 41 /** 42 * Based on the study id contained in the parameters given by the user, a list of 'fields' is returned. This list can be used to select what data should be visualized 43 * @return List containing fields 44 * @see parseGetDataParams 45 * @see getFields 46 */ 47 def getFields = { 42 48 def input_object 43 49 def studies … … 112 118 } 113 119 120 /** 121 * Based on the field ids contained in the parameters given by the user, a list of possible visualization types is returned. This list can be used to select how data should be visualized. 122 * @return List containing the possible visualization types, with each element containing 123 * - a unique id 124 * - a unique name 125 * For example: ["id": "barchart", "name": "Barchart"] 126 * @see parseGetDataParams 127 * @see determineFieldType 128 * @see determineVisualizationTypes 129 */ 114 130 def getVisualizationTypes = { 115 131 def inputData = parseGetDataParams(); … … 130 146 131 147 // Determine possible visualization types 132 // TODO: Determine possible visualization types 133 def types = [] 134 if(rowType==categoricalData){ 135 if(columnType==categoricalData){ 136 types = [ [ "id": "table", "name": "Table"] ]; 137 } 138 if(columnType==numericalData){ 139 types = [ [ "id": "horizontal_barchart", "name": "Horizontal barchart"] ]; 140 } 141 } 142 if(rowType==numericalData){ 143 if(columnType==categoricalData){ 144 types = [ [ "id": "barchart", "name": "Barchart"], [ "id": "linechart", "name": "Linechart"] ]; 145 } 146 if(columnType==numericalData){ 147 types = [ [ "id": "scatterplot", "name": "Scatterplot"], [ "id": "linechart", "name": "Linechart"] ]; 148 } 149 } 150 148 def types = determineVisualizationTypes(rowType, columnType) 149 150 println "types: "+types 151 151 return sendResults(types) 152 152 } 153 153 154 def getFields(source, assay){ 155 /* 156 Gather fields related to this study from modules. 154 /** 155 * Gather fields related to this study from modules. 157 156 This will use the getMeasurements RESTful service. That service returns measurement types, AKA features. 158 157 getMeasurements does not actually return measurements (the getMeasurementData call does). 159 The getFields method (or rather, the getMeasurements service) requires one or more assays and will return all measurement 160 types related to these assays. 161 So, the required variables for such a call are: 162 - a source variable, which can be obtained from AssayModule.list() (use the 'name' field) 163 - a list of assays, which can be obtained with study.getAssays() 164 165 Output is a list of items. Each item contains 166 - an 'id' 167 - a 'source', which is a module identifier 168 - a 'category', which indicates where the field can be found. When dealing with module data as we are here, this is the assay name(s) 169 - a 'name', which is the the name of the field 170 */ 158 * @param source The id of the module that is the source of the requested fields, as can be obtained from AssayModule.list() (use the 'id' field) 159 * @param assay The assay that the source module and the requested fields belong to 160 * @return A list of map objects, containing the following: 161 * - a key 'id' with a value formatted by the createFieldId function 162 * - a key 'source' with a value equal to the input parameter 'source' 163 * - a key 'category' with a value equal to the 'name' field of the input paramater 'assay' 164 * - a key 'name' with a value equal to the name of the field in question, as determined by the source value 165 */ 166 def getFields(source, assay){ 171 167 def fields = [] 172 168 def callUrl = "" … … 195 191 } 196 192 197 def getFields(study, category, type){ 198 /* 199 Gather fields related to this study from GSCF. 200 This requires: 201 - a study. 202 - a category variable, e.g. "events". 203 - a type variable, either "domainfields" or "templatefields". 204 205 Output is a list of items, which are formatted by the formatGSCFFields function. 206 */ 207 193 /** 194 * Gather fields related to this study from GSCF. 195 * @param study The study that is the source of the requested fields 196 * @param category The domain that a field (a property in this case) belongs to, e.g. "subjects", "samplingEvents" 197 * @param type A string that indicates the type of field, either "domainfields" or "templatefields". 198 * @return A list of map objects, formatted by the formatGSCFFields function 199 */ 200 def getFields(study, category, type){ 208 201 // Collecting the data from it's source 209 202 def collection = [] … … 240 233 } 241 234 242 def formatGSCFFields(type, inputObject, source, category){ 243 /* The formatGSCFFields function can receive both lists of fields and single fields. If it receives a list, it calls itself again for each item in the list. This way, the original formatGSCFFields call will return single fields regardless of it's input. 244 245 Output is a list of items. Each item contains 246 - an 'id' 247 - a 'source', which in this case will be "GSCF" 248 - a 'category', which indicates where the field can be found, e.g. "subjects", "samplingEvents" 249 - a 'name', which is the the name of the field 250 */ 251 if(inputObject==null || inputObject == []){ 235 /** 236 * Format the data contained in the input parameter 'collection' for use as so-called fields, that will be used by the user interface to allow the user to select data from GSCF for visualization 237 * @param type A string that indicates the type of field, either "domainfields" or "templatefields". 238 * @param collectionOfFields A collection of fields, which could also contain only one item 239 * @param source Likely to be "GSCF" 240 * @param category The domain that a field (a property in this case) belongs to, e.g. "subjects", "samplingEvents" 241 * @return A list containing list objects, containing the following: 242 * - a key 'id' with a value formatted by the createFieldId function 243 * - a key 'source' with a value equal to the input parameter 'source' 244 * - a key 'category' with a value equal to the input parameter 'category' 245 * - a key 'name' with a value equal to the name of the field in question, as determined by the source value 246 */ 247 def formatGSCFFields(type, collectionOfFields, source, category){ 248 249 if(collectionOfFields==null || collectionOfFields == []){ 252 250 return [] 253 251 } 254 252 def fields = [] 255 if( inputObjectinstanceof Collection){253 if(collectionOfFields instanceof Collection){ 256 254 // Apparently this field is actually a list of fields. 257 255 // We will call ourselves again with the list's elements as input. 258 256 // These list elements will themselves go through this check again, effectively flattening the original input 259 for(int i = 0; i < inputObject.size(); i++){260 fields += formatGSCFFields(type, inputObject[i], source, category)257 for(int i = 0; i < collectionOfFields.size(); i++){ 258 fields += formatGSCFFields(type, collectionOfFields[i], source, category) 261 259 } 262 260 return fields … … 264 262 // This is a single field. Format it and return the result. 265 263 if(type=="domainfields"){ 266 fields << [ "id": createFieldId( id: inputObject.name, name: inputObject.name, source: source, type: category ), "source": source, "category": category, "name": inputObject.name ]264 fields << [ "id": createFieldId( id: collectionOfFields.name, name: collectionOfFields.name, source: source, type: category ), "source": source, "category": category, "name": collectionOfFields.name ] 267 265 } 268 266 if(type=="templatefields"){ 269 fields << [ "id": createFieldId( id: inputObject.id, name: inputObject.name, source: source, type: category ), "source": source, "category": category, "name": inputObject.name ]267 fields << [ "id": createFieldId( id: collectionOfFields.id, name: collectionOfFields.name, source: source, type: category ), "source": source, "category": category, "name": collectionOfFields.name ] 270 268 } 271 269 return fields … … 275 273 /** 276 274 * Retrieves data for the visualization itself. 275 * Returns, based on the field ids contained in the parameters given by the user, a map containing the actual data and instructions on how the data should be visualized. 276 * @return A map containing containing (at least, in the case of a barchart) the following: 277 * - a key 'type' containing the type of chart that will be visualized 278 * - a key 'xaxis' containing the title and unit that should be displayed for the x-axis 279 * - a key 'yaxis' containing the title and unit that should be displayed for the y-axis* 280 * - a key 'series' containing a list, that contains one or more maps, which contain the following: 281 * - a key 'name', containing, for example, a feature name or field name 282 * - a key 'y', containing a list of y-values 283 * - a key 'error', containing a list of, for example, standard deviation or standard error of the mean values, each having the same index as the 'y'-values they are associated with 277 284 */ 278 285 def getData = { … … 750 757 } 751 758 759 /** 760 * Set the response code and an error message 761 * @param code HTTP status code 762 * @param msg Error message, string 763 */ 752 764 protected void returnError(code, msg){ 753 765 response.sendError(code , msg) 754 766 } 755 767 768 /** 769 * Determines what type of data a field contains 770 * @param studyId An id that can be used with Study.get/1 to retrieve a study from the database 771 * @param fieldId The field id as returned from the client, will be used to retrieve the data required to determine the type of data a field contains 772 * @return Either CATEGORICALDATA of NUMERICALDATA 773 */ 756 774 protected int determineFieldType(studyId, fieldId){ 757 775 // Parse the fieldId as given by the user … … 769 787 TemplateField tf = TemplateField.get(parsedField.id) 770 788 if(tf.type=="DOUBLE" || tf.type=="LONG" || tf.type=="DATE" || tf.type=="RELTIME"){ 771 return numericalData789 return NUMERICALDATA 772 790 } else { 773 return categoricalData791 return CATEGORICALDATA 774 792 } 775 793 } catch(Exception e){ 776 794 log.error("VisualizationController: determineFieldType: "+e) 777 795 // If we cannot figure out what kind of a datatype a piece of data is, we treat it as categorical data 778 return categoricalData796 return CATEGORICALDATA 779 797 } 780 798 } else { … … 784 802 case "Study": 785 803 case "studies": 786 return determineCategoryFromClass(Study. fields[parsedField.name].type)804 return determineCategoryFromClass(Study.class.getDeclaredField(parsedField.name).type) 787 805 break 788 806 case "Subject": 789 807 case "subjects": 790 return determineCategoryFromClass(Subject. fields[parsedField.name].type)808 return determineCategoryFromClass(Subject.class.getDeclaredField(parsedField.name).type) 791 809 break 792 810 case "Sample": 793 811 case "samples": 794 return determineCategoryFromClass(Sample. fields[parsedField.name].type)812 return determineCategoryFromClass(Sample.class.getDeclaredField(parsedField.name).type) 795 813 break 796 814 case "Event": 797 815 case "events": 798 return determineCategoryFromClass(Event. fields[parsedField.name].type)816 return determineCategoryFromClass(Event.class.getDeclaredField(parsedField.name).type) 799 817 break 800 818 case "SamplingEvent": 801 819 case "samplingEvents": 802 return determineCategoryFromClass(SamplingEvent. fields[parsedField.name].type)820 return determineCategoryFromClass(SamplingEvent.class.getDeclaredField(parsedField.name).type) 803 821 break 804 822 case "Assay": 805 823 case "assays": 806 return determineCategoryFromClass(Assay. fields[parsedField.name].type)824 return determineCategoryFromClass(Assay.class.getDeclaredField(parsedField.name).type) 807 825 break 808 826 } 809 827 } catch(Exception e){ 810 828 log.error("VisualizationController: determineFieldType: "+e) 829 e.printStackTrace() 811 830 // If we cannot figure out what kind of a datatype a piece of data is, we treat it as categorical data 812 return categoricalData831 return CATEGORICALDATA 813 832 } 814 833 } 815 816 // Check parsedField.id == number817 834 } else { 818 835 data = getModuleData( study, study.getSamples(), parsedField.source, parsedField.name ); … … 823 840 } 824 841 825 protected int determineCategoryFromClass(inputObject){ 826 if(inputObject==java.lang.String){ 827 return categoricalData 842 /** 843 * Determines a field category, based on the input parameter 'classObject', which is an instance of type 'class' 844 * @param classObject 845 * @return Either CATEGORICALDATA of NUMERICALDATA 846 */ 847 protected int determineCategoryFromClass(classObject){ 848 println "classObject: "+classObject+", of class: "+classObject.class 849 if(classObject==java.lang.String){ 850 return CATEGORICALDATA 828 851 } else { 829 return numericalData 830 } 831 } 832 852 return NUMERICALDATA 853 } 854 } 855 856 /** 857 * Determines a field category based on the actual data contained in the field. The parameter 'inputObject' can be a single item with a toString() function, or a collection of such items. 858 * @param inputObject Either a single item, or a collection of items 859 * @return Either CATEGORICALDATA of NUMERICALDATA 860 */ 833 861 protected int determineCategoryFromData(inputObject){ 834 862 def results = [] … … 841 869 } else { 842 870 if(inputObject.toString().isDouble()){ 843 results << numericalData871 results << NUMERICALDATA 844 872 } else { 845 results << categoricalData873 results << CATEGORICALDATA 846 874 } 847 875 } … … 851 879 if(results.size()>1){ 852 880 // If we cannot figure out what kind of a datatype a piece of data is, we treat it as categorical data 853 results[0] = categoricalData881 results[0] = CATEGORICALDATA 854 882 } 855 883 … … 857 885 } 858 886 887 888 /** 889 * Properly formats the object that will be returned to the client. Also adds an informational message, if that message has been set by a function. Resets the informational message to the empty String. 890 * @param returnData The object containing the data 891 * @return results A JSON object 892 */ 859 893 protected void sendResults(returnData){ 860 894 def results = [:] … … 867 901 } 868 902 903 /** 904 * Properly formats an informational message that will be returned to the client. Resets the informational message to the empty String. 905 * @param returnData The object containing the data 906 * @return results A JSON object 907 */ 869 908 protected void sendInfoMessage(){ 870 909 def results = [:] … … 874 913 } 875 914 876 /* 877 Combine several blocks of formatted data into one 915 /** 916 * Combine several blocks of formatted data into one. These blocks have been formatted by the formatData function. 917 * @param inputData Contains a list of maps, of the following format 918 * - a key 'series' containing a list, that contains one or more maps, which contain the following: 919 * - a key 'name', containing, for example, a feature name or field name 920 * - a key 'y', containing a list of y-values 921 * - a key 'error', containing a list of, for example, standard deviation or standard error of the mean values, 878 922 */ 879 923 protected def formatCategoryData(inputData){ … … 890 934 return ret 891 935 } 936 937 /** 938 * Given two objects of either CATEGORICALDATA or NUMERICALDATA 939 * @param rowType The type of the data that has been selected for the row, either CATEGORICALDATA or NUMERICALDATA 940 * @param columnType The type of the data that has been selected for the column, either CATEGORICALDATA or NUMERICALDATA 941 * @return 942 */ 943 protected def determineVisualizationTypes(rowType, columnType){ 944 def types = [] 945 if(rowType==CATEGORICALDATA){ 946 if(columnType==CATEGORICALDATA){ 947 types = [ [ "id": "table", "name": "Table"] ]; 948 } 949 if(columnType==NUMERICALDATA){ 950 types = [ [ "id": "horizontal_barchart", "name": "Horizontal barchart"] ]; 951 } 952 } 953 if(rowType==NUMERICALDATA){ 954 if(columnType==CATEGORICALDATA){ 955 types = [ [ "id": "barchart", "name": "Barchart"], [ "id": "linechart", "name": "Linechart"] ]; 956 } 957 if(columnType==NUMERICALDATA){ 958 types = [ [ "id": "scatterplot", "name": "Scatterplot"], [ "id": "linechart", "name": "Linechart"] ]; 959 } 960 } 961 return types 962 } 892 963 }
Note: See TracChangeset
for help on using the changeset viewer.