Changeset 74 for trunk


Ignore:
Timestamp:
Jun 28, 2011, 5:26:31 PM (8 years ago)
Author:
robert@…
Message:
  • Several bugfixes
  • Added an extra step in the worker process for importing data
Location:
trunk/grails-app
Files:
1 added
10 edited

Legend:

Unmodified
Added
Removed
  • trunk/grails-app/controllers/nl/tno/massSequencing/AssayController.groovy

    r71 r74  
    99class AssayController {
    1010        def synchronizationService
     11        def classificationService
    1112        def gscfService
    1213        def fuzzySearchService
     
    124125
    125126                // Determine other parameters to show on screen
    126                 def numClassified = Classification.executeQuery( "SELECT SUM( c.unclassified ) FROM Classification c WHERE c.assaySample IN (:assaySamples)", [ "assaySamples": assay.assaySamples ] );
     127                def numClassified = assay.assaySamples ? Classification.executeQuery( "SELECT SUM( c.unclassified ) FROM Classification c WHERE c.assaySample IN (:assaySamples)", [ "assaySamples": assay.assaySamples ] ) : 0;
    127128
    128129                // Send the assay information to the view
     
    555556                def numFiles = fastaService.deleteSequenceData( assaySamples );
    556557               
     558                // Reset classification for given samples
     559                classificationService.updateClassificationForAssaySamples( assaySamples );
     560
    557561                flash.message = numFiles + " files have been removed from the assay.";
    558562                redirect( controller: 'assay', action: 'show', id: assay.id );
  • trunk/grails-app/controllers/nl/tno/massSequencing/AssaySampleController.groovy

    r73 r74  
    115115                // Start the export in the background
    116116                def name = "samples";
    117                 def returnUrl = createLink( controller: params.entityType, action: "show", id: params.entityId ).toString()
     117                def returnUrl;
     118               
     119                if( params.entityType && params.entityId )
     120                        returnUrl = createLink( controller: params.entityType, action: "show", id: params.entityId ).toString()
     121                else
     122                        returnUrl = createLink( controller: "query", action: "list" ).toString()
     123                       
    118124                def finishUrl = createLink( controller: "assaySample", action: 'downloadFasta', params: [ processId: '%s' ] ).toString();
    119125                def url = fastaService.startExportProcess( assaySamples, session, name, returnUrl, finishUrl )
  • trunk/grails-app/controllers/nl/tno/massSequencing/RunController.groovy

    r71 r74  
    112112
    113113                // Determine several parameters to show on screen
    114                 def numClassified = Classification.executeQuery( "SELECT SUM( c.unclassified ) FROM Classification c WHERE c.assaySample IN (:assaySamples)", [ "assaySamples": run.assaySamples ] );
     114                def numClassified = run.assaySamples ? Classification.executeQuery( "SELECT SUM( c.unclassified ) FROM Classification c WHERE c.assaySample IN (:assaySamples)", [ "assaySamples": run.assaySamples ] ) : 0;
    115115               
    116116                // Send the assay information to the view
     
    799799               
    800800                // Reset classification for given samples
    801                 classificationService.updateClassificationForAssaySample( assaySamples );
     801                classificationService.updateClassificationForAssaySamples( assaySamples );
    802802               
    803803                flash.message = numFiles + " files have been removed from the run.";
  • trunk/grails-app/controllers/nl/tno/massSequencing/files/ExportController.groovy

    r57 r74  
    1212class ExportController {
    1313        def fileService
     14        def workerService
    1415       
    1516        /**
     
    1718         */
    1819        def zip = {
    19                 // Find all studies
    20                 def studies = Study.list().findAll { it.canRead( session.user ) };
     20                // Determine the number of samples and sequences to be processed.
     21                // The number of samples gives an indication for the time to export the xml
     22                // the number of sequences give an indication for the tim to export the files
     23                def studies = Study.findAll( "FROM Study s WHERE exists( FROM Auth a WHERE a.study = s AND a.user = :user AND a.canRead = true )", [ "user": session.user ] );
     24                def totalProgress = 0
     25               
     26                if( studies ) {
     27                        def measures = AssaySample.executeQuery( "SELECT COUNT(*), SUM(a.numSequences) FROM AssaySample a WHERE a.assay.study IN (:studies)", [ 'studies': studies ] )[0]
     28                        totalProgress = measures[ 0 ] + measures[ 1 ];
     29                }
     30               
     31                def httpSession = session;
     32               
     33                // Create a worker screen to create the fasta file in the background
     34                def processId = workerService.initProcess( httpSession, "Creating your download", 1, totalProgress )
     35
     36                // Create URLs to return to after downloading
     37                def returnUrl = createLink( controller: "run" ).toString()
     38                def finishUrl = createLink( controller: "export", action: 'downloadZip', params: [ processId: '%s' ] ).toString();
     39                finishUrl = sprintf( finishUrl, processId );
     40
     41                httpSession.process[ processId ][ "returnUrl" ] = returnUrl
     42               
     43                // Retrieve worker URL; returnUrl is the same as the errorUrl
     44                def url = workerService.startProcess( httpSession, processId, finishUrl, returnUrl, returnUrl )
     45               
     46                // Make sure the background process can send the progress to the HTTP session.
     47                def onProgress = { progress ->
     48                        // Update progress
     49                        httpSession.progress[ processId ].stepProgress += progress;
     50                }
     51
     52                // Perform the real work in a background thread
     53                def studyIds = studies.collect { it.id }
     54                studies = null;
     55               
     56                runAsync {
     57                        def filename = fileService.getUniqueFilename( "zipdownload" );
     58                        def file = fileService.get( filename );
     59                       
     60                        studies = studyIds ? Study.findAll( "FROM Study WHERE id in (:ids)", [ "ids": studyIds ] ) : []
     61                       
     62                        try {
     63                                createZip( file, studies, onProgress );
     64                        } catch( Exception e ) {
     65                                log.error( "Exception occurred during export of data: " );
     66                                e.printStackTrace();
     67
     68                                httpSession.progress[ processId ].error = true;
     69                                httpSession.progress[ processId ].errorMessage += e.getMessage();
     70                        }
     71                       
     72                        // Store the filename for later download
     73                        httpSession.process[ processId ].filename = filename;
     74                       
     75                        // Tell the frontend we are finished
     76                        httpSession.progress[ processId ].finished = true;
     77                }
     78 
     79                // Show a waiting screen
     80                redirect( url: url );
     81        }
     82       
     83        protected void createZip( File file, def studies, Closure onProgress ) {
     84                // Find all runs
    2185                def runs = Run.list();
    2286               
    23                 // Send the right headers
    24                 response.setHeader "Content-disposition", "attachment; filename=sequencing_studies.zip"
    25                
    2687                // First create zip file
    27                 ZipOutputStream zipFile = new ZipOutputStream( new BufferedOutputStream( response.getOutputStream() ) );
     88                ZipOutputStream zipFile = new ZipOutputStream( new BufferedOutputStream( new FileOutputStream( file ) ) );
    2889                BufferedWriter zipWriter = new BufferedWriter( new OutputStreamWriter( zipFile, 'utf-8' ) );
    2990               
    30                 // Add the study XML file
    31                 zipFile.putNextEntry( new ZipEntry( "studies.xml" ) );
    32                 createStudiesXML( zipWriter, studies, runs )
    33                 zipWriter.flush();
    34                 zipFile.closeEntry();
    35                
    36                 // Add all other files. These files are stored in the directory given in the configuration
    37                 def permanentDir = fileService.absolutePath( ConfigurationHolder.config.massSequencing.fileDir );
    38                 def buffer = new byte[ 1024 ];
    39                
    40                 // First add all sequence and quality files
    41                 studies.each { study -> study.assays?.each { assay -> assay.assaySamples?.each { assaySample -> assaySample.sequenceData?.each { data ->
    42                         if( data ) {
    43                                 // Loop through all sequence- and quality files.
    44                                 def files = [ data.sequenceFile, data.qualityFile ];
    45                                 files.each { filename ->
    46                                         if( filename ) {
    47                                                 // Create a zip entry for each file
    48                                                 // All filenames are unique, so the filenames are taken just the way they are
    49                                                 zipFile.putNextEntry( new ZipEntry( filename ) );
    50                                                
    51                                                 zipFile << new FileInputStream( fileService.get( filename, permanentDir ) )
    52                                                
    53                                                 // Close the zip entry
    54                                                 zipWriter.flush();
    55                                                 zipFile.closeEntry();
    56                                         }
     91                try {
     92                        // Add the study XML file
     93                        zipFile.putNextEntry( new ZipEntry( "studies.xml" ) );
     94                        createStudiesXML( zipWriter, studies, runs, onProgress )
     95                        zipWriter.flush();
     96                        zipFile.closeEntry();
     97                       
     98                        // Add all other files. These files are stored in the directory given in the configuration
     99                        def permanentDir = fileService.absolutePath( ConfigurationHolder.config.massSequencing.fileDir );
     100                        def buffer = new byte[ 1024 ];
     101               
     102                        // First add all sequence and quality files
     103                        studies.each { study -> study.assays?.each { assay -> assay.assaySamples?.each { assaySample -> assaySample.sequenceData?.each { data ->
     104                                if( data ) {
     105                                        // Loop through all sequence- and quality files.
     106                                        def files = [ data.sequenceFile, data.qualityFile ];
     107                                        files.each { filename ->
     108                                                if( filename ) {
     109                                                        // Create a zip entry for each file
     110                                                        // All filenames are unique, so the filenames are taken just the way they are
     111                                                        zipFile.putNextEntry( new ZipEntry( filename ) );
     112                                                       
     113                                                        zipFile << new FileInputStream( fileService.get( filename, permanentDir ) )
     114                                                       
     115                                                        // Close the zip entry
     116                                                        zipWriter.flush();
     117                                                        zipFile.closeEntry();
     118                                                }
     119                                        }
     120                                }
     121                               
     122                        }
     123                       
     124                        // Update progress
     125                        onProgress( assaySample.numSequences() );
     126                        } } }
     127                       
     128                        // Afterwards, add all parameterFiles for the runs
     129                        runs.each { run ->
     130                                if( run.parameterFile ) {
     131                                        // Create a zip entry for each file
     132                                        // All filenames are unique, so the filenames are taken just the way they are
     133                                        zipFile.putNextEntry( new ZipEntry( run.parameterFile ) );
     134                                       
     135                                        zipFile << new FileInputStream( fileService.get( run.parameterFile, permanentDir ) )
     136                                       
     137                                        // Close the zip entry
     138                                        zipWriter.flush();
     139                                        zipFile.closeEntry();
    57140                                }
    58141                        }
    59                 } } } }
    60                
    61                 // Afterwards, add all parameterFiles for the runs
    62                 runs.each { run ->
    63                         if( run.parameterFile ) {
    64                                 // Create a zip entry for each file
    65                                 // All filenames are unique, so the filenames are taken just the way they are
    66                                 zipFile.putNextEntry( new ZipEntry( run.parameterFile ) );
    67                                
    68                                 zipFile << new FileInputStream( fileService.get( run.parameterFile, permanentDir ) )
    69                                
    70                                 // Close the zip entry
    71                                 zipWriter.flush();
    72                                 zipFile.closeEntry();
    73                         }
    74                 }
    75                
    76                 zipFile.close();
    77                
    78                 response.outputStream.flush();
    79         }
     142                } catch( Exception e ) {
     143                        log.warn "An error occurred during download of export zip file. Error message is: " + e.getMessage();
     144                        e.printStackTrace()
     145                       
     146                        // An exception might occur if the user cancels the download. Continue (with finally) anyway
     147                        try {
     148                                if( zipFile )
     149                                        zipFile.closeEntry();
     150                        } catch( Exception e2 ) {}
     151                } finally {
     152                        try {
     153                                if( zipFile )
     154                                        zipFile.close();
     155                        } catch( Exception e2 ) {}
     156                }
     157
     158        }
     159   
     160   def downloadZip = {
     161           def processId = params.processId;
     162           
     163           // Retrieve the file
     164           def file = fileService.get( session.process[ processId ].filename );
     165
     166           try {
     167                   // Send the file to the user
     168                   response.setHeader "Content-disposition", "attachment; filename=sequencing_studies.zip"
     169                   response.setHeader "Content-Length", file.size().toString();
     170                   
     171                   response.outputStream << file.newInputStream();
     172                   response.outputStream.flush();
     173           } catch( Exception e ) {
     174                   log.error( "Exception occurred during download of exported data. " );
     175                   e.printStackTrace();
     176           } finally {
     177                   // Delete the file since it has to be downloaded only once
     178                   fileService.delete( session.process[ processId ].filename );
     179           }
     180   }
     181       
     182       
     183       
     184       
    80185
    81186        /**
     
    89194        }
    90195
    91         protected void createStudiesXML( Writer w, def studyObjects = null, def runObjects = null ) {
     196        protected void createStudiesXML( Writer w, def studyObjects = null, def runObjects = null, Closure onProgress = null ) {
    92197                if( !studyObjects )
    93198                        studyObjects = Study.list().findAll { it.canRead( session.user ) };
     
    160265                                                                                        }
    161266                                                                                }
     267                                                                               
     268                                                                                // Update progress of exporting
     269                                                                                if( onProgress )
     270                                                                                        onProgress( 1 );
    162271                                                                        }
    163272                                                                }
  • trunk/grails-app/controllers/nl/tno/massSequencing/files/ImportController.groovy

    r72 r74  
    4848
    4949                // Create a unique process identifier
    50                 String processId = workerService.initProcess( session, "Parsing files", 2, filesize );
     50                String processId = workerService.initProcess( session, "Parsing files", 1, filesize );
    5151                                       
    5252                session.process[ processId ].filenames = names;
     
    296296                processId = workerService.initProcess( session, "Store sequence data and classification", 2, filesize );
    297297               
    298                 session.progress[ processId ].stepNum = 2;
    299298                session.process[ processId ] = processInfo;
    300299               
     
    335334                        // Update classification (summary) for updated samples
    336335                        def samplesClassified = [] + fastaReturn.samplesClassified + classificationReturn.samplesClassified;
    337                         classificationService.updateClassificationForAssaySamples( samplesClassified.findAll { it }.unique() )
     336                        def uniqueSamples = samplesClassified.findAll { it }.unique();
     337
     338                        // Now all classification files have been parsed, start a new step. This might take a while, so
     339                        // the progress should be shown.
     340                        workerService.nextStep( httpSession, processId, "Updating classification statistics in database", uniqueSamples.size() );
     341                        classificationService.updateClassificationForAssaySamples( uniqueSamples, onProgress )
    338342                       
    339343                        def returnStructure = [
  • trunk/grails-app/services/nl/tno/massSequencing/ClassificationService.groovy

    r63 r74  
    226226                if( !assaySample )
    227227                        return;
    228                        
    229                 updateClassificationForAssaySamples( [ assaySample ] );
    230         }
    231 
    232         /**
    233          * Updates the Classification table with data computed from the raw sequences for the given assay sample
    234          * @param assaySample
    235          */
    236         public void updateClassificationForAssaySamples( List assaySamples ) {
    237                 if( !assaySamples )
    238                         return;
    239 
     228               
    240229                // Clear hibernate session for otherwise the session will be out of sync with the
    241230                // database
     
    245234
    246235                // Remove all Classifications for the given assaySample
    247                 Classification.executeUpdate( "DELETE FROM Classification WHERE assaySample IN (:assaySamples)", [ 'assaySamples': assaySamples ] );
     236                Classification.executeUpdate( "DELETE FROM Classification WHERE assaySample = :assaySample", [ 'assaySample': assaySample ] );
    248237
    249238                // Update classification table with data from sequences
     
    286275                        LEFT JOIN       sequence_data sd ON s.sequence_data_id = sd.id
    287276                        LEFT JOIN       assay_sample sa ON sd.sample_id = sa.id
    288                         WHERE           sa.id IN (""" + assaySamples.id.join( ", " ) + """ )
     277                        WHERE           sa.id = """ + assaySample.id + """
    289278                        GROUP BY        sa.id, s.classification_id, t.lft, t.rgt
    290279           """ ) );
     
    294283                INSERT INTO     classification (assay_sample_id, taxon_id, unclassified, total)
    295284                SELECT * FROM (
    296                         SELECT          a.id as assay_sample_id, t.id as taxon_id, 0 as unclassified, 
     285                        SELECT          a.id as assay_sample_id, t.id as taxon_id, 0 as unclassified,
    297286                                                (
    298287                                                        SELECT          sum( c.unclassified )
     
    304293                                                ) AS total
    305294                        FROM            taxonomy t, assay_sample a
    306                         WHERE           a.id IN (""" + assaySamples.id.join( ", " ) + """ )
     295                        WHERE           a.id = """ + assaySample.id + """
    307296                        AND             t.id NOT IN (
    308297                                                        SELECT          t2.id
     
    313302                ) missingClassifications WHERE missingClassifications.total IS NOT NULL
    314303          """ ) );
     304       
     305        }
     306
     307        /**
     308         * Updates the Classification table with data computed from the raw sequences for the given assay sample
     309         * @param assaySample
     310         */
     311        public void updateClassificationForAssaySamples( List assaySamples, Closure onProgress = null ) {
     312                if( !assaySamples )
     313                        return;
     314
     315                       
     316                assaySamples.each {
     317                        updateClassificationForAssaySample( it );
     318                       
     319                        if( onProgress )
     320                                onProgress( 1 );
     321                }
    315322        }
    316323
  • trunk/grails-app/services/nl/tno/massSequencing/integration/SynchronizationService.groovy

    r73 r74  
    121121                } else {
    122122                        studies = Study.findAllWhere( [trashcan: false, isDirty: true] );
     123                       
    123124                        log.trace "Default synchronization: " + studies.size()
    124125
  • trunk/grails-app/services/nl/tno/massSequencing/worker/WorkerService.groovy

    r70 r74  
    6565                httpSession.progress?.remove( processId );
    6666        }
     67       
     68        public void nextStep( def httpSession, String processId, String description, Number stepTotal ) {
     69                httpSession.progress[ processId ].stepNum++;
     70                httpSession.progress[ processId ].stepDescription = description;
     71               
     72                httpSession.progress[ processId ].stepProgress = 0
     73                httpSession.progress[ processId ].stepTotal = stepTotal
     74
     75        }
    6776}
  • trunk/grails-app/views/assay/show.gsp

    r72 r74  
    4545                <label># samples</label>: ${assay.assaySamples?.size()}<br />
    4646                <label># sequences</label>: <g:formatNumber number="${assay.numSequences()}" format="###,###,##0" /><br />
    47                 <label>% classified</label>: <g:formatNumber number="${numClassified / assay.numSequences()}" format="0.0%" /><br />
     47                <label>% classified</label>: <g:if test="${assay.numSequences() > 0}"><g:formatNumber number="${numClassified / assay.numSequences()}" format="0.0%" /></g:if><g:else>-</g:else><br />
    4848        </div>
    4949        <!-- Samples -->
  • trunk/grails-app/views/run/show.gsp

    r72 r74  
    7979                <br />
    8080                <label># sequences</label>: <g:formatNumber number="${run.numSequences()}" format="###,###,##0" /><br />
    81                 <label>% classified</label>: <g:formatNumber number="${numClassified / run.numSequences()}" format="0.0%" /><br />
     81                <label>% classified</label>: <g:if test="${run.numSequences() > 0}"><g:formatNumber number="${numClassified / run.numSequences()}" format="0.0%" /></g:if><g:else>-</g:else><br />
    8282        </div>
    8383        <p class="options">
Note: See TracChangeset for help on using the changeset viewer.