source: trunk/grails-app/controllers/nl/tno/metagenomics/FastaController.groovy @ 14

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

Implemented improved authorization (#16)
Built in select all checkboxes (#25)

File size: 9.4 KB
Line 
1package nl.tno.metagenomics
2
3import org.codehaus.groovy.grails.commons.ConfigurationHolder
4import grails.converters.*;
5
6class FastaController {
7        def fileService
8        def fastaService
9       
10        /**************************************************************************
11         *
12         * Methods for handling uploaded sequence and quality files
13         *
14         *************************************************************************/
15
16        /**
17         * Shows a screen that processing is done
18         */
19        def showProcessScreen = {
20                def entityType = params.entityType
21
22                // Check whether files are given
23                def names = params.sequencefiles
24
25                if( !names ) {
26                        flash.message = "No files uploaded for processing"
27                        if( params.entityType && params.id)
28                                redirect( controller: params.entityType, action: 'show', 'id': params.id)
29                        else
30                                redirect( url: "" )
31                               
32                        return
33                }
34               
35                // If only 1 file is uploaded, it is given as String
36                ArrayList filenames = []
37                if( names instanceof String )
38                        filenames << names
39                else
40                        names.each { filenames << it }
41                       
42                // Save filenames in session
43                session.processFilenames = names;
44               
45                // Check for total size of the files in order to be able
46                // to show a progress bar
47                long filesize = 0;
48                names.each {
49                        filesize += fileService.get( it )?.length()
50                }
51                session.processProgress = [
52                        numFiles: names.size(),
53                        numBytes: filesize,
54                        filesProcessed: 0,
55                        bytesProcessed: 0
56                ]
57                       
58                [entityId: params.id, entityType: params.entityType, filenames: names, url: createLink( action: 'showProcessResult', id: params.id, params: [entityType: entityType] ) ]
59        }
60       
61        /**
62         * Processes uploaded files and tries to combine them with samples
63         */
64        def process = {
65                def entity
66                def assaySamples
67               
68                switch( params.entityType ) {
69                        case "run":
70                                entity = getRun( params.id );
71                                assaySamples = entity.assaySamples.findAll { it.assay.study.canRead( session.user ) };
72                                break;
73                        case "assay":
74                                entity = getAssay( params.id );
75                                assaySamples = entity.assaySamples;
76                                break;
77                        default:
78                                response.setStatus( 404, "No controller found" );
79                                render "";
80                                return;
81                }
82
83                if (!entity) {
84                        response.setStatus( 404, flash.error )
85                        render "";
86                        return
87                }
88
89                // Check whether files are given
90                def names = session.processFilenames
91
92                if( !names ) {
93                        response.setStatus( 500, "No files uploaded for processing" )
94                        render "";
95                        return
96                }
97
98                // If only 1 file is uploaded, it is given as String
99                ArrayList filenames = []
100                if( names instanceof String )
101                        filenames << names
102                else
103                        names.each { filenames << it }
104
105                /* Parses uploaded files, discards files we can not handle
106                 *
107                 * [
108                 *              success: [
109                 *                      [filename: 'abc.fasta', type: FASTA, numSequences: 190]
110                 *                      [filename: 'cde.fasta', type: FASTA, numSequences: 140]
111                 *                      [filename: 'abc.qual', type: QUAL, numSequences: 190, avgQuality: 38]
112                 *                      [filename: 'cde.qual', type: QUAL, numSequences: 140, avgQuality: 29]
113                 *              ],
114                 *              failure: [
115                 *                      [filename: 'testing.xls', message: 'Type not recognized']
116                 *              ]
117                 * ]
118                 *
119                 * The second parameter is a callback function to update progress indicators
120                 */
121                def httpSession = session;
122                def parsedFiles = fastaService.parseFiles( filenames, { files, bytes, totalFiles, totalBytes -> 
123                        httpSession.processProgress.numFiles += totalFiles;
124                        httpSession.processProgress.numBytes += totalBytes;
125                        httpSession.processProgress.filesProcessed = files; 
126                        httpSession.processProgress.bytesProcessed = bytes; 
127                } );
128       
129                // Check which assaySamples to use (only the ones visible to the user)
130                assaySamples = assaySamples.findAll { it.assay.study.canWrite( session.user ) }
131
132                // Match files with samples in the database
133                def matchedFiles = fastaService.matchFiles( parsedFiles.success, assaySamples );
134
135                // Sort files on filename
136                matchedFiles.sort { a,b -> a.fasta?.originalfilename <=> b.fasta?.originalfilename }
137
138                // Saved file matches in session to use them later on
139                session.processedFiles = [ parsed: parsedFiles, matched: matchedFiles ];
140
141                render ""
142        }
143       
144        def getProgress = {
145                if( !session.processProgress ) {
146                        response.setStatus( 500, "No progress information found" );
147                        render ""
148                        return
149                }
150               
151                render session.processProgress as JSON
152        }
153       
154        /**
155         * Show result of processing
156         */
157        def showProcessResult = {
158                // load study with id specified by param.id
159                def entity
160               
161                switch( params.entityType ) {
162                        case "run":
163                                entity = getRun( params.id )
164                                break;
165                        case "assay":
166                                entity = getAssay( params.id )
167                                break;
168                        default:
169                                response.setStatus( 404, "No entity found" );
170                                render "";
171                                return;
172                }
173
174                if (!entity) {
175                        response.setStatus( 404, flash.error )
176                        render "";
177                        return
178                }
179               
180                if( !session.processedFiles ) {
181                        flash.error = "Processing of files failed. Maybe the session timed out."
182                        redirect( controller: 'assay', action: 'show', 'id': params.id)
183                        return
184                }
185               
186                [entityType: params.entityType, entity: entity, id: params.id, parsedFiles: session.processedFiles.parsed, matchedFiles: session.processedFiles.matched, selectedRun: params.selectedRun ]
187        }
188
189        /**
190         * Saves processed files to the database, based on the selections made by the user
191         */
192        def saveProcessedFiles = {
193                // load entity with id specified by param.id
194                def entity
195               
196                switch( params.entityType ) {
197                        case "run":
198                                entity = getRun( params.id );
199                                break;
200                        case "assay":
201                                entity = getAssay( params.id );
202                                break;
203                        default:
204                                response.setStatus( 404, "No entity found" );
205                                render "";
206                                return;
207                }
208
209                if (!entity) {
210                        response.setStatus( 404, flash.error )
211                        render "";
212                        return
213                }
214
215                // Check whether files are given
216                def files = params.file
217
218                if( !files ) {
219                        flash.message = "No files were selected."
220                        redirect( controller: params.entityType, action: 'show', 'id': params.id)
221                        return
222                }
223
224                File permanentDir = fileService.absolutePath( ConfigurationHolder.config.metagenomics.fileDir )
225                int numSuccesful = 0;
226                def errors = [];
227               
228                // Loop through all files Those are the numeric elements in the 'files' array
229                def digitRE = ~/^\d+$/;
230                files.findAll { it.key.matches( digitRE ) }.each { file ->
231                        def filevalue = file.value;
232                       
233                        // Check if the file is selected
234                        if( filevalue.include == "on" ) {
235                                if( fileService.fileExists( filevalue.fasta ) ) {
236                                        try {
237                                                def permanent = fastaService.savePermanent( filevalue.fasta, filevalue.qual, session.processedFiles );
238                                               
239                                                // Save the data into the database
240                                                SequenceData sd = new SequenceData();
241                                               
242                                                sd.sequenceFile = permanent.fasta
243                                                sd.qualityFile = permanent.qual
244                                                sd.numSequences = permanent.numSequences
245                                                sd.averageQuality = permanent.avgQuality
246                                                       
247                                                def sample = AssaySample.get( filevalue.assaySample );
248                                                if( sample )
249                                                        sample.addToSequenceData( sd );
250                                               
251                                                if( !sd.validate() ) {
252                                                        errors << "an error occurred while saving " + filevalue.fasta + ": validation of SequenceData failed.";
253                                                } else {
254                                                        sd.save(flush:true);
255                                                }
256                                               
257                                                numSuccesful++;
258                                        } catch( Exception e ) {
259                                                errors << "an error occurred while saving " + filevalue.fasta + ": " + e.getMessage()
260                                        }
261                                }
262                        } else {
263                                // File doesn't need to be included in the system. Delete it also from disk
264                                fileService.delete( filevalue.fasta );
265                        }
266                }
267
268                // Return a message to the user
269                if( numSuccesful == 0 ) {
270                        flash.error = "None of the files were imported, because "
271                       
272                        if( errors.size() > 0 ) {
273                                errors.each {
274                                        flash.error += "<br />- " + it
275                                }
276                        } else {
277                                flash.error = "none of the files were selected for import."
278                        }
279                } else {
280                        flash.message = numSuccesful + " files have been added to the system. "
281
282                        if( errors.size() > 0 ) {
283                                flash.error += errors.size() + " errors occurred during import: "
284                                errors.each {
285                                        flash.error += "<br />- " + it
286                                }
287                        }
288                }
289               
290                redirect( controller: params.entityType, action: "show", id: params.id )
291        }
292       
293        def deleteData = { 
294                // load study with id specified by param.id
295                def sequenceData
296               
297                try {
298                        sequenceData = SequenceData.get(params.id as Long)
299                } catch( Exception e ) {}
300
301                if (!sequenceData) {
302                        flash.error = "No sequencedata found with id: $params.id"
303                        redirect( controller: 'study' )
304                        return
305                }
306
307                def entityId
308                def entityType
309               
310                switch( params.entityType ) {
311                        case "run":
312                                entityId = sequenceData.sample.run?.id;
313                                entityType = "run"
314                                break;
315                        case "assay":
316                        default:
317                                entityType = "assay";
318                                entityId = sequenceData.sample.assay.id;
319                                break;
320                }
321                 
322                def numFiles = sequenceData.numFiles();
323                sequenceData.delete();
324
325                flash.message = numFiles + " file" + (numFiles != 1 ? "s have" : " has" ) + " been deleted from this sample"
326
327                redirect( controller: entityType, action: 'show', id: entityId )
328        }
329       
330        protected Assay getAssay(def assayId) {
331                // load assay with id specified by param.id
332                def assay
333                try {
334                        assay = Assay.get(assayId as Long)
335                } catch( Exception e ) {
336                        flash.error = "Incorrect id given: " + assayId
337                        return null
338                }
339
340                if (!assay) {
341                        flash.error = "No assay found with id: " + assayId
342                        return null
343                }
344               
345                if (!assay.study.canRead( session.user ) ) {
346                        flash.error = "You don't have the right authorizaton to access assay " + assay.name
347                        return null
348                }
349               
350                return assay
351        }
352       
353        protected Run getRun(def runId) {
354                // load run with id specified by param.id
355                def run
356                try {
357                        run = Run.get(runId as Long)
358                } catch( Exception e ) {
359                        flash.error = "Incorrect id given: " + runId
360                        return null
361                }
362
363                if (!run) {
364                        flash.error = "No run found with id: " + runId
365                        return null
366                }
367
368                return run
369        }
370}
Note: See TracBrowser for help on using the repository browser.