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

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

Externalized configuration; improved assay view (detail views of runs and samples); implemented uploading and parsing of FASTA and QUAL files

File size: 7.5 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 -> 
110                        session.processProgress.filesProcessed = files; 
111                        session.processProgress.bytesProcessed = bytes; 
112                } );
113
114                // Match files with samples in the database
115                def matchedFiles = fastaService.matchFiles( parsedFiles.success, assay.assaySamples );
116
117                // Sort files on filename
118                matchedFiles.sort { a,b -> a.fasta?.originalfilename <=> b.fasta?.originalfilename }
119
120                // Saved file matches in session to use them later on
121                session.processedFiles = [ parsed: parsedFiles, matched: matchedFiles ];
122
123                render ""
124        }
125       
126        def getProgress = {
127                if( !session.processProgress ) {
128                        response.setStatus( 500, "No progress information found" );
129                        render ""
130                        return
131                }
132               
133                render session.processProgress as JSON
134        }
135       
136        /**
137         * Show result of processing
138         */
139        def showProcessResult = {
140                // load study with id specified by param.id
141                def assay = Assay.get(params.id as Long)
142
143                if (!assay) {
144                        flash.message = "No assay found with id: $params.id"
145                        redirect( controller: 'assay', action:  'errorPage')
146                        return
147                }
148
149                if( !session.processedFiles ) {
150                        flash.error = "Processing of files failed. Maybe the session timed out."
151                        redirect( controller: 'assay', action: 'show', 'id': params.id)
152                        return
153                }
154               
155                [assay: assay, parsedFiles: session.processedFiles.parsed, matchedFiles: session.processedFiles.matched, selectedRun: params.selectedRun ]
156        }
157
158        /**
159         * Saves processed files to the database, based on the selections made by the user
160         */
161        def saveProcessedFiles = {
162                // load study with id specified by param.id
163                def assay = Assay.get(params.id as Long)
164
165                if (!assay) {
166                        flash.message = "No assay found with id: $params.id"
167                        redirect( controller: 'assay', action: 'errorPage')
168                        return
169                }
170
171                // Check whether files are given
172                def files = params.file
173
174                if( !files ) {
175                        flash.message = "No files were selected."
176                        redirect( controller: 'assay', action: 'show', 'id': params.id)
177                        return
178                }
179
180                File permanentDir = fileService.absolutePath( ConfigurationHolder.config.metagenomics.fileDir )
181                int numSuccesful = 0;
182                def errors = [];
183               
184                // Loop through all files Those are the numeric elements in the 'files' array
185                def digitRE = ~/^\d+$/;
186                files.findAll { it.key.matches( digitRE ) }.each { file ->
187                        def filevalue = file.value;
188                       
189                        // Check if the file is selected
190                        if( filevalue.include == "on" ) {
191                                if( fileService.fileExists( filevalue.fasta ) ) {
192                                        try {
193                                                def permanent = fastaService.savePermanent( filevalue.fasta, filevalue.qual, session.processedFiles );
194                                               
195                                                // Save the data into the database
196                                                SequenceData sd = new SequenceData();
197                                               
198                                                sd.sequenceFile = permanent.fasta
199                                                sd.qualityFile = permanent.qual
200                                                sd.numSequences = permanent.numSequences
201                                                sd.averageQuality = permanent.avgQuality
202                                               
203                                                // Couple the data to the right run and sample
204                                                def run = Run.get( filevalue.run )
205                                                if( run )
206                                                        run.addToSequenceData( sd );
207                                                       
208                                                def sample = AssaySample.get( filevalue.assaySample );
209                                                if( sample )
210                                                        sample.addToSequenceData( sd );
211                                               
212                                                if( !sd.validate() ) {
213                                                        errors << "an error occurred while saving " + filevalue.fasta + ": validation of SequenceData failed.";
214                                                } else {
215                                                        sd.save(flush:true);
216                                                }
217                                               
218                                                numSuccesful++;
219                                        } catch( Exception e ) {
220                                                errors << "an error occurred while saving " + filevalue.fasta + ": " + e.getMessage()
221                                        }
222                                }
223                        } else {
224                                // File doesn't need to be included in the system. Delete it also from disk
225                                fileService.delete( filevalue.fasta );
226                        }
227                }
228
229                // Return a message to the user
230                if( numSuccesful == 0 ) {
231                        flash.error = "None of the files were imported, because "
232                       
233                        if( errors.size() > 0 ) {
234                                errors.each {
235                                        flash.error += "<br />- " + it
236                                }
237                        } else {
238                                flash.error = "none of the files were selected for import."
239                        }
240                } else {
241                        flash.message = numSuccesful + " files have been added to the system. "
242
243                        if( errors.size() > 0 ) {
244                                flash.error += errors.size() + " errors occurred during import: "
245                                errors.each {
246                                        flash.error += "<br />- " + it
247                                }
248                        }
249                }
250               
251                redirect( controller: 'assay', action: "show", id: params.id )
252        }
253       
254        def deleteData = { 
255                // load study with id specified by param.id
256                def sequenceData = SequenceData.get(params.id as Long)
257
258                if (!sequenceData) {
259                        flash.error = "No sequencedata found with id: $params.id"
260                        redirect( controller: 'assay', action: 'errorPage')
261                        return
262                }
263
264                def assayId = sequenceData.sample.assay.id;
265                def numFiles = sequenceData.numFiles();
266                sequenceData.delete();
267
268                flash.message = numFiles + " file" + (numFiles != 1 ? "s have" : " has" ) + " been deleted from this sample"
269                redirect( controller: 'assay', action: 'show', id: assayId )
270        }
271}
Note: See TracBrowser for help on using the repository browser.