root/trunk/grails-app/controllers/dbnp/studycapturing/AssayController.groovy @ 2095

Revision 2095, 14.7 KB (checked in by s.h.sikkema@…, 2 years ago)

- Merged assay export and galaxy assay fetch code - now possible to select fetched data in galaxy
- removed unused assay methods and gsps generated by grails
- getUser now returns displayName instead of username in case of shibboleth users

  • Property svn:keywords set to Rev Author Date
Line 
1package dbnp.studycapturing
2
3import grails.plugins.springsecurity.Secured
4import grails.converters.JSON
5
6@Secured(['IS_AUTHENTICATED_REMEMBERED'])
7class AssayController {
8
9        def assayService
10        def authenticationService
11        def fileService
12
13        def showByToken = {
14                def assayInstance = Assay.findByAssayUUID(params.id)
15                if (!assayInstance) {
16                        flash.message = "${message(code: 'default.not.found.message', args: [message(code: 'assay.label', default: 'Assay'), params.id])}"
17                        redirect(action: "list")
18                }
19                else {
20                        redirect(action: "show", id: assayInstance.id)
21                }
22        }
23
24        def assayExportFlow = {
25                entry {
26                        action{
27                                def user            = authenticationService.getLoggedInUser()
28                                flow.userStudies    = Study.giveReadableStudies(user)
29
30                                // store Galaxy parameters in the flow. Will be null when not in galaxy mode.
31                                flow.GALAXY_URL = params.GALAXY_URL
32                                flow.tool_id = params.tool_id
33                        }
34                        on("success").to "selectAssay"
35                }
36
37                selectAssay {
38                        on ("submit"){
39                                flow.assay = Assay.get(params.assayId)
40
41                                // check if assay exists
42                                if (!flow.assay) throw new Exception("No assay found with id: ${params.assayId}")
43
44                                // obtain fields for each category
45                                flow.fieldMap = assayService.collectAssayTemplateFields(flow.assay, null)
46
47                                flash.errorMessage = flow.fieldMap.remove('Module Error')
48                                flow.measurementTokens = flow.fieldMap.remove('Module Measurement Data')
49                        }.to "selectFields"
50
51                        on(Exception).to "handleError"
52                }
53
54                selectFields {
55                        on ("submit"){
56
57                                def (fieldMapSelection, measurementTokens) = processExportSelection(flow.assay, flow.fieldMap, params)
58
59                                // interpret the params set and gather the data
60                                flow.rowData = collectAssayData(flow.assay, fieldMapSelection, measurementTokens, [])
61
62                                // remember the selected file type
63                                flow.exportFileType = params.exportFileType
64
65                                // prepare the assay data preview
66                                def previewRows         = Math.min(flow.rowData.size()    as int, 5) - 1
67                                def previewCols         = Math.min(flow.rowData[0].size() as int, 5) - 1
68
69                                flow.assayDataPreview   = flow.rowData[0..previewRows].collect{ it[0..previewCols] as ArrayList }
70
71                        }.to "compileExportData"
72
73                        on("submitToGalaxy") {
74
75                                def (fieldMapSelection, measurementTokens) = processExportSelection(flow.assay, flow.fieldMap, params)
76
77                                // create a random session token that will be used to allow to module to
78                                // sync with gscf prior to presenting the measurement data
79                                def sessionToken = UUID.randomUUID().toString()
80                                def consumer = "galaxy"
81
82                                // put the session token to work
83                                authenticationService.logInRemotely( consumer, sessionToken, authenticationService.getLoggedInUser() )
84
85                                // create a link to the galaxy fetch action where galaxy can fetch the data from
86                                flow.fetchUrl = g.createLink(
87                                        absolute: true,
88                                        controller: "assay",
89                                        action: "fetchGalaxyData",
90                                        params: [
91                                                assayToken: flow.assay.assayUUID,
92                                                sessionToken: sessionToken,
93                                                fieldMapSelection: fieldMapSelection as JSON,
94                                                measurementTokens: measurementTokens as JSON] )
95
96                        }.to "galaxySubmitPage"
97
98                        on(Exception).to "handleError"
99                }
100
101                compileExportData {
102                        on ("ok"){
103
104                                session.rowData = flow.rowData
105                                session.exportFileType = flow.exportFileType
106
107                        }.to "export"
108                        on ("cancel").to "selectAssay"
109                }
110
111                export {
112                        redirect(action: 'doExport')
113                }
114
115                handleError {
116                        render(view: 'errorPage')
117                }
118
119                // renders a page that directly POSTs a form to galaxy
120                galaxySubmitPage()
121
122        }
123
124        def processExportSelection(assay, fieldMap, params) {
125
126                def fieldMapSelection = [:]
127
128                fieldMap.eachWithIndex { category, categoryIndex ->
129
130                        if (params."cat_$categoryIndex" == 'on') {
131                                fieldMapSelection[category.key] = []
132
133                                category.value.eachWithIndex { field, field_i ->
134
135                                if (params."cat_${categoryIndex}_${field_i}" == 'on')
136                                        fieldMapSelection[category.key] += field
137                                }
138
139                                if (fieldMapSelection[category.key] == [])
140                                        fieldMapSelection.remove(category.key)
141                        }
142                }
143
144                def measurementTokens = []
145
146                if (params."cat_4" == 'on') {
147                        measurementTokens = params.list( "measurementToken" )
148                }
149
150                [fieldMapSelection, measurementTokens]
151        }
152
153        def collectAssayData(assay, fieldMapSelection, measurementTokens, samples, remoteUser = null) {
154                // collect the assay data according to user selection
155                def assayData           = assayService.collectAssayData(assay, fieldMapSelection, measurementTokens, samples, remoteUser)
156
157                flash.errorMessage      = assayData.remove('Module Error')
158
159                assayService.convertColumnToRowStructure(assayData)
160        }
161
162        // This method is accessible for each user. However, he should return with a valid
163        // session token
164        @Secured(['true'])
165        def fetchGalaxyData = {
166
167                def fieldMapSelection = JSON.parse((String) params.fieldMapSelection)
168                def measurementTokens = JSON.parse((String) params.measurementTokens)
169
170                println fieldMapSelection
171                println measurementTokens
172
173                // Check accessibility
174                def consumer = "galaxy"
175                def remoteUser = authenticationService.getRemotelyLoggedInUser( consumer, params.sessionToken )
176                if( !remoteUser ) {
177                        response.status = 401
178                        render "You must be logged in"
179                        return
180                }
181
182                // retrieve assay
183                def assay = Assay.findByAssayUUID( params.assayToken )
184
185                if( !assay ) {
186                        response.status = 404
187                        render "No assay found"
188                        return
189                }
190
191                def rowData = collectAssayData(assay, fieldMapSelection, measurementTokens, [], remoteUser)
192
193                // Invalidate session token
194                authenticationService.logOffRemotely( consumer, params.sessionToken )
195
196                def outputDelimiter = '\t'
197                def outputFileExtension = 'txt'
198
199                def filename = "export.$outputFileExtension"
200                response.setHeader("Content-disposition", "attachment;filename=\"${filename}\"")
201                response.setContentType("application/octet-stream")
202                try {
203                        // Skip first row for now to support the current PLS tool in Galaxy, will change in the future
204                        assayService.exportRowWiseDataToCSVFile(rowData[1..-1], response.outputStream, outputDelimiter, java.util.Locale.US)
205                } catch (Exception e) {
206                        flash.errorMessage = e.message
207                        redirect action: 'errorPage'
208                }
209        }
210
211        /**
212         * Export the row data in session.rowData to the outputStream of the http
213         * response.
214         */
215        def doExport = {
216
217                // make sure we're coming from the export flow, otherwise redirect there
218                if (!(session.rowData && session.exportFileType))
219                        redirect(action: 'assayExportFlow')
220
221                // process requested output file type
222                def outputDelimiter, outputFileExtension, locale = java.util.Locale.US
223
224                switch(session.exportFileType) {
225                        case '2': // Comma delimited csv
226                                outputDelimiter = ','
227                                outputFileExtension = 'csv'
228                                break
229                        case '3': // Semicolon delimited csv
230                                outputDelimiter = ';'
231                                outputFileExtension = 'csv'
232                                locale = java.util.Locale.GERMAN // force use of comma as decimal separator
233                                break
234                        default: // Tab delimited with .txt extension
235                                outputDelimiter = '\t'
236                                outputFileExtension = 'txt'
237                }
238
239                def filename = "export.$outputFileExtension"
240                response.setHeader("Content-disposition", "attachment;filename=\"${filename}\"")
241                response.setContentType("application/octet-stream")
242                try {
243
244                        assayService.exportRowWiseDataToCSVFile(session.rowData, response.outputStream, outputDelimiter, locale)
245
246                        // clear the data from the session
247                        session.removeAttribute('rowData')
248                        session.removeAttribute('exportFileType')
249
250                } catch (Exception e) {
251
252                        flash.errorMessage = e.message
253                        redirect action: 'errorPage'
254
255                }
256        }
257
258        /**
259         * Method to export one or more assays to excel in separate sheets.
260         *
261         * @param       params.ids              One or more assay IDs to export
262         * @param       params.format   "list" in order to export all assays in one big excel sheet
263         *                                                      "sheets" in order to export every assay on its own sheet (default)
264         */
265        def exportToExcel = {
266                def format = params.get( 'format', 'sheets' );
267                if( format == 'list' ) {
268                        exportToExcelAsList( params );
269                } else {
270                        exportToExcelAsSheets( params );
271                }
272        }
273
274        /**
275         * Method to export one or more assays to excel in separate sheets.
276         *
277         * @param       params.ids              One or more assay IDs to export
278         */
279        def exportToExcelAsSheets = {
280                def assays = getAssaysFromParams( params );
281
282                if( !assays )
283                        return;
284
285                // Send headers to the browser so the user can download the file
286                def filename = 'export.xlsx'
287                response.setHeader("Content-disposition", "attachment;filename=\"${filename}\"")
288                response.setContentType("application/octet-stream")
289
290                try {
291                        // Loop through all assays to collect the data
292                        def rowWiseAssayData = [];
293
294                        assays.each { assay ->
295                                // Determine which fields should be exported for this assay
296                                def fieldMap = assayService.collectAssayTemplateFields(assay, null)
297                                def measurementTokens = fieldMap.remove('Module Measurement Data')
298
299                                // Retrieve row based data for this assay
300                                def assayData = assayService.collectAssayData( assay, fieldMap, measurementTokens, [] );
301                                def rowData   = assayService.convertColumnToRowStructure(assayData)
302
303                                // Put each assay on another sheet
304                                rowWiseAssayData << rowData;
305                        }
306
307                        assayService.exportRowWiseDataForMultipleAssaysToExcelFile( rowWiseAssayData, response.getOutputStream() )
308
309                        response.outputStream.flush()
310
311                } catch (Exception e) {
312                        throw e;
313                }
314        }
315
316        /**
317         * Method to export one or more assays to excel.
318         *
319         * @param       params.ids              One or more assay IDs to export
320         */
321        def exportToExcelAsList = {
322                def assays = getAssaysFromParams( params );
323
324                if( !assays )
325                        return;
326
327                // Send headers to the browser so the user can download the file
328                def filename = 'export.csv'
329                response.setHeader("Content-disposition", "attachment;filename=\"${filename}\"")
330                response.setContentType("application/octet-stream")
331
332                try {
333                        // Loop through all assays to collect the data
334                        def columnWiseAssayData = [];
335
336                        assays.each { assay ->
337                                // Determine which fields should be exported for this assay
338                                def fieldMap = assayService.collectAssayTemplateFields(assay, null)
339                                def measurementTokens = fieldMap.remove('Module Measurement Data')
340
341                                // Retrieve row based data for this assay
342                                def assayData = assayService.collectAssayData( assay, fieldMap, measurementTokens, [] );
343
344                                // Prepend study and assay data to the list
345                                assayData = assayService.prependAssayData( assayData, assay, assay.samples?.size() )
346                                assayData = assayService.prependStudyData( assayData, assay, assay.samples?.size() )
347
348                                // Put each assay on another sheet
349                                columnWiseAssayData << assayData;
350                        }
351
352                        // Merge data from all assays
353                        def mergedColumnWiseData = assayService.mergeColumnWiseDataOfMultipleStudies( columnWiseAssayData );
354
355                        def rowData   = assayService.convertColumnToRowStructure(mergedColumnWiseData)
356                        assayService.exportRowWiseDataToCSVFile( rowData, response.getOutputStream() )
357
358                        response.outputStream.flush()
359
360                } catch (Exception e) {
361                        throw e;
362                }
363        }
364
365        /**
366         * Method to export one or more samples to csv in separate sheets.
367         *
368         * @param       params.ids              One or more sample ids to export
369         */
370        def exportSamplesToCsv = {
371                def samples = getSamplesFromParams( params );
372
373                if( !samples ) {
374                        return;
375                }
376
377                // Determine a list of assays these samples have been involved in. That way, we can
378                // retrieve the data for that assay once, and save precious time doing HTTP calls
379                def assays = [:];
380
381                samples.each { sample ->
382                        def thisAssays = sample.getAssays();
383
384                        // Loop through all assays. If it already exists, add the sample it to the list
385                        thisAssays.each { assay ->
386                                if( !assays[ assay.id ] ) {
387                                        assays[ assay.id ] = [ 'assay': assay, 'samples': [] ]
388                                }
389
390                                assays[ assay.id ].samples << sample
391                        }
392                }
393
394                // Now collect data for all assays
395                try {
396                        // Loop through all assays to collect the data
397                        def columnWiseAssayData = [];
398
399                        assays.each { assayInfo ->
400                                def assay = assayInfo.value.assay;
401                                def assaySamples = assayInfo.value.samples;
402
403                                // Determine which fields should be exported for this assay
404                                def fieldMap = assayService.collectAssayTemplateFields(assay, null)
405                                def measurementTokens = fieldMap.remove('Module Measurement Data')
406
407                                // Retrieve row based data for this assay
408                                def assayData = assayService.collectAssayData( assay, fieldMap, measurementTokens, assaySamples );
409
410                                // Prepend study and assay data to the list
411                                assayData = assayService.prependAssayData( assayData, assay, assaySamples.size() )
412                                assayData = assayService.prependStudyData( assayData, assay, assaySamples.size() )
413
414                                // Make sure the assay data can be distinguished later
415                                assayData.put( "Assay data - " + assay.name, assayData.remove( "Assay Data") )
416                                assayData.put( "Module measurement data - " + assay.name, assayData.remove( "Module Measurement Data") )
417
418                                // Add the sample IDs to the list, in order to be able to combine
419                                // data for a sample that has been processed in multiple assays
420                                assayData[ "Sample Data" ][ "id" ] = assaySamples*.id;
421
422                                columnWiseAssayData << assayData;
423                        }
424
425                        def mergedColumnWiseData = assayService.mergeColumnWiseDataOfMultipleStudiesForASetOfSamples( columnWiseAssayData );
426
427                        def rowData   = assayService.convertColumnToRowStructure(mergedColumnWiseData)
428
429                        // Send headers to the browser so the user can download the file
430                        def filename = 'export.csv'
431                        response.setHeader("Content-disposition", "attachment;filename=\"${filename}\"")
432                        response.setContentType("application/octet-stream")
433
434                        assayService.exportRowWiseDataToCSVFile( rowData, response.getOutputStream() )
435
436                        response.outputStream.flush()
437
438                } catch (Exception e) {
439                        throw e;
440                }
441        }
442
443
444        def getAssaysFromParams( params ) {
445                def ids = params.list( 'ids' ).findAll { it.isLong() }.collect { Long.valueOf( it ) };
446                def tokens = params.list( 'tokens' );
447
448                if( !ids && !tokens ) {
449                        flash.errorMessage = "No assay ids given";
450                        redirect( action: "errorPage" );
451                        return [];
452                }
453
454                // Find all assays for the given ids
455                def assays = [];
456                ids.each { id ->
457                        def assay = Assay.get( id );
458                        if( assay )
459                                assays << assay;
460                }
461
462                // Also accept tokens for defining studies
463                tokens.each { token ->
464                        def assay = Assay.findByAssayUUID( token );
465                        if( assay )
466                                assays << assay;
467                }
468
469                if( !assays ) {
470                        flash.errorMessage = "No assays found";
471                        redirect( action: "errorPage" );
472                        return [];
473                }
474
475                return assays.unique();
476        }
477
478        def getSamplesFromParams( params ) {
479                def ids = params.list( 'ids' ).findAll { it.isLong() }.collect { Long.valueOf( it ) };
480                def tokens = params.list( 'tokens' );
481
482                if( !ids && !tokens ) {
483                        flash.errorMessage = "No sample ids given";
484                        redirect( action: "errorPage" );
485                        return [];
486                }
487
488                // Find all assays for the given ids
489                def samples = [];
490                ids.each { id ->
491                        def sample = Sample.get( id );
492                        if( sample )
493                                samples << sample;
494                }
495
496                // Also accept tokens for defining studies
497                tokens.each { token ->
498                        def sample = Sample.findBySampleUUID( token );
499                        if( sample )
500                                samples << sample;
501                }
502
503                if( !samples ) {
504                        flash.errorMessage = "No assays found";
505                        redirect( action: "errorPage" );
506                        return [];
507                }
508
509                return samples.unique();
510        }
511
512        def errorPage = {
513                render(view: 'assayExport/errorPage')
514        }
515}
Note: See TracBrowser for help on using the browser.