source: trunk/grails-app/controllers/nl/tno/metagenomics/RunController.groovy @ 12

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

Implemented basic exporting functionality

File size: 12.2 KB
Line 
1package nl.tno.metagenomics
2
3import java.util.Date;
4
5import org.codehaus.groovy.grails.commons.ConfigurationHolder
6
7class RunController {
8        def fileService
9        def synchronizationService
10        def sampleExcelService
11
12        def index = {
13                [runs: Run.list()]
14        }
15
16        def show = {
17                // load run with id specified by param.id
18                def run = getRun( params.id );
19
20                if (!run) {
21                        redirect(controller: 'study', action: 'index')
22                        return
23                }
24
25                // Make sure the newest data is available
26                synchronizationService.sessionToken = session.sessionToken
27                synchronizationService.synchronizeStudies();
28
29                // Determine runs not used in this assay
30                def otherAssays = Assay.list( sort: "name" ).findAll { !it.runs.contains( run ) }
31
32                // Send the assay information to the view
33                [run: run, allRuns: Run.list(), otherAssays: otherAssays, editable: true]
34        }
35
36        /**
37         * Shows a form to edit the specified run in dialog mode
38         */
39        def editForm = {
40                // load run with id specified by param.id
41                Run run = getRun( params.id );
42
43                if (!run) {
44                        render flash.error
45                        return
46                }
47
48                Assay assay = null
49                if( params.assayId ) {
50                        assay = getAssay( params.assayId )
51
52                        if( !assay ) {
53                                render flash.error;
54                                return
55                        }
56                }
57
58                [assay: assay, run: run]
59        }
60
61        def create = {
62                Assay a = getAssay(params.id);
63                flash.error = "";
64
65                // Create run based on given parameters
66                Run run = new Run();
67
68                run.setPropertiesFromForm( params );
69
70                if( a )
71                        a.addToRuns( run );
72
73                if( !run.save() ) {
74                        flash.message = "Run could not be saved: " + run.getErrors();
75                } else {
76                        flash.message = "Run " + run.name + " has been added to the system."
77                }
78
79                if( a )
80                        redirect( controller: "assay", action: "show", id: a.id )
81                else
82                        redirect( controller: 'run' );
83        }
84
85        def update = {
86                Run run = getRun( params.id );
87
88                if( !run ) {
89                        redirect(controller: 'assay', action: 'show', id: params.assayId)
90                        return
91                }
92
93                // Set properties to the run
94                params.parameterFile = params.editParameterFile
95
96                run.setPropertiesFromForm( params );
97
98                if( run.save() ) {
99                        flash.message = "Run succesfully saved";
100                } else {
101                        flash.error = "Run could not be saved: " + run.getErrors();
102                }
103
104                Assay assay = getAssay(params.assayId);
105                flash.error = "";
106
107                if( assay ) {
108                        redirect( controller: 'assay', action: 'show', id: assay.id)
109                } else {
110                        redirect( controller: 'run', action: 'show', id: run.id )
111                }
112        }
113
114        def delete = {
115                Run run = getRun( params.id );
116
117                if( !run ) {
118                        redirect(controller: 'assay', action: 'show', id: params.assayId)
119                        return
120                }
121
122                // Don't remove runs for which data exists
123                if( run.sequenceData?.size() ) {
124                        flash.message = "Run could not be deleted because samples are associated with it.";
125                        redirect( controller: "assay", action: "show", id: params.assayId )
126                }
127
128                // Remove all associations
129                run.assays.each {
130                        run.removeFromAssays( it );
131                }
132
133                def name = run.name
134                run.delete();
135                flash.message = "Run " + name + " has been deleted from the system."
136
137                redirect( controller: "assay", action: "show", id: params.assayId )
138        }
139
140        /**************************************************************************
141         *
142         * Methods for handling data about the samples in this run
143         *
144         *************************************************************************/
145
146        /**
147         * Downloads an excel sheet with data about the assay samples, to enter data in excel
148         */
149        def downloadTagsExcel = {
150                Run run = getRun( params.id );
151
152                if( !run ) {
153                        redirect(controller: 'study')
154                        return
155                }
156
157                // Make it only possible to update samples writable by the user
158                def assaySamples = run.assaySamples.findAll { it.assay.study.canWrite( session.user ) }
159
160                def filename = "Run " + run.name + "_tags.xls"
161                def wb = sampleExcelService.downloadSampleExcel( assaySamples, false );
162
163                // Make file downloadable
164                log.trace( "Creation for downloading the file " + filename )
165                sampleExcelService.excelService.downloadFile( wb, filename, response )
166        }
167
168
169        /**
170         * Parses an uploaded excel file and shows a form to match columns
171         */
172        def parseTagExcel = {
173                Run run = getRun( params.id );
174
175                if( !run ) {
176                        redirect(controller: 'study')
177                        return
178                }
179
180                def filename = params.filename
181
182                // Security check to prevent accessing files in other directories
183                if( !filename || filename.contains( '..' ) ) {
184                        response.status = 500;
185                        render "Invalid filename given";
186                        return;
187                }
188
189                // Check for existence and readability
190                File file = new File( fileService.getUploadDir(), filename)
191
192                if( !file.exists() || !file.canRead() ) {
193                        response.status = 404;
194                        render "The uploaded file doesn't exist or doesn't work as expected.";
195                        return;
196                }
197
198                // Save the filename in session for later use
199                session.filename = filename;
200                def excelData;
201                try {
202                        excelData = sampleExcelService.parseTagsExcel( file, false );
203                } catch( Throwable e ) { // Catch a throwable here instead of an exception, since the apache poi stuff gives an Error on failure
204                        // Couldn't create a workbook from this file.
205                        response.status = 400 // Bad request
206                        render "Uploaded file is not a valid excel file: " + e.getMessage()
207                        return
208                }
209                session.possibleFields = excelData.possibleFields
210
211                [run: run, headers: excelData.headers, exampleData: excelData.exampleData, filename: filename, possibleFields: [ "Don't import" ] + excelData.possibleFields, bestMatches: excelData.bestMatches]
212        }
213
214        /**
215         * Updates the assay samples based on the given excel file and the column matches
216         */
217        def updateTagsByExcel = {
218                Run run = getRun( params.id );
219
220                if( !run ) {
221                        // Now delete the file, since we don't need it anymore
222                        _deleteUploadedFileFromSession()
223
224                        redirect(controller: 'study')
225                        return
226                }
227
228                if( !session.filename ) {
229                        // Now delete the file, since we don't need it anymore
230                        _deleteUploadedFileFromSession()
231
232                        flash.error = "No excel file found because session timed out. Please try again."
233                        redirect( action: 'show', id: params.id)
234                        return
235                }
236
237                // Determine the match-columns
238                def matchColumns = params[ 'matches'];
239
240                // Now loop through the excel sheet and update all samples with the specified data
241                File file = new File( fileService.getUploadDir(), session.filename );
242
243                if( !file.exists() || !file.canRead() ) {
244                        flash.error = "Excel file has been removed since previous step. Please try again."
245                        redirect( action: 'show', id: params.id)
246                        return
247                }
248
249                // Make it only possible to update samples writable by the user
250                def assaySamples = run.assaySamples.findAll { it.assay.study.canWrite( session.user ) }
251
252                def excelData = sampleExcelService.updateTagsByExcel( matchColumns, session.possibleFields, file, assaySamples );
253
254                // Return a message to the user
255                if( !excelData.success ) {
256                        flash.error = excelData.message
257                } else if( excelData.numSuccesful == 0 ) {
258                        flash.error = "None of the " + excelData.failedRows.size() + " row(s) could be imported, because none of the sample names matched or no samples are writable. Have you provided the right excel file?"
259                } else {
260                        flash.message = excelData.numSuccesful + " samples have been updated. "
261
262                        if( excelData.failedRows.size() > 0 )
263                                flash.message += excelData.failedRows.size() + " row(s) could not be imported, because the sample names could not be found in the database or you don't have the proper permissions to change them."
264                }
265                redirect( action: 'show', id: params.id )
266        }
267
268
269        /**
270         * Update the properties of the assay samples manually
271         */
272        def updateTagsManually = {
273                Run run = getRun( params.id );
274
275                if( !run ) {
276                        redirect(controller: 'study')
277                        return
278                }
279
280                // Loop through all assay samples and set data
281                def sampleParams = params.assaySample;
282
283                if( sampleParams ) {
284                        run.assaySamples.each { assaySample ->
285                                def assaySampleParams = sampleParams.get( assaySample.id as String );
286                                if( assaySampleParams ) {
287                                        assaySample.tagName = assaySampleParams.tagName
288                                        assaySample.oligoNumber = assaySampleParams.oligoNumber
289                                        assaySample.tagSequence = assaySampleParams.tagSequence
290
291                                        assaySample.save()
292                                }
293                        }
294                }
295
296                flash.message = "Data about samples is saved."
297                redirect( action: 'show', id: params.id )
298        }
299
300        /**************************************************************************
301         *
302         * Methods for handling data about assays for this run
303         *
304         *************************************************************************/
305
306        /**
307         * Adds existing samples to this run
308         */
309        def addSamples = {
310                Run run = getRun( params.id );
311
312                if( !run ) {
313                        redirect(controller: 'study')
314                        return
315                }
316
317                // Add checked runs to this assay
318                def assaySamples = params.assaySamples
319                if( assaySamples instanceof String ) {
320                        assaySamples = [ assaySamples ]
321                }
322
323                def numAdded = 0;
324                assaySamples.each { assaySampleId ->
325                        try {
326                                def assaySample = AssaySample.findById( assaySampleId as Long )
327                                if( run.assaySamples == null || !run.assaySamples.contains( assaySample ) ) {
328                                        run.addToAssaySamples( assaySample );
329                                        numAdded++;
330                                }
331                        } catch( Exception e ) {}
332                }
333
334                flash.message = numAdded + " samples are added to this run."
335                redirect( action: 'show', id: params.id)
336        }
337
338        /**
339         * Removes sample from this run
340         */
341        def removeSample = {
342                Run run = getRun( params.id );
343
344                if( !run ) {
345                        redirect(controller: 'study')
346                        return
347                }
348
349                if( !params.assaySampleId ) {
350                        flash.message = "No sample id given"
351                        redirect(action: 'show', id: params.id)
352                        return
353                }
354
355                def assaySample
356
357                try {
358                        assaySample = AssaySample.findById( params.assaySampleId as Long )
359                } catch( Exception e ) {
360                        log.error e
361                        flash.message = "Incorrect assaysample id given: " + params.assaySampleId
362                        redirect(action: 'show', id: params.id)
363                        return
364                }
365
366                if( run.assaySamples.contains( assaySample ) ) {
367                        run.removeFromAssaySamples( assaySample );
368                        flash.message = "The sample has been removed from this run."
369                } else {
370                        flash.message = "The given sample was not associated with this run."
371                }
372
373                redirect( action: 'show', id: params.id)
374        }
375
376
377        /**
378         * Adds existing assays to this run
379         */
380        def addAssays = {
381                Run run = getRun( params.id );
382
383                if( !run ) {
384                        redirect(controller: 'study')
385                        return
386                }
387
388                // Add checked runs to this assay
389                def assays = params.assays
390                if( assays instanceof String ) {
391                        assays = [ assays ]
392                }
393
394                def numAdded = 0;
395                assays.each { assay_id ->
396                        try {
397                                def assay = Assay.findById( assay_id as Long )
398                                if( run.assays == null || !run.assays.contains( assay ) ) {
399                                        run.addToAssays( assay );
400                                        numAdded++;
401                                }
402                        } catch( Exception e ) {}
403                }
404
405                flash.message = numAdded + " assays are added to this run."
406                redirect( action: 'show', id: params.id)
407        }
408
409        /**
410         * Removes assay for this run
411         */
412        def removeAssay = {
413                Run run = getRun( params.id );
414
415                if( !run ) {
416                        redirect(controller: 'study')
417                        return
418                }
419
420                if( !params.assay_id ) {
421                        flash.message = "No assay id given"
422                        redirect(action: 'show', id: params.id)
423                        return
424                }
425
426                def assay
427
428                try {
429                        assay = Assay.findById( params.assay_id as Long )
430                } catch( Exception e ) {
431                        throw e
432                        flash.message = "Incorrect assay id given: "
433                        redirect(action: 'show', id: params.id)
434                        return
435                }
436
437                if( run.assays.contains( assay ) ) {
438                        run.removeFromAssays( assay );
439                        flash.message = "The assay has been removed from this run."
440                } else {
441                        flash.message = "The given assay was not associated with this run."
442                }
443
444                redirect( action: 'show', id: params.id)
445        }
446
447
448        /**
449         * Deletes an uploaded file for which the filename is given in the session.
450         * @return
451         */
452        def _deleteUploadedFileFromSession() {
453                if( !session.filename )
454                        return
455
456                // Now delete the file, since we don't need it anymore
457                fileService.delete( session.filename  )
458                session.filename = ''
459        }
460
461        protected Run getRun(def runId) {
462                // load study with id specified by param.id
463                def run
464                try {
465                        run = Run.get(runId as Long)
466                } catch( Exception e ) {
467                        flash.error = "Incorrect id given: " + runId
468                        return null
469                }
470
471                if (!run) {
472                        flash.error = "No run found with id: " + runId
473                        return null
474                }
475
476                return run
477        }
478
479        protected Assay getAssay(def assayId) {
480                // load study with id specified by param.id
481                def assay
482                try {
483                        assay = Assay.get(assayId as Long)
484                } catch( Exception e ) {
485                        flash.error = "Incorrect id given: " + assayId
486                        return null
487                }
488
489                if (!assay) {
490                        flash.error = "No assay found with id: " + assayId
491                        return null
492                }
493
494                if (!assay.study.canRead( session.user ) ) {
495                        flash.error = "You don't have the right authorizaton to access assay " + assay.name
496                        return null
497                }
498
499                return assay
500        }
501}
Note: See TracBrowser for help on using the repository browser.