Changeset 63


Ignore:
Timestamp:
Jun 8, 2011, 4:08:49 PM (8 years ago)
Author:
robert@…
Message:

Improved querying (#40)

  • Removed continuous synchronization while importing data
  • Added action buttons to search result screen
  • Implemented authorization checks while searching
Location:
trunk
Files:
20 added
26 edited

Legend:

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

    r53 r63  
    121121                                                if( !authentication.authenticated ) {
    122122                                                        log.info "Not authenticated: " + authentication.authenticated
    123                                                         redirect( url: gscfService.urlAuthRemote(params, session.sessionToken) )
     123                                                        if( request.method == "GET" ) {
     124                                                                redirect( url: gscfService.urlAuthRemote(params, session.sessionToken) )
     125                                                        } else {
     126                                                                log.debug "POST request: redirect can't be handled properly";
     127                                                                flash.message = "Unfortunately, your request could not be completed, because the system had to log you in first. Please try again."
     128               
     129                                                                redirect( url: gscfService.urlAuthRemote(null, session.sessionToken) )
     130                                                        }
    124131                                                        return false
    125132                                                }
     
    149156                                        session.sessionToken = "${UUID.randomUUID()}"
    150157                                        log.info("SessionToken created, redirecting to GSCF to let the user login! (SessionToken: ${session.sessionToken})")
    151 
    152                                         redirect( url: gscfService.urlAuthRemote(params, session.sessionToken) )
     158                                       
     159                                        if( request.method == "GET" ) {
     160                                                redirect( url: gscfService.urlAuthRemote(params, session.sessionToken) )
     161                                        } else {
     162                                                log.debug "POST request: redirect can't be handled properly";
     163                                                flash.message = "Unfortunately, your request could not be completed, because the system had to log you in first. Please try again."
     164                                                redirect( url: gscfService.urlAuthRemote(null, session.sessionToken) )
     165                                        }
     166                                       
    153167                                        return false
    154168                                }
     
    184198                                // Also never perform synchronization when files are uploaded. That could lead to concurrent modification
    185199                                // errors, since the progress of the upload is retrieved many times, while the processing is still busy
    186                                 if( controllerName == "fasta" ) {
     200                                if( controllerName == "import" ) {
    187201                                        return true;
    188202                                }
  • trunk/grails-app/conf/BootStrap.groovy

    r36 r63  
    11import nl.tno.massSequencing.auth.*;
     2import nl.tno.massSequencing.AssaySample;
    23
    34class BootStrap {       
     
    1718                        }
    1819                }
     20               
     21                // Recalculate sequence totals per assaysample, because they might have not
     22                // been calculated before
     23                AssaySample.recalculateNumSequences()
    1924    }
    2025    def destroy = {
  • trunk/grails-app/conf/Config.groovy

    r59 r63  
    6262// Path in GSCF that is used after baseURL for adding a new study
    6363gscf.addStudyPath = "/simpleWizard"
     64gscf.registerSearchPath = "/advancedQuery/refineExternal"
    6465
    6566grails.project.groupId = appName // change this to alter the default package name and Maven publishing destination
  • trunk/grails-app/controllers/masssequencing/SandboxController.groovy

    r61 r63  
    282282                ) as JSON
    283283        }
     284       
     285        def query = {
     286                def q = "SELECT a, a.sample.name, a.assay.name, a.assay.study.name, a.numSequences FROM AssaySample a WHERE ( EXISTS( FROM Auth auth WHERE auth.study = a.assay.study AND auth.canRead = true AND auth.user = :sessionUser ) ) ";
     287                println AssaySample.executeQuery( q, [ "sessionUser": User.get( 2 ) ] )
     288        }
    284289}
  • trunk/grails-app/controllers/nl/tno/massSequencing/AssayController.groovy

    r60 r63  
    4040                   "a.study.name",
    4141                   "COUNT( DISTINCT s )",
    42                    "SUM( sd.numSequences ) / COUNT( DISTINCT s )",
     42                   "SUM( s.numSequences ) / COUNT( DISTINCT s )",
    4343                   "a.study.studyToken"
    4444           ]
     
    4848           
    4949           // Retrieve data from assaySample table
    50            def from = "Assay a LEFT JOIN a.assaySamples s LEFT JOIN s.sequenceData sd"
     50           def from = "Assay a LEFT JOIN a.assaySamples s"
    5151           def where = " EXISTS( FROM Auth auth WHERE auth.study = a.study AND auth.user = :user AND auth.canRead = true )"
    5252           def parameters = [ "user": session.user ]
  • trunk/grails-app/controllers/nl/tno/massSequencing/AssaySampleController.groovy

    r62 r63  
    8989         */
    9090        protected void renderSequenceLengthHistogram( String title, def assaySamples ) {
     91                println "Rendering histogram for " + assaySamples?.size() + " samples";
     92               
    9193                def numSequences = assaySamples.collect { it.numSequences() }.sum()
    9294                render( view: "sequenceLengthHistogram", model: [ title: title, numSequences: numSequences, histogram: fastaService.sequenceLengthHistogram( assaySamples ) ] );
     
    200202        protected List getAssaySamples( params ) {
    201203                def ids = params.list( 'ids' );
    202 
     204                def tokens = params.list( 'tokens' );
     205               
    203206                ids = ids.findAll { it.isLong() }.collect { Long.parseLong( it ) }
    204 
    205                 if( !ids ) {
     207               
     208                if( !ids && !tokens ) {
    206209                        def message = "No assaysample ids given"
    207210                        flash.error = message
     
    209212                        return;
    210213                }
    211 
    212                 return ids.collect { id -> AssaySample.get( id ) }.findAll{ it }
     214               
     215                def samples = [];
     216               
     217                if( ids )
     218                        samples += AssaySample.executeQuery( "FROM AssaySample a WHERE a.id IN (:ids)", [ "ids": ids ] );
     219               
     220                if( tokens )
     221                        samples += AssaySample.executeQuery( "FROM AssaySample a WHERE a.sample.sampleToken IN (:tokens)", [ "tokens": tokens ] );
     222                       
     223                return samples;
    213224        }
    214225}
  • trunk/grails-app/controllers/nl/tno/massSequencing/FastaController.groovy

    r58 r63  
    5151                sample.save();
    5252               
     53                // Recalculate the number of sequences for this sample
     54                AssaySample.recalculateNumSequences( sample );
     55               
    5356                flash.message = numFiles + " file" + (numFiles != 1 ? "s have" : " has" ) + " been deleted from this sample"
    5457
  • trunk/grails-app/controllers/nl/tno/massSequencing/RunController.groovy

    r60 r63  
    3333                        "r.name",
    3434                        "COUNT( DISTINCT a )",
    35                         "SUM( sd.numSequences )"
     35                        "SUM( a.numSequences )"
    3636                ]
    3737
     
    4040
    4141                // Retrieve data from assaySample table
    42                 def from = "Run r LEFT JOIN r.assaySamples a LEFT JOIN a.sequenceData sd"
     42                def from = "Run r LEFT JOIN r.assaySamples a"
    4343
    4444                // This closure determines what to do with a row that is retrieved from the database.
     
    9898                        redirect(controller: 'study', action: 'index')
    9999                        return
    100                 }
    101 
    102                 // Make sure the newest data is available
    103                 synchronizationService.sessionToken = session.sessionToken
    104                 synchronizationService.user = session.user
    105                 try {
    106                         synchronizationService.synchronizeStudies();
    107                 } catch( Exception e ) {
    108                         log.error "Exception occurred during synchronization in " + params.controller + ": " + e.getMessage()
    109                         redirect( url: synchronizationService.gscfService.urlAuthRemote(params, session.sessionToken) )
    110100                }
    111101
     
    199189                        [
    200190                                g.checkBox( name: "ids", value: sampleId, checked: false, onClick: "updateCheckAll(this);" ),
    201                                 g.link( url: "#", onClick:"showSample( " + sampleId + ", 'assay' );", title: "Show sample details" ) { sampleName },    // it.sample.name
     191                                g.link( url: "#", onClick:"showSample( " + sampleId + ", 'run' );", title: "Show sample details" ) { sampleName },      // it.sample.name
    202192                                it[ 2 ],        // it.assay.study.name
    203193                                it[ 3 ],        // it.assay.name
  • trunk/grails-app/controllers/nl/tno/massSequencing/classification/ClassificationController.groovy

    r62 r63  
    11package nl.tno.massSequencing.classification
     2
     3import java.util.List;
    24
    35import nl.tno.massSequencing.*
     
    101103       
    102104        protected List getAssaySamples() {
    103                 // Retrieve assaySamples from ids
    104                 def assaySampleIds = params.list( 'ids' );
    105                 def assaySamples = [];
    106                 assaySampleIds.each { id ->
    107                         if( id && id.isLong() ) {
    108                                 def assaySample = AssaySample.get( Long.valueOf( id ) );
    109                                 if( assaySample )
    110                                         assaySamples << assaySample;
    111                         }
     105                def ids = params.list( 'ids' );
     106                def tokens = params.list( 'tokens' );
     107               
     108                ids = ids.findAll { it.isLong() }.collect { Long.parseLong( it ) }
     109               
     110                if( !ids && !tokens ) {
     111                        def message = "No assaysample ids given"
     112                        flash.error = message
     113                        redirect( controller: "run", action: "index" );
     114                        return;
    112115                }
    113116               
    114                 return assaySamples;
     117                def samples = [];
     118               
     119                if( ids )
     120                        samples += AssaySample.executeQuery( "FROM AssaySample a WHERE a.id IN (:ids)", [ "ids": ids ] );
     121               
     122                if( tokens )
     123                        samples += AssaySample.executeQuery( "FROM AssaySample a WHERE a.sample.sampleToken IN (:tokens)", [ "tokens": tokens ] );
     124                       
     125                return samples;
    115126        }
    116127}
  • trunk/grails-app/controllers/nl/tno/massSequencing/files/ImportController.groovy

    r59 r63  
    421421                                                       
    422422                                                def sample = AssaySample.get( filevalue.assaySample );
    423                                                 if( sample )
     423                                                if( sample ) {
    424424                                                        sample.addToSequenceData( sd );
     425                                                       
     426                                                        AssaySample.recalculateNumSequences( sample );
     427                                                }
    425428                                               
    426429                                                if( !sd.validate() ) {
  • trunk/grails-app/controllers/nl/tno/massSequencing/integration/RestController.groovy

    r52 r63  
    3333class RestController {
    3434        def synchronizationService
     35        def queryService
    3536       
    3637        /****************************************************************/
     
    164165                        case "Assay":
    165166                        case "Sample":
    166                                 return [ "# sequences", "Forward primer", "Mid name", "Oligo number", "Run name" ]
     167                                return [ "# sequences", "Forward primer", "Mid name", "Oligo number", "Run name", "Classification" ]
    167168                                break;
    168169                        default:
     
    186187         */
    187188        def getQueryableFieldData = {
    188                 println "Get queryable Field data: " + params
     189                log.debug "Get queryable Field data: " + params
    189190               
    190191                def entity = params.entity;
     
    218219                }
    219220
     221                def start = System.currentTimeMillis();
     222                def lap
     223                 
    220224                for( token in tokens ) {
     225                        lap = System.currentTimeMillis();
     226                         
    221227                        def object = getQueryableObject( entity, token );
    222228
     
    244250                                map[ token ] = [:]
    245251                                fields.each { field ->
    246                                         def v = getQueryableFieldValue( entity, object, field );
     252                                        def v = queryService.getQueryableFieldValue( entity, object, field );
    247253                                        if( v != null )
    248254                                                map[ token ][ field ] = v
     
    252258                        }
    253259                }
    254                
     260                 
    255261                render map as JSON
    256262        }
    257 
     263       
    258264        /**
    259265         * Searches for a specific entity
     
    293299                def assaySamples
    294300               
     301                def where = [];
     302                def from = "FROM AssaySample a"
     303               
    295304                switch( entity ) {
    296305                        case "Study":
     306                                where << "a.assay.study = :object";
     307                               
    297308                                assaySamples = object.assays*.assaySamples;
    298309                                if( assaySamples ) {
     
    301312                                break;
    302313                        case "Assay":
     314                                where << " a.assay = :object ";
    303315                        case "Sample":
     316                                where << " a.sample == :sample ";
    304317                                assaySamples = object.assaySamples;
    305318                                break;
     
    328341                        case "Run name":
    329342                                return assaySamples.collect { it.run?.name }.findAll { it != null }.unique();
     343                        case "Classification":
     344                                // Return the names of all species that have been found in the given sample
     345                               
    330346                        // Other fields are not handled
    331347                        default:
     
    335351        }
    336352       
     353       
     354       
    337355        private def checkAssayToken( def assayToken ) {
    338356                if( !assayToken || assayToken == null ) {
     
    354372
    355373        /**
    356          * Retrieves a list of actions that can be performed on data with a specific entity.
     374         * Retrieves a list of actions that can be performed on data with a specific entity. This includes actions that
     375         * refine the search result.
    357376         *
    358377         * The module is allowed to return different fields when the user searches for different entities
     
    365384         *                                                      a list of searchable fields for all entities is given
    366385         * @return      JSON                    Hashmap with keys being the entities and the values are lists with the action this module can
    367          *                                                      perform on this entity. The actions as hashmaps themselves, with keys 'name' and 'description'
     386         *                                                      perform on this entity. The actions as hashmaps themselves, with keys
     387         *                                                      'name'                  Unique name of the action, as used for distinguishing actions
     388         *                                                      'description'   Human readable description
     389         *                                                      'url'                   URL to send the user to when performing this action. The user is sent there using POST with
     390         *                                                                                      the following parameters:
     391         *                                                                                              actionName:             Name of the action to perform
     392         *                                                                                              name:                   Name of the search that the action resulted from
     393         *                                                                                              url:                    Url of the search that the action resulted from
     394         *                                                                                              entity:                 Type of entity being returned
     395         *                                                                                              tokens:                 List of entity tokens
     396         *                                                      'type'                  (optional) Determines what type of action it is. Possible values: 'default', 'refine', 'export', ''
    368397         */
    369398        def getPossibleActions = {
     
    383412                                case "Study":
    384413                                        actions[ entity ] = [
    385                                                 [ name: "excel", description: "Export metadata", url: createLink( controller: "study", action: "exportMetaData", absolute: true ) ],
    386                                                 [ name: "fasta", description: "Export as fasta", url: createLink( controller: "study", action: "exportAsFasta", absolute: true ) ]
     414                                                [ name: "excel", type: 'export', description: "Export metadata", url: createLink( controller: "study", action: "exportMetaData", absolute: true ) ],
     415                                                [ name: "fasta", type: 'export', description: "Export as fasta", url: createLink( controller: "study", action: "exportAsFasta", absolute: true ) ],
     416                                                [ name: "refine", type: 'refine', description: "Refine by classification", url: createLink( controller: "query", action: "refineExternal", absolute: true ) ],
    387417                                        ]
    388418                                        break;
    389419                                case "Assay":
    390420                                        actions[ entity ] = [
    391                                                 [ name: "fasta", description: "Export as fasta", url: createLink( controller: "assay", action: "exportAsFasta", absolute: true ) ],
    392                                                 [ name: "excel", description: "Export metadata", url: createLink( controller: "assay", action: "exportMetaData", absolute: true ) ]
     421                                                [ name: "fasta", type: 'export', description: "Export as fasta", url: createLink( controller: "assay", action: "exportAsFasta", absolute: true ) ],
     422                                                [ name: "excel", type: 'export', description: "Export metadata", url: createLink( controller: "assay", action: "exportMetaData", absolute: true ) ],
     423                                                [ name: "refine", type: 'refine', description: "Refine by classification", url: createLink( controller: "query", action: "refineExternal", absolute: true ) ],
    393424                                        ]
    394425                                        break;
    395426                                case "Sample":
    396427                                        actions[ entity ] = [
    397                                                 [ name: "fasta", description: "Export as fasta", url: createLink( controller: "sample", action: "exportAsFasta", absolute: true ) ],
    398                                                 [ name: "excel", description: "Export metadata", url: createLink( controller: "sample", action: "exportMetaData", absolute: true ) ]
     428                                                [ name: "fasta", type: 'export', description: "Export as fasta", url: createLink( controller: "sample", action: "exportAsFasta", absolute: true ) ],
     429                                                [ name: "excel", type: 'export', description: "Export metadata", url: createLink( controller: "sample", action: "exportMetaData", absolute: true ) ],
     430                                                [ name: "refine", type: 'refine', description: "Refine by classification", url: createLink( controller: "query", action: "refineExternal", absolute: true ) ],
    399431                                        ]
    400432                                        break;
     
    407439                render actions as JSON
    408440        }
    409 
     441       
    410442        /****************************************************************/
    411443        /* REST resources for providing basic data to the GSCF          */
  • trunk/grails-app/domain/nl/tno/massSequencing/Assay.groovy

    r52 r63  
    7070               
    7171                return numFiles;
    72         }       
     72        }
     73       
     74        public String token() { return assayToken; }
     75       
    7376}
  • trunk/grails-app/domain/nl/tno/massSequencing/AssaySample.groovy

    r59 r63  
    11package nl.tno.massSequencing
     2
     3import org.codehaus.groovy.grails.commons.ApplicationHolder as AH
    24
    35/**
     
    1113        def dataSource
    1214       
    13         // To be computed at run time
    14         private long _numSequences = -1;
     15        // To be computed at run time (and saved in cache)
    1516        private float _averageQuality = -1.0;
    1617        private long _numQualScores = -1;
     
    1920       
    2021        Integer numUniqueSequences      // Number of unique sequences / OTUs. Is only available after preprocessing
    21 
     22        Long numSequences                       // Number of sequences in this assaySample. Is used in many calculations and therefore stored
     23       
    2224        String fwOligo
    2325        String fwMidName
     
    5153
    5254        static mapping = {
    53                 columns {
    54                         numSequences index:'numsequences_idx'
    55                 }
     55
    5656                sequenceData cascade: "all-delete-orphan"
    5757                sample fetch: 'join'
     
    129129         */
    130130        public long numSequences() {
    131                 if( _numSequences > -1 )
    132                         return _numSequences;
    133 
    134                 if( !sequenceData )
    135                         return 0
    136 
    137                 long numSequences = 0;
    138                 sequenceData.each { numSequences += it.numSequences }
    139 
    140                 // Save as cache
    141                 _numSequences = numSequences;
    142 
    143                 return numSequences;
     131                return numSequences ?: 0;
    144132        }
    145133       
     
    196184         */
    197185        public void resetStats() {
    198                 _numSequences = -1;
    199186                _numQualScores = -1;
    200187                _averageQuality = -1;
     
    230217                                count( s.sequence_file ) AS numSequenceFiles,
    231218                                count( s.quality_file ) AS numQualityFiles,
    232                                 sum( s.num_sequences ) AS numSequences,
    233219                                sum( CASE WHEN s.quality_file IS NOT NULL THEN s.num_sequences ELSE 0 END ) AS numQualScores
    234220                        FROM assay_sample a
     
    251237                                log.error "ID of the database row and the domain object don't match. DB: " + it.id + ", Domain object: " + assaySamples[ listIndex ]?.id
    252238                        } else {
    253                                 assaySamples[ listIndex ]._numSequences = it.numSequences ?: 0;
    254239                                assaySamples[ listIndex ]._numQualScores = it.numQualScores ?: 0;
    255240                                assaySamples[ listIndex ]._numSequenceFiles = it.numSequenceFiles ?: 0;
     
    291276                otherAssaySample.revMidSeq              = revMidSeq;
    292277                otherAssaySample.revPrimerSeq   = revPrimerSeq;
     278               
     279                otherAssaySample.numSequences   = numSequences;
    293280
    294281                // Move attached data
     
    352339                }
    353340               
     341                numSequences = 0;
     342               
    354343                resetStats();
    355344                save();
     
    367356                Classification.executeUpdate( "DELETE FROM Classification c WHERE c.assaySample = ?", [this])
    368357        }
     358       
     359        /**
     360         * Recalculates the number of sequences for the given assaysample(s)
     361         */
     362        public static void recalculateNumSequences( def selection = null ) {
     363                def whereClause = "";
     364                def parameters = [:];
     365               
     366                // Determine which samples to handle
     367                if( !selection ) {
     368                        whereClause = "";
     369                } else if( selection instanceof AssaySample ) {
     370                        whereClause = "WHERE a = :selection ";
     371                        parameters[ "selection" ] = selection;
     372                } else if( selection instanceof Collection ) {
     373                        if( selection.findAll { it } ) {
     374                                whereClause = "WHERE a IN (:selection) ";
     375                                parameters[ "selection" ] = selection.findAll { it };
     376                        }
     377                }
     378               
     379                // Flush and clear session before updating
     380                def sessionFactory = AH.application.mainContext.sessionFactory;
     381                sessionFactory.getCurrentSession().flush();
     382                sessionFactory.getCurrentSession().clear();
     383               
     384                // Execute update query
     385                AssaySample.executeUpdate( "UPDATE AssaySample a SET a.numSequences = ( SELECT SUM( sd.numSequences ) FROM SequenceData sd WHERE sd.sample.id = a.id ) " + whereClause, parameters );
     386        }
     387       
     388        /**
     389         * If an assaysample is used for communication to GSCF, the sample token is used.
     390         * @return
     391         */
     392        public String token() {
     393                return sample.token();
     394        }
    369395}
  • trunk/grails-app/domain/nl/tno/massSequencing/Sample.groovy

    r57 r63  
    3636                );
    3737        }
     38       
     39        public String token() { return sampleToken; }
    3840
    3941}
  • trunk/grails-app/domain/nl/tno/massSequencing/Study.groovy

    r58 r63  
    9696                return authorization.isOwner
    9797        }
     98       
     99        public String token() { return studyToken; }
    98100
    99101}
  • trunk/grails-app/domain/nl/tno/massSequencing/classification/Classification.groovy

    r62 r63  
    6262               
    6363                // Determine level names for the necessary levels
    64                 levels = [:];
    65                 minLevel.upto( maxLevel ) { level ->
    66                         def levelEnum = Taxon.Level.find { it.number() == level }
    67                         levels[ level ] = levelEnum?.description();
    68                 }
     64                levels = Taxon.retrieveLevelNames( minLevel, maxLevel );
    6965               
    7066                // First build a list of percentages for each assaysample, in order to have default values
     
    127123         * @return      [ maxLevel, minLevel ]
    128124         */
    129         public static determineMinAndMaxLevels( List assaySamples ) {
    130                 def levels = Classification.executeQuery( "SELECT MAX(t.level), MIN(t.level) FROM Classification c LEFT JOIN c.taxon t LEFT JOIN c.assaySample a WHERE a IN (:assaySamples)", [ "assaySamples": assaySamples ] )[ 0 ];
     125        public static determineMinAndMaxLevels( List assaySamples = null ) {
     126                def levels
     127               
     128                if( !assaySamples )
     129                        levels = [ null, null ];
     130                else
     131                        levels = Classification.executeQuery( "SELECT MAX(t.level), MIN(t.level) FROM Classification c LEFT JOIN c.taxon t LEFT JOIN c.assaySample a WHERE a IN (:assaySamples)", [ "assaySamples": assaySamples ] )[ 0 ];
    131132               
    132133                if( levels[ 0 ] == null )
  • trunk/grails-app/domain/nl/tno/massSequencing/classification/Taxon.groovy

    r61 r63  
    314314           return t
    315315   }
     316   
     317   public static Map retrieveLevelNames( def minLevel = 1, def maxLevel = 6 ) {
     318                def levels = [:];
     319                minLevel.upto( maxLevel ) { level ->
     320                        def levelEnum = Taxon.Level.find { it.number() == level }
     321                        levels[ level ] = levelEnum?.description();
     322                }
     323               
     324                return levels;
     325   }
    316326
    317327}
  • trunk/grails-app/services/nl/tno/massSequencing/ClassificationService.groovy

    r62 r63  
    255255                def statement = connection.createStatement();
    256256
    257                 // This statements searches within the sequences for every taxon that is mentioned (as a leaf-node in the tree)
     257                // This statement searches within the sequences for every taxon that is mentioned (as a leaf-node in the tree)
    258258                // and inserts statistics about those taxons into the classification table.
    259259                //
  • trunk/grails-app/services/nl/tno/massSequencing/FastaService.groovy

    r60 r63  
    134134                def length
    135135                def lengthMatches
     136                def currentLength = 0;
     137                def countingLines = false;
    136138                try {
    137139                        file.eachLine { line ->
    138140                                if( line ) {
    139141                                        if( line[0] == '>' ) {
     142                                                if( countingLines ) {
     143                                                        // If counting the length in lines, we must stop now and store the length
     144                                                        histogram[ currentLength ] = ( histogram[ currentLength ] ?: 0 ) + 1;
     145                                                        countingLines = false;
     146                                                }
     147                                               
    140148                                                // Comments line: find length=###
    141149                                                lengthMatches = ( line =~ lengthPattern );
    142150                                                if( lengthMatches ) {
    143151                                                        length = Integer.valueOf( lengthMatches[0][1] );
     152                                                       
     153                                                        histogram[ length ] = ( histogram[length] ?: 0 ) + 1;
    144154                                                } else {
    145                                                         length = 'unknown'
     155                                                        // Length is not mentioned in the comments line
     156                                                        countingLines = true;
     157                                                        currentLength = 0;
    146158                                                }
    147 
    148                                                 histogram[ length ] = ( histogram[length] ?: 0 ) + 1;
     159                                        } else if( countingLines ) {
     160                                                // Compute the length by counting the files themselves.
     161                                                currentLength += line.trim().size();
    149162                                        }
    150163                                }
     
    155168
    156169                log.trace "Finished parsing FASTA " + file.getName() + ": " + ( System.nanoTime() - startTime ) / 1000000L
    157 
     170                log.trace "Histogram: " + histogram
     171               
    158172                return histogram;
    159173        }
     
    569583
    570584                                        assaySample.sequenceData.each { sequenceData ->
    571                                                 copyFastaFileForExport( fileService.get( sequenceData.sequenceFile, permanentDirectory ), currentTag.tag, zipWriter)
     585                                                if( sequenceData && sequenceData.sequenceFile )
     586                                                        copyFastaFileForExport( fileService.get( sequenceData.sequenceFile, permanentDirectory ), currentTag.tag, zipWriter)
    572587                                        }
    573588                                }
     
    584599
    585600                                                assaySample.sequenceData.each { sequenceData ->
    586                                                         copyQualFileForExport( fileService.get( sequenceData.qualityFile, permanentDirectory ), currentTag.tag, zipWriter)
     601                                                        if( sequenceData && sequenceData.sequenceFile && sequenceData.qualityFile )
     602                                                                copyQualFileForExport( fileService.get( sequenceData.qualityFile, permanentDirectory ), currentTag.tag, zipWriter)
    587603                                                }
    588604                                        }
     
    595611                } catch( Exception e ) {
    596612                        log.error "Error while writing to fastafile or qualfile: " + e.getMessage();
     613                        e.printStackTrace();
    597614                } finally {
    598615                        // Always close zip entry
     
    779796
    780797                        String line = null
    781                         String newLine = null
     798                        String newLine = ""
    782799                        String sequence = "";
    783800
     
    806823                                                length = Integer.valueOf( lengthMatches[0][1] ) + tagLength;
    807824                                                newLine = lengthMatches.replaceAll( "length=" + length );
     825                                        } else {
     826                                                newLine = line;
    808827                                        }
    809828
     
    823842                } catch( Exception e ) {
    824843                        log.error( "An error occurred while copying contents from " + inFile.getName() + ": " + e.getMessage() );
     844                        e.printStackTrace();
    825845                        return false;
    826846                }
     
    897917                                                length = Integer.valueOf( lengthMatches[0][1] ) + tagLength;
    898918                                                newLine = lengthMatches.replaceAll( "length=" + length );
     919                                        } else {
     920                                                newLine = line
    899921                                        }
    900922
  • trunk/grails-app/services/nl/tno/massSequencing/integration/GscfService.groovy

    r53 r63  
    2424         * @return                      URL to redirect the user to
    2525         */
    26         public String urlAuthRemote( def params, def token ) {
     26        public String urlAuthRemote( def params, def token, appendParameters = true ) {
    2727                def redirectURL = "${config.gscf.baseURL}/login/auth_remote?moduleURL=${this.moduleURL()}&consumer=${this.consumerID()}&token=${token}&"
    28 
    29                 def returnUrl = config.grails.serverURL
    30                 if (params.controller != null){
    31                         returnUrl += "/${params.controller}"
    32                         if (params.action != null){
    33                                 returnUrl += "/${params.action}"
    34                                 if (params.id != null){
    35                                         returnUrl += "/${params.id}"
     28                def returnUrl
     29               
     30                // Append other parameters (but only if this request is a GET request)
     31                if( appendParameters && params ) {
     32                        def request = RequestContextHolder.getRequestAttributes().getRequest();
     33                       
     34                        if( request.method != "GET" ) {
     35                                // Only GET parameters can be sent to the user. The calling method can call the method again with the appendParameters
     36                                // property set to false or without a parameters object.
     37                                throw new Exception( "Parameters can only be added in GET requests" );
     38                        }
     39
     40                        returnUrl = config.grails.serverURL
     41                        if (params.controller != null){
     42                                returnUrl += "/${params.controller}"
     43                                if (params.action != null){
     44                                        returnUrl += "/${params.action}"
     45                                        if (params.id != null){
     46                                                returnUrl += "/${params.id}"
     47                                        }
    3648                                }
    3749                        }
    38                 }
    39 
    40                 // Append other parameters
    41                 returnUrl += "?" + params.collect {
    42                         if( it.key != "controller" && it.key != "action" && it.key != "id" )
    43                                 return it.key.toString().encodeAsURL() + "=" + it.value.toString().encodeAsURL();
    44                         else
    45                                 return ""
    46                 }.findAll { it }.join( "&" );
     50       
     51                        returnUrl += "?" + params.collect { param ->
     52                                if( param.key != "controller" && param.key != "action" && param.key != "id" ) {
     53                                        if( param.value instanceof List ) {
     54                                                return param.value.collect { return param.key.toString().encodeAsURL() + "=" + it.toString().encodeAsURL() }.findAll { it }.join( "&" )
     55                                        } else if( param.value instanceof Map ) {
     56                                                // Discard this parameter as it is present in other keys as well.
     57                                        } else {
     58                                                return param.key.toString().encodeAsURL() + "=" + param.value.toString().encodeAsURL();
     59                                        }
     60                                } else {
     61                                        return ""
     62                                }
     63                        }.findAll { it }.join( "&" );
     64                } else {
     65                        returnUrl = config.grails.serverURL;
     66                }
    4767
    4868                // After logging in, send the user to /synchronize/authorization first to synchronize
     
    7292        }
    7393
     94        /**
     95        * Returns the URL to register an external search with GSCF
     96        *
     97        * @return                       URL to redirect the user to
     98        */
     99   public String urlRegisterSearch( def search ) {
     100           return config.gscf.baseURL + config.gscf.registerSearchPath
     101   }
     102       
    74103        /**
    75104         * Retrieves the currently logged in user from GSCF
     
    400429                                        }
    401430
    402                                         log.info("GSCF REST-RESP: ${jsonResponse}")
     431                                        if( jsonResponse.size() > 2000 )
     432                                                log.info("GSCF REST-RESP: " + jsonResponse[0..2000] + "..." )
     433                                        else
     434                                                log.info("GSCF REST-RESP: " + jsonResponse )
    403435                                } catch(Exception e) {
    404436                                        log.error("Parsing GSCF JSON response failed at ${addr}. Reponse was " + connection.content.text,e)
  • trunk/grails-app/services/nl/tno/massSequencing/integration/SynchronizationService.groovy

    r52 r63  
    671671                                        // Create a new assay-sample combination
    672672                                        log.trace( "Connecting sample to assay" )
    673                                         assaySampleFound = new AssaySample();
     673                                        assaySampleFound = new AssaySample( numSequences: 0 );
    674674
    675675                                        assay.addToAssaySamples( assaySampleFound );
    676676                                        sampleFound.addToAssaySamples( assaySampleFound );
    677677
    678                                         assaySampleFound.save()
     678                                        if( !assaySampleFound.save() ) {
     679                                                log.error( "Error while connecting sample to assay: " + assaySampleFound.errors )
     680                                        }
    679681                                }
    680682                        }
  • trunk/grails-app/views/classification/show.gsp

    r62 r63  
    3636                        </p>
    3737                </g:if>
    38                 <table border="0" class="sortable classification">
     38                <table border="0" class="sortable classification scrollX">
    3939                        <thead>
    4040                                <tr>
  • trunk/grails-app/views/common/_topnav.gsp

    r53 r63  
    99      </ul>
    1010    </li>
     11    <li><g:link controller="query">Query</g:link></li>
    1112        <li>
    1213      <a href="#" onClick="return false;">GSCF</a>
  • trunk/grails-app/views/import/showProcessScreen.gsp

    r60 r63  
    3333                          data: "${processParameters*.toString().join("&")}",
    3434                          success: function(data) {
    35                                  
    3635                                  // Stop update progress bar
    3736                                  clearTimeout( progressInterval );
     37
     38                                  alert( "success" );
    3839                                   
    3940                                  window.location.replace( "${finishUrl.encodeAsJavaScript()}" );
    4041                          },
    4142                          error: function(xhr, textStatus, errorThrown) {
    42 
     43                                       
    4344                                  // Stop update progress bar (but update it for the last time)
    4445                                  updateProgress()
  • trunk/web-app/css/buttons.css

    r62 r63  
    5757}
    5858
     59/* Classification */
    5960.options a.classification {
    6061        background-image: url(../plugins/famfamfam-1.0.1/images/icons/tag_blue.png);
     
    6970}
    7071
     72/* Querying */
     73.options a.searchIn {
     74        background-image: url(../plugins/famfamfam-1.0.1/images/icons/arrow_branch.png);
     75}
     76.options a.search {
     77        background-image: url(../plugins/famfamfam-1.0.1/images/icons/arrow_undo.png);
     78}
     79.options a.discard {
     80        background-image: url(../plugins/famfamfam-1.0.1/images/icons/basket_remove.png);
     81}
     82.options a.listPrevious {
     83        background-image: url(../plugins/famfamfam-1.0.1/images/icons/basket.png);
     84}
     85.options a.combine {
     86        background-image: url(../plugins/famfamfam-1.0.1/images/icons/arrow_join.png);
     87}
  • trunk/web-app/js/paginate.js

    r62 r63  
    9595                        iCookieDuration: 86400,                         // Save cookie one day
    9696                        sScrollY: '350px',
    97                         sScrollX: '100%',
     97                        sScrollX: $el.hasClass( 'scrollX' ) ? '100%' : '',
    9898                        bScrollCollapse: true,
    9999                        aoColumnDefs: [
     
    175175
    176176function checkAllPaginatedClientSide( paginatedTable ) {
    177         var paginatedTable = $(input).closest( '.paginate' );
    178177        var checkAll = $( '#checkAll', paginatedTable );
    179178       
Note: See TracChangeset for help on using the changeset viewer.