source: trunk/grails-app/controllers/dbnp/visualization/VisualizeController.groovy @ 1984

Last change on this file since 1984 was 1984, checked in by taco@…, 8 years ago

Update for the visualization controller

File size: 19.3 KB
Line 
1/**
2 * Visualize Controller
3 *
4 * This controller enables the user to visualize his data
5 *
6 * @author  robert@thehyve.nl
7 * @since       20110825
8 * @package     dbnp.visualization
9 *
10 * Revision information:
11 * $Rev$
12 * $Author$
13 * $Date$
14 */
15package dbnp.visualization
16
17import dbnp.studycapturing.*;
18import grails.converters.JSON
19import org.dbnp.gdt.*
20
21class VisualizeController {
22        def authenticationService
23        def moduleCommunicationService
24       
25        /**
26         * Shows the visualization screen
27         */
28    def index = {
29                [ studies: Study.giveReadableStudies( authenticationService.getLoggedInUser() )]
30        }
31       
32        def getStudies = {
33                def studies = Study.giveReadableStudies( authenticationService.getLoggedInUser() );
34                render studies as JSON
35        }
36       
37        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        }
93
94                render fields as JSON
95        }
96       
97        def getVisualizationTypes = {
98                def types = [ [ "id": "barchart", "name": "Barchart"] ];
99                render types as JSON
100        }
101       
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    }
477}
Note: See TracBrowser for help on using the repository browser.