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

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

Implemented uploading and parsing of zip files

File size: 7.7 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                // load study with id specified by param.id
21                def assay = Assay.get(params.id as Long)
22
23                if (!assay) {
24                        flash.message = "No assay found with id: $params.id"
25                        redirect( controller: 'assay', action:  'errorPage')
26                        return
27                }
28
29                // Check whether files are given
30                def names = params.sequencefiles
31
32                if( !names ) {
33                        flash.message = "No files uploaded for processing"
34                        redirect( controller: 'assay', action: 'show', 'id': params.id)
35                        return
36                }
37               
38                // If only 1 file is uploaded, it is given as String
39                ArrayList filenames = []
40                if( names instanceof String )
41                        filenames << names
42                else
43                        names.each { filenames << it }
44                       
45                // Save filenames in session
46                session.processFilenames = names;
47               
48                // Check for total size of the files in order to be able
49                // to show a progress bar
50                long filesize = 0;
51                names.each {
52                        filesize += fileService.get( it )?.length()
53                }
54                session.processProgress = [
55                        numFiles: names.size(),
56                        numBytes: filesize,
57                        filesProcessed: 0,
58                        bytesProcessed: 0
59                ]
60                       
61                [assay: assay, filenames: names, url: createLink( action: 'showProcessResult', id: assay.id, params: [ selectedRun: params.selectedRun ] ) ]
62        }
63       
64        /**
65         * Processes uploaded files and tries to combine them with samples
66         */
67        def process = {
68                // load study with id specified by param.id
69                def assay = Assay.get(params.id as Long)
70
71                if (!assay) {
72                        response.setStatus( 404, "No assay found with id: $params.id" )
73                        render "";
74                        return
75                }
76
77                // Check whether files are given
78                def names = session.processFilenames
79
80                if( !names ) {
81                        response.setStatus( 500, "No files uploaded for processing" )
82                        render "";
83                        return
84                }
85
86                // If only 1 file is uploaded, it is given as String
87                ArrayList filenames = []
88                if( names instanceof String )
89                        filenames << names
90                else
91                        names.each { filenames << it }
92
93                /* Parses uploaded files, discards files we can not handle
94                 *
95                 * [
96                 *              success: [
97                 *                      [filename: 'abc.fasta', type: FASTA, numSequences: 190]
98                 *                      [filename: 'cde.fasta', type: FASTA, numSequences: 140]
99                 *                      [filename: 'abc.qual', type: QUAL, numSequences: 190, avgQuality: 38]
100                 *                      [filename: 'cde.qual', type: QUAL, numSequences: 140, avgQuality: 29]
101                 *              ],
102                 *              failure: [
103                 *                      [filename: 'testing.xls', message: 'Type not recognized']
104                 *              ]
105                 * ]
106                 *
107                 * The second parameter is a callback function to update progress indicators
108                 */
109                def parsedFiles = fastaService.parseFiles( filenames, { files, bytes, totalFiles, totalBytes -> 
110                        session.processProgress.numFiles += totalFiles;
111                        session.processProgress.numBytes += totalBytes;
112                        session.processProgress.filesProcessed = files; 
113                        session.processProgress.bytesProcessed = bytes; 
114                } );
115
116                // Match files with samples in the database
117                def matchedFiles = fastaService.matchFiles( parsedFiles.success, assay.assaySamples );
118
119                // Sort files on filename
120                matchedFiles.sort { a,b -> a.fasta?.originalfilename <=> b.fasta?.originalfilename }
121
122                // Saved file matches in session to use them later on
123                session.processedFiles = [ parsed: parsedFiles, matched: matchedFiles ];
124
125                render ""
126        }
127       
128        def getProgress = {
129                if( !session.processProgress ) {
130                        response.setStatus( 500, "No progress information found" );
131                        render ""
132                        return
133                }
134               
135                render session.processProgress as JSON
136        }
137       
138        /**
139         * Show result of processing
140         */
141        def showProcessResult = {
142                // load study with id specified by param.id
143                def assay = Assay.get(params.id as Long)
144
145                if (!assay) {
146                        flash.message = "No assay found with id: $params.id"
147                        redirect( controller: 'assay', action:  'errorPage')
148                        return
149                }
150
151                if( !session.processedFiles ) {
152                        flash.error = "Processing of files failed. Maybe the session timed out."
153                        redirect( controller: 'assay', action: 'show', 'id': params.id)
154                        return
155                }
156               
157                [assay: assay, parsedFiles: session.processedFiles.parsed, matchedFiles: session.processedFiles.matched, selectedRun: params.selectedRun ]
158        }
159
160        /**
161         * Saves processed files to the database, based on the selections made by the user
162         */
163        def saveProcessedFiles = {
164                // load study with id specified by param.id
165                def assay = Assay.get(params.id as Long)
166
167                if (!assay) {
168                        flash.message = "No assay found with id: $params.id"
169                        redirect( controller: 'assay', action: 'errorPage')
170                        return
171                }
172
173                // Check whether files are given
174                def files = params.file
175
176                if( !files ) {
177                        flash.message = "No files were selected."
178                        redirect( controller: 'assay', action: 'show', 'id': params.id)
179                        return
180                }
181
182                File permanentDir = fileService.absolutePath( ConfigurationHolder.config.metagenomics.fileDir )
183                int numSuccesful = 0;
184                def errors = [];
185               
186                // Loop through all files Those are the numeric elements in the 'files' array
187                def digitRE = ~/^\d+$/;
188                files.findAll { it.key.matches( digitRE ) }.each { file ->
189                        def filevalue = file.value;
190                       
191                        // Check if the file is selected
192                        if( filevalue.include == "on" ) {
193                                if( fileService.fileExists( filevalue.fasta ) ) {
194                                        try {
195                                                def permanent = fastaService.savePermanent( filevalue.fasta, filevalue.qual, session.processedFiles );
196                                               
197                                                // Save the data into the database
198                                                SequenceData sd = new SequenceData();
199                                               
200                                                sd.sequenceFile = permanent.fasta
201                                                sd.qualityFile = permanent.qual
202                                                sd.numSequences = permanent.numSequences
203                                                sd.averageQuality = permanent.avgQuality
204                                               
205                                                // Couple the data to the right run and sample
206                                                def run = Run.get( filevalue.run )
207                                                if( run )
208                                                        run.addToSequenceData( sd );
209                                                       
210                                                def sample = AssaySample.get( filevalue.assaySample );
211                                                if( sample )
212                                                        sample.addToSequenceData( sd );
213                                               
214                                                if( !sd.validate() ) {
215                                                        errors << "an error occurred while saving " + filevalue.fasta + ": validation of SequenceData failed.";
216                                                } else {
217                                                        sd.save(flush:true);
218                                                }
219                                               
220                                                numSuccesful++;
221                                        } catch( Exception e ) {
222                                                errors << "an error occurred while saving " + filevalue.fasta + ": " + e.getMessage()
223                                        }
224                                }
225                        } else {
226                                // File doesn't need to be included in the system. Delete it also from disk
227                                fileService.delete( filevalue.fasta );
228                        }
229                }
230
231                // Return a message to the user
232                if( numSuccesful == 0 ) {
233                        flash.error = "None of the files were imported, because "
234                       
235                        if( errors.size() > 0 ) {
236                                errors.each {
237                                        flash.error += "<br />- " + it
238                                }
239                        } else {
240                                flash.error = "none of the files were selected for import."
241                        }
242                } else {
243                        flash.message = numSuccesful + " files have been added to the system. "
244
245                        if( errors.size() > 0 ) {
246                                flash.error += errors.size() + " errors occurred during import: "
247                                errors.each {
248                                        flash.error += "<br />- " + it
249                                }
250                        }
251                }
252               
253                redirect( controller: 'assay', action: "show", id: params.id )
254        }
255       
256        def deleteData = { 
257                // load study with id specified by param.id
258                def sequenceData = SequenceData.get(params.id as Long)
259
260                if (!sequenceData) {
261                        flash.error = "No sequencedata found with id: $params.id"
262                        redirect( controller: 'assay', action: 'errorPage')
263                        return
264                }
265
266                def assayId = sequenceData.sample.assay.id;
267                def numFiles = sequenceData.numFiles();
268                sequenceData.delete();
269
270                flash.message = numFiles + " file" + (numFiles != 1 ? "s have" : " has" ) + " been deleted from this sample"
271                redirect( controller: 'assay', action: 'show', id: assayId )
272        }
273}
Note: See TracBrowser for help on using the repository browser.