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

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