source: trunk/grails-app/controllers/nl/tno/massSequencing/RunController.groovy @ 67

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

Removed needless debug printlns

File size: 27.8 KB
Line 
1package nl.tno.massSequencing
2
3import java.util.Date;
4import grails.converters.JSON
5import nl.tno.massSequencing.auth.*
6
7import org.codehaus.groovy.grails.commons.ConfigurationHolder
8
9class RunController {
10        def fileService
11        def synchronizationService
12        def sampleExcelService
13        def fastaService
14        def dataTablesService
15
16        def index = {
17                [runs: Run.list(), user: session.user]
18        }
19
20        /**
21         * Returns JSON data for the datatable with runs
22         * @see http://www.datatables.net/usage/server-side
23         * @see DataTablesService.retrieveData
24         */
25        def showRunList = {
26                // Determine the total number of assaysamples for this run
27                def ids = Run.executeQuery( "SELECT r.id FROM Run r" );
28                def total = ids.size();
29
30                // Which columns are shown on screen and should be retrieved from the database
31                def columns = [
32                        "r.id",
33                        "r.name",
34                        "COUNT( DISTINCT a )",
35                        "SUM( a.numSequences )"
36                ]
37
38                def groupColumns = columns[0..1];
39                def orderByMapping = null;      // Meaning: order by column 2 on screen = order by column 1 in the table (screen starts at column 0, table starts at column 1 )   
40
41                // Retrieve data from assaySample table
42                def from = "Run r LEFT JOIN r.assaySamples a"
43
44                // This closure determines what to do with a row that is retrieved from the database.
45                def convertClosure = {
46                        def runId = it[ 0 ];
47                        def runName = it[ 1 ];
48                        def numSamples = it[ 2 ];
49                        def numSequences = it[ 3 ];
50
51                        // Create buttons in the last three columns
52                        def editButton = g.link( controller: "run", action: "show", id: it[ 0 ], "title": "View run"  ) { '<img src="' + fam.icon( name: 'application_form_magnify' ) + '" title="View run" />' };
53                        def deleteButton = '';
54                        def chartButton = '';
55
56                        if( numSequences > 0 ) {
57                                chartButton = g.link( controller: "run", action: "sequenceLengthHistogram", id: runId, title: "Sequence length histogram" ) { '<img src="' + fam.icon( name: 'chart_bar' ) + '" alt="Sequence length histogram" title="Sequence length histogram" />' }
58                        } else {
59                                chartButton = '<img src="' + fam.icon( name: 'chart_bar' ) + '" class="disabled" alt="No histogram available because no sequences are uploaded." title="No histogram available because no sequences are uploaded." />'
60                        }
61
62                        if( numSequences > 0 || Run.hasNonWritableAssays( runId, session.user.id ) ) {
63                                deleteButton = '<img src="' + fam.icon( name: 'delete' ) + '" class="disabled" alt="Run can not be deleted because data is associated with it." title="Run can not be deleted because data is associated with it." />'
64                        } else {
65                                deleteButton = g.link( title:"Delete run", onClick:"return confirm( 'Are you sure you want to delete this run?' );", controller:"run", action:"deleteRun", id: runId ) { '<img src="' + fam.icon( name: 'delete' ) + '" alt="Delete run" title="Delete run" />' }
66                        }
67
68                        [
69                                g.checkBox( name: "ids", value: runId, checked: false, onClick: "updateCheckAll(this);" ),
70                                g.link( title:"View run", controller:"run", action:"show", id: runId ) { runName },     // it.name
71                                numSamples > 0 ? g.formatNumber( number: numSamples, format: "###,###,##0" ) : "-",     // it.numSequences(),
72                                numSequences > 0 ? g.formatNumber( number: numSequences, format: "###,###,##0" ) : "-", // it.numQualScores(),
73                                editButton,
74                                deleteButton,
75                                chartButton
76                        ]
77                }
78
79                // Send the data to the user
80                render dataTablesService.retrieveData(
81                                params,
82                                Run.class,
83                                convertClosure,
84                                columns,
85                                groupColumns,
86                                from,
87                                total,
88                                ids,
89                                orderByMapping
90                                ) as JSON
91        }
92
93        def show = {
94                // load run with id specified by param.id
95                def run = getRun( params.id );
96
97                if (!run) {
98                        redirect(controller: 'study', action: 'index')
99                        return
100                }
101
102                // Find statistics for all assaySamples in order to improve performance
103                AssaySample.initStats( run.assaySamples?.toList() )
104
105                // Determine runs not used in this assay
106                def otherAssays = Assay.list( sort: "name" ).findAll { !it.runs.contains( run ) && it.study.canRead( session.user ) }
107
108                // Determine several parameters to show on screen
109               
110               
111                // Send the assay information to the view
112                [run: run, allRuns: Run.list(), otherAssays: otherAssays, editable: true]
113        }
114       
115        /**
116         * Returns JSON data for the datatable with assaysamples
117         * @see http://www.datatables.net/usage/server-side
118         * @see DataTablesService.retrieveData
119         */
120        def showSampleData = {
121                // load run with id specified by params.id
122                def run = getRun( params.id );
123
124                if (!run) {
125                        response.sendError(404, "Run not found" )
126                        return
127                }
128               
129                // Determine the total number of assaysamples for this run
130                def ids = AssaySample.executeQuery( "SELECT s.id FROM AssaySample s WHERE s.run.id = :runId AND EXISTS( FROM Auth a3 WHERE s.assay.study = a3.study AND a3.user = :user AND a3.canRead = true )", [ "runId": run.id, "user": session.user  ] );
131                def total = ids.size();
132               
133                // Which columns are shown on screen and should be retrieved from the database
134                def columns = [
135                        "s.id",
136                        "s.sample.name",
137                        "s.assay.study.name",
138                        "s.assay.name",
139                        "s.fwMidName",
140                        "SUM( sd.numSequences )",
141                        "SUM( CASE WHEN sd.qualityFile IS NULL THEN 0 ELSE sd.numSequences END )",
142                        's.run.id',
143                        's.assay.id',
144                        's.assay.study.id',
145                ]
146               
147                def groupColumns = columns[0..4] + columns[7..9];
148                def orderByMapping = null;      // Meaning: order by column 2 on screen = order by column 1 in the table (screen starts at column 0, table starts at column 1 )
149               
150                // Retrieve data from assaySample table
151                def from = "AssaySample s LEFT JOIN s.sequenceData as sd"
152
153                // And filter by runId
154                def where = "s.run.id = :runId AND EXISTS( FROM Auth a3 WHERE s.assay.study = a3.study AND a3.user = :user AND a3.canRead = true )"
155                def parameters = [ "runId": run.id, "user": session.user ];
156               
157                // This closure determines what to do with a row that is retrieved from the database.
158                def convertClosure = {
159                        def sampleId = it[ 0 ];
160                        def sampleName = it[ 1 ];
161                        def runId = it[ 7 ];
162                        def assayId = it[ 8 ];
163                        def studyId = it[ 9 ];
164                        def numSequences = it[ 5 ];
165                       
166                        // TODO: Rewrite this authorization part in a more efficient way
167                        def auth = Auth.executeQuery( "SELECT a.canWrite FROM Auth a WHERE a.study.id = :studyId AND a.user.id = :userId", [ 'studyId': studyId, 'userId': session.user?.id ] );
168                        def canWrite = auth ? auth[ 0 ] : false;
169                       
170                        // Create buttons in the last three columns
171                        def editButton = '';
172                        def deleteButton = '';
173                        def chartButton = '';
174                       
175                        if( canWrite ) {
176                                editButton = g.link( url: '#', title: "Edit sample", onClick: "showEditSampleDialog(" + sampleId + ", 'run', " + runId + ");"  ) { '<img src="' + fam.icon( name: 'pencil' ) + '" title="Edit sample" />' };
177                                deleteButton = g.link( controller: 'run', action: 'removeSample', id: it[ 7 ], params: [ 'assaySampleId': sampleId ], title: "Remove sample from run", onClick: "return confirm( 'Are you sure you want to remove the selected sample from this run?' );"  ) { '<img src="' + fam.icon( name: 'application_delete' ) + '" title="Remove sample from run" />' };
178                        } else {
179                                editButton = '<img src="' + fam.icon( name: 'pencil' ) + '" title="You can\'t edit this sample because you don\'t have sufficient privileges." class="disabled" />';
180                                deleteButton = '<img src="' + fam.icon( name: 'application_delete' ) + '" title="You can\'t remove this sample because you don\'t have sufficient privileges." class="disabled" />';
181                        }
182                       
183                        if( numSequences > 0 ) {
184                                chartButton = g.link( controller: "assaySample", action: "sequenceLengthHistogram", id: sampleId, title: "Sequence length histogram" ) { '<img src="' + fam.icon( name: 'chart_bar' ) + '" alt="Sequence length histogram" title="Sequence length histogram" />' }
185                        } else {
186                                chartButton = '<img src="' + fam.icon( name: 'chart_bar' ) + '" class="disabled" alt="No histogram available because no sequences are uploaded." title="No histogram available because no sequences are uploaded." />'
187                        }
188                       
189                        [
190                                g.checkBox( name: "ids", value: sampleId, checked: false, onClick: "updateCheckAll(this);" ),
191                                g.link( url: "#", onClick:"showSample( " + sampleId + ", 'run' );", title: "Show sample details" ) { sampleName },      // it.sample.name
192                                it[ 2 ],        // it.assay.study.name
193                                it[ 3 ],        // it.assay.name
194                                it[ 4 ],        // it.fwMidName
195                                it[ 5 ] > 0 ? g.formatNumber( number: it[ 5 ], format: "###,###,##0" ) : "-",   // it.numSequences(),
196                                it[ 6 ] > 0 ? g.formatNumber( number: it[ 6 ], format: "###,###,##0" ) : "-",   // it.numQualScores(),
197                                editButton,
198                                deleteButton,
199                                chartButton
200                        ]
201                }
202               
203                // Send the data to the user
204                render dataTablesService.retrieveData(
205                        params,
206                        AssaySample.class,
207                        convertClosure,
208                        columns,
209                        groupColumns,
210                        from,
211                        total,
212                        ids,
213                        orderByMapping, 
214                        where,
215                        parameters
216                ) as JSON
217       
218        }
219
220        /**
221         * Shows a form to edit the specified run in dialog mode
222         */
223        def editForm = {
224                // load run with id specified by param.id
225                Run run = getRun( params.id );
226
227                if (!run) {
228                        render flash.error
229                        return
230                }
231
232                Assay assay = null
233                if( params.assayId ) {
234                        assay = getAssay( params.assayId )
235
236                        if( !assay ) {
237                                render flash.error;
238                                return
239                        }
240                }
241
242                [assay: assay, run: run]
243        }
244
245        def create = {
246                // Retrieve the assay from the database, but don't exit with an error if no assay is found
247                Assay a = getAssay(params.id);
248                flash.error = "";
249
250                // Create run based on given parameters
251                Run run = new Run();
252
253                run.setPropertiesFromForm( params );
254
255                if( a )
256                        a.addToRuns( run );
257
258                if( !run.save() ) {
259                        flash.message = "Run could not be saved: " + run.getErrors();
260                } else {
261                        flash.message = "Run " + run.name + " has been added to the system."
262                }
263
264                if( a )
265                        redirect( controller: "assay", action: "show", id: a.id )
266                else
267                        redirect( controller: 'run' );
268        }
269
270        def update = {
271                Run run = getRun( params.id );
272
273                if( !run ) {
274                        redirect(controller: 'assay', action: 'show', id: params.assayId)
275                        return
276                }
277
278                // Set properties to the run
279                params.parameterFile = params.editParameterFile
280
281                run.setPropertiesFromForm( params );
282
283                if( run.save() ) {
284                        flash.message = "Run succesfully saved";
285                } else {
286                        flash.error = "Run could not be saved: " + run.getErrors();
287                }
288
289                Assay assay = getAssay(params.assayId);
290                flash.error = "";
291
292                if( assay ) {
293                        redirect( controller: 'assay', action: 'show', id: assay.id)
294                } else {
295                        redirect( controller: 'run', action: 'show', id: run.id )
296                }
297        }
298
299        def delete = {
300                Run run = getRun( params.id );
301
302                if( !run ) {
303                        redirect(controller: 'assay', action: 'show', id: params.assayId)
304                        return
305                }
306
307                // Don't remove runs for which data exists
308                if( run.assaySamples*.sequenceData.flatten().size() ) {
309                        flash.message = "Run could not be deleted because samples with data are associated with it.";
310                        redirect( controller: "assay", action: "show", id: params.assayId )
311                }
312
313                // Check whether the user has sufficient privileges to remove the run from all assays
314                def hasPrivileges = true;
315                run.assay.each {
316                        if( !it.study.canWrite( session.user ) ) 
317                                hasPrivileges = false
318                }
319               
320                if( !hasPrivileges ) {
321                        flash.message = "Run could not be deleted because you don't have sufficient privileges to remove the run from all assays.";
322                        redirect( controller: "assay", action: "show", id: params.assayId )
323                }
324               
325                // Remove all associations
326                def a = [] + run.assays
327                a.each {
328                        run.removeFromAssays( it );
329                }
330
331                def name = run.name
332                run.delete();
333                flash.message = "Run " + name + " has been deleted from the system."
334
335                redirect( controller: "assay", action: "show", id: params.assayId )
336        }
337
338        def deleteRun = {
339                Run run = getRun( params.id );
340
341                if( !run ) {
342                        redirect(controller: 'run', action: 'index')
343                        return
344                }
345
346                // Don't remove runs for which data exists
347                if( run.assaySamples*.sequenceData.flatten().size() ) {
348                        flash.message = "Run could not be deleted because samples with data are associated with it.";
349                        redirect(controller: 'run', action: 'index')
350                }
351
352                // Check whether the user has sufficient privileges to remove the run from all assays
353                def hasPrivileges = true;
354                run.assays.each {
355                        if( !it.study.canWrite( session.user ) )
356                                hasPrivileges = false
357                }
358               
359                if( !hasPrivileges ) {
360                        flash.message = "Run could not be deleted because you don't have sufficient privileges to remove the run from all assays.";
361                        redirect(controller: 'run', action: 'index')
362                }
363               
364                // Remove all associations
365                def a = [] + run.assays
366                a.each {
367                        run.removeFromAssays( it );
368                }
369
370                def name = run.name
371                run.delete();
372                flash.message = "Run " + name + " has been deleted from the system."
373
374                redirect(controller: 'run', action: 'index')
375        }
376
377        /**************************************************************************
378         *
379         * Methods for handling data about the samples in this run
380         *
381         *************************************************************************/
382
383        /**
384         * Downloads an excel sheet with data about the assay samples, to enter data in excel
385         */
386        def downloadTagsExcel = {
387                Run run = getRun( params.id );
388
389                if( !run ) {
390                        redirect(controller: 'run')
391                        return
392                }
393
394                // Make it only possible to update samples writable by the user
395                def assaySamples = run.assaySamples.findAll { it.assay.study.canWrite( session.user ) }
396
397                def filename = "Run " + run.name + "_tags.xls"
398                def wb = sampleExcelService.downloadSampleExcel( assaySamples, false );
399
400                // Make file downloadable
401                log.trace( "Creation for downloading the file " + filename )
402                sampleExcelService.excelService.downloadFile( wb, filename, response )
403        }
404       
405        /**
406        * Downloads an example excel sheet to describe the format of a file-matching sheet. This
407        * file is used when uploading sequence files.
408        */
409   def downloadMatchExcel = {
410           Run run = getRun( params.id );
411
412           if( !run ) {
413                   redirect(controller: 'run')
414                   return
415           }
416
417           // Make it only possible to update samples writable by the user
418           def assaySamples = run.assaySamples.findAll { it.assay.study.canWrite( session.user ) }
419
420           def filename = "Run " + run.name + "_filenames.xls"
421           def wb = sampleExcelService.downloadMatchExcel( assaySamples );
422
423           // Make file downloadable
424           log.trace( "Creation for downloading the file " + filename )
425           sampleExcelService.excelService.downloadFile( wb, filename, response )
426   }
427
428        /**
429         * Parses an uploaded excel file and shows a form to match columns
430         */
431        def parseTagExcel = {
432                Run run = getRun( params.id );
433
434                if( !run ) {
435                        redirect(controller: 'study')
436                        return
437                }
438
439                def filename = params.filename
440
441                // Security check to prevent accessing files in other directories
442                if( !filename || filename.contains( '..' ) ) {
443                        response.status = 500;
444                        response.setContentType( "text/plain" );
445                        render "Invalid filename given";
446                        return;
447                }
448
449                // Check for existence and readability
450                File file = new File( fileService.getUploadDir(), filename)
451
452                if( !file.exists() || !file.canRead() ) {
453                        response.status = 404;
454                        response.setContentType( "text/plain" );
455                        render "The uploaded file doesn't exist or doesn't work as expected.";
456                        return;
457                }
458
459                // Save the filename in session for later use
460                session.filename = filename;
461                def excelData;
462                try {
463                        excelData = sampleExcelService.parseTagsExcel( file, false );
464                } catch( Throwable e ) { // Catch a throwable here instead of an exception, since the apache poi stuff gives an Error on failure
465                        e.printStackTrace()
466                        // Couldn't create a workbook from this file.
467                        response.status = 400 // Bad request
468                        response.setContentType( "text/plain" );
469                        render "Uploaded file is not a valid excel file: " + e.getMessage()
470                        return
471                }
472                session.possibleFields = excelData.possibleFields
473
474                [run: run, headers: excelData.headers, exampleData: excelData.exampleData, filename: filename, possibleFields: [ "Don't import" ] + excelData.possibleFields, bestMatches: excelData.bestMatches]
475        }
476
477        /**
478         * Updates the assay samples based on the given excel file and the column matches
479         */
480        def updateTagsByExcel = {
481                Run run = getRun( params.id );
482
483                if( !run ) {
484                        // Now delete the file, since we don't need it anymore
485                        _deleteUploadedFileFromSession()
486
487                        redirect(controller: 'study')
488                        return
489                }
490
491                if( !session.filename ) {
492                        // Now delete the file, since we don't need it anymore
493                        _deleteUploadedFileFromSession()
494
495                        flash.error = "No excel file found because session timed out. Please try again."
496                        redirect( action: 'show', id: params.id)
497                        return
498                }
499
500                // Determine the match-columns
501                def matchColumns = params[ 'matches'];
502
503                // Now loop through the excel sheet and update all samples with the specified data
504                File file = new File( fileService.getUploadDir(), session.filename );
505
506                if( !file.exists() || !file.canRead() ) {
507                        flash.error = "Excel file has been removed since previous step. Please try again."
508                        redirect( action: 'show', id: params.id)
509                        return
510                }
511
512                // Make it only possible to update samples writable by the user
513                def assaySamples = run.assaySamples.findAll { it.assay.study.canWrite( session.user ) }
514               
515                def excelData = sampleExcelService.updateTagsByExcel( matchColumns, session.possibleFields, file, assaySamples );
516
517                // Return a message to the user
518                if( !excelData.success ) {
519                        flash.error = excelData.message
520                } else if( excelData.numSuccesful == 0 ) {
521                        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?"
522                } else {
523                        flash.message = excelData.numSuccesful + " samples have been updated. "
524
525                        if( excelData.failedRows.size() > 0 )
526                                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."
527                }
528
529                // Now delete the file, since we don't need it anymore
530                _deleteUploadedFileFromSession()
531
532                redirect( action: 'show', id: params.id )
533        }
534
535
536        /**
537         * Update the properties of the assay samples manually
538         */
539        def updateTagsManually = {
540                Run run = getRun( params.id );
541
542                if( !run ) {
543                        redirect(controller: 'study')
544                        return
545                }
546
547                // Loop through all assay samples and set data
548                def sampleParams = params.assaySample;
549
550                if( sampleParams ) {
551                        run.assaySamples.findAll { it.assay.study.canWrite( session.user ) }.each { assaySample ->
552                                def assaySampleParams = sampleParams.get( assaySample.id as String );
553                                if( assaySampleParams ) {
554                                        sampleExcelService.variableFields.each { k, v ->
555                                                assaySample[ k ] = assaySampleParams[ k ];
556                                        }
557                                        assaySample.save()
558                                }
559                        }
560                }
561
562                flash.message = "Data about samples is saved."
563                redirect( action: 'show', id: params.id )
564        }
565
566        /**************************************************************************
567         *
568         * Methods for handling data about assays for this run
569         *
570         *************************************************************************/
571
572        /**
573         * Adds existing samples to this run
574         */
575        def addSamples = {
576                Run run = getRun( params.id );
577
578                if( !run ) {
579                        redirect(controller: 'run', action: 'index')
580                        return
581                }
582
583                // Add checked runs to this assay
584                def assaySamples = params.assaySamples
585                if( assaySamples instanceof String ) {
586                        assaySamples = [ assaySamples ]
587                }
588
589                def numAdded = 0;
590                assaySamples.each { assaySampleId ->
591                        try {
592                                def assaySample = AssaySample.findById( assaySampleId as Long )
593                                if( !assaySample.run && assaySample.assay.study.canWrite( session.user ) ) {
594                                        if( run.assaySamples == null || !run.assaySamples.contains( assaySample ) ) {
595                                                run.addToAssaySamples( assaySample );
596                                                numAdded++;
597                                        }
598                                }
599                        } catch( Exception e ) {}
600                }
601
602                flash.message = numAdded + " samples are added to this run."
603                redirect( action: 'show', id: params.id)
604        }
605
606        /**
607         * Removes sample from this run
608         */
609        def removeSample = {
610                Run run = getRun( params.id );
611
612                if( !run ) {
613                        redirect(controller: 'study')
614                        return
615                }
616
617                if( !params.assaySampleId ) {
618                        flash.error = "No sample id given"
619                        redirect(action: 'show', id: params.id)
620                        return
621                }
622
623                def assaySample
624
625                try {
626                        assaySample = AssaySample.findById( params.assaySampleId as Long )
627                } catch( Exception e ) {
628                        log.error e
629                        flash.error = "Incorrect assaysample id given: " + params.assaySampleId
630                        redirect(action: 'show', id: params.id)
631                        return
632                }
633               
634                if( !assaySample.assay.study.canWrite( session.user ) ) {
635                        flash.error = "You don't have sufficient privileges to remove the specified sample from this run."
636                        redirect(action: 'show', id: params.id)
637                        return
638                }
639               
640                if( run.assaySamples.contains( assaySample ) ) {
641                        run.removeFromAssaySamples( assaySample );
642                        flash.message = "The sample has been removed from this run."
643                } else {
644                        flash.message = "The given sample was not associated with this run."
645                }
646
647                redirect( action: 'show', id: params.id)
648        }
649       
650        /**
651         * Removes samples from this run
652         */
653        def removeSamples = {
654                // Determine the run we are in
655                Run run = getRun( params.runId );
656
657                if( !run ) {
658                        redirect(controller: 'run', action: 'list')
659                        return
660                }
661
662                // Find the selected assaysamples
663                def ids = params.list( 'ids' );
664                ids = ids.findAll { it.isLong() }.collect { Long.parseLong( it ) }
665                def assaySamples = ids.collect { AssaySample.get( it ) }.findAll { it }
666
667                if( !assaySamples ) {
668                        flash.message = "No samples selected for removal"
669                        redirect( action: 'show', id: run.id );
670                        return;
671                }
672
673                def numRemoved = 0;
674                assaySamples.each { assaySample ->             
675                        if( assaySample.assay.study.canWrite( session.user ) ) {
676                                if( assaySample.run ) {
677                                        assaySample.run.removeFromAssaySamples( assaySample );
678                                        numRemoved++;
679                                }
680                        }
681                }
682               
683                if( numRemoved > 0 ) 
684                        flash.message = numRemoved + " sample(s) have been removed from this run."
685                else
686                        flash.message = "No samples have been removed from this run, because you don't have the right privileges to do so."
687
688                redirect( action: 'show', id: run.id)
689        }
690
691
692        /**
693         * Adds existing assays to this run
694         */
695        def addAssays = {
696                Run run = getRun( params.id );
697
698                if( !run ) {
699                        redirect(controller: 'study')
700                        return
701                }
702
703                // Add checked runs to this assay
704                def assays = params.assays
705                if( assays instanceof String ) {
706                        assays = [ assays ]
707                }
708
709                def numAdded = 0;
710                assays.each { assay_id ->
711                        try {
712                                def assay = Assay.findById( assay_id as Long )
713                                if( assay.study.canWrite( session.user ) ) {
714                                        if( run.assays == null || !run.assays.contains( assay ) ) {
715                                                run.addToAssays( assay );
716                                                numAdded++;
717                                        }
718                                }
719                        } catch( Exception e ) {}
720                }
721
722                flash.message = numAdded + " assays are added to this run."
723                redirect( action: 'show', id: params.id)
724        }
725
726        /**
727         * Removes assay for this run
728         */
729        def removeAssay = {
730                Run run = getRun( params.id );
731
732                if( !run ) {
733                        redirect(controller: 'run', action: 'index')
734                        return
735                }
736
737                if( !params.assay_id ) {
738                        flash.message = "No assay id given"
739                        redirect(action: 'show', id: params.id)
740                        return
741                }
742
743                def assay
744
745                try {
746                        assay = Assay.findById( params.assay_id as Long )
747                } catch( Exception e ) {
748                        throw e
749                        flash.message = "Incorrect assay id given: "
750                        redirect(action: 'show', id: params.id)
751                        return
752                }
753
754                if( !assay.study.canWrite( session.user ) ) {
755                        flash.error = "You don't have sufficient privileges to remove the specified assay from this run."
756                        redirect(action: 'show', id: params.id)
757                        return
758                }
759               
760                if( run.assays.contains( assay ) ) {
761                        run.removeFromAssays( assay );
762                        flash.message = "The assay has been removed from this run."
763                } else {
764                        flash.message = "The given assay was not associated with this run."
765                }
766
767                redirect( action: 'show', id: params.id)
768        }
769       
770        /**
771         * Deletes all sequences for a given run
772         */
773        def deleteSequenceData = {
774                // Determine the run we are in
775                Run run = getRun( params.runId );
776
777                if( !run ) {
778                        redirect(controller: 'run', action: 'index')
779                        return
780                }
781
782                // Find the selected assaysamples
783                def ids = params.list( 'ids' );
784                ids = ids.findAll { it.isLong() }.collect { Long.parseLong( it ) }
785                def assaySamples = ids.collect { AssaySample.get( it ) }.findAll { it }
786
787                if( !assaySamples ) {
788                        flash.message = "No samples selected"
789                        redirect( action: 'show', id: run.id );
790                        return;
791                }
792               
793                def numFiles = fastaService.deleteSequenceData( assaySamples );
794               
795                flash.message = numFiles + " files have been removed from the run.";
796                redirect( controller: 'run', action: 'show', id: run.id );
797        }
798
799        /**
800         * Exports data about one or more runs in fasta format
801         */
802        def exportAsFasta = {
803                def assaySamples = getAssaySamples( params );
804
805                if( assaySamples == null )
806                        return;
807
808                def name
809
810                if( assaySamples.size() == 0 ) {
811                        flash.error = "No samples found for selected runs";
812                        redirect( action: "index" );
813                        return;
814                } else if( assaySamples*.run.unique().size() == 1 )
815                        name = "Run_" + assaySamples[0].run?.name?.replace( ' ', '_' );
816                else
817                        name = "runs";
818
819                // Export the sequences and quality scores
820                response.setHeader "Content-disposition", "attachment; filename=${name}.zip"
821                try {
822                        fastaService.export( assaySamples.unique(), response.getOutputStream() );
823                        response.outputStream.flush();
824                } catch( Exception e ) {
825                        log.error( "Exception occurred during export of sequences. Probably the user has cancelled the download." );
826                        e.printStackTrace();
827                }
828        }
829       
830        /**
831         * Export metadata of selected samples in excel format
832         */
833        def exportMetaData = {
834                def assaySamples = getAssaySamples( params );
835                def name
836               
837                if( assaySamples == null )
838                        return;
839                       
840                if( assaySamples.size() == 0 ) {
841                        flash.error = "No samples found for selected runs";
842                        redirect( action: "index" );
843                        return;
844                } else if( assaySamples*.run.unique().size() == 1 ) {
845                        name = "Run_" + assaySamples[0].run?.name?.replace( ' ', '_' );
846                } else {
847                        name = "runs";
848                }
849
850                // Export the metadata
851                try {
852                        // The export functionality needs a assaysSample-tag list, but it
853                        // should be empty when only exporting metadata
854                        def tags = [];
855                        assaySamples.unique().each { assaySample ->
856                                tags << [assaySampleId: assaySample.id, sampleName: assaySample.sample.name, assayName: assaySample.assay.name, studyName: assaySample.assay.study.name, tag: "-"]
857                        }
858                        response.setHeader "Content-disposition", "attachment; filename=${name}.xls"
859
860                        if( !sampleExcelService.exportExcelSampleData( assaySamples.unique(), tags, response.outputStream ) ) {
861                                flash.error = "An error occurred while fetching sample data. Maybe the session has timed out.";
862                                response.setHeader( "Content-disposition", "" );
863                                redirect( action: "index" );
864                        }
865                        response.outputStream.flush();
866                } catch( Exception e ) {
867                        log.error( "Exception occurred during export of sequences. Probably the user has cancelled the download." );
868                        e.printStackTrace();
869                }
870        }
871       
872        def sequenceLengthHistogram = {
873                redirect( controller: "assaySample", action: "sequenceLengthHistogramForRun", id: params.id );
874        }
875       
876        protected List getAssaySamples( params ) {
877                def ids = params.list( 'ids' );
878               
879                ids = ids.findAll { it.isLong() }.collect { Long.parseLong( it ) }
880
881                if( !ids ) {
882                        def message = "No run ids given"
883                        flash.error = message
884                        redirect( action: "index" );
885                        return;
886                }
887
888                def assaySamples = [];
889
890                // Determine which assaySamples to export
891                ids.each { id ->
892                        def run = Run.get( id );
893                        if( run )
894                                assaySamples += run.assaySamples.findAll { it.assay.study.canRead( session.user ) }
895                }
896               
897                return assaySamples;
898        }
899
900        /**
901         * Deletes an uploaded file for which the filename is given in the session.
902         * @return
903         */
904        def _deleteUploadedFileFromSession() {
905                if( !session.filename )
906                        return
907
908                // Now delete the file, since we don't need it anymore
909                fileService.delete( session.filename  )
910                session.filename = ''
911        }
912
913        protected Run getRun(def runId) {
914                // load study with id specified by param.id
915                def run
916                try {
917                        run = Run.get(runId as Long)
918                } catch( Exception e ) {
919                        flash.error = "Incorrect id given: " + runId
920                        return null
921                }
922
923                if (!run) {
924                        flash.error = "No run found with id: " + runId
925                        return null
926                }
927
928                return run
929        }
930
931        protected Assay getAssay(def assayId) {
932                // load study with id specified by param.id
933                def assay
934                try {
935                        assay = Assay.get(assayId as Long)
936                } catch( Exception e ) {
937                        flash.error = "Incorrect id given: " + assayId
938                        return null
939                }
940
941                if (!assay) {
942                        flash.error = "No assay found with id: " + assayId
943                        return null
944                }
945
946                if (!assay.study.canRead( session.user ) ) {
947                        flash.error = "You don't have the right authorizaton to access assay " + assay.name
948                        return null
949                }
950
951                return assay
952        }
953}
Note: See TracBrowser for help on using the repository browser.