source: trunk/grails-app/controllers/nl/tno/metagenomics/AssayController.groovy @ 27

Last change on this file since 27 was 27, checked in by robert@…, 9 years ago

Renamed module to massSequencing

File size: 12.4 KB
Line 
1package nl.tno.massSequencing
2
3import java.util.List;
4
5import org.codehaus.groovy.grails.commons.ConfigurationHolder
6
7class AssayController {
8        def synchronizationService
9        def gscfService
10        def fuzzySearchService
11
12        def fileService
13        def excelService
14        def sampleExcelService
15        def fastaService
16
17        def index = {
18                // Filter studies for the ones the user is allowed to see
19                def studies = Study.list();
20                [studies: studies.findAll { it.canRead( session.user ) },
21                        gscfAddUrl: gscfService.urlAddStudy() ]
22        }
23
24        def show = {
25                def assay = getAssay( params.id );
26                if( !assay )
27                        return
28
29                // Make sure the newest data is available
30                synchronizationService.sessionToken = session.sessionToken
31                synchronizationService.synchronizeAssay( assay );
32
33                if( !assay.study || !assay.study.canRead( session.user ) ) {
34                        flash.error = "You don't have sufficient privileges to access assay " + assay.name
35                        redirect( action: "index" );
36                        return;
37                }
38               
39                // Determine runs not used in this assay
40                def otherRuns = Run.list( sort: "name" ).findAll { !it.assays.contains( assay ) }
41
42                // Send the assay information to the view
43                [assay: assay, editable: assay.study.canWrite( session.user ), otherRuns: otherRuns]
44        }
45
46        def showByToken = {
47                // load study with token specified by param.id
48                def assay = Assay.findByAssayToken(params.id as String)
49
50                if (!assay) {
51                        // Initialize synchronizationService
52                        synchronizationService.sessionToken = session.sessionToken
53                        synchronizationService.user = session.user
54
55                        // If the assay is not found, synchronize studies and try again
56                        synchronizationService.synchronizeStudies();
57
58                        assay = Assay.findByAssayToken(params.id as String)
59
60                        if (!assay) {
61                                // If the assay is not found, synchronize all studies and try again, since we might be out of sync
62                                synchronizationService.eager = true
63                                synchronizationService.synchronizeStudies();
64
65                                assay = Assay.findByAssayToken(params.id as String)
66
67                                // If after synchronization still no assay is found, show an error
68                                if( !assay ) {
69                                        flash.message = "No assay found with token: $params.id"
70                                        redirect(controller: 'study')
71                                }
72                        }
73                }
74
75                if (!assay.study.canRead( session.user ) ) {
76                        flash.error = "You don't have the right authorizaton to access assay " + assay.name
77                        redirect(action: 'index')
78                        return null
79                }
80               
81                redirect( action: "show", id: assay.id );
82        }
83
84        /**************************************************************************
85         *
86         * Method for handling data about samples for this assay
87         *
88         *************************************************************************/
89
90        /**
91         * Downloads an excel sheet with data about the assay samples, to enter data in excel
92         */
93        def downloadTagsExcel = {
94                // load study with id specified by param.id
95                def assay = getAssay( params.id );
96                if( !assay )
97                        return
98
99                def filename = assay.study.name + "_" + assay.name + "_tags.xls"
100                def wb = sampleExcelService.downloadSampleExcel( assay.assaySamples );
101
102                // Make file downloadable
103                log.trace( "Creation for downloading the file " + filename )
104                sampleExcelService.excelService.downloadFile( wb, filename, response )
105        }
106
107        /**
108         * Parses an uploaded excel file and shows a form to match columns
109         */
110        def parseTagExcel = {
111                def assay = getAssay( params.id, true );
112                if( !assay )
113                        return
114
115                def filename = params.filename
116
117                // Security check to prevent accessing files in other directories
118                if( !filename || filename.contains( '..' ) ) {
119                        response.status = 500;
120                        render "Invalid filename given";
121                        return;
122                }
123
124                // Check for existence and readability
125                File file = new File( fileService.getUploadDir(), filename)
126
127                if( !file.exists() || !file.canRead() ) {
128                        response.status = 404;
129                        render "The uploaded file doesn't exist or doesn't work as expected.";
130                        return;
131                }
132
133                // Save the filename in session for later use
134                session.filename = filename;
135                def excelData;
136                try {
137                        excelData = sampleExcelService.parseTagsExcel( file );
138                } catch( Throwable e ) { // Catch a throwable here instead of an exception, since the apache poi stuff gives an Error on failure
139                        // Couldn't create a workbook from this file.
140                        response.status = 400 // Bad request
141                        render "Uploaded file is not a valid excel file: " + e.getMessage()
142                        return
143                }
144                session.possibleFields = excelData.possibleFields
145
146                [assay: assay, headers: excelData.headers, exampleData: excelData.exampleData, filename: filename, possibleFields: [ "Don't import" ] + excelData.possibleFields, bestMatches: excelData.bestMatches]
147        }
148
149        /**
150         * Updates the assay samples based on the given excel file and the column matches
151         */
152        def updateTagsByExcel = {
153                def assay = getAssay( params.id, true );
154                if( !assay ) {
155                        // Now delete the file, since we don't need it anymore
156                        _deleteUploadedFileFromSession()
157                        return;
158                }
159
160                if( !session.filename ) {
161                        // Now delete the file, since we don't need it anymore
162                        _deleteUploadedFileFromSession()
163
164                        flash.error = "No excel file found because session timed out. Please try again."
165                        redirect( action: 'show', id: params.id)
166                        return
167                }
168
169                // Determine the match-columns
170                def matchColumns = params[ 'matches'];
171
172                // Now loop through the excel sheet and update all samples with the specified data
173                File file = new File( fileService.getUploadDir(), session.filename );
174
175                if( !file.exists() || !file.canRead() ) {
176                        flash.error = "Excel file has been removed since previous step. Please try again."
177                        redirect( action: 'show', id: params.id)
178                        return
179                }
180
181                def excelData = sampleExcelService.updateTagsByExcel( matchColumns, session.possibleFields, file, assay.assaySamples );
182
183                // Return a message to the user
184                if( !excelData.success ) {
185                        flash.error = excelData.message
186                } else if( excelData.numSuccesful == 0 ) {
187                        flash.error = "None of the " + excelData.failedRows.size() + " row(s) could be imported, because none of the sample names matched. Have you provided the right excel file?"
188                } else {
189                        flash.message = excelData.numSuccesful + " samples have been updated. "
190
191                        if( excelData.failedRows.size() > 0 )
192                                flash.message += excelData.failedRows.size() + " row(s) could not be imported, because the sample names could not be found in the database."
193                }
194               
195                // Now delete the file, since we don't need it anymore
196                _deleteUploadedFileFromSession()
197
198                redirect( action: 'show', id: params.id )
199        }
200
201        /**
202         * Update the properties of the assay samples manually
203         */
204        def updateTagsManually = {
205                def assay = getAssay( params.id, true );
206                if( !assay )
207                        return
208
209                // Loop through all assay samples and set data
210                def sampleParams = params.assaySample;
211
212                if( sampleParams ) {
213                        assay.assaySamples.each { assaySample ->
214                                def assaySampleParams = sampleParams.get( assaySample.id as String );
215
216                                if( assaySampleParams ) {
217                                        sampleExcelService.variableFields.each { k, v ->
218                                                assaySample[ k ] = assaySampleParams[ k ];
219                                        }
220                                        assaySample.save()
221                                       
222                                        try {
223                                                assaySample.run = Run.get( assaySampleParams.run as Long );
224                                        } catch( Exception e ) {}
225
226                                        assaySample.save()
227                                }
228                        }
229                }
230
231                flash.message = "Data about samples is saved."
232                redirect( action: 'show', id: params.id )
233        }
234
235        /**************************************************************************
236         *
237         * Methods for handling data about runs for this assay
238         *
239         *************************************************************************/
240
241        /**
242         * Adds existing runs to this assay
243         */
244        def addExistingRuns = {
245                def assay = getAssay( params.id, true );
246                if( !assay )
247                        return
248
249                // Add checked runs to this assay
250                def runs = params.runs
251                if( runs instanceof String ) {
252                        runs = [ runs ]
253                }
254
255                def numAdded = 0;
256                runs.each { run_id ->
257                        try {
258                                def run = Run.findById( run_id as Long )
259                                if( run.assays == null || !run.assays.contains( assay ) ) {
260                                        run.addToAssays( assay );
261                                        numAdded++;
262                                }
263                        } catch( Exception e ) {}
264                }
265
266                flash.message = numAdded + " runs are added to this assay."
267                redirect( action: 'show', id: params.id)
268        }
269
270        /**
271         * Removes a run from this assay
272         */
273        def removeRun = {
274                def assay = getAssay( params.id, true );
275                if( !assay )
276                        return
277
278                if( !params.run_id ) {
279                        flash.message = "No run id given"
280                        redirect(action: 'show', id: params.id)
281                        return
282                }
283
284                def run
285
286                try {
287                        run = Run.findById( params.run_id as Long )
288                } catch( Exception e ) {
289                        throw e
290                        flash.message = "Incorrect run id given: "
291                        redirect(action: 'show', id: params.id)
292                        return
293                }
294
295                if( assay.runs.contains( run ) ) {
296                        assay.removeFromRuns( run );
297                        flash.message = "The run has been removed from this assay."
298                } else {
299                        flash.message = "The given run was not associated with this assay."
300                }
301
302                redirect( action: 'show', id: params.id)
303        }
304
305        def errorPage = {
306                render "An error has occured. $flash.message"
307        }
308
309        /**
310         * Deletes an uploaded file for which the filename is given in the session.
311         * @return
312         */
313        def _deleteUploadedFileFromSession() {
314                if( !session.filename )
315                        return
316
317                // Now delete the file, since we don't need it anymore
318                fileService.delete( session.filename  )
319                session.filename = ''
320        }
321
322        /**
323         * Exports data about one or more assays in fasta format
324         */
325        def exportAsFasta = {
326                def assaySamples = getAssaySamples( params );
327                def name
328
329                if( assaySamples == null ) {
330                        return
331                } else if( assaySamples*.assay.unique().size() == 1 ) {
332                        name = "Assay_" + assaySamples[0].assay?.name?.replace( ' ', '_' );
333                } else {
334                        name = "assays";
335                }
336
337                // Export the sequences and quality scores
338                response.setHeader "Content-disposition", "attachment; filename=" + name.trim() + ".zip"
339                try {
340                        fastaService.export( assaySamples.unique(), response.getOutputStream() );
341                        response.outputStream.flush();
342                } catch( Exception e ) {
343                        log.error( "Exception occurred during export of sequences. Probably the user has cancelled the download." );
344                }
345        }
346
347        /**
348         * Export metadata of selected samples in excel format
349         */
350        def exportMetaData = {
351                def assaySamples = getAssaySamples( params );
352                def name
353
354                if( assaySamples == null ) {
355                        return
356                } else if( assaySamples*.assay.unique().size() == 1 ) {
357                        name = "Assay_" + assaySamples[0].assay?.name?.replace( ' ', '_' );
358                } else {
359                        name = "assays";
360                }
361
362                // Export the metadata
363                response.setHeader "Content-disposition", "attachment; filename=${name}.xls"
364                try {
365                        // The export functionality needs a assaySample-tag list, but it
366                        // should be empty when only exporting metadata
367                        def tags = [];
368                        assaySamples.unique().each { assaySample ->
369                                tags << [assaySampleId: assaySample.id, sampleName: assaySample.sample.name, assayName: assaySample.assay.name, studyName: assaySample.assay.study.name, tag: "-"]
370                        }
371                        sampleExcelService.exportExcelSampleData( assaySamples.unique(), tags, response.getOutputStream() );
372                        response.outputStream.flush();
373                } catch( Exception e ) {
374                        log.error( "Exception occurred during export of metadata. Probably the user has cancelled the download." );
375                }
376        }
377
378       
379        /**
380         * Retrieves an assay from the database, based on the assay ID given
381         * @param assayId               ID of the assay
382         * @param writeAccess   True if you require write access to this assay. The system will check for sufficient privileges
383         * @return
384         */
385        protected Assay getAssay(def assayId, boolean writeAccess = false ) {
386                // load study with id specified by param.id
387                def assay
388                try {
389                        assay = Assay.get(assayId as Long)
390                } catch( Exception e ) {
391                        flash.error = "Incorrect id given: " + assayId
392                        redirect(action: 'index')
393                        return null
394                }
395
396                if (!assay) {
397                        flash.error = "No assay found with id: " + assayId
398                        redirect(action: 'index')
399                        return null
400                }
401
402                if ( !assay.study.canRead( session.user ) || ( writeAccess && !assay.study.canWrite( session.user ) ) ) {
403                        flash.error = "You don't have the right authorizaton to access assay " + assay.name
404                        redirect(action: 'index')
405                        return null
406                }
407               
408                return assay
409        }
410
411       
412        protected List getAssaySamples( params ) {
413                def tokens = params.list( 'tokens' );
414                def ids = params.list( 'ids' );
415                def name;
416
417                ids = ids.findAll { it.isLong() }.collect { Long.parseLong( it ) }
418
419                if( !tokens && !ids ) {
420                        def message = "No assay tokens or ids given"
421                        flash.error = message
422                        redirect( action: "index" );
423                        return;
424                }
425
426                def assaySamples = [];
427
428                // Determine which assaySamples to export
429                def assay;
430                tokens.each { token ->
431                        assay = Assay.findByAssayToken( token );
432                        if( assay && assay.study.canRead( session.user ) )
433                                assaySamples += assay.assaySamples
434                }
435                ids.each { id ->
436                        assay = Assay.get( id );
437                        if( assay && assay.study.canRead( session.user ) )
438                                assaySamples += assay.assaySamples
439                }
440
441                return assaySamples;
442        }
443
444
445}
Note: See TracBrowser for help on using the repository browser.