source: trunk/grails-app/controllers/dbnp/studycapturing/AssayController.groovy @ 2157

Last change on this file since 2157 was 2157, checked in by m.s.vanvliet@…, 11 years ago

Bug fix. Calling the user that is logged in correct again. Sorry for this.

  • Property svn:keywords set to Rev Author Date
File size: 15.3 KB
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, flow.assay.samples)
61                               
62                                // save the measurementTokes to the session for use later
63                                session.measurementTokens = measurementTokens
64
65                                // remember the selected file type
66                                flow.exportFileType = params.exportFileType
67
68                                // prepare the assay data preview
69                                def previewRows         = Math.min(flow.rowData.size()    as int, 5) - 1
70                                def previewCols         = Math.min(flow.rowData[0].size() as int, 5) - 1
71
72                                flow.assayDataPreview   = flow.rowData[0..previewRows].collect{ it[0..previewCols] as ArrayList }
73
74                        }.to "compileExportData"
75
76                        on("submitToGalaxy") {
77
78                                def (fieldMapSelection, measurementTokens) = processExportSelection(flow.assay, flow.fieldMap, params)
79
80                                // create a random session token that will be used to allow to module to
81                                // sync with gscf prior to presenting the measurement data
82                                def sessionToken = UUID.randomUUID().toString()
83                                def consumer = "galaxy"
84
85                                // put the session token to work
86                                authenticationService.logInRemotely( consumer, sessionToken, authenticationService.getLoggedInUser() )
87
88                                // create a link to the galaxy fetch action where galaxy can fetch the data from
89                                flow.fetchUrl = g.createLink(
90                                        absolute: true,
91                                        controller: "assay",
92                                        action: "fetchGalaxyData",
93                                        params: [
94                                                assayToken: flow.assay.assayUUID,
95                                                sessionToken: sessionToken,
96                                                fieldMapSelection: fieldMapSelection as JSON,
97                                                measurementTokens: measurementTokens as JSON] )
98
99                        }.to "galaxySubmitPage"
100
101                        on(Exception).to "handleError"
102                }
103
104                compileExportData {
105                        on ("ok"){
106                                session.assay = flow.assay
107                                session.rowData = flow.rowData
108                                session.exportFileType = flow.exportFileType
109
110                        }.to "export"
111                        on ("cancel").to "selectAssay"
112                }
113
114                export {
115                        redirect(action: 'doExport')
116                }
117
118                handleError {
119                        render(view: 'errorPage')
120                }
121
122                // renders a page that directly POSTs a form to galaxy
123                galaxySubmitPage()
124
125        }
126
127        def processExportSelection(assay, fieldMap, params) {
128
129                def fieldMapSelection = [:]
130
131                fieldMap.eachWithIndex { category, categoryIndex ->
132
133                        if (params."cat_$categoryIndex" == 'on') {
134                                fieldMapSelection[category.key] = []
135
136                                category.value.eachWithIndex { field, field_i ->
137
138                                if (params."cat_${categoryIndex}_${field_i}" == 'on')
139                                        fieldMapSelection[category.key] += field
140                                }
141
142                                if (fieldMapSelection[category.key] == [])
143                                        fieldMapSelection.remove(category.key)
144                        }
145                }
146
147                def measurementTokens = []
148
149                if (params."cat_4" == 'on') {
150                        measurementTokens = params.list( "measurementToken" )
151                }
152
153                [fieldMapSelection, measurementTokens]
154        }
155
156        def collectAssayData(assay, fieldMapSelection, measurementTokens, samples, remoteUser = null) {
157                // collect the assay data according to user selection
158                def assayData           = assayService.collectAssayData(assay, fieldMapSelection, measurementTokens, samples, remoteUser)
159
160                flash.errorMessage      = assayData.remove('Module Error')
161
162                assayService.convertColumnToRowStructure(assayData)
163        }
164
165        // This method is accessible for each user. However, he should return with a valid
166        // session token
167        @Secured(['true'])
168        def fetchGalaxyData = {
169
170                def fieldMapSelection = JSON.parse((String) params.fieldMapSelection)
171                def measurementTokens = JSON.parse((String) params.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                def remoteUser = authenticationService.getLoggedInUser()
222                if( !remoteUser ) {
223                        response.status = 401
224                        render "You must be logged in"
225                        return
226                }
227
228                // process requested output file type
229                def outputDelimiter, outputFileExtension, locale = java.util.Locale.US
230
231                switch(session.exportFileType) {
232                        case '2': // Comma delimited csv
233                                outputDelimiter = ','
234                                outputFileExtension = 'csv'
235                                break
236                        case '3': // Semicolon delimited csv
237                                outputDelimiter = ';'
238                                outputFileExtension = 'csv'
239                                locale = java.util.Locale.GERMAN // force use of comma as decimal separator
240                                break
241                        default: // Tab delimited with .txt extension
242                                outputDelimiter = '\t'
243                                outputFileExtension = 'txt'
244                }
245
246                def filename = "export.$outputFileExtension"
247                response.setHeader("Content-disposition", "attachment;filename=\"${filename}\"")
248                response.setContentType("application/octet-stream")
249                try {
250                       
251                        //merge data with metadata if possible
252                        def metadata = assayService.requestModuleMeasurementMetaDatas(session.assay, session.measurementTokens, remoteUser) ?: null
253                        session.rowData = assayService.mergeModuleDataWithMetadata(session.rowData, metadata)
254                               
255                        assayService.exportRowWiseDataToCSVFile(session.rowData, response.outputStream, outputDelimiter, locale)
256
257                        // clear the data from the session
258                        session.removeAttribute('rowData')
259                        session.removeAttribute('measurementTokens')
260                        session.removeAttribute('exportFileType')
261
262                } catch (Exception e) {
263
264                        flash.errorMessage = e.message
265                        redirect action: 'errorPage'
266
267                }
268        }
269
270        /**
271         * Method to export one or more assays to excel in separate sheets.
272         *
273         * @param       params.ids              One or more assay IDs to export
274         * @param       params.format   "list" in order to export all assays in one big excel sheet
275         *                                                      "sheets" in order to export every assay on its own sheet (default)
276         */
277        def exportToExcel = {
278                def format = params.get( 'format', 'sheets' );
279                if( format == 'list' ) {
280                        exportToExcelAsList( params );
281                } else {
282                        exportToExcelAsSheets( params );
283                }
284        }
285
286        /**
287         * Method to export one or more assays to excel in separate sheets.
288         *
289         * @param       params.ids              One or more assay IDs to export
290         */
291        def exportToExcelAsSheets = {
292                def assays = getAssaysFromParams( params );
293
294                if( !assays )
295                        return;
296
297                // Send headers to the browser so the user can download the file
298                def filename = 'export.xlsx'
299                response.setHeader("Content-disposition", "attachment;filename=\"${filename}\"")
300                response.setContentType("application/octet-stream")
301
302                try {
303                        // Loop through all assays to collect the data
304                        def rowWiseAssayData = [];
305
306                        assays.each { assay ->
307                                // Determine which fields should be exported for this assay
308                                def fieldMap = assayService.collectAssayTemplateFields(assay, null)
309                                def measurementTokens = fieldMap.remove('Module Measurement Data')
310
311                                // Retrieve row based data for this assay
312                                def assayData = assayService.collectAssayData( assay, fieldMap, measurementTokens, [] );
313                                def rowData   = assayService.convertColumnToRowStructure(assayData)
314
315                                // Put each assay on another sheet
316                                rowWiseAssayData << rowData;
317                        }
318
319                        assayService.exportRowWiseDataForMultipleAssaysToExcelFile( rowWiseAssayData, response.getOutputStream() )
320
321                        response.outputStream.flush()
322
323                } catch (Exception e) {
324                        throw e;
325                }
326        }
327
328        /**
329         * Method to export one or more assays to excel.
330         *
331         * @param       params.ids              One or more assay IDs to export
332         */
333        def exportToExcelAsList = {
334                def assays = getAssaysFromParams( params );
335
336                if( !assays )
337                        return;
338
339                // Send headers to the browser so the user can download the file
340                def filename = 'export.csv'
341                response.setHeader("Content-disposition", "attachment;filename=\"${filename}\"")
342                response.setContentType("application/octet-stream")
343
344                try {
345                        // Loop through all assays to collect the data
346                        def columnWiseAssayData = [];
347
348                        assays.each { assay ->
349                                // Determine which fields should be exported for this assay
350                                def fieldMap = assayService.collectAssayTemplateFields(assay, null)
351                                def measurementTokens = fieldMap.remove('Module Measurement Data')
352
353                                // Retrieve row based data for this assay
354                                def assayData = assayService.collectAssayData( assay, fieldMap, measurementTokens, [] );
355
356                                // Prepend study and assay data to the list
357                                assayData = assayService.prependAssayData( assayData, assay, assay.samples?.size() )
358                                assayData = assayService.prependStudyData( assayData, assay, assay.samples?.size() )
359
360                                // Put each assay on another sheet
361                                columnWiseAssayData << assayData;
362                        }
363
364                        // Merge data from all assays
365                        def mergedColumnWiseData = assayService.mergeColumnWiseDataOfMultipleStudies( columnWiseAssayData );
366
367                        def rowData   = assayService.convertColumnToRowStructure(mergedColumnWiseData)
368                        assayService.exportRowWiseDataToCSVFile( rowData, response.getOutputStream() )
369
370                        response.outputStream.flush()
371
372                } catch (Exception e) {
373                        throw e;
374                }
375        }
376
377        /**
378         * Method to export one or more samples to csv in separate sheets.
379         *
380         * @param       params.ids              One or more sample ids to export
381         */
382        def exportSamplesToCsv = {
383                def samples = getSamplesFromParams( params );
384
385                if( !samples ) {
386                        return;
387                }
388
389                // Determine a list of assays these samples have been involved in. That way, we can
390                // retrieve the data for that assay once, and save precious time doing HTTP calls
391                def assays = [:];
392
393                samples.each { sample ->
394                        def thisAssays = sample.getAssays();
395
396                        // Loop through all assays. If it already exists, add the sample it to the list
397                        thisAssays.each { assay ->
398                                if( !assays[ assay.id ] ) {
399                                        assays[ assay.id ] = [ 'assay': assay, 'samples': [] ]
400                                }
401
402                                assays[ assay.id ].samples << sample
403                        }
404                }
405
406                // Now collect data for all assays
407                try {
408                        // Loop through all assays to collect the data
409                        def columnWiseAssayData = [];
410
411                        assays.each { assayInfo ->
412                                def assay = assayInfo.value.assay;
413                                def assaySamples = assayInfo.value.samples;
414
415                                // Determine which fields should be exported for this assay
416                                def fieldMap = assayService.collectAssayTemplateFields(assay, null)
417                                def measurementTokens = fieldMap.remove('Module Measurement Data')
418
419                                // Retrieve row based data for this assay
420                                def assayData = assayService.collectAssayData( assay, fieldMap, measurementTokens, assaySamples );
421
422                                // Prepend study and assay data to the list
423                                assayData = assayService.prependAssayData( assayData, assay, assaySamples.size() )
424                                assayData = assayService.prependStudyData( assayData, assay, assaySamples.size() )
425
426                                // Make sure the assay data can be distinguished later
427                                assayData.put( "Assay data - " + assay.name, assayData.remove( "Assay Data") )
428                                assayData.put( "Module measurement data - " + assay.name, assayData.remove( "Module Measurement Data") )
429
430                                // Add the sample IDs to the list, in order to be able to combine
431                                // data for a sample that has been processed in multiple assays
432                                assayData[ "Sample Data" ][ "id" ] = assaySamples*.id;
433
434                                columnWiseAssayData << assayData;
435                        }
436
437                        def mergedColumnWiseData = assayService.mergeColumnWiseDataOfMultipleStudiesForASetOfSamples( columnWiseAssayData );
438
439                        def rowData   = assayService.convertColumnToRowStructure(mergedColumnWiseData)
440
441                        // Send headers to the browser so the user can download the file
442                        def filename = 'export.csv'
443                        response.setHeader("Content-disposition", "attachment;filename=\"${filename}\"")
444                        response.setContentType("application/octet-stream")
445
446                        assayService.exportRowWiseDataToCSVFile( rowData, response.getOutputStream() )
447
448                        response.outputStream.flush()
449
450                } catch (Exception e) {
451                        throw e;
452                }
453        }
454
455
456        def getAssaysFromParams( params ) {
457                def ids = params.list( 'ids' ).findAll { it.isLong() }.collect { Long.valueOf( it ) };
458                def tokens = params.list( 'tokens' );
459
460                if( !ids && !tokens ) {
461                        flash.errorMessage = "No assay ids given";
462                        redirect( action: "errorPage" );
463                        return [];
464                }
465
466                // Find all assays for the given ids
467                def assays = [];
468                ids.each { id ->
469                        def assay = Assay.get( id );
470                        if( assay )
471                                assays << assay;
472                }
473
474                // Also accept tokens for defining studies
475                tokens.each { token ->
476                        def assay = Assay.findByAssayUUID( token );
477                        if( assay )
478                                assays << assay;
479                }
480
481                if( !assays ) {
482                        flash.errorMessage = "No assays found";
483                        redirect( action: "errorPage" );
484                        return [];
485                }
486
487                return assays.unique();
488        }
489
490        def getSamplesFromParams( params ) {
491                def ids = params.list( 'ids' ).findAll { it.isLong() }.collect { Long.valueOf( it ) };
492                def tokens = params.list( 'tokens' );
493
494                if( !ids && !tokens ) {
495                        flash.errorMessage = "No sample ids given";
496                        redirect( action: "errorPage" );
497                        return [];
498                }
499
500                // Find all assays for the given ids
501                def samples = [];
502                ids.each { id ->
503                        def sample = Sample.get( id );
504                        if( sample )
505                                samples << sample;
506                }
507
508                // Also accept tokens for defining studies
509                tokens.each { token ->
510                        def sample = Sample.findBySampleUUID( token );
511                        if( sample )
512                                samples << sample;
513                }
514
515                if( !samples ) {
516                        flash.errorMessage = "No assays found";
517                        redirect( action: "errorPage" );
518                        return [];
519                }
520
521                return samples.unique();
522        }
523
524        def errorPage = {
525                render(view: 'assayExport/errorPage')
526        }
527}
Note: See TracBrowser for help on using the repository browser.