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

Revision 128, 15.2 KB (checked in by jahn, 4 years ago)

Added 2.5 new REST methods exposing the assay related data according to specs.

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