Changeset 12


Ignore:
Timestamp:
Feb 9, 2011, 9:04:29 AM (8 years ago)
Author:
robert@…
Message:

Implemented basic exporting functionality

Location:
trunk
Files:
6 added
16 edited

Legend:

Unmodified
Added
Removed
  • trunk/grails-app/conf/BaseFilters.groovy

    r9 r12  
    1616import grails.converters.*
    1717import javax.servlet.http.HttpServletResponse
     18
     19import org.apache.catalina.connector.Response;
    1820import org.codehaus.groovy.grails.commons.ConfigurationHolder
    1921
    2022import nl.tno.metagenomics.auth.User;
     23import nl.tno.metagenomics.integration.*;
    2124 
    2225class BaseFilters {
     
    121124                                                        session.user = new User(identifier: user.id, username: user.username).save(flush: true)
    122125                                                }
     126                                               
     127                                                return true;
    123128                                        } catch(Exception e) {
    124129                                                log.error("Unable to fetch user from GSCF", e)
     
    134139                        }
    135140                }
     141                restSynchronization(controller:'rest', action:'*') {
     142                        before = {
     143                                // Synchronize studies normally, but never issue a redirect
     144                                synchronizationService.sessionToken = session.sessionToken
     145                                synchronizationService.user = session.user
    136146               
    137                 fullSynchronization(controller:'*', action:'*') {
    138                         before = {
    139                                 // Never perform full synchronization on rest call when the synchronize controller is used
     147                                try {
     148                                        synchronizationService.synchronizeStudies()
     149                                } catch( NotAuthenticatedException e ) {
     150                                        // Something went wrong in authentication. Redirect the user to GSCF to authenticate again
     151                                        log.info "User is not authenticated during synchronizatino. Returning 401 status"
     152                                        response.setStatus( Response.SC_UNAUTHORIZED, "User is not authorized" )
     153                                        return false;
     154                                } catch( Exception e ) {
     155                                        // Synchronization fails. Log error and continue; don't bother the user with it
     156                                        log.error e.getMessage()
     157                                }
     158                                return true;
     159                        }
     160                }
     161                synchronization(controller:'*', action:'*') {
     162                        before = {
     163                                // Never perform full synchronization on rest call or when the synchronize controller is used
    140164                                if( controllerName == "rest" || controllerName == "synchronize" || controllerName == "dbUtil" ) {
    141165                                        return true;
     
    149173                                        redirect( url: synchronizationService.urlForFullSynchronization( params ) );
    150174                                        return false
     175                                } else {
     176                                        // Synchronize studies normally
     177                                        synchronizationService.sessionToken = session.sessionToken
     178                                        synchronizationService.user = session.user
     179                       
     180                                        try {
     181                                                synchronizationService.synchronizeStudies()
     182                                        } catch( NotAuthenticatedException e ) {
     183                                                // Something went wrong in authentication. Redirect the user to GSCF to authenticate again
     184                                                log.info "User is not authenticated during synchronizatino. Redirecting to GSCF."
     185                                                redirect( url: gscfService.urlAuthRemote(params, session.sessionToken) )
     186                                        } catch( Exception e ) {
     187                                                // Synchronization fails. Log error and continue; don't bother the user with it
     188                                                log.error e.getMessage()
     189                                        }
    151190                                }
    152191                               
  • trunk/grails-app/controllers/nl/tno/metagenomics/AssayController.groovy

    r9 r12  
    194194                        assay.assaySamples.each { assaySample ->
    195195                                def assaySampleParams = sampleParams.get( assaySample.id as String );
    196                                 println assaySampleParams
     196
    197197                                if( assaySampleParams ) {
    198198                                        assaySample.tagName = assaySampleParams.tagName
  • trunk/grails-app/controllers/nl/tno/metagenomics/FastaController.groovy

    r7 r12  
    2525                if( !names ) {
    2626                        flash.message = "No files uploaded for processing"
    27                         redirect( controller: params.entityType, action: 'show', 'id': params.id)
     27                        if( params.entityType && params.id)
     28                                redirect( controller: params.entityType, action: 'show', 'id': params.id)
     29                        else
     30                                redirect( url: "" )
     31                               
    2832                        return
    2933                }
     
    115119                 * The second parameter is a callback function to update progress indicators
    116120                 */
     121                def httpSession = session;
    117122                def parsedFiles = fastaService.parseFiles( filenames, { files, bytes, totalFiles, totalBytes ->
    118                         session.processProgress.numFiles += totalFiles;
    119                         session.processProgress.numBytes += totalBytes;
    120                         session.processProgress.filesProcessed = files;
    121                         session.processProgress.bytesProcessed = bytes;
     123                        httpSession.processProgress.numFiles += totalFiles;
     124                        httpSession.processProgress.numBytes += totalBytes;
     125                        httpSession.processProgress.filesProcessed = files;
     126                        httpSession.processProgress.bytesProcessed = bytes;
    122127                } );
    123128       
  • trunk/grails-app/controllers/nl/tno/metagenomics/RunController.groovy

    r9 r12  
    4545                        return
    4646                }
    47                
     47
    4848                Assay assay = null
    4949                if( params.assayId ) {
    5050                        assay = getAssay( params.assayId )
    51        
     51
    5252                        if( !assay ) {
    5353                                render flash.error;
     
    9393                // Set properties to the run
    9494                params.parameterFile = params.editParameterFile
    95                
    96                 println "Edit run: " + params
     95
    9796                run.setPropertiesFromForm( params );
    9897
     
    102101                        flash.error = "Run could not be saved: " + run.getErrors();
    103102                }
    104                
     103
    105104                Assay assay = getAssay(params.assayId);
    106105                flash.error = "";
    107                
     106
    108107                if( assay ) {
    109108                        redirect( controller: 'assay', action: 'show', id: assay.id)
     
    247246                        return
    248247                }
    249                
     248
    250249                // Make it only possible to update samples writable by the user
    251250                def assaySamples = run.assaySamples.findAll { it.assay.study.canWrite( session.user ) }
    252                
     251
    253252                def excelData = sampleExcelService.updateTagsByExcel( matchColumns, session.possibleFields, file, assaySamples );
    254253
     
    304303         *
    305304         *************************************************************************/
     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
    306376
    307377        /**
     
    333403                }
    334404
    335                 flash.message = numAdded + " runs are added to this assay."
     405                flash.message = numAdded + " assays are added to this run."
    336406                redirect( action: 'show', id: params.id)
    337407        }
     
    347417                        return
    348418                }
    349                
     419
    350420                if( !params.assay_id ) {
    351421                        flash.message = "No assay id given"
  • trunk/grails-app/controllers/nl/tno/metagenomics/StudyController.groovy

    r4 r12  
    88       
    99        def index = {
    10                 // Synchronize all studies
    11                 synchronizationService.sessionToken = session.sessionToken
    12                 synchronizationService.user = session.user
    13 
    14                 synchronizationService.synchronizeStudies()
    15                
    1610                // Clean the upload directory
    1711                fileService.cleanDirectory();
  • trunk/grails-app/controllers/nl/tno/metagenomics/integration/RestController.groovy

    r9 r12  
    11package nl.tno.metagenomics.integration
    2 
    32
    43import grails.converters.*
    54import nl.tno.metagenomics.Study
     5import nl.tno.metagenomics.Sample
     6import nl.tno.metagenomics.Assay
     7
     8import org.apache.catalina.connector.Response;
    69import org.codehaus.groovy.grails.commons.ConfigurationHolder
    710
     
    3336class RestController {
    3437        def synchronizationService
     38       
     39        /****************************************************************/
     40        /* REST resource for handling study change in GSCF              */
     41        /****************************************************************/
     42       
     43        /**
     44         * Is called by GSCF when a study is added, changed or deleted.
     45         * Sets the 'dirty' flag of a study to true, so that it will be updated
     46         * next time the study is asked for.
     47         *
     48         * @param       studyToken
     49         */
     50        def notifyStudyChange = {
     51                def studyToken = params.studyToken
     52
     53                if( !studyToken ) {
     54                        response.sendError(400, "No studyToken given" )
     55                        return
     56                }
     57
     58                // Search for the changed study
     59                def study = Study.findByStudyToken( studyToken );
     60
     61                // If the study is not found, it is added in GSCF. Add a dummy (dirty) study, in order to
     62                // update it immediately when asked for
     63                if( !study ) {
     64                        log.info( "METAGENOMICS: GSCF notification for new study " + studyToken );
     65                        study = new Study(
     66                                        name: "",
     67                                        studyToken: studyToken,
     68                                        isDirty: true
     69                                        )
     70                } else {
     71                        log.info( "METAGENOMICS: GSCF notification for existing study " + studyToken );
     72                        study.isDirty = true;
     73                }
     74                study.save(flush:true);
     75
     76                def jsonData = [ 'studyToken': studyToken, message: "Notify succesful" ];
     77
     78                render jsonData as JSON
     79        }
     80
     81        /**
     82         * Return URL to view an assay.
     83         *
     84         * @param  assayToken
     85         * @return URL to view an assay as single hash entry with key 'url'.
     86         *
     87         */
     88        def getAssayURL = {
     89                def assayToken = params.assayToken
     90
     91                if( !assayToken ) {
     92                        render [] as JSON
     93                        return
     94                }
     95
     96                def assay = Assay.findByAssayToken( assayToken )
     97
     98                // If the assay is not found, try synchronizing
     99                synchronizationService.sessionToken = session.sessionToken
     100
     101                if( !assay ) {
     102                        synchronizationService.synchronizeStudies()
     103                        assay = Assay.findByAssayToken( assayToken );
     104
     105                        if( !assay ) {
     106                                response.sendError(404, "Not Found" )
     107                                return;
     108                        }
     109                } else {
     110                        try {
     111                                synchronizationService.synchronizeAssay(assay);
     112                        } catch( Exception e ) {
     113                                response.sendError( 500, e.getMessage())
     114                                return
     115                        }
     116
     117                        def url = [ 'url' : ConfigurationHolder.config.grails.serverURL + '/assay/show/' + assay.id.toString() ]
     118
     119                        render url as JSON
     120                }
     121        }
     122
     123        /***************************************************/
     124        /* REST resources related to the querying in GSCF  */
     125        /***************************************************/
     126
     127        /**
     128         * Retrieves a list of fields that could be queried when searching for a specific entity.
     129         *
     130         * The module is allowed to return different fields when the user searches for different entities
     131         *
     132         * Example call:                [moduleurl]/rest/getQueryableFields?entity=Study&entity=Sample
     133         * Example response:    { "Study": [ "# sequences" ], "Sample": [ "# sequences", "# bacteria" ] }
     134         *
     135         * @param       params.entity   Entity that is searched for. Might be more than one. If no entity is given,
     136         *                                                      a list of searchable fields for all entities is given
     137         * @return      JSON                    List with the names of the fields
     138         */
     139        def getQueryableFields = {
     140                // We don't really care about the entity. The only thing is that this module
     141                // is only aware of studies, assays and samples, but doesn't know anything about
     142                // subjects or events. If the user searches for those entities (maybe in the future)
     143                // this module doesn't have anything to search for.
     144
     145                def entities = params.entity ?: []
     146               
     147                if( entities instanceof String )
     148                        entities = [entities]
     149                else
     150                        entities = entities.toList()
     151
     152                if( !entities )
     153                        entities = [ "Study", "Assay", "Sample" ]
     154                       
     155
     156                def fields = [:];
     157                entities.unique().each { entity ->
     158                        switch( entity ) {
     159                                case "Study":
     160                                case "Assay":
     161                                case "Sample":
     162                                        fields[ entity ] = [ "# sequences", "Tag name", "Run name" ]
     163                                        break;
     164                                default:
     165                                        // Do nothing
     166                                        break;
     167                        }
     168                }
     169               
     170                render fields as JSON
     171        }
     172       
     173        /**
     174         * Returns data for the given field and entities.
     175         *
     176         * Example call:                [moduleurl]/rest/getQueryableFieldData?entity=Study&tokens=abc1&tokens=abc2&fields=# sequences&fields=# bacteria
     177         * Example response:    { "abc1": { "# sequences": 141, "# bacteria": 0 }, "abc2": { "#sequences": 412 } }
     178         *
     179         * @param       params.entity   Entity that is searched for
     180         * @param       params.tokens   One or more tokens of the entities that the data should be returned for
     181         * @param       params.fields   One or more field names of the data to be returned.
     182         * @return      JSON                    Map with keys being the entity tokens and the values being maps with entries [field] = [value]. Not all
     183         *                                                      fields and tokens that are asked for have to be returned by the module (e.g. when a specific entity can
     184         *                                                      not be found, or a value is not present for an entity)
     185         */
     186        def getQueryableFieldData = {
     187                def entity = params.entity;
     188                def tokens = params.tokens ?: []
     189                def fields = params.fields ?: []
     190
     191                if( tokens instanceof String )
     192                        tokens = [tokens]
     193                else
     194                        tokens = tokens.toList();
     195
     196                if( fields instanceof String )
     197                        fields = [fields]
     198                else
     199                        fields = fields.toList();
     200
     201                // Only search for unique tokens and fields
     202                tokens = tokens.unique()
     203                fields = fields.unique()
     204
     205                // Without tokens or fields we can only return an empty list
     206                def map = [:]
     207                if( tokens.size() == 0 || fields.size() == 0 ) {
     208                        log.trace "Return empty string for getQueryableFieldData: #tokens: " + tokens.size() + " #fields: " + fields.size()
     209                        render map as JSON
     210                        return;
     211                }
     212
     213                for( token in tokens ) {
     214                        def object = getQueryableObject( entity, token );
     215
     216                        if( object ) {
     217                                // Check whether the user has sufficient privileges:
     218                                def study;
     219                                switch( entity ) {
     220                                        case "Study":
     221                                                study = object;
     222                                                break;
     223                                        case "Assay":
     224                                        case "Sample":
     225                                                study = object.study
     226                                                break;
     227                                        default:
     228                                                log.error "Incorrect entity used: " + entity;
     229                                                continue;
     230                                }
     231                               
     232                                if( !study.canRead( session.user ) ) {
     233                                        log.error "Data was requested for " + entity.toLowerCase() + " " + object.name + " but the user " + session.user.username + " doesn't have the right privileges."
     234                                        continue;
     235                                } else {
     236                                        log.error "Data was requested for " + entity.toLowerCase() + " " + object.name + ": " + session.user.username + " - " + study.canRead( session.user )
     237                                }
     238                               
     239                                map[ token ] = [:]
     240                                fields.each { field ->
     241                                        def v = getQueryableFieldValue( entity, object, field );
     242                                        if( v != null )
     243                                                map[ token ][ field ] = v
     244                                }
     245                        } else {
     246                                log.trace "No " + entity + " with token " + token + " found."
     247                        }
     248                }
     249               
     250                render map as JSON
     251        }
     252
     253        /**
     254         * Searches for a specific entity
     255         *
     256         * @param entity                Entity to search in
     257         * @param token         Token of the entity to search in
     258         * @return
     259         */
     260        protected def getQueryableObject( def entity, def token ) {
     261                switch( entity ) {
     262                        case "Study":
     263                                return Study.findByStudyToken( token );
     264                        case "Assay":
     265                                return Assay.findByAssayToken( token );
     266                        case "Sample":
     267                                return Sample.findBySampleToken( token );
     268                        default:
     269                        // Other entities can't be handled
     270                                return null;
     271                }
     272        }
     273
     274        /**
     275         * Searches for the value of a specific field in a specific entity
     276         *
     277         * @param entity        Entity of the given object
     278         * @param object        Object to search in
     279         * @param field         Field value to retrieve         
     280         * @return
     281         */
     282        protected def getQueryableFieldValue( def entity, def object, def field ) {
     283                if( !entity || !object || !field )
     284                        return null;
     285                       
     286                // First determine all assaysamples involved, in order to return data later.
     287                // All data that has to be returned is found in assaysamples
     288                def assaySamples
     289               
     290                switch( entity ) {
     291                        case "Study":
     292                                assaySamples = object.assays*.assaySamples;
     293                                if( assaySamples ) {
     294                                        assaySamples = assaySamples.flatten()
     295                                }
     296                                break;
     297                        case "Assay":
     298                        case "Sample":
     299                                assaySamples = object.assaySamples;
     300                                break;
     301                        default:
     302                        // Other entities can't be handled
     303                                return null;
     304                }
     305               
     306                // If no assaySamples are involved, return null as empty value
     307                if( !assaySamples ) {
     308                        return null;
     309                }
     310               
     311                // Now determine the exact field to return
     312                switch( field ) {
     313                        // Returns the total number of sequences in this sample
     314                        case "# sequences":
     315                                return assaySamples.collect { it.numSequences() }.sum();
     316                        // Returns the unique tag names
     317                        case "Tag name":
     318                                return assaySamples.collect { it.tagName }.findAll { it != null }.unique();
     319                        case "Run name":
     320                                return assaySamples.collect { it.run?.name }.findAll { it != null }.unique();
     321                        // Other fields are not handled
     322                        default:
     323                                return null;
     324                }
     325
     326        }
     327       
     328        private def checkAssayToken( def assayToken ) {
     329                if( !assayToken || assayToken == null ) {
     330                        return false
     331                }
     332                def list = []
     333                def assay = Assay.findByAssayToken( assayToken )
     334
     335                if( !assay || assay == null ) {
     336                        return false;
     337                }
     338
     339                return assay;
     340        }
     341
     342        /****************************************************************/
     343        /* REST resources for exporting data from GSCF                          */
     344        /****************************************************************/
     345
     346        /**
     347         * Retrieves a list of actions that can be performed on data with a specific entity.
     348         *
     349         * The module is allowed to return different fields when the user searches for different entities
     350         *
     351         * Example call:                [moduleurl]/rest/getPossibleActions?entity=Assay&entity=Sample
     352         * Example response:    { "Assay": [ { name: "excel", description: "Export as excel" } ],
     353         *                                                "Sample": [ { name: "excel", description: "Export as excel" }, { name: "fasta", description: : "Export as fasta" } ] }
     354         *
     355         * @param       params.entity   Entity that is searched for. Might be more than one. If no entity is given,
     356         *                                                      a list of searchable fields for all entities is given
     357         * @return      JSON                    Hashmap with keys being the entities and the values are lists with the action this module can
     358         *                                                      perform on this entity. The actions as hashmaps themselves, with keys 'name' and 'description'
     359         */
     360        def getPossibleActions = {
     361                def entities = params.entity ?: []
     362               
     363                if( entities instanceof String )
     364                        entities = [entities]
     365                else
     366                        entities = entities.toList()
     367
     368                if( !entities )
     369                        entities = [ "Study", "Assay", "Sample" ]
     370
     371                def actions = [:];
     372                entities.unique().each { entity ->
     373                        switch( entity ) {
     374                                case "Study":
     375                                        actions[ entity ] = [ [ name: "excel", description: "Export as excel" ] ]
     376                                        break;
     377                                case "Assay":
     378                                        actions[ entity ] = [ [ name: "fasta", description: "Export as fasta" ] ]
     379                                        break;
     380                                case "Sample":
     381                                        actions[ entity ] = [ [ name: "fasta", description: "Export as fasta" ] ]
     382                                        break;
     383                                default:
     384                                        // Do nothing
     385                                        break;
     386                        }
     387                }
     388               
     389                render actions as JSON
     390        }
    35391
    36392        /****************************************************************/
     
    68424        }
    69425
    70 
    71426        /**
    72427         * Return measurement metadata for measurement
     
    109464        }
    110465
    111 
    112         /*
     466        /**
    113467         * Return list of measurement data.
    114468         *
     
    265619        }
    266620
    267         /**
    268          * Is called by GSCF when a study is added, changed or deleted.
    269          * Sets the 'dirty' flag of a study to true, so that it will be updated
    270          * next time the study is asked for.
    271          *
    272          * @param       studyToken
    273          */
    274         def notifyStudyChange = {
    275                 def studyToken = params.studyToken
    276 
    277                 if( !studyToken ) {
    278                         response.sendError(400, "No studyToken given" )
    279                         return
    280                 }
    281 
    282                 // Search for the changed study
    283                 def study = Study.findByStudyToken( studyToken );
    284 
    285                 // If the study is not found, it is added in GSCF. Add a dummy (dirty) study, in order to
    286                 // update it immediately when asked for
    287                 if( !study ) {
    288                         log.info( "METAGENOMICS: GSCF notification for new study " + studyToken );
    289                         study = new Study(
    290                                         name: "",
    291                                         studyToken: studyToken,
    292                                         isDirty: true
    293                                         )
    294                 } else {
    295                         log.info( "METAGENOMICS: GSCF notification for existing study " + studyToken );
    296                         study.isDirty = true;
    297                 }
    298                 study.save(flush:true);
    299 
    300                 def jsonData = [ 'studyToken': studyToken, message: "Notify succesful" ];
    301 
    302                 render jsonData as JSON
    303         }
    304 
    305         /**
    306          * Return URL to view an assay.
    307          *
    308          * @param  assayToken
    309          * @return URL to view an assay as single hash entry with key 'url'.
    310          *
    311          */
    312         def getAssayURL = {
    313                 def assayToken = params.assayToken
    314 
    315                 if( !assayToken ) {
    316                         render [] as JSON
    317                         return
    318                 }
    319 
    320                 def assay = Assay.findByAssayToken( assayToken )
    321 
    322                 // If the assay is not found, try synchronizing
    323                 synchronizationService.sessionToken = session.sessionToken
    324 
    325                 if( !assay ) {
    326                         synchronizationService.synchronizeStudies()
    327                         assay = Assay.findByAssayToken( assayToken );
    328 
    329                         if( !assay ) {
    330                                 response.sendError(404, "Not Found" )
    331                                 return;
    332                         }
    333                 } else {
    334                         try {
    335                                 synchronizationService.synchronizeAssay(assay);
    336                         } catch( Exception e ) {
    337                                 response.sendError( 500, e.getMessage())
    338                                 return
    339                         }
    340 
    341                         def url = [ 'url' : ConfigurationHolder.config.grails.serverURL + '/assay/show/' + assay.id.toString() ]
    342 
    343                         render url as JSON
    344                 }
    345         }
    346 
    347         /***************************************************/
    348         /* REST resources related to the querying in GSCF  */
    349         /***************************************************/
    350 
    351         /**
    352          * Retrieves a list of fields that could be queried when searching for a specific entity.
    353          *
    354          * The module is allowed to return different fields when the user searches for different entities
    355          *
    356          * Example call:                [moduleurl]/rest/getQueryableFields?entity=Study&entity=Sample
    357          * Example response:    { "Study": [ "# sequences" ], "Sample": [ "# sequences", "# bacteria" ] }
    358          *
    359          * @param       params.entity   Entity that is searched for. Might be more than one. If no entity is given,
    360          *                                                      a list of searchable fields for all entities is given
    361          * @return      JSON                    List with the names of the fields
    362          */
    363         def getQueryableFields = {
    364                 // We don't really care about the entity. The only thing is that this module
    365                 // is only aware of studies, assays and samples, but doesn't know anything about
    366                 // subjects or events. If the user searches for those entities (maybe in the future)
    367                 // this module doesn't have anything to search for.
    368 
    369                 def entities = params.entity ?: []
    370                
    371                 if( entities instanceof String )
    372                         entities = [entities]
    373                 else
    374                         entities = entities.toList()
    375 
    376                 if( !entities )
    377                         entities = [ "Study", "Assay", "Sample" ]
    378                        
    379 
    380                 def fields = [:];
    381                 entities.unique().each { entity ->
    382                         switch( entity ) {
    383                                 case "Study":
    384                                 case "Assay":
    385                                 case "Sample":
    386                                         fields[ entity ] = [ "# sequences" ]
    387                                         break;
    388                                 default:
    389                                         // Do nothing
    390                                         break;
    391                         }
    392                 }
    393                
    394                 render fields as JSON
    395         }
    396 
    397         /**
    398          * Returns data for the given field and entities.
    399          *
    400          * Example call:                [moduleurl]/rest/getQueryableFieldData?entity=Study&tokens=abc1&tokens=abc2&fields=# sequences&fields=# bacteria
    401          * Example response:    { "abc1": { "# sequences": 141, "# bacteria": 0 }, "abc2": { "#sequences": 412 } }
    402          *
    403          * @param       params.entity   Entity that is searched for
    404          * @param       params.tokens   One or more tokens of the entities that the data should be returned for
    405          * @param       params.fields   One or more field names of the data to be returned.
    406          * @return      JSON                    Map with keys being the entity tokens and the values being maps with entries [field] = [value]. Not all
    407          *                                                      fields and tokens that are asked for have to be returned by the module (e.g. when a specific entity can
    408          *                                                      not be found, or a value is not present for an entity)
    409          */
    410         def getQueryableFieldData = {
    411                 def entity = params.entity;
    412                 def tokens = params.tokens ?: []
    413                 def fields = params.fields ?: []
    414 
    415                 if( tokens instanceof String )
    416                         tokens = [tokens]
    417                 else
    418                         tokens = tokens.toList();
    419 
    420                 if( fields instanceof String )
    421                         fields = [fields]
    422                 else
    423                         fields = fields.toList();
    424 
    425                 // Only search for unique tokens and fields
    426                 tokens = tokens.unique()
    427                 fields = fields.unique()
    428 
    429                 // Without tokens or fields we can only return an empty list
    430                 def map = [:]
    431                 if( tokens.size() == 0 || fields.size() == 0 ) {
    432                         log.trace "Return empty string for getQueryableFieldData: #tokens: " + tokens.size() + " #fields: " + fields.size()
    433                         render map as JSON
    434                         return;
    435                 }
    436 
    437                 tokens.each { token ->
    438                         def object = getQueryableObject( entity, token );
    439                         if( object ) {
    440                                 map[ token ] = [:]
    441                                 fields.each { field ->
    442                                         def v = getQueryableFieldValue( entity, object, field );
    443                                         if( v != null )
    444                                                 map[ token ][ field ] = v
    445                                 }
    446                         } else {
    447                                 log.trace "No " + entity + " with token " + token + " found."
    448                         }
    449                 }
    450                
    451                 render map as JSON
    452         }
    453 
    454         /**
    455          * Searches for a specific entity
    456          *
    457          * @param entity                Entity to search in
    458          * @param token         Token of the entity to search in
    459          * @return
    460          */
    461         protected def getQueryableObject( def entity, def token ) {
    462                 switch( entity ) {
    463                         case "Study":
    464                                 return Study.findByStudyToken( token );
    465                         case "Assay":
    466                                 return Assay.findByAssayToken( token );
    467                         case "Sample":
    468                                 return Sample.findBySampleToken( token );
    469                         default:
    470                         // Other entities can't be handled
    471                                 return null;
    472                 }
    473         }
    474 
    475         /**
    476          * Searches for the value of a specific field in a specific entity
    477          *
    478          * @param entity        Entity of the given object
    479          * @param object        Object to search in
    480          * @param field         Field value to retrieve         
    481          * @return
    482          */
    483         protected def getQueryableFieldValue( def entity, def object, def field ) {
    484                 if( !entity || !object || !field )
    485                         return null;
    486                        
    487                 switch( entity ) {
    488                         case "Study":
    489                                 switch( field ) {
    490                                         // Returns the total number of sequences in this study
    491                                         case "# sequences":
    492                                                 def assaySamples = object.assays*.assaySamples;
    493                                                 if( assaySamples ) {
    494                                                         assaySamples = assaySamples.flatten()
    495                                                         return assaySamples.collect { it.numSequences() }.sum();
    496                                                 } else {
    497                                                         return null;
    498                                                 }
    499                                         // Other fields are not handled
    500                                         default:
    501                                                 return null;
    502                                 }
    503                         case "Assay":
    504                                 switch( field ) {
    505                                         // Returns the total number of sequences in this study
    506                                         case "# sequences":
    507                                                 def assaySamples = assay.assaySamples;
    508                                                 if( assaySamples ) {
    509                                                         return assaySamples.collect { it.numSequences() }.sum();
    510                                                 } else {
    511                                                         return null;
    512                                                 }
    513                                         // Other fields are not handled
    514                                         default:
    515                                                 return null;
    516                                 }
    517                         case "Sample":
    518                                 switch( field ) {
    519                                         // Returns the total number of sequences in this study
    520                                         case "# sequences":
    521                                                 def assaySamples = sample.assaySamples;
    522                                                 if( assaySamples ) {
    523                                                         return assaySamples.collect { it.numSequences() }.sum();
    524                                                 } else {
    525                                                         return null;
    526                                                 }
    527                                         // Other fields are not handled
    528                                         default:
    529                                                 return null;
    530                                 }
    531                         default:
    532                         // Other entities can't be handled
    533                                 return null;
    534                 }
    535         }
    536 
    537         /**
    538          * @param   (no parameters)
    539          * @return  metadata fields that can be available for any assay stored in the module
    540          */
    541         def getAssayMetadataFields = {
    542                 def exampleType = Assay.find()
    543                 def list = []
    544 
    545                 exampleType.giveFields.each{
    546                         list.push( it.name )
    547                 }
    548 
    549                 render list as JSON
    550         }
    551 
    552         /**
    553          *
    554          * @param   assayToken
    555          * @param   measurementTokens
    556          * @return  metadata values for the specified assay
    557          */
    558         def getAssayMetadataValues = {
    559                 def error = CommunicationManager.hasValidParameters( params, 'assayToken' )
    560                 if(error) {
    561                         render error as JSON
    562                         return
    563                 }
    564                 def assay = SimpleAssay.findByExternalAssayID( params.assayToken )
    565                 def types = []
    566                 def hash = []
    567                 token.getMeasurements.each {
    568                         types.push( it.type     )
    569                 }
    570                 types.unique.each { type ->
    571                         type.fields.each { field ->
    572                                 hash[field.name] = field.value
    573                         }
    574                 }
    575                 render hash as JSON
    576         }
    577 
    578 
    579 
    580         /**
    581          * @param   assayToken
    582          * @return  list of metadata fields that are available for the samples in the specified assay
    583          */
    584         def getSampleMetadataFields = {
    585                 def error = CommunicationManager.hasValidParameters( params, 'assayToken' )
    586                 if(error) {
    587                         render error as JSON
    588                         return
    589                 }
    590                 def assay = SimpleAssay.findByExternalAssayID( params.assayToken )
    591                 def types = []
    592                 assay.getTypes.each{
    593                         types.push(it.name)
    594                 }
    595                 render types as JSON
    596         }
    597 
    598 
    599 
    600         /**
    601          * @param   assayToken
    602          * @param   list of sampleTokens
    603          * @return  List of sample metadata values for the specified samples in the specified assay.
    604          *          Each element of the list is a hash containing values for one sample.
    605          *          The hash contains the key "sampleToken" to indentify the sample. The other keys
    606          *          of the hash contain information on the actual metadata values for the sample.
    607          *          The keys are metadata fields, the values are the values of the corresponding
    608          *          SimpleAssayMeasurements.
    609          */
    610         def getSampleMetadata = {
    611                 def error = CommunicationManager.hasValidParameters( params, 'assayToken', 'sampleToken' )
    612                 if(error) {
    613                         render error as JSON
    614                         return
    615                 }
    616                 def assay = SimpleAssay.findByExternalAssayID( params.assayToken )
    617                 def samples = []
    618                 def list = []
    619                 params.sampleToken.each{
    620                         if( SimpleAssaySample.findByName(it).assay == assay ) {
    621                                 samples.push(it)
    622                         }
    623                 }
    624                 samples.each{ sample ->
    625                         def hash = ['sampleToken':sample.name]
    626                         SimpleAssayMeasurement.findAllBySample(sample).each{ measurement ->
    627                                 hash[measurement.type.name]=measumrent.value
    628                         }
    629                         list.push hash
    630                 }
    631                 render list as JSON
    632         }
    633 
    634         private def checkAssayToken( def assayToken ) {
    635                 if( !assayToken || assayToken == null ) {
    636                         return false
    637                 }
    638                 def list = []
    639                 def assay = Assay.findByAssayToken( assayToken )
    640 
    641                 if( !assay || assay == null ) {
    642                         return false;
    643                 }
    644 
    645                 return assay;
    646         }
    647 
     621       
    648622        /* helper function for getSamples
    649623         *
  • trunk/grails-app/controllers/nl/tno/metagenomics/integration/TrashController.groovy

    r7 r12  
    5353                        redirect( action: "index" );
    5454                }
    55                 println "Restore To: " + restoreSample
    56                 println "Original Sample: " + originalSample
     55
    5756                trashService.restoreSample( restoreSample, originalSample );
    5857                restoreSample.sample?.delete();
  • trunk/grails-app/domain/nl/tno/metagenomics/AssaySample.groovy

    r9 r12  
    1111        private long _numSequences = -1;
    1212        private float _averageQuality = -1.0;
     13        private long _numQualScores = -1;
    1314
    1415        Integer numUniqueSequences      // Number of unique sequences / OTUs. Is only available after preprocessing
     
    137138                return numSequences;
    138139        }
    139 
     140       
     141        /**
     142         * Returns the number of quality scores in the files on the system, belonging to this
     143         * assay-sample combination.
     144         *
     145         * @return
     146         */
     147        public long numQualScores() {
     148                if( _numQualScores > -1 )
     149                        return _numQualScores;
     150
     151                if( !sequenceData )
     152                        return 0
     153
     154                long numQualScores = 0;
     155                sequenceData.each { numQualScores += it.numQualScores() }
     156
     157                // Save as cache
     158                _numQualScores = numQualScores;
     159
     160                return numQualScores;
     161        }
    140162        /**
    141163         * Returns the average quality of the sequences in the files on the system,
     
    170192        public void resetStats() {
    171193                _numSequences = -1;
     194                _numQualScores = -1;
    172195                _averageQuality = -1;
    173196        }
  • trunk/grails-app/domain/nl/tno/metagenomics/SequenceData.groovy

    r7 r12  
    3333        }
    3434       
     35       
     36        /**
     37         * Returns the number of quality scores in this data object. This equals
     38         * the number of sequences iff a quality score is attached to this object
     39         *
     40         * @return      Number of quality scores in this object
     41         */
     42        public int numQualScores() {
     43                if( qualityFile != null )
     44                        return numSequences
     45                else
     46                        return 0;
     47        }
     48       
    3549        def beforeDelete = {
    3650                def permanentDir = fileService.absolutePath( ConfigurationHolder.config.metagenomics.fileDir.toString() )
  • trunk/grails-app/services/nl/tno/metagenomics/FastaService.groovy

    r5 r12  
    11package nl.tno.metagenomics
    22
     3import java.io.BufferedWriter;
    34import java.io.File;
    45import java.util.ArrayList;
     6import java.util.zip.ZipEntry
     7import java.util.zip.ZipOutputStream
    58import org.codehaus.groovy.grails.commons.ConfigurationHolder
    69
     
    5356                for( int i = 0; i < filenames.size(); i++ ) {
    5457                        def filename = filenames[ i ];
    55                        
     58
    5659                        if( fileService.isZipFile( filename ) ) {
    5760                                // ZIP files are extracted and appended to the filenames list.
    5861                                def newfiles = fileService.extractZipFile( filename, { files, bytes, totalFiles, totalBytes ->
    59                                                         filesProcessed += files;
    60                                                         bytesProcessed += bytes;
    61        
    62                                                         onProgress( filesProcessed, bytesProcessed, totalFiles, totalBytes );
     62                                        filesProcessed += files;
     63                                        bytesProcessed += bytes;
     64
     65                                        onProgress( filesProcessed, bytesProcessed, totalFiles, totalBytes );
    6366                                } );
    6467                                if( newfiles ) {
     
    7073                                def file = fileService.get( filename );
    7174                                String filetype = fileService.determineFileType( file );
    72        
     75
    7376                                if( !fileTypeValid( filetype ) ) {
    7477                                        // If files are not valid for parsing, delete them and return a message to the user
     
    8083                                                        filesProcessed += files;
    8184                                                        bytesProcessed += bytes;
    82        
     85
    8386                                                        onProgress( filesProcessed, bytesProcessed, 0, 0 );
    8487                                                } );
    85        
     88
    8689                                                contents.filename = file.getName();
    8790                                                contents.originalfilename = fileService.originalFilename( contents.filename )
    88        
     91
    8992                                                if( contents.success ) {
    9093                                                        success << contents;
     
    253256                        }
    254257
    255 
    256 
    257258                }
    258259
     
    396397        }
    397398
    398 
     399        /**
     400         * Exports the fasta data of a list of assaysamples
     401         * @param assaySamples  Assaysamples to export
     402         * @param outStream             Outputstream to send the data to
     403         * @return
     404         */
     405        public def export( List assaySamples, OutputStream outStream, String name ) {
     406                if( !assaySamples || assaySamples.size() == 0 )
     407                        return false;
     408
     409                // Determine the directory the uploaded files are stored in
     410                File permanentDirectory = fileService.absolutePath( ConfigurationHolder.config.metagenomics.fileDir );
     411
     412                // First check whether qual files should be exported or not
     413                // It is only exported if qual scores are available for all sequences
     414                def exportQual = ( assaySamples*.numSequences().sum() == assaySamples*.numQualScores().sum() );
     415
     416                // First create tags for every sample
     417                def tags = [];
     418
     419                // Determine new tag length. Since we can use 4 characters per
     420                // tag position, we only have to use 4log( #samples)
     421                int tagLength = Math.ceil( Math.log( assaySamples.size() ) / Math.log( 4 ) )
     422                int tagNumber = 0;
     423
     424                assaySamples.each { assaySample ->
     425                        if( assaySample.numSequences() > 0 ) {
     426                                // Create a new tag for this assaysample
     427                                def tag = createTag( tagLength, tagNumber++);
     428
     429                                // Save the tag for exporting
     430                                tags << [assaySampleId: assaySample.id, sampleName: assaySample.sample.name, assayName: assaySample.assay.name, tag: tag]
     431                        }
     432                }
     433
     434                // Now create zip file for fasta and qual files
     435                ZipOutputStream zipFile = new ZipOutputStream( new BufferedOutputStream( outStream ) );
     436                BufferedWriter zipWriter = new BufferedWriter( new OutputStreamWriter( zipFile ) );
     437               
     438                // We have to loop twice through the sequenceData, since we can't write part of the sequence
     439                // file and part of the qual files mixed. We have to write the full sequence file first.
     440                try {
     441                        zipFile.putNextEntry( new ZipEntry( name + ".fasta" ) );
     442                       
     443                        assaySamples.each { assaySample ->
     444                                if( assaySample.numSequences() > 0 ) {
     445                                        def currentTag = tags.find { it.assaySampleId == assaySample.id };
     446
     447                                        assaySample.sequenceData.each { sequenceData ->
     448                                                copyFastaFileForExport( fileService.get( sequenceData.sequenceFile, permanentDirectory ), currentTag.tag, zipWriter)
     449                                        }
     450                                }
     451                        }
     452                        zipWriter.flush();
     453                        zipFile.closeEntry();
     454
     455                        if( exportQual ) {
     456                                zipFile.putNextEntry( new ZipEntry( name + ".qual" ) );
     457
     458                                assaySamples.each { assaySample ->
     459                                        if( assaySample.numSequences() > 0 ) {
     460                                                def currentTag = tags.find { it.assaySampleId == assaySample.id };
     461
     462                                                assaySample.sequenceData.each { sequenceData ->
     463                                                        copyQualFileForExport( fileService.get( sequenceData.qualityFile, permanentDirectory ), currentTag.tag, zipWriter)
     464                                                }
     465                                        }
     466                                }
     467                               
     468                                zipWriter.flush();
     469                                zipFile.closeEntry();
     470                        }
     471
     472                } catch( Exception e ) {
     473                        log.error "Error while writing to fastafile or qualfile: " + e.getMessage();
     474                } finally {
     475                        // Always close zip entry
     476                        try {
     477                                zipFile.closeEntry();
     478                        } catch( Exception e ) {
     479                                log.error "Error while closing zip entry for fasta and qual: " + e.getMessage();
     480                        }
     481                }
     482               
     483                zipFile.close();
     484
     485                // Export a tab delimited file with tags
     486                //exportSampleTagFile( tags );
     487        }
     488
     489        /**
     490         * Creates a unique tag for the given number
     491         * @param length
     492         * @param tagNumber
     493         * @return
     494         */
     495        public String createTag( int length, int tagNumber ) {
     496                def chars = ["C", "A", "G", "T"];
     497                def numChars = chars.size();
     498
     499                if( tagNumber > numChars ** length )
     500                        throw new Exception( "Given tag number (" + tagNumber + ") is too large for the specified length (" + length + ")")
     501
     502                String tag = "";
     503
     504                for( def i = 0; i < length; i++ ) {
     505                        int currentChar = tagNumber % numChars
     506
     507                        tag = chars[ currentChar ] + tag;
     508
     509                        tagNumber = Math.floor( tagNumber / numChars );
     510                }
     511
     512                return tag
     513        }
     514
     515        /**
     516         * Copies the contents of the given sequence file to the output file and prepends the tag to every sequences
     517         * @param inFile        Filename of the file to be read
     518         * @param tag           
     519         * @param outWriter
     520         * @return
     521         */
     522        protected boolean copyFastaFileForExport( File inFile, String tag, BufferedWriter outWriter ) {
     523                // Walk through the lines in the file, starting with '>'
     524                // (and where the following line contains a character other than '>')
     525
     526                try {
     527                        BufferedReader inReader = new BufferedReader( new FileReader( inFile ) );
     528
     529                        String line = null
     530                        String newLine = null
     531                        String sequence = "";
     532
     533                        def lengthPattern = ~/length=(\d+)/
     534                        def lengthMatches
     535                        int length = 0;
     536                        int tagLength = tag.size();
     537
     538                        while( ( line = inReader.readLine()) != null) {
     539                                if( line.size() == 0 ) {
     540                                        // Print the sequence we collected, before writing the empty line
     541                                        printSequence( outWriter, sequence, tag );
     542                                        sequence = "";
     543
     544                                        // Empty line
     545                                        outWriter.newLine();
     546                                } else if( line[ 0 ] == '>' ) {
     547                                        // Print the sequence we collected, before writing the new comments tag
     548                                        printSequence( outWriter, sequence, tag );
     549                                        sequence = "";
     550
     551                                        // Comments line: replace length=### with the
     552                                        // updated length, and put the line in the
     553                                        lengthMatches = ( line =~ lengthPattern );
     554                                        if( lengthMatches ) {
     555                                                length = Integer.valueOf( lengthMatches[0][1] ) + tagLength;
     556                                                newLine = lengthMatches.replaceAll( "length=" + length );
     557                                        }
     558
     559                                        outWriter.write(newLine);
     560                                        outWriter.newLine();
     561                                } else {
     562                                        // This is part of the sequence. We collect the whole sequence and
     563                                        // determine in the end how to write it to the file
     564                                        sequence += line;
     565                                }
     566                        }
     567
     568                        // Print the sequence we collected, before ending the file
     569                        printSequence( outWriter, sequence, tag );
     570                        sequence = "";
     571
     572                } catch( Exception e ) {
     573                        log.error( "An error occurred while copying contents from " + inFile.getName() + ": " + e.getMessage() );
     574                        return false;
     575                }
     576        }
     577
     578        /**
     579         * Prints a sequence to the output file
     580         * @param outWriter
     581         * @param sequence
     582         * @param tag
     583         */
     584        private void printSequence( BufferedWriter outWriter, String sequence, String tag, int maxWidth = 60 ) {
     585                // If no sequence is given, also don't prepend it with the tag
     586                if( sequence.size() == 0 )
     587                        return
     588
     589                // Prepend the tag to the sequence
     590                sequence = tag + sequence;
     591
     592                // Write the sequence with a width of maxWidth characters per line
     593                while( sequence ) {
     594                        if( sequence.size() > maxWidth ) {
     595                                outWriter.write( sequence[0..maxWidth-1] );
     596                                sequence = sequence[maxWidth..-1]
     597                        } else {
     598                                outWriter.write( sequence );
     599                                sequence = null;
     600                        }
     601                        outWriter.newLine();
     602                }
     603        }
     604
     605        /**
     606         * Copies the contents of the given qual file to the output file and prepends the tag quality score to every sequence.
     607         * For every tag character '40' is prepended to the qual scores
     608         *
     609         * @param inFile        Filename of the file to be read
     610         * @param tag
     611         * @param outWriter
     612         * @return
     613         */
     614        protected boolean copyQualFileForExport( File inFile, String tag, BufferedWriter outWriter ) {
     615                // Walk through the lines in the file, starting with '>'
     616                // (and where the following line contains a character other than '>')
     617                try {
     618                        BufferedReader inReader = new BufferedReader( new FileReader( inFile ) );
     619
     620                        String line = null
     621                        String newLine = null
     622                        List<Integer> qualScores = []
     623
     624                        def lengthPattern = ~/length=(\d+)/
     625                        def lengthMatches
     626                        int length = 0;
     627                        int tagLength = tag.size();
     628
     629                        while( ( line = inReader.readLine()) != null) {
     630                                if( line.size() == 0 ) {
     631                                        // Print the quality scores we collected, before writing the empty line
     632                                        printQualScore( outWriter, qualScores, tagLength );
     633                                        qualScores = [];
     634
     635                                        // Empty line
     636                                        outWriter.newLine();
     637                                } else if( line[ 0 ] == '>' ) {
     638                                        // Print the quality scores we collected, before writing the empty line
     639                                        printQualScore( outWriter, qualScores, tagLength );
     640                                        qualScores = [];
     641
     642                                        // Comments line: replace length=### with the
     643                                        // updated length, and put the line in the
     644                                        lengthMatches = ( line =~ lengthPattern );
     645                                        if( lengthMatches ) {
     646                                                length = Integer.valueOf( lengthMatches[0][1] ) + tagLength;
     647                                                newLine = lengthMatches.replaceAll( "length=" + length );
     648                                        }
     649
     650                                        outWriter.write(newLine);
     651                                        outWriter.newLine();
     652                                } else {
     653                                        // This is part of the quality score. We collect the whole set of quality
     654                                        // scores and determine in the end how to write it to the file
     655                                        qualScores += line.split( " " ).collect {
     656                                                if( !it.isInteger() )
     657                                                        return 0;
     658                                                else
     659                                                        return Integer.parseInt( it );
     660                                        };
     661                                }
     662                        }
     663
     664                        // Print the quality scores we collected, before ending the file
     665                        printQualScore( outWriter, qualScores, tagLength );
     666                        qualScores = [];
     667
     668                } catch( Exception e ) {
     669                        log.error( "An error occurred while copying contents from " + inFile.getName() + ": " + e.getMessage() );
     670                        return false;
     671                }
     672        }
     673
     674        /**
     675         * Prints a sequence to the output file
     676         * @param outWriter
     677         * @param sequence
     678         * @param tag
     679         */
     680        private void printQualScore( BufferedWriter outWriter, List<Integer> qualScores, int tagLength, int maxWidth = 60 ) {
     681                // If no qualScores are given, also don't prepend it with the tag
     682                if( qualScores.size() == 0 )
     683                        return
     684
     685                // Prepend the tag to the sequence
     686                qualScores = Collections.nCopies( tagLength, 40 ) + qualScores;
     687
     688                // Write the sequence with a width of maxWidth characters per line
     689                while( qualScores ) {
     690                        if( qualScores.size() > maxWidth ) {
     691                                outWriter.write( qualScores[0..maxWidth-1].join( " " ) );
     692                                qualScores = qualScores[maxWidth..-1]
     693                        } else {
     694                                outWriter.write( qualScores.join( " " ) );
     695                                qualScores = null;
     696                        }
     697                        outWriter.newLine();
     698                }
     699        }
    399700
    400701}
  • trunk/grails-app/services/nl/tno/metagenomics/files/FileService.groovy

    r5 r12  
    147147                directory = getUploadDir()
    148148
    149                 println "Moving " + file + " with originalname " + originalFilename + " to " + directory
    150 
    151149                try {
    152150                        if( file.exists() ) {
     
    238236                }
    239237        }
    240 
    241238
    242239        /**
     
    274271
    275272        /**
    276          * Returns the absolute path for the given pathname. It the pathname is relative, it is taken relative to the web-app directory
     273         * Returns the absolute path for the given pathname. If the pathname is relative, it is taken relative to the web-app directory
    277274         * @param pathname
    278275         * @return
     
    359356        }
    360357
    361 
    362 
     358        /**********************************************************************************
     359         *
     360         * Methods for handling zip files
     361         *
     362         **********************************************************************************/
     363       
    363364        /**
    364365         * Determines whether a given file is a parsable zip file
  • trunk/grails-app/services/nl/tno/metagenomics/integration/SynchronizationService.groovy

    r9 r12  
    9191         * @return      ArrayList       List of studies or null if the synchronization has failed
    9292         */
    93         public ArrayList<Study> synchronizeStudies() {
     93        public ArrayList<Study> synchronizeStudies() throws NotAuthenticatedException, Exception {
    9494                if( !performSynchronization() )
    9595                        return Study.findAllWhereTrashcan(false)
     
    467467
    468468                // Copy properties from gscf object
    469                 println "GSCF auth: " + gscfAuthorization
    470                
    471469                if( gscfAuthorization.canRead instanceof Boolean  )
    472470                        a.canRead = gscfAuthorization.canRead.booleanValue()
     
    478476                        a.isOwner = gscfAuthorization.isOwner.booleanValue()
    479477                       
    480                 println "Saved auth: " + a.canRead.toString() + " - " + a.canWrite.toString() + " - " + a.isOwner.toString()
    481 
    482478                a.save()
    483479
  • trunk/grails-app/views/assay/index.gsp

    r7 r12  
    1818                                                <th>Study</th>
    1919                                                <th># samples</th>
     20                                                <th>avg sequences / sample</th>
    2021                                        </tr>
    2122                                </thead>
     
    2829                                                                        <td><a href="${study.viewUrl()}">${study.name}</a></td>
    2930                                                                        <td>${assay.assaySamples?.size()}</td>
     31                                                                        <td>
     32                                                                                <g:if test="${assay.assaySamples?.size()}">
     33                                                                                        <g:formatNumber number="${assay.numSequences() / assay.assaySamples?.size()}" format="0"/>
     34                                                                                </g:if>
     35                                                                                <g:else>
     36                                                                                        -
     37                                                                                </g:else>
     38                                                                        </td>
    3039                                                                </tr>
    3140                                                        </g:each>
  • trunk/grails-app/views/assay/show.gsp

    r10 r12  
    6666                                        <th nowrap>name</th>
    6767                                        <th nowrap>run</th>
    68                                         <th nowrap>subject</th>
    69                                         <th nowrap>event</th>
     68                                        <th nowrap>tag name</th>
     69                                        <th nowrap>tag sequence</th>
    7070                                        <th nowrap># sequences</th>
     71                                        <th nowrap># qual</th>
    7172                                </tr>
    7273                        </thead>                       
     
    7778                                                <td><a href="#" onClick="showSample(${assaySample.id}, 'assay'); return false;">${assaySample.sample.name}</a></td>
    7879                                                <td>${assaySample.run?.name}</td>
    79                                                 <td>${assaySample.sample.subject}</td>
    80                                                 <td>${assaySample.sample.event}</td>
    81                                                 <td>${assaySample.numSequences()}</td>
     80                                                <td>${assaySample.tagName}</td>
     81                                                <td>${assaySample.tagSequence}</td>
     82                                                <td>
     83                                                        <g:if test="${assaySample.numSequenceFiles() > 0}">
     84                                                                ${assaySample.numSequences()}
     85                                                        </g:if>
     86                                                        <g:else>
     87                                                                -
     88                                                        </g:else>
     89                                                </td>
     90                                                <td>
     91                                                        <g:if test="${assaySample.numQualityFiles() > 0}">
     92                                                                ${assaySample.numQualScores()}
     93                                                        </g:if>
     94                                                        <g:else>
     95                                                                -
     96                                                        </g:else>
     97                                                </td>
    8298                                        </tr>
    8399                                </g:each>
  • trunk/grails-app/views/fasta/showProcessScreen.gsp

    r7 r12  
    4343                                          clearInterval( progressInterval );
    4444
    45                                           alert( "Error " + xhr.responseCode + ": " + textStatus );
    46                                           window.location.replace( "<g:createLink controller="${entityType}" action="show" id="${entityId}" />" );
     45                                          alert( "Error " + xhr.getStatus() + ": " + xhr.getStatusText() );
     46                                          //window.location.replace( "<g:createLink controller="${entityType}" action="show" id="${entityId}" />" );
    4747                                }
    4848                                });
  • trunk/grails-app/views/run/show.gsp

    r10 r12  
    1313                <g:javascript src="run.show.assayDialog.js" />
    1414                <g:javascript src="run.show.runDialog.js" />
     15                <g:javascript src="run.show.addSamplesDialog.js" />
    1516                <g:javascript src="showSampleDialogUniversal.js" />
    1617
     
    8990                                <tr>
    9091                                        <th nowrap>name</th>
     92                                        <th nowrap>study</th>
    9193                                        <th nowrap>assay</th>
    92                                         <th nowrap>subject</th>
    93                                         <th nowrap>event</th>
     94                                        <th nowrap>tag name</th>
     95                                        <th nowrap>tag sequence</th>
    9496                                        <th nowrap># sequences</th>
     97                                        <th nowrap># qual</th>
     98                                        <th class="nonsortable"></th>
    9599                                </tr>
    96100                        </thead>                       
     
    100104                                        <tr>
    101105                                                <td><a href="#" onClick="showSample(${assaySample.id}, 'run'); return false;">${assaySample.sample.name}</a></td>
    102                                                 <td>${assaySample.assay.study.name} - ${assaySample.assay.name}</td>
    103                                                 <td>${assaySample.sample.subject}</td>
    104                                                 <td>${assaySample.sample.event}</td>
    105                                                 <td>${assaySample.numSequences()}</td>
     106                                                <td>${assaySample.assay.study.name}</td>
     107                                                <td>${assaySample.assay.name}</td>
     108                                                <td>${assaySample.tagName}</td>
     109                                                <td>${assaySample.tagSequence}</td>
     110                                                <td>
     111                                                        <g:if test="${assaySample.numSequenceFiles() > 0}">
     112                                                                ${assaySample.numSequences()}
     113                                                        </g:if>
     114                                                        <g:else>
     115                                                                -
     116                                                        </g:else>
     117                                                </td>
     118                                                <td>
     119                                                        <g:if test="${assaySample.numQualityFiles() > 0}">
     120                                                                ${assaySample.numQualScores()}
     121                                                        </g:if>
     122                                                        <g:else>
     123                                                                -
     124                                                        </g:else>
     125                                                </td>
     126                                                <td class="button">
     127                                                        <g:if test="${!assaySample.assay.study.canWrite(session.user)}">
     128                                                                <img src="${fam.icon(name: 'application_delete')}" class="disabled" title="You can't remove this sample because you don't have sufficient privileges." />
     129                                                        </g:if>
     130                                                        <g:else>
     131                                                                <g:link onClick="return confirm( 'Are you sure you want to remove the selected sample from this run?' );" controller="run" action="removeSample" id="${run.id}" params="${[assaySampleId: assaySample.id]}" ><img src="${fam.icon(name: 'application_delete')}" /></g:link>
     132                                                        </g:else>
     133                                                </td>
    106134                                        </tr>
    107135                                </g:each>
     
    120148                                <input type="button" value="Add sequence files" disabled="disabled">
    121149                        </g:else>
    122 
     150                        <input type="button" value="Add sample" onClick="showAddSamplesDialog();">
     151                        <g:render template="addSamplesDialog" model="[run: run]" />
    123152                </g:if>
    124153                <div id="showSampleDialog" class="dialog"></div>
     
    164193                                                        </g:if>
    165194                                                        <g:else>
    166                                                                 <g:link onClick="return confirm( 'Are you sure you want to remove the selected assay from this run?' );" controller="run" action="removeAssay" id="${run.id}" params="${[assay_id: assay.id]}" ><img src="${fam.icon(name: 'application_delete')}" /></g:link>
     195                                                                <g:if test="${!assay.study.canWrite(session.user)}">
     196                                                                        <img src="${fam.icon(name: 'application_delete')}" class="disabled" title="You can't remove this assay because you don't have sufficient privileges." />
     197                                                                </g:if>
     198                                                                <g:else>
     199                                                                        <g:link onClick="return confirm( 'Are you sure you want to remove the selected assay from this run?' );" controller="run" action="removeAssay" id="${run.id}" params="${[assay_id: assay.id]}" ><img src="${fam.icon(name: 'application_delete')}" /></g:link>
     200                                                                </g:else>
    167201                                                        </g:else>
    168202                                                </td>
Note: See TracChangeset for help on using the changeset viewer.