source: trunk/grails-app/controllers/nl/tno/massSequencing/FastaController.groovy @ 44

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

Removed mass sample editing (to prevent the edit tags screen opening very slowly). Also added the possibility to add an excel file which matches sequence files to samples (when uploading) (#13). Finally added some 'return false' to onClick events, when dialogs were opened, to prevent the browser from scrolling to the top.

File size: 9.4 KB
Line 
1package nl.tno.massSequencing
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.massSequencing.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.