root/grails-app/controllers/RestController.groovy @ 135

Revision 135, 16.5 KB (checked in by jahn, 4 years ago)

Fixed bug in getAssayURL.

Line 
1import data.*
2import grails.converters.*
3import java.io.PrintWriter
4import java.io.StringWriter
5import org.codehaus.groovy.grails.web.json.*
6import org.compass.core.engine.SearchEngineQueryParseException
7import org.compass.core.impl.*
8import dbnp.rest.common.CommunicationManager
9
10
11class RestController {
12
13    def searchableService
14
15    def simpleAssayList = []
16    def measurementDataList = []
17
18
19
20    /** Expose the list of features within a certain assay
21     *
22     * @author Adem and Jahn
23     * @since 20100525
24     *
25     * $Rev$
26     *
27     * This class provides a REST-full service for getting and setting the data
28     * in the Simple Assay Module (SAM). The service consists of several
29     * resources listed below. So far, all resources are GET resoruces, i.e. we
30     * do not use PUT, POST or DELETE. Because we are using Grails' web libaries,
31     * each resource corresponds to exactly one action of this controller.
32     *
33     *
34     *
35     * The REST resources implemented in this controller are:
36     *
37     * SAM-host:port/sam/rest/getMeasurements
38     * SAM-host:port/sam/rest/getMeasurementsForValue
39     * SAM-host:port/sam/rest/getMeasurementsForRange
40     * SAM-host:port/sam/rest/getMeasurementsForSample
41     * SAM-host:port/sam/rest/getDataSimple
42     * SAM-host:port/sam/rest/getAvailableBiomarkers
43     *
44     * Where 'SAM-host-url' is the url of the SAM server's host with port number 'port'.
45     *
46     *
47     *
48     * Some of the resources use additional parameters such, e.g. because they Grails'
49     * Searchable plugin. For instance, the getMeasurement resource is addressed as follows:
50     *
51     * SAM-host:port/sam/rest/getMeasurements?submit=Query&ump;q=ldl
52     *
53     * In this example the first paramter is 'submit' with value 'Query' and the second
54     * parameter is 'q' with value 'ldl'. The parameter 'submit' is neccessary to exploit
55     * automatic parsing functionality of the the Searchable plugin. The parameter 'q'
56     * is the keyword requested by the same plugin.
57     */
58
59
60
61
62      /****************************************************************/
63     /* REST resources for providing basic data to the GSCF          */
64    /****************************************************************/
65
66
67
68    /**
69     * Return a list of simple assay measurements matching the querying text.
70     * Uses Searchable plugin.
71     *
72     * @param assayToken
73     * @return list of meassurements for token. Each member of the list is a hash.
74         *                      the hash contains the three keys values pairs: value, sampleToken, and
75         *                      measurementMetadata.
76     *
77     * Example REST call:
78     * http://localhost:8182/sam/rest/getMeasurements/z?assayToken=2
79     *
80     * Resulting JSON object:
81     *
82     * [{"value":135,"sampleToken":"1_A","measurementToken":"total carbon dioxide (tCO)"},
83         * {"value":4.2,"sampleToken":"1_A","measurementToken":"sodium (Na+)"} ]
84     */
85    def getMeasurements = {
86                def assayToken = params.assayToken
87        if( !assayToken ) { return render [] as JSON }
88
89                def list = []
90                def assay = SimpleAssay.find( "from SimpleAssay as a where a.id = " + assayToken )
91                def mesaurements = SimpleAssayMeasurement
92                        .findAll( "from SimpleAssayMeasurement as m where m.assay =?", [assay] )
93                                .each{ measurement ->
94                                        def tripple = [:]
95                                tripple['value'] = measurement.value
96                                tripple['sampleToken'] = measurement.sample.externalSampleId
97                                tripple['measurementToken'] = measurement.type.name
98                                        list.push tripple
99                                }
100                render list as JSON
101    }
102
103
104    /**
105     * Return measurement metadata for measurement
106     *
107     * @param assayToken
108     * @param measurementTokens
109     * @return list of measurements
110     *
111     * Example REST call:
112         * http://localhost:8182/sam/rest/getMeasurementMetadata/33?assayToken=2
113     *      &measurementToken=total calcium (Ca)
114         *              &measurementToken=glucose
115     *
116     * Resulting JSON object:
117     *
118         * [ {"name":"glucose","unit":"mg/dl","referenceValues":null,"correctionMethod":null,"isDrug":false,
119         *    "isIntake":false,"inSerum":false,"isNew":false},
120         *   {"name":"total calcium (Ca)","unit":"mg/dl",
121         *    "referenceValues":null,"correctionMethod":null,"isDrug":false,"isIntake":false,"inSerum":false,
122         *    "isNew":false} ]
123         */
124    def getMeasurementMetadata = {
125
126                def assayToken = params.assayToken
127                def measurementTokens = params.measurementToken
128
129        if( !assayToken || !measurementTokens ) {
130                        return render [] as JSON
131                }
132
133        def list = []
134        def types = []
135        def assay = SimpleAssay.find( "from SimpleAssay as a where a.externalAssayID =?", [assayToken] )
136
137                if( measurementTokens.class == java.lang.String ) {
138                        measurementTokens = [ measurementTokens ]
139                }
140
141        SimpleAssayMeasurement
142            .findAll( "from SimpleAssayMeasurement as m where m.assay =?", [assay] )
143                .each{ if( !types.contains(it.type)) types.push( it.type ) }
144   
145        types.each { type ->
146                        def isInList = false
147                    measurementTokens.each { if(it==type.name) { isInList = true } }
148                        println type
149                        if(     isInList ) {
150                        def tuple = [:]
151                        tuple['name'] = type.name
152                        tuple['unit'] = type.unit
153                        tuple['referenceValues'] = type.referenceValues
154                        tuple['correctionMethod'] = type.correctionMethod
155                        tuple['isDrug'] = type.isDrug
156                        tuple['isIntake'] = type.isIntake
157                        tuple['inSerum'] = type.inSerum
158                        tuple['isNew'] = type.isNew
159                        list.push tuple
160                }
161        }
162
163        render list as JSON
164    }
165
166
167
168    /*
169     * Return list of metadata fields available for the samples in an assay
170     *
171     * @param assayTokes
172     * @param measurementTokens
173     * @param sampleTokens
174     * @return  table (as hash) with values for given samples and measurements
175         *
176     * Example REST call:
177         * http://localhost:8182/sam/rest/getMeasurementData/doit?assayToken=PPSH-Glu-A
178     *    &measurementToken=total carbon dioxide (tCO)
179         *    &sampleToken=5_A
180         *    &sampleToken=1_A
181         *
182     * Resulting JSON object:
183         * [ {"sampleToken":"1_A","typeToken":"total carbon dioxide (tCO)","value":28},
184         *   {"sampleToken":"5_A","typeToken":"total carbon dioxide (tCO)","value":29} ]
185     */
186    def getMeasurementData = {
187                def assayToken = params.assayToken
188                def measurementTokens = params.measurementToken
189                def sampleTokens = params.sampleToken
190
191        if( !assayToken || !measurementTokens || !sampleTokens ) {
192                        return render [] as JSON
193                }
194
195                if( measurementTokens.class == java.lang.String ) {
196                        measurementTokens = [ measurementTokens ]
197                }
198
199                if( sampleTokens.class == java.lang.String ) {
200                        sampleTokens = [ sampleTokens ]
201                }
202
203        def assay = SimpleAssay.find( "from SimpleAssay as a where a.externalAssayID =?", [assayToken] )
204
205                def data = SimpleAssayMeasurement
206                        .findAll( "from SimpleAssayMeasurement as m where m.assay =?", [assay] )
207
208                def results = []
209                data.each { measurement ->
210                        def type = measurement.type.name
211                        def sample = measurement.sample.externalSampleId
212                        def isMatch = false
213
214                        measurementTokens.each { if(it==type) isMatch=true }
215                        if( isMatch ) {
216                           isMatch=false
217                           sampleTokens.each { if(it==sample) isMatch=true }
218                        }
219                        if( isMatch ) {
220                                results.push( [ 'sampleToken':sample, 'typeToken':type, 'value':measurement.value ] )
221                        }       
222                }
223
224                render results as JSON
225    }
226
227
228
229    /**
230     * Return URL to view an assay.
231     *
232     * @param  assayToken
233     * @return URL to view an assay.
234         *
235     */
236    def getAssayURL = {
237
238                def assayToken = params.assayToken
239                def assay = SimpleAssay.find( "from SimpleAssay as a where a.id = " + assayToken )
240
241        if( !assayToken || !sampleTokens ) {
242                        return render [] as JSON
243                }
244
245            String url = 'CommunicationManager.SAMServerURL' +
246                        '/SimpleAssay/show/' + assay.id
247
248                render url as JSON
249        }
250
251
252
253
254
255      /***************************************************/
256     /* REST resources related to the querying in GSCF  */
257    /***************************************************/
258
259
260    /**
261     * Run searchable on query string for finding hits in SimpleAssays and SimpleAssayMeasurementTypes.
262     *
263     * @param  string: Query string
264     * @return map.    The map contains two keys "studyIds" and "assays". studyIds represent the studies for 
265     *                 which MeasurementTypes were found containing the query string. assayIds are the
266     *                 externalAssayIDs of the SimpleAssays for which a match was found.
267     *                     Example of a returned map:  [ assays:[data.SimpleAssay : 1], studyIds:[PPSH] ].
268     */
269    def getQueryResult = {
270                def query = params['query']
271                def searchResult = querySearchable( query )
272                def results = getResultingObjects( searchResult )       // get assays and studies
273                render results as JSON 
274    }
275
276
277
278    /**
279     * Run searchable on query string and values for finding hits in SimpleAssayMeasurement and
280     * SimpleAssayMeasurementType. (This rest resource is called by the SimpleQuery of GSCF.)
281     *
282     * @param  string: Query string
283     * @return map. The map contains enriched information on SimpleAssayMeasurements.   
284     *                 
285     * Example return value (contained in JSON):                 
286         *              [[externalAssayId:1, externalSampleId:A10_B, type:Glucose, unit:Insulin, value:202.0],
287         *               [externalAssayId:1, externalSampleId:A1_B, type:Insulin, unit:g, value:101.0]]
288     */
289    def getQueryResultWithOperator = {
290                def userQuery = params['query']
291                def operator  = params['operator']
292                def value     = params['value']
293                def dValue = value.toDouble()
294                def searchResult = querySearchable( userQuery )
295
296                def query = "from SimpleAssayMeasurement as m where m.type =:type AND m.value "
297                if( operator=="<" )      { query += " <:value " }
298                else if( operator==">" ) { query += " >:value " }
299                else                                 { query += " = :value " }
300
301                def measurements = []
302                searchResult.each{ result ->
303                        if( result.class == SimpleAssayMeasurementType ) {
304
305                                SimpleAssayMeasurement.findAll( query, [type:result,value:dValue] ).each {
306                                        measurements.add( it.getRestRepresentation() )
307                                }
308                        }
309                }
310
311                render measurements as JSON 
312    }
313
314
315
316
317
318
319
320    /**
321     * Gets measurement for value, return a list of Measurement value matching the querying keyword and value
322     * Uses Searchable plugin.
323     *
324     * @param measurement name
325     * @param value
326     * @return list of measurement values
327     */
328    def getMeasurementsForValue = {
329        try {
330                        render params
331            def measurementName = params.q.getAt(0)
332            def measurementValue = params.q.getAt(1)
333
334            for ( i in SimpleAssayMeasurement.list()){
335                if ( (i.assay.name.equals(measurementName))&&(i.value.equals(measurementValue.toFloat()))){
336                    measurementDataList.add(i)
337                }
338            }
339            render measurementDataList
340        } catch (SearchEngineQueryParseException ex) {
341            return [parseException: true]
342        }
343    }
344
345
346    /**
347     * Return a list of Measurement corresponding to the searched
348     * Measurement with the searched min and max values
349     * Uses Searchable plugin.
350     *
351     * @params measurement name
352     * @params min value
353     * @params max value
354     */
355    def getMeasurementsForRange = {
356        try {
357            def measurementName = params.q.getAt(0)
358            def measurementMinValue = params.q.getAt(1)
359            def measurementMaxValue = params.q.getAt(2)
360
361            for ( i in SimpleAssayMeasurement.list()){
362                if ( (i.assay.name.equals(measurementName))&&(i.value > measurementMinValue.toFloat() )
363                &&(i.value < measurementMaxValue.toFloat() )){
364                    measurementDataList.add(i)
365                }
366            }
367            render measurementDataList
368        } catch (SearchEngineQueryParseException ex) {
369            return [parseException: true]
370        }
371    }
372
373
374
375    /**
376     * Get a measurement for a given sample, return a list of all measurements
377     * that reference the given sample in the study.
378     *
379     * @param sample ID
380     * @return list of Measurements linked to the param
381     */
382
383    def getMeasurementsForSample = {
384    try {
385            def paramId = params.q
386            for ( i in SimpleAssay.list()){
387                if ( i.sampleID.equals(paramId) ){
388                    simpleAssayList.add(i)
389                }
390            }
391            render simpleAssayList
392        } catch (SearchEngineQueryParseException ex) {
393            return [parseException: true]
394        }
395    }
396
397
398    /**
399     * Return measurement value referencing a given measurement.
400     *
401     * @param measurement name
402     * @return list of measurement values
403     */
404    def getDataSimple = {
405         try {
406            def measurementName = params.q
407
408            for ( i in SimpleAssayMeasurement.list()){
409               if ( i.assay.name.equals(measurementName)){
410                   measurementDataList.add(i)
411               }
412            }
413            render measurementDataList
414        } catch (SearchEngineQueryParseException ex) {
415            return [parseException: true]
416        }
417    }
418
419    /**
420     * getDataForSample return the data relatives to a sample
421     * @param sample ID
422     * @return list of Measurements
423     */
424    def getDataForSample = {
425        try {
426            //change this value depending on the GET method
427            def sampleId = params.q
428                   
429            for ( i in SimpleAssay.list()){         
430               if ( i.sampleID.equals(sampleId)){
431                   measurementDataList.add(i.measurements)
432                 render i.measurements
433               }
434            }
435            render measurementDataList
436           
437        } catch (SearchEngineQueryParseException ex) {
438            return [parseException: true]
439        }
440    }
441
442
443    def getAvailableBiomarkers = {
444
445         try {
446            def measurementName = params.q
447            render measurementsList
448        } catch (SearchEngineQueryParseException ex) {
449            return [parseException: true]
450        }
451    }
452
453
454
455
456
457
458    /**  Launch a Searchable query and return its result object
459     *
460     *   @param  searchable query string, e.g. "Insulin"
461     *   @return Resulting searchable object
462     */
463    private Object querySearchable( query ) {
464                def searchResult = []
465                try {
466                        searchResult = searchableService.search( query )
467                        searchResult = searchResult['results']
468                }
469                catch( SearchEngineQueryParseException e) { println "SEQPE: " + e }
470                catch( Exception ex) {
471                        StringWriter sw = new StringWriter();
472                        ex.printStackTrace(new PrintWriter(sw));
473                        String stacktrace = sw.toString();
474                        System.out.println("stacktrace = " + stacktrace);
475                }
476                return searchResult
477    }
478
479
480
481    /**  For SimpleAssayMeasurement objects, collect their correpsonding externalAssayIDs
482     *   in a set (i.e., collection without double entries).
483     *   
484     *   @param  SimpleAssayMeasurement object
485     *   @return List of externalAssayIds corresponding to studies, which contain samples, which
486     *           use the given type.
487     */
488    private Object getAssaysForMeasurementType( type ) {
489                // unfortunately there seems to be no easy way to get the table name for a domain class?
490                // a method via the metaclass is mentioned here: http://taapps-javalibs.blogspot.com/2009/03/grailshow-to-get-database-table-and.html
491                // but if the names are changed, this has to be refactored inside this query
492                // Use 'select distinct a' to get a list of SimpleAssay objects instead of a list of ids
493                def assayQuery  = "select distinct a.externalAssayID from SimpleAssayMeasurement m, SimpleAssay a "
494                        assayQuery += "where m.type.id = ${type.id} and m.assay.id=a.id"
495                def assays = SimpleAssay.executeQuery(assayQuery)
496                return assays
497    }
498
499   
500
501    /** Collect studyIds and SimpleAssay domain objects related to the result of a
502     *  Searchable query.
503     *   
504     *   @param  queryResult: Searchable result (Map).
505     *   @return Map with one key that has a list as value. The key is 'assays'.
506     *           The list contains externalAssayIds of the assays that match the given query string.
507     *           Example of a returned map:  [ assays:[2,3] ].
508     */
509    private Map getResultingObjects( queryResult ) {
510                def results = [assays:[]]
511                queryResult.each { result ->
512                        switch( result ) {
513                                case { it instanceof data.SimpleAssayMeasurementType }:
514                                        def assays = getAssaysForMeasurementType(result)
515                                        results['assays'] = results['assays'].plus(assays)
516                                                break
517                                case { it instanceof data.SimpleAssay }:
518                                        results['assays'] = results['assays'].plus(result.id)
519                                        break
520                        }
521                }
522                return results
523    }
524
525
526
527
528        def test = {
529                params.x.each{ println it }
530                render params
531        }
532
533}
534
Note: See TracBrowser for help on using the browser.