Changeset 1991


Ignore:
Timestamp:
Sep 2, 2011, 12:37:09 PM (12 years ago)
Author:
robert@…
Message:

Updated visualization controller and added log debug statements to module communication service

Location:
trunk
Files:
2 added
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/grails-app/controllers/dbnp/visualization/VisualizeController.groovy

    r1984 r1991  
    1717import dbnp.studycapturing.*;
    1818import grails.converters.JSON
     19import groovy.lang.Closure;
     20
    1921import org.dbnp.gdt.*
    2022
     
    2224        def authenticationService
    2325        def moduleCommunicationService
    24        
     26
    2527        /**
    2628         * Shows the visualization screen
    2729         */
    28     def index = {
     30        def index = {
    2931                [ studies: Study.giveReadableStudies( authenticationService.getLoggedInUser() )]
    3032        }
    31        
     33
    3234        def getStudies = {
    3335                def studies = Study.giveReadableStudies( authenticationService.getLoggedInUser() );
    3436                render studies as JSON
    3537        }
    36        
     38
    3739        def getFields = {
    38         def input_object
    39         def studies
    40 
    41         try{
    42             input_object = JSON.parse(params.get('data'))
    43             studies = input_object.get('studies').id
    44         } catch(Exception e) {
    45             // TODO: properly handle this exception
    46             println e
    47         }
    48 
    49         def fields = [];
    50         studies.each {
    51             /*
    52             Gather fields related to this study from GSCF.
    53             This requires:
    54               - a study.
    55               - a category variable, e.g. "events".
    56               - a type variable, either "domainfields" or "templatefields".
    57             */
    58             def study = Study.get(it)
    59             fields += getFields(study, "subjects", "domainfields")
    60             fields += getFields(study, "subjects", "templatefields")
    61             fields += getFields(study, "events", "domainfields")
    62             fields += getFields(study, "events", "templatefields")
    63             fields += getFields(study, "samplingEvents", "domainfields")
    64             fields += getFields(study, "samplingEvents", "templatefields")
    65             fields += getFields(study, "assays", "domainfields")
    66             fields += getFields(study, "assays", "templatefields")
    67             fields += getFields(study, "samples", "domainfields")
    68             fields += getFields(study, "samples", "domainfields")
    69 
    70 
    71             /*
    72             Gather fields related to this study from modules.
    73             This will use the getMeasurements RESTful service. That service returns measurement types, AKA features.
    74             It does not actually return measurements (the getMeasurementData call does).
    75             The getFields method (or rather, the getMeasurements service) requires one or more assays and will return all measurement
    76             types related to these assays.
    77             So, the required variables for such a call are:
    78               - a source variable, which can be obtained from AssayModule.list() (use the 'name' field)
    79               - a list of assays, which can be obtained with study.getAssays()
    80              */
    81             AssayModule.list().each { module ->
    82                 def list = []
    83                 list = getFields(module.name, study.getAssays())
    84                 if(list!=null){
    85                     if(list.size()!=0){
    86                         fields += list
    87                     }
    88                 }
    89             }
    90 
    91             // TODO: Maybe we should add study's own fields
    92         }
     40                def input_object
     41                def studies
     42
     43                try{
     44                        input_object = JSON.parse(params.get('data'))
     45                        studies = input_object.get('studies').id
     46                } catch(Exception e) {
     47                        // TODO: properly handle this exception
     48                        println e
     49                }
     50
     51                def fields = [];
     52                studies.each {
     53                        /*
     54                         Gather fields related to this study from GSCF.
     55                         This requires:
     56                         - a study.
     57                         - a category variable, e.g. "events".
     58                         - a type variable, either "domainfields" or "templatefields".
     59                         */
     60                        def study = Study.get(it)
     61                        fields += getFields(study, "subjects", "domainfields")
     62                        fields += getFields(study, "subjects", "templatefields")
     63                        fields += getFields(study, "events", "domainfields")
     64                        fields += getFields(study, "events", "templatefields")
     65                        fields += getFields(study, "samplingEvents", "domainfields")
     66                        fields += getFields(study, "samplingEvents", "templatefields")
     67                        fields += getFields(study, "assays", "domainfields")
     68                        fields += getFields(study, "assays", "templatefields")
     69                        fields += getFields(study, "samples", "domainfields")
     70                        fields += getFields(study, "samples", "domainfields")
     71
     72                        /*
     73                         Gather fields related to this study from modules.
     74                         This will use the getMeasurements RESTful service. That service returns measurement types, AKA features.
     75                         It does not actually return measurements (the getMeasurementData call does).
     76                         The getFields method (or rather, the getMeasurements service) requires one or more assays and will return all measurement
     77                         types related to these assays.
     78                         So, the required variables for such a call are:
     79                         - a source variable, which can be obtained from AssayModule.list() (use the 'name' field)
     80                         - a list of assays, which can be obtained with study.getAssays()
     81                         */
     82                        AssayModule.list().each { module ->
     83                                def list = []
     84                                list = getFields(module.name, study.getAssays())
     85                                if(list!=null){
     86                                        if(list.size()!=0){
     87                                                fields += list
     88                                        }
     89                                }
     90                        }
     91
     92                        // TODO: Maybe we should add study's own fields
     93                }
    9394
    9495                render fields as JSON
    9596        }
    96        
     97
    9798        def getVisualizationTypes = {
    9899                def types = [ [ "id": "barchart", "name": "Barchart"] ];
    99100                render types as JSON
    100101        }
     102
     103        def getFields(source, assays){
     104                /*
     105                 Gather fields related to this study from modules.
     106                 This will use the getMeasurements RESTful service. That service returns measurement types, AKA features.
     107                 It does not actually return measurements (the getMeasurementData call does).
     108                 The getFields method (or rather, the getMeasurements service) requires one or more assays and will return all measurement
     109                 types related to these assays.
     110                 So, the required variables for such a call are:
     111                 - a source variable, which can be obtained from AssayModule.list() (use the 'name' field)
     112                 - a list of assays, which can be obtained with study.getAssays()
     113                 */
     114                def collection = []
     115                def callUrl = ""
     116
     117                // Making a different call for each assay
     118                // TODO: Change this to one call that requests fields for all assays, when you get that to work (in all cases)
     119                assays.each { assay ->
     120                        def urlVars = "assayToken="+assay.assayUUID
     121                        AssayModule.list().each { module ->
     122                                if(source==module.name){
     123                                        try {
     124                                                callUrl = module.url + "/rest/getMeasurements/query?"+urlVars
     125                                                def json = moduleCommunicationService.callModuleRestMethodJSON( module.url, callUrl );
     126                                                json.each{ jason ->
     127                                                        collection.add(jason)
     128                                                }
     129                                        } catch(Exception e){
     130                                                // Todo: properly handle this exception
     131                                                println "No success with\n\t"+callUrl+"\n"+e
     132                                                return null
     133                                        }
     134                                }
     135                        }
     136                }
     137
     138                def fields = []
     139                // Formatting the data
     140                collection.each { field ->
     141                        fields << [ "id": createFieldId( name: field, source: source, type: "feature" ), "source": source, "category": "feature", "name": source+" feature "+field ]
     142                }
     143                return fields
     144        }
     145
     146        def getFields(study, category, type){
     147                /*
     148                 Gather fields related to this study from GSCF.
     149                 This requires:
     150                 - a study.
     151                 - a category variable, e.g. "events".
     152                 - a type variable, either "domainfields" or "templatefields".
     153                 */
     154
     155                // Collecting the data from it's source
     156                def collection
     157                def fields = []
     158                def source = "GSCF"
     159
     160                // Gathering the data
     161                if(category=="subjects"){
     162                        if(type=="domainfields"){
     163                                collection = Subject.giveDomainFields()
     164                        }
     165                        if(type=="templatefields"){
     166                                collection = study.giveSubjectTemplates().fields
     167                        }
     168                }
     169                if(category=="events"){
     170                        if(type=="domainfields"){
     171                                collection = Event.giveDomainFields()
     172                        }
     173                        if(type=="templatefields"){
     174                                collection = study.giveEventTemplates().fields
     175                        }
     176                }
     177                if(category=="samplingEvents"){
     178                        if(type=="domainfields"){
     179                                collection = SamplingEvent.giveDomainFields()
     180                        }
     181                        if(type=="templatefields"){
     182                                collection = study.giveSamplingEventTemplates().fields
     183                        }
     184                }
     185                if(category=="samples"){
     186                        if(type=="domainfields"){
     187                                collection = Sample.giveDomainFields()
     188                        }
     189                        if(type=="templatefields"){
     190                                collection = study.giveEventTemplates().fields
     191                        }
     192                }
     193                if(category=="assays"){
     194                        if(type=="domainfields"){
     195                                collection = Event.giveDomainFields()
     196                        }
     197                        if(type=="templatefields"){
     198                                collection = study.giveEventTemplates().fields
     199                        }
     200                }
     201
     202                // Formatting the data
     203                if(type=="domainfields"){
     204                        collection.each { field ->
     205                                fields << [ "id": createFieldId( name: field.name, source: source, type: category ), "source": source, "category": category, "name": category.capitalize()+" "+field.name ]
     206                        }
     207                }
     208                if(type=="templatefields"){
     209                        collection.each { field ->
     210                                for(int i = 0; i < field.size(); i++){
     211                                        fields << [ "id": createFieldId( id: field[i].id, name: field[i].name, source: source, type: category ), "source": source, "category": category, "name": category.capitalize()+" "+field[i].name ]
     212                                }
     213                        }
     214                }
     215
     216                return fields
     217        }
     218
     219        /**
     220         * Retrieves data for the visualization itself.
     221         */
     222        def getData = {
     223                // Extract parameters
     224                // TODO: handle erroneous input data
     225                def inputData = parseGetDataParams();
     226               
     227                // TODO: handle the case that we have multiple studies
     228                def studyId = inputData.studyIds[ 0 ];
     229                def study = Study.get( studyId as Integer );
     230
     231                // Find out what samples are involved
     232                def samples = study.samples
     233
     234                // Retrieve the data for both axes for all samples
     235                // TODO: handle the case of multiple fields on an axis
     236                def fields = [ "x": inputData.columnIds[ 0 ], "y": inputData.rowIds[ 0 ] ];
     237                def data = getAllFieldData( study, samples, fields );
     238
     239                // Group data based on the y-axis if categorical axis is selected
     240                // TODO: handle categories and continuous data
     241                def groupedData = groupFieldData( data );
     242               
     243                // Format data so it can be rendered as JSON
     244                def returnData = formatData( groupedData, fields );
     245               
     246                render returnData as JSON
     247        }
     248
     249        /**
     250         * Parses the parameters given by the user into a proper list
     251         * @return Map with 4 keys:
     252         *              studyIds:       list with studyIds selected
     253         *              rowIds:         list with fieldIds selected for the rows
     254         *              columnIds:      list with fieldIds selected for the columns
     255         *              visualizationType:      String with the type of visualization required
     256         * @see getFields
     257         * @see getVisualizationTypes
     258         */
     259        def parseGetDataParams() {
     260                def studyIds, rowIds, columnIds, visualizationType;
     261               
     262                def inputData = params.get( 'data' );
     263                try{
     264                        def input_object = JSON.parse(inputData)
     265                       
     266                        studyIds = input_object.get('studies')*.id
     267                        rowIds = input_object.get('rows')*.id
     268                        columnIds = input_object.get('columns')*.id
     269                        visualizationType = "barchart"
     270                } catch(Exception e) {
     271                        // TODO: properly handle this exception
     272                        println e
     273                }
     274
     275                return [ "studyIds" : studyIds, "rowIds": rowIds, "columnIds": columnIds, "visualizationType": visualizationType ];
     276        }
     277
     278        /**
     279         * Retrieve the field data for the selected fields
     280         * @param study         Study for which the data should be retrieved
     281         * @param samples       Samples for which the data should be retrieved
     282         * @param fields        Map with key-value pairs determining the name and fieldId to retrieve data for. Example:
     283         *                                              [ "x": "field-id-1", "y": "field-id-3" ]
     284         * @return                      A map with the same keys as the input fields. The values in the map are lists of values of the
     285         *                                      selected field for all samples. If a value could not be retrieved for a sample, null is returned. Example:
     286         *                                              [ "x": [ 3, 6, null, 10 ], "y": [ "male", "male", "female", "female" ] ]
     287         */
     288        def getAllFieldData( study, samples, fields ) {
     289                def fieldData = [:]
     290                fields.each{ field ->
     291                        fieldData[ field.key ] = getFieldData( study, samples, field.value );
     292                }
     293               
     294                return fieldData;
     295        }
    101296       
    102         def getData = {
    103         println params
    104         def input_object
    105         def studies
    106         def rows
    107         def columns
    108         def vizualisation_type
    109 
    110         try{
    111             input_object = JSON.parse(params.get('data'))
    112             studies = input_object.get('studies')
    113             rows = input_object.get('rows')
    114             columns = input_object.get('columns')
    115             vizualisation_type = "barchart"
    116         } catch(Exception e) {
    117             // TODO: properly handle this exception
    118             println e
    119         }
    120 
    121         def data = [:]
    122 
    123         Collection row_data = []
    124         Collection column_data = []
    125                 studies.each {
    126             // TODO: Get rid of code duplication
    127             def study = Study.get(it.id)
    128             rows.eachWithIndex { r, index ->
    129                 println "  - field "+r
    130                 def case_switch
    131                 def input_id = r.id.split(",")
    132                 def field_id = input_id[0]
    133                 def source_module = input_id[1]
    134                 def field_type = input_id[2]
    135                 def field_name = input_id[3]
    136                 def templatefield_source
    137                 if(source_module=="GSCF"){
    138                     if(field_type!=TemplateField.class.toString()){
    139                         case_switch = "domain"
    140                     } else {
    141                         templatefield_source = input_id[4]
    142                     }
    143                     row_data[index] = getFieldData(study, case_switch, field_id, field_name, source_module, templatefield_source, field_type)
    144                 } else {
    145                     // Grabbing field data from a module
    146                     row_data[index] = getFieldData(study, "", field_id, "", source_module, "", "")
    147                 }
    148             }
    149 
    150             columns.eachWithIndex { r, index ->
    151                 def case_switch
    152                 def input_id = r.id.split(",")
    153                 def field_id = input_id[0]
    154                 def source_module = input_id[1]
    155                 def field_type = input_id[2]
    156                 def field_name = input_id[3]
    157                 def templatefield_source
    158 
    159                 if(source_module=="GSCF"){
    160                     if(field_type!=TemplateField.class.toString()){
    161                         case_switch = "domain"
    162                     } else {
    163                         templatefield_source = input_id[4]
    164                     }
    165                     column_data[index] = getFieldData(study, case_switch, field_id, field_name, source_module, templatefield_source, field_type)
    166                 } else {
    167                     // Grabbing field data from a module
    168                     column_data[index] = getFieldData(study, "", field_id, "", source_module, "", "")
    169                 }
    170             }
    171         }
    172 
    173         if(row_data.size()!=0 && column_data.size()!=0 && row_data[0].size()!=0 && column_data[0].size()!=0){
    174             // Going to build the return object now
    175             def return_data = [:]
    176             def series = []
    177             def possible_xaxis_title = ""
    178             def possible_yaxis_title = ""
    179             return_data.put("type", vizualisation_type)
    180             if(vizualisation_type=='barchart'){
    181 
    182                 // Determining what different bars we need (x-axis)
    183                 def list_of_row_contents = []
    184                 rows.eachWithIndex { r, j ->
    185                     row_data[j].each { datapoint ->
    186                         list_of_row_contents.add(datapoint)
    187                     }
    188                 }
    189                 def bars = []
    190                 // Make the list unique and stringify the individual objects
    191                 list_of_row_contents.unique().each {
    192                     item ->
    193                     bars << item.toString()
    194                 }
    195                 bars.sort()
    196                 return_data.put("x", bars)
    197                 // Determining what different bars we need (x-axis)
    198 
    199                 // Determine the different categories that datapoints can fall under
    200                 def categories = []
    201                 columns.eachWithIndex { c, i ->
    202                     column_data[i].each {
    203                         cd ->
    204                         categories << cd
    205                     }
    206                 }
    207                 categories.unique().sort()
    208                
    209                 // Looking at the actual datapoints ...
    210                 columns.eachWithIndex { column, column_index ->
    211                     // ... for each column
    212                     categories.each { category ->
    213                         def data_per_bar = [:] // To store the datapoints contained in the current category, ordered by bar
    214                         // Make an entry for each of the bars that will be in the barchart
    215                         list_of_row_contents.each { bar ->
    216                             data_per_bar.put(bar, 0)
    217                         }
    218                         rows.eachWithIndex { row, row_index ->
    219                             // ... check for each row what it contains for each bar and category combination
    220                             // TODO: properly determine axis titles
    221                             if(possible_xaxis_title==""){
    222                                 possible_xaxis_title = row.id.split(',')[3]
    223                             }
    224                             list_of_row_contents.each { bar ->
    225                                 // Check for the current bar, how many datapoints each category has
    226                                 column_data[column_index].eachWithIndex { cd, cdi ->
    227                                     if(bar==row_data[row_index][cdi] && cd==category){
    228                                         // Apparently this column contains a datapoint, whose entry in the relevant row equals the bar we are currently looking for and whose column equals the category that we are currently checking for. What this means is that the current datapoint should be included in this series
    229                                         data_per_bar.put(bar, data_per_bar.get(bar)+1)
    230                                     }
    231                                 }
    232                             }
    233                         }
    234                         // Now add the data in the correct order (corresponding to bar order, so that the values end up in the right bar)
    235                         def data_per_bar_sorted = []
    236                         list_of_row_contents.each { bar ->
    237                             data_per_bar_sorted.add(data_per_bar.get(bar))
    238                         }
    239                         series.add(["name":category.toString(),"y":data_per_bar_sorted])
    240                     }
    241                 }
    242 
    243                 if(possible_yaxis_title==""){
    244                     // TODO: properly determine axis titles
    245                     possible_yaxis_title = "Amount"
    246                     if(possible_xaxis_title!=""){
    247                         possible_yaxis_title += " of each "+possible_xaxis_title
    248                     }
    249                 }
    250                 return_data.put("yaxis", ["title" : possible_yaxis_title, "unit" : "..."])
    251                 return_data.put("xaxis", ["title" : possible_xaxis_title, "unit": "..."])
    252                 return_data.put("series", series)
    253                 data = return_data
    254             }
    255         } else {
    256             // TODO: handle this exception properly
    257             // We couldn't get any data to display...
    258         }
    259         println "\n\nReturn object: "+(data as JSON)+" ... "
    260         render data as JSON
    261         }
    262 
    263     def getFieldData(study, case_switch, field_id, field_name, source, templatefield_source, field_type){
    264         if(source=="GSCF"){
    265             if(case_switch=="domain"){
    266                 if(!study.getProperty(field_type)){
    267                     // TODO: handle this exception properly
    268                     println "getFieldData: domainfield: Requested property '"+field_type+"' does not appear to exist in the study '"+study+"'."
    269                     return
    270                 }
    271                 def domain_objects = study.getProperty(field_type) // Simple way of getting at the relevant domain objects
    272 
    273                 if(domain_objects==null){
    274                     // TODO: handle this exception properly
    275                     println "getFieldData: domainfield: A problem occurred... Nothing was collected."
    276                 }
    277 
    278                 // Get the value of the requested field out of the domain objects
    279                 def dat = []
    280                 domain_objects.each{
    281                     try{
    282                         dat.add(it.getFieldValue(field_name))
    283                         //println "getFieldData: domainfield: *** It appears as though we were successful"
    284                     } catch(Exception e){
    285                         // TODO: handle this exception properly
    286                         println "getFieldData: domainfield: A problem occurred... "+e
    287                     }
    288                 }
    289                 return dat
    290             } else {
    291                 TemplateField tf
    292                 try{
    293                     tf = TemplateField.get(field_id)
    294                 } catch (Exception e){
    295                     // TODO: handle this exception properly
    296                     println "getFieldData: templatefield: A problem occurred... "+e
    297                 }
    298                 def dat = []
    299                 def collection
    300                 if(templatefield_source=="subjects"){
    301                     collection = study.getSubjects()
    302                 }
    303                 if(templatefield_source=="assays"){
    304                     collection = study.getAssays()
    305                 }
    306                 if(templatefield_source=="events"){
    307                     collection = study.getEvents()
    308                 }
    309                 if(templatefield_source=="samplingEvents"){
    310                     collection = study.getSamplingEvents()
    311                 }
    312                 if(templatefield_source=="samples"){
    313                     collection = study.getSamples()
    314                 }
    315                 if(collection==null){
    316                     // TODO: handle this exception properly
    317                     println "getFieldData: templatefield: A problem occurred... Nothing was collected."
    318                 }
    319                 collection.each {
    320                     try{
    321                         dat.add(it.getFieldValue(tf.name))
    322                     } catch(Exception e){
    323                         // TODO: handle this exception properly
    324                         println "getFieldData: templatefield: A problem occurred... "+e
    325                     }
    326                 }
    327                 return dat
    328             }
    329         } else {
    330             // Request for module data
    331             def dat = []
    332 
    333             // User requested a particular feature
    334             study.getAssays().each { assay ->
    335                 // Request for a particular assay and a particular feature
    336                 def urlVars = "assayToken="+assay.assayUUID+"&measurementToken="+field_id
    337                 def callUrl
    338                 AssayModule.list().each { module ->
    339                     if(source==module.name){
    340                         try {
    341                             callUrl = module.url + "/rest/getMeasurementData/query?"+urlVars
    342                             def json = moduleCommunicationService.callModuleRestMethodJSON( module.url, callUrl );
    343                             // First element contains sampletokens
    344                             // Second element contains the featurename
    345                             // Third element contains the measurement value
    346                             // NOTE: There is no need to couple a measurement value to a sampletoken, because that just doesn't produce interesting data
    347                             json[2].each { val ->
    348                                 dat << val
    349                             }
    350                         } catch(Exception e){
    351                             // TODO: handle this exception properly
    352                             println "No success with\n\t"+callUrl+"\n"+e
    353                             return null
    354                         }
    355                     }
    356                 }
    357             }
    358             return dat
    359         }
    360     }
    361 
    362     def getFields(source, assays){
    363         /*
    364         Gather fields related to this study from modules.
    365         This will use the getMeasurements RESTful service. That service returns measurement types, AKA features.
    366         It does not actually return measurements (the getMeasurementData call does).
    367         The getFields method (or rather, the getMeasurements service) requires one or more assays and will return all measurement
    368         types related to these assays.
    369         So, the required variables for such a call are:
    370           - a source variable, which can be obtained from AssayModule.list() (use the 'name' field)
    371           - a list of assays, which can be obtained with study.getAssays()
    372          */
    373         def collection = []
    374         def callUrl = ""
    375 
    376         // Making a different call for each assay
    377         // TODO: Change this to one call that requests fields for all assays, when you get that to work (in all cases)
    378         assays.each { assay ->
    379             def urlVars = "assayToken="+assay.assayUUID
    380             AssayModule.list().each { module ->
    381                 if(source==module.name){
    382                     try {
    383                         callUrl = module.url + "/rest/getMeasurements/query?"+urlVars
    384                         def json = moduleCommunicationService.callModuleRestMethodJSON( module.url, callUrl );
    385                         json.each{ jason ->
    386                             collection.add(jason)
    387                         }
    388                     } catch(Exception e){
    389                         // Todo: properly handle this exception
    390                         println "No success with\n\t"+callUrl+"\n"+e
    391                         return null
    392                     }
    393                 }
    394             }
    395         }
    396 
    397         def fields = []
    398         // Formatting the data
    399         collection.each { field ->
    400             fields << [ "id": field+","+source+","+"feature"+","+field, "source": source, "category": "feature", "name": source+" feature "+field ]
    401         }
    402         return fields
    403     }
    404    
    405     def getFields(study, category, type){
    406         /*
    407         Gather fields related to this study from GSCF.
    408         This requires:
    409           - a study.
    410           - a category variable, e.g. "events".
    411           - a type variable, either "domainfields" or "templatefields".
    412         */
    413 
    414         // Collecting the data from it's source
    415         def collection
    416         def fields = []
    417         def source = "GSCF"
    418 
    419         // Gathering the data
    420         if(category=="subjects"){
    421             if(type=="domainfields"){
    422                 collection = Subject.giveDomainFields()
    423             }
    424             if(type=="templatefields"){
    425                 collection = study.giveSubjectTemplates().fields
    426             }
    427         }
    428         if(category=="events"){
    429             if(type=="domainfields"){
    430                 collection = Event.giveDomainFields()
    431             }
    432             if(type=="templatefields"){
    433                 collection = study.giveEventTemplates().fields
    434             }
    435         }
    436         if(category=="samplingEvents"){
    437             if(type=="domainfields"){
    438                 collection = SamplingEvent.giveDomainFields()
    439             }
    440             if(type=="templatefields"){
    441                 collection = study.giveSamplingEventTemplates().fields
    442             }
    443         }
    444         if(category=="samples"){
    445             if(type=="domainfields"){
    446                 collection = Sample.giveDomainFields()
    447             }
    448             if(type=="templatefields"){
    449                 collection = study.giveEventTemplates().fields
    450             }
    451         }
    452         if(category=="assays"){
    453             if(type=="domainfields"){
    454                 collection = Event.giveDomainFields()
    455             }
    456             if(type=="templatefields"){
    457                 collection = study.giveEventTemplates().fields
    458             }
    459         }
    460 
    461         // Formatting the data
    462         if(type=="domainfields"){
    463             collection.each { field ->
    464                 fields << [ "id": field.name+","+source+","+category+","+field.name, "source": source, "category": category, "name": category.capitalize()+" "+field.name ]
    465             }
    466         }
    467         if(type=="templatefields"){
    468             collection.each { field ->
    469                 for(int i = 0; i < field.size(); i++){
    470                     fields << [ "id": field[i].id+","+source+","+TemplateField.toString()+","+field[i].name+","+category, "source": source, "category": category, "name": category.capitalize()+" "+field[i].name ]
    471                 }
    472             }
    473         }
    474 
    475         return fields
    476     }
     297        /**
     298        * Retrieve the field data for the selected field
     299        * @param study          Study for which the data should be retrieved
     300        * @param samples        Samples for which the data should be retrieved
     301        * @param fieldId        ID of the field to return data for
     302        * @return                       A list of values of the selected field for all samples. If a value
     303        *                                       could not be retrieved for a sample, null is returned. Examples:
     304        *                                               [ 3, 6, null, 10 ] or [ "male", "male", "female", "female" ]
     305        */
     306        def getFieldData( study, samples, fieldId ) {
     307                // Parse the fieldId as given by the user
     308                def parsedField = parseFieldId( fieldId );
     309               
     310                def data = []
     311               
     312                if( parsedField.source == "GSCF" ) {
     313                        // Retrieve data from GSCF itself
     314                        def closure = valueCallback( parsedField.type )
     315                       
     316                        if( closure ) {
     317                                samples.each { sample ->
     318                                        // Retrieve the value for the selected field for this sample
     319                                        def value = closure( sample, parsedField.name );
     320                                       
     321                                        if( value ) {
     322                                                data << value;
     323                                        } else {
     324                                                // Return null if the value is not found
     325                                                data << null
     326                                        }
     327                                }
     328                        } else {
     329                                // TODO: Handle error properly
     330                                // Closure could not be retrieved, probably because the type is incorrect
     331                                data = samples.collect { return null }
     332                        }
     333                } else {
     334                        // Data must be retrieved from a module
     335                        data = getModuleData( study, samples, parsedField.source, parsedField.name );
     336                }
     337               
     338                return data
     339        }
     340       
     341        /**
     342         * Retrieve data for a given field from a data module
     343         * @param study                 Study to retrieve data for
     344         * @param samples               Samples to retrieve data for
     345         * @param source_module Name of the module to retrieve data from
     346         * @param fieldName             Name of the measurement type to retrieve (i.e. measurementToken)
     347         * @return                              A list of values of the selected field for all samples. If a value
     348         *                                              could not be retrieved for a sample, null is returned. Examples:
     349         *                                                      [ 3, 6, null, 10 ] or [ "male", "male", "female", "female" ]
     350         */
     351        def getModuleData( study, samples, source_module, fieldName ) {
     352                def data = []
     353               
     354                // TODO: Handle values that should be retrieved from multiple assays
     355                // TODO: Use Assay ID or AssayModule ID in field-ids, instead of names
     356                def assay = study.assays.find { it.module.name == source_module };
     357               
     358                if( assay ) {
     359                        // Request for a particular assay and a particular feature
     360                        def urlVars = "assayToken=" + assay.assayUUID + "&measurementToken="+fieldName
     361                        urlVars += "&" + samples.collect { "sampleToken=" + it.sampleUUID }.join( "&" );
     362                       
     363                        def callUrl
     364                        try {
     365                                callUrl = assay.module.url + "/rest/getMeasurementData"
     366                                def json = moduleCommunicationService.callModuleMethod( assay.module.url, callUrl, urlVars, "POST" );
     367                               
     368                                if( json ) {
     369                                        // First element contains sampletokens
     370                                        // Second element contains the featurename
     371                                        // Third element contains the measurement value
     372                                        def sampleTokens = json[ 0 ]
     373                                        def measurements = json[ 2 ]
     374                                       
     375                                        // Loop through the samples
     376                                        samples.each { sample ->
     377                                                // Search for this sampletoken
     378                                                def sampleToken = sample.sampleUUID;
     379                                                def index = sampleTokens.findIndexOf { it == sampleToken }
     380                                               
     381                                                if( index > -1 ) {
     382                                                        data << measurements[ index ];
     383                                                } else {[ "x": inputData.columnIds[ 0 ], "y": inputData.rowIds[ 0 ] ]
     384                                                        data << null
     385                                                }
     386                                        }
     387                                } else {
     388                                        // TODO: handle error
     389                                        // Returns an empty list with as many elements as there are samples
     390                                        data = samples.collect { return null }
     391                                }
     392                               
     393                        } catch(Exception e){
     394                                // TODO: handle this exception properly
     395                                println "No success with\n\t"+callUrl+"\n"+e
     396                                e.printStackTrace();
     397                               
     398                                // Returns an empty list with as many elements as there are samples
     399                                data = samples.collect { return null }
     400                        }
     401                } else {
     402                        // TODO: Handle error correctly
     403                        // Returns an empty list with as many elements as there are samples
     404                        data = samples.collect { return null }
     405                }
     406               
     407                return data
     408
     409        }
     410
     411        /**
     412         * Group the field data on the values of the specified axis. For example, for a bar chart, the values
     413         * on the x-axis should be grouped. Currently, the values for each group are averaged, and the standard
     414         * error of the mean is returned in the 'error' property
     415         * @param data          Data for both group- and value axis. The output of getAllFieldData fits this input
     416         * @param groupAxis     Name of the axis to group on. Defaults to "x"
     417         * @param valueAxis     Name of the axis where the values are. Defaults to "y"
     418         * @param errorName     Key in the output map where 'error' values (SEM) are stored. Defaults to "error"
     419         * @param unknownName   Name of the group for all null groups. Defaults to "unknown"
     420         * @return                      A map with the keys 'groupAxis', 'valueAxis' and 'errorName'. The values in the map are lists of values of the
     421         *                                      selected field for all groups. For example, if the input is
     422         *                                              [ "x": [ "male", "male", "female", "female", null, "female" ], "y": [ 3, 6, null, 10, 4, 5 ] ]
     423         *                                      the output will be:
     424         *                                              [ "x": [ "male", "female", "unknown" ], "y": [ 4.5, 7.5, 4 ], "error": [ 1.5, 2.5, 0 ] ]
     425         *
     426         *                                      As you can see: null values in the valueAxis are ignored. Null values in the
     427         *                                      group axis are combined into a 'unknown' category.
     428         */
     429        def groupFieldData( data, groupAxis = "x", valueAxis = "y", errorName = "error", unknownName = "unknown" ) {
     430                // Create a unique list of values in the groupAxis. First flatten the list, since it might be that a
     431                // sample belongs to multiple groups. In that case, the group names should not be the lists, but the list
     432                // elements. A few lines below, this case is handled again by checking whether a specific sample belongs
     433                // to this group.
     434                // After flattening, the list is uniqued. The closure makes sure that values with different classes are
     435                // always treated as different items (e.g. "" should not equal 0, but it does if using the default comparator)
     436                def groups = data[ groupAxis ]
     437                                                .flatten()
     438                                                .unique { it == null ? "null" : it.class.name + it.toString() }
     439               
     440                // Make sure the null category is last
     441                groups = groups.findAll { it != null } + groups.findAll { it == null }
     442               
     443                // Gather names for the groups. Most of the times, the group names are just the names, only with
     444                // a null value, the unknownName must be used
     445                def groupNames = groups.collect { it != null ? it : unknownName }
     446               
     447                // Generate the output object
     448                def outputData = [:]
     449                outputData[ valueAxis ] = [];
     450                outputData[ errorName ] = [];
     451                outputData[ groupAxis ] = groupNames;
     452               
     453                // Loop through all groups, and gather the values for this group
     454                groups.each { group ->
     455                        // Find the indices of the samples that belong to this group. if a sample belongs to multiple groups (i.e. if
     456                        // the samples groupAxis contains multiple values, is a collection), the value should be used in all groups.
     457                        def indices= data[ groupAxis ].findIndexValues { it instanceof Collection ? it.contains( group ) : it == group };
     458                        def values = data[ valueAxis ][ indices ]
     459                       
     460                        def dataForGroup = computeMeanAndError( values );
     461                       
     462                        outputData[ valueAxis ] << dataForGroup.value
     463                        outputData[ errorName ] << dataForGroup.error
     464                }
     465
     466                return outputData
     467        }
     468       
     469        /**
     470         * Formats the grouped data in such a way that the clientside visualization method
     471         * can handle the data correctly.
     472         * @param groupedData   Data that has been grouped using the groupFields method
     473         * @param fields                Map with key-value pairs determining the name and fieldId to retrieve data for. Example:
     474         *                                                      [ "x": "field-id-1", "y": "field-id-3" ]
     475         * @param groupAxis             Name of the axis to with group data. Defaults to "x"
     476         * @param valueAxis             Name of the axis where the values are stored. Defaults to "y"
     477         * @param errorName             Key in the output map where 'error' values (SEM) are stored. Defaults to "error"         *
     478         * @return                              A map like the following:
     479         *
     480                        {
     481                                "type": "barchart",
     482                                "x": [ "Q1", "Q2", "Q3", "Q4" ],
     483                                "xaxis": { "title": "quarter 2011", "unit": "" },
     484                                "yaxis": { "title": "temperature", "unit": "degrees C" },
     485                                "series": [
     486                                        {
     487                                                "name": "series name",
     488                                                "y": [ 5.1, 3.1, 20.6, 15.4 ],
     489                                                "error": [ 0.5, 0.2, 0.4, 0.5 ]
     490                                        },
     491                                ]
     492                        }
     493         *
     494         */
     495        def formatData( groupedData, fields, groupAxis = "x", valueAxis = "y", errorName = "error" ) {
     496                // TODO: Handle name and unit of fields correctly
     497               
     498                def return_data = [:]
     499                return_data[ "type" ] = "barchart"
     500                return_data[ "x" ] = groupedData[ groupAxis ].collect { it.toString() }
     501                return_data.put("yaxis", ["title" : parseFieldId( fields[ valueAxis ] ).name, "unit" : "" ])
     502                return_data.put("xaxis", ["title" : parseFieldId( fields[ groupAxis ] ).name, "unit": "" ])
     503                return_data.put("series", [[
     504                        "name": "Y",
     505                        "y": groupedData[ valueAxis ],
     506                        "error": groupedData[ errorName ]
     507                ]])
     508               
     509                return return_data;
     510        }
     511
     512        /**
     513         * Returns a closure for the given entitytype that determines the value for a criterion
     514         * on the given object. The closure receives two parameters: the sample and a field.
     515         *
     516         * For example:
     517         *              How can one retrieve the value for subject.name, given a sample? This can be done by
     518         *              returning the field values sample.parentSubject:
     519         *                      { sample, field -> return getFieldValue( sample.parentSubject, field ) }
     520         * @return      Closure that retrieves the value for a field and the given field
     521         */
     522        protected Closure valueCallback( String entity ) {
     523                switch( entity ) {
     524                        case "Study":
     525                        case "studies":
     526                                return { sample, field -> return getFieldValue( sample.parent, field ) }
     527                        case "Subject":
     528                        case "subjects":
     529                                return { sample, field -> return getFieldValue( sample.parentSubject, field ); }
     530                        case "Sample":
     531                        case "samples":
     532                                return { sample, field -> return getFieldValue( sample, field ) }
     533                        case "Event":
     534                        case "events":
     535                                return { sample, field ->
     536                                        if( !sample || !sample.parentEventGroup || !sample.parentEventGroup.events || sample.parentEventGroup.events.size() == 0 )
     537                                                return null
     538
     539                                        return sample.parentEventGroup.events?.collect { getFieldValue( it, field ) };
     540                                }
     541                        case "SamplingEvent":
     542                        case "samplingEvents":
     543                                return { sample, field -> return getFieldValue( sample.parentEvent, field ); }
     544                        case "Assay":
     545                        case "assays":
     546                                return { sample, field ->
     547                                        def sampleAssays = Assay.findByParent( sample.parent ).findAll { it.samples?.contains( sample ) };
     548                                        if( sampleAssays && sampleAssays.size() > 0 )
     549                                                return sampleAssays.collect { getFieldValue( it, field ) }
     550                                        else
     551                                                return null
     552                                }
     553                }
     554        }
     555       
     556        /**
     557         * Computes the mean value and Standard Error of the mean (SEM) for the given values
     558         * @param values        List of values to compute the mean and SEM for. Strings and null
     559         *                                      values are ignored
     560         * @return                      Map with two keys: 'value' and 'error'
     561         */
     562        protected Map computeMeanAndError( values ) {
     563                // TODO: Handle the case that one of the values is a list. In that case,
     564                // all values should be taken into account.     
     565                def mean = computeMean( values );
     566                def error = computeSEM( values, mean );
     567               
     568                return [
     569                        "value": mean,
     570                        "error": error
     571                ]
     572        }
     573       
     574        /**
     575         * Computes the mean of the given values. Values that can not be parsed to a number
     576         * are ignored. If no values are given, the mean of 0 is returned.
     577         * @param values        List of values to compute the mean for
     578         * @return                      Arithmetic mean of the values
     579         */
     580        protected def computeMean( List values ) {
     581                def sumOfValues = 0;
     582                def sizeOfValues = 0;
     583                values.each { value ->
     584                        def num = getNumericValue( value );
     585                        if( num != null ) {
     586                                sumOfValues += num;
     587                                sizeOfValues++
     588                        }
     589                }
     590
     591                if( sizeOfValues > 0 )
     592                        return sumOfValues / sizeOfValues;
     593                else
     594                        return 0;
     595        }
     596
     597        /**
     598        * Computes the standard error of mean of the given values.
     599        * Values that can not be parsed to a number are ignored.
     600        * If no values are given, the standard deviation of 0 is returned.
     601        * @param values         List of values to compute the standard deviation for
     602        * @param mean           Mean of the list (if already computed). If not given, the mean
     603        *                                       will be computed using the computeMean method
     604        * @return                       Standard error of the mean of the values or 0 if no values can be used.
     605        */
     606   protected def computeSEM( List values, def mean = null ) {
     607           if( mean == null )
     608                        mean = computeMean( values )
     609           
     610           def sumOfDifferences = 0;
     611           def sizeOfValues = 0;
     612           values.each { value ->
     613                   def num = getNumericValue( value );
     614                   if( num != null ) {
     615                           sumOfDifferences += Math.pow( num - mean, 2 );
     616                           sizeOfValues++
     617                   }
     618           }
     619
     620           if( sizeOfValues > 0 ) {
     621                   def std = Math.sqrt( sumOfDifferences / sizeOfValues );
     622                   return std / Math.sqrt( sizeOfValues );
     623           } else {
     624                   return 0;
     625           }
     626   }
     627               
     628        /**
     629         * Return the numeric value of the given object, or null if no numeric value could be returned
     630         * @param       value   Object to return the value for
     631         * @return                      Number that represents the given value
     632         */
     633        protected Number getNumericValue( value ) {
     634                // TODO: handle special types of values
     635                if( value instanceof Number ) {
     636                        return value;
     637                } else if( value instanceof RelTime ) {
     638                        return value.value;
     639                }
     640               
     641                return null
     642        }
     643
     644        /**
     645         * Returns a field for a given templateentity
     646         * @param object        TemplateEntity (or subclass) to retrieve data for
     647         * @param fieldName     Name of the field to return data for.
     648         * @return                      Value of the field or null if the value could not be retrieved
     649         */
     650        protected def getFieldValue( TemplateEntity object, String fieldName ) {
     651                if( !object || !fieldName )
     652                        return null;
     653               
     654                try {
     655                        return object.getFieldValue( fieldName );
     656                } catch( Exception e ) {
     657                        return null;
     658                }
     659        }
     660
     661        /**
     662         * Parses a fieldId that has been created earlier by createFieldId
     663         * @param fieldId       FieldId to parse
     664         * @return                      Map with attributes of the selected field. Keys are 'name', 'id', 'source' and 'type'
     665         * @see createFieldId
     666         */
     667        protected Map parseFieldId( String fieldId ) {
     668                def attrs = [:]
     669               
     670                def parts = fieldId.split(",")
     671               
     672                attrs = [
     673                        "id": parts[ 0 ],
     674                        "name": parts[ 1 ],
     675                        "source": parts[ 2 ],
     676                        "type": parts[ 3 ]
     677                ]
     678        }
     679       
     680        /**
     681         * Create a fieldId based on the given attributes
     682         * @param attrs         Map of attributes for this field. Keys may be 'name', 'id', 'source' and 'type'
     683         * @return                      Unique field ID for these parameters
     684         * @see parseFieldId
     685         */
     686        protected String createFieldId( Map attrs ) {
     687                // TODO: What is one of the attributes contains a comma?
     688                def name = attrs.name;
     689                def id = attrs.id ?: name;
     690                def source = attrs.source;
     691                def type = attrs.type ?: ""
     692               
     693                return id + "," + name + "," + source + "," + type;
     694        }
     695
    477696}
  • trunk/grails-app/services/dbnp/modules/ModuleCommunicationService.groovy

    r1864 r1991  
    143143                        switch( requestMethod.toUpperCase() ) {
    144144                                case "GET":
     145                                        log.trace( "Using GET method" );
    145146                                        def url = restUrl + "?" + args;
    146147                                        def connection = url.toURL().openConnection();
     
    150151                                        break
    151152                                case "POST":
     153                                        log.trace( "Using POST method" );
    152154                                        def connection = restUrl.toURL().openConnection()
    153155                                        connection.setRequestMethod( "POST" );
  • trunk/grails-app/views/visualize/index.gsp

    r1983 r1991  
    1212        <g:javascript src="jqplot/plugins/jqplot.barRenderer.min.js" />
    1313        <g:javascript src="jqplot/plugins/jqplot.categoryAxisRenderer.min.js" />
    14         <g:javascript src="jqplot/src/plugins/jqplot.pointLabels.min.js" />     
    15         <g:javascript src="jqplot/src/plugins/jqplot.canvasTextRenderer.min.js" />     
    16         <g:javascript src="jqplot/src/plugins/jqplot.canvasAxisLabelRenderer.min.js" />
     14        <g:javascript src="jqplot/plugins/jqplot.pointLabels.min.js" />
     15        <g:javascript src="jqplot/plugins/jqplot.canvasTextRenderer.min.js" /> 
     16        <g:javascript src="jqplot/plugins/jqplot.canvasAxisLabelRenderer.min.js" />     
     17
     18        <g:javascript src="visualization.js" />
     19        <link rel="stylesheet" type="text/css" href="<g:resource dir='css' file='visualization.css' />" />
    1720       
    1821        <script type="text/javascript">
     
    2528                        "getData": "<g:createLink action="getData" />"
    2629                };
    27 
    28                 function showError( message ) {
    29                         $( '#ajaxError' ).text( message );
    30                         $( '#ajaxError' ).show();
    31                 }
    32 
    33                 /**
    34                  * Gathers data for the given request type from the form elements on the page
    35                  * @param type  String  Can be 'getStudies', 'getFields', 'getVisualizationType' or 'getData'
    36                  * @return Object               Object with the data to be sent to the server
    37                  */
    38                 function gatherData( type ) {
    39                         var data = {};
    40                
    41                         // different types of request require different data arrays
    42                         // However, some data is required for all types. For that reason,
    43                         // the fallthrough option in the switch statement is used.
    44                         switch( type ) {
    45                                 case "getData":
    46                                         var typeElement = $( '#type' );
    47                                         data[ "type" ] = { "id": typeElement.val() };                                   
    48                                 case "getVisualizationTypes":
    49                                         var rowsElement = $( '#rows' );
    50                                         var columnsElement = $( '#columns' );
    51                                         data[ "rows" ] = [
    52                                                 { "id": rowsElement.val() }
    53                                         ];                                     
    54                                         data[ "columns" ] = [
    55                                                         { "id": columnsElement.val() }
    56                                         ];                                     
    57                                 case "getFields":
    58                                         var studyElement = $( '#study' );
    59                                         data[ "studies" ] = [
    60                                                 { "id": studyElement.val() }
    61                                         ];                                     
    62                                        
    63                                 case "getStudies":
    64                         }
    65 
    66                         return data;
    67                 }
    68 
    69                 /**
    70                  * Executes an ajax call in a standardized way. Retrieves data to be sent with gatherData
    71                  * The ajaxParameters map will be sent to the $.ajax call
    72                  * @param action                        Name of the action to execute. Is also given to the gatherData method
    73                  *                                                      as a parameter and the url will be determined based on this parameter.
    74                  * @param ajaxParameters        Hashmap with parameters that are sent to the $.ajax call. The entries
    75                  *                                                      url, data and dataType are set by this method.
    76                  *                                                      An additional key 'errorMessage' can be given, with the message that will be
    77                  *                                                      shown if an error occurrs in this method. In that case, the 'error' method from
    78                  *                                                      the ajaxParameters method will be overwritten.
    79                  * @see visualizationUrls
    80                  * @see jQuery.ajax
    81                  */
    82                 function executeAjaxCall( action, ajaxParameters ) {
    83                         var data = gatherData( action );
    84 
    85                         // If no parameters are given, create an empty map
    86                         if( !ajaxParameters )
    87                                 ajaxParameters = {}
    88 
    89                         if( ajaxParameters[ "errorMessage" ] ) {
    90                                 var message = ajaxParameters[ "errorMessage" ];
    91                                 ajaxParameters[ "error" ] = function( jqXHR, textStatus, errorThrown ) {
    92                                         // An error occurred while retrieving fields from the server
    93                                         showError( "An error occurred while retrieving variables from the server. Please try again or contact a system administrator." );
    94                                 }
    95 
    96                                 // Remove the error message
    97                                 delete ajaxParameters[ "errorMessage" ];
    98                         }
    99                        
    100                         // Retrieve a new list of fields from the controller
    101                         // based on the study we chose
    102                         $.ajax($.extend({
    103                                 url: visualizationUrls[ action ],
    104                                 data: "data=" + JSON.stringify( data ),
    105                                 dataType: "json",
    106                         }, ajaxParameters ) );
    107                 }
    108                
    109                 function changeStudy() {
    110                         executeAjaxCall( "getFields", {
    111                                 "errorMessage": "An error occurred while retrieving variables from the server. Please try again or contact a system administrator.",
    112                                 "success": function( data, textStatus, jqXHR ) {
    113                                         // Remove all previous entries from the list
    114                                         $( '#rows, #columns' ).empty();
    115 
    116                                         // Add all fields to the lists
    117                                         $.each( data, function( idx, field ) {
    118                                                 $( '#rows, #columns' ).append( $( "<option>" ).val( field.id ).text( field.name ) );
    119                                         });
    120                                        
    121                                         $( "#step2" ).show();
    122                                         $( "#step3" ).hide();
    123                                 }
    124                         });
    125                 }
    126 
    127                 function changeFields() {
    128                         executeAjaxCall( "getVisualizationTypes", {
    129                                 "errorMessage": "An error occurred while retrieving visualization types from the server. Please try again or contact a system administrator.",
    130                                 "success": function( data, textStatus, jqXHR ) {
    131                                         // Remove all previous entries from the list
    132                                         $( '#types' ).empty();
    133 
    134                                         // Add all fields to the lists
    135                                         $.each( data, function( idx, field ) {
    136                                                 $( '#types' ).append( $( "<option>" ).val( field.id ).text( field.name ) );
    137                                         });
    138 
    139                                         $( "#step3" ).show();
    140                                 }
    141                         });
    142                 }
    143 
    144                 function visualize() {
    145                         executeAjaxCall( "getData", {
    146                                 "errorMessage": "An error occurred while retrieving data from the server. Please try again or contact a system administrator.",
    147                                 "success": function( data, textStatus, jqXHR ) {
    148                                         /*
    149                                                 Data expected:
    150                                                 {
    151                                                         "type": "barchart",
    152                                                         "x": [ "Q1", "Q2", "Q3", "Q4" ],
    153                                                         "xaxis": { "title": "quarter 2011", "unit": "" },
    154                                                         "yaxis": { "title": "temperature", "unit": "degrees C" },
    155                                                         "series": [
    156                                                                 {
    157                                                                         "name": "series name",
    158                                                                         "y": [ 5.1, 3.1, 20.6, 15.4 ],
    159                                                                         "error": [ 0.5, 0.2, 0.4, 0.5 ]
    160                                                                 },
    161                                                         ]
    162                                                 }
    163                                         */
    164 
    165                                         // TODO: error handling if incorrect data is returned
    166                                          
    167                                         // Retrieve the datapoints from the json object
    168                                         var dataPoints = [];
    169                                         var series = [];
    170 
    171                                         $.each(data.series, function(idx, element ) {
    172                                                 dataPoints[ dataPoints.length ] = element.y;
    173                                                 series[ series.length ] = { "label": element.name };
    174                                         });
    175                                        
    176                                         // TODO: create a chart based on the data that is sent by the user and the type of chart
    177                                         // chosen by the user
    178                                         chart = $.jqplot('visualization', dataPoints, {
    179                                                 // Tell the plot to stack the bars.
    180                                                 stackSeries: true,
    181                                                 captureRightClick: true,
    182                                                 seriesDefaults:{
    183                                                         renderer:$.jqplot.BarRenderer,
    184                                                         rendererOptions: {
    185                                                                         // Put a 30 pixel margin between bars.
    186                                                                         barMargin: 30,
    187                                                                         // Highlight bars when mouse button pressed.
    188                                                                         // Disables default highlighting on mouse over.
    189                                                                         highlightMouseDown: true         
    190                                                         },
    191                                                         pointLabels: {show: true}
    192                                                 },
    193                                                 series: series,
    194                                                 axes: {
    195                                                         xaxis: {
    196                                                                         renderer: $.jqplot.CategoryAxisRenderer,
    197                                                                         ticks: data.x,
    198                                                                         label: data[ "xaxis" ].title + " (" + data[ "xaxis" ].unit + ")",
    199                                                                         labelRenderer: $.jqplot.CanvasAxisLabelRenderer
    200                                                         },
    201                                                         yaxis: {
    202                                                                 // Don't pad out the bottom of the data range.  By default,
    203                                                                 // axes scaled as if data extended 10% above and below the
    204                                                                 // actual range to prevent data points right on grid boundaries.
    205                                                                 // Don't want to do that here.
    206                                                                 padMin: 0,
    207                                                                 label: data[ "yaxis" ].title + " (" + data[ "yaxis" ].unit + ")",
    208                                                                 labelRenderer: $.jqplot.CanvasAxisLabelRenderer
    209                                                         }
    210                                                 },
    211                                                 legend: {
    212                                                         show: true,
    213                                                         location: 'e',
    214                                                         placement: 'outside'
    215                                                 }                       
    216                                         });
    217                                        
    218                                         $( "#visualization" ).show();
    219                                 },
    220                         });
    221                 }
    22230        </script>
    223         <style type="text/css">
    224                 /* #step2, #step3 { display: none; } */
    225                 #ajaxError {
    226                         display: none;
    227                         border: 1px solid #f99; /* #006dba; */
    228                         margin-bottom: 10px;
    229                         margin-top: 10px;
    230                
    231                         background: #ffe0e0 url(${fam.icon( name: 'error' )}) 10px 10px no-repeat;
    232                         padding: 10px 10px 10px 33px;
    233                 }
    234                
    235                 label { display: inline-block; zoom: 1; *display: inline; width: 110px; margin-top: 10px;  }
    236 
    237                 #visualizationForm { position: relative; margin: 10px 0; font-size: 11px; }
    238                 #visualizationForm h3 { font-size: 13px; }
    239                 #visualizationForm h3 .nummer { display: inline-block; zoom: 1; *display: inline; width: 25px; }
    240                
    241                 #visualizationForm p { margin-left: 25px; }
    242                
    243                 table.jqplot-table-legend { width: 100px; }
    244         </style>
    24531</head>
    24632<body>
Note: See TracChangeset for help on using the changeset viewer.