Changeset 13


Ignore:
Timestamp:
Feb 10, 2011, 5:03:02 PM (12 years ago)
Author:
robert@…
Message:

Improved user interface and implemented basic export functionality

Location:
trunk
Files:
3 added
16 edited

Legend:

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

    r3 r13  
    4141// Otherwise, it should be given as an absolute path (e.g. '/home/user/sequences')
    4242// The directory should be writable by the webserver user
    43 metagenomics.fileUploadDir = "filuploads/temp"
     43metagenomics.fileUploadDir = "fileuploads/temp"
    4444
    4545// Maximum age that uploaded files should be kept on the server before deleting them. When a user uploads a file,
  • trunk/grails-app/controllers/nl/tno/metagenomics/AssayController.groovy

    r12 r13  
    1111        def excelService
    1212        def sampleExcelService
     13        def fastaService
    1314
    1415        def index = {
     
    326327        }
    327328       
    328 
     329        /**
     330         * Exports data about one or more assays in fasta format
     331         */
     332        def exportAsFasta = {
     333                def tokens = params.list( 'tokens' );
     334                def ids = params.list( 'ids' );
     335                def name;
     336               
     337                ids = ids.findAll { it.isLong() }.collect { Long.parseLong( it ) }
     338               
     339                if( !tokens && !ids ) {
     340                        def message = "No assay tokens or ids given"
     341                        response.setStatus( 400, message)
     342                        render message;
     343                        return;
     344                }
     345                               
     346                def assaySamples = [];
     347               
     348                // Determine which assaySamples to export
     349                def assay;
     350                tokens.each { token ->
     351                        assay = Assay.findByAssayToken( token );
     352                        if( assay )
     353                                assaySamples += assay.assaySamples
     354                }
     355                ids.each { id ->
     356                        assay = Assay.get( id );
     357                        if( assay )
     358                                assaySamples += assay.assaySamples
     359                }
     360
     361                if( ( ids.size() + tokens.size() ) == 1 && assay )
     362                        name = "Assay_" + assay?.name?.replace( ' ', '_' );
     363                else
     364                        name = "assays";
     365                       
     366                // Export the sequences and quality scores
     367                response.setHeader "Content-disposition", "attachment; filename=" + name.trim() + ".zip"
     368                try {
     369                        fastaService.export( assaySamples.unique(), response.getOutputStream(), name );
     370                        response.outputStream.flush();
     371                } catch( Exception e ) {
     372                        log.error( "Exception occurred during export of sequences. Probably the user has cancelled the download." );
     373                }
     374        }
    329375}
  • trunk/grails-app/controllers/nl/tno/metagenomics/RunController.groovy

    r12 r13  
    99        def synchronizationService
    1010        def sampleExcelService
     11        def fastaService
    1112
    1213        def index = {
     
    2526                // Make sure the newest data is available
    2627                synchronizationService.sessionToken = session.sessionToken
    27                 synchronizationService.synchronizeStudies();
     28                synchronizationService.user = session.user
     29                try {
     30                        synchronizationService.synchronizeStudies();
     31                } catch( Exception e ) {
     32                        log.error "Exception occurred during synchronization in " + params.controller + ": " + e.getMessage()
     33                        redirect( url: synchronizationService.gscfService.urlAuthRemote(params, session.sessionToken) )
     34                }
    2835
    2936                // Determine runs not used in this assay
     
    445452        }
    446453
     454        /**
     455         * Exports data about one or more runs in fasta format
     456         */
     457        def exportAsFasta = {
     458                def ids = params.list( 'ids' );
     459
     460                ids = ids.findAll { it.isLong() }.collect { Long.parseLong( it ) }
     461
     462                if( !ids ) {
     463                        def message = "No run ids given"
     464                        response.setStatus( 400, message)
     465                        render message;
     466                        return;
     467                }
     468
     469                def assaySamples = [];
     470                def name
     471
     472                if( ids.size() == 1 )
     473                        name = "Run_" + Run.get( ids[ 0 ] )?.name?.replace( ' ', '_' );
     474                else
     475                        name = "runs";
     476
     477                // Determine which assaySamples to export
     478                ids.each { id ->
     479                        def run = Run.get( id );
     480                        if( run )
     481                                assaySamples += run.assaySamples
     482                }
     483
     484                // Export the sequences and quality scores
     485                response.setHeader "Content-disposition", "attachment; filename=${name}.zip"
     486                try {
     487                        fastaService.export( assaySamples.unique(), response.getOutputStream(), name );
     488                        response.outputStream.flush();
     489                } catch( Exception e ) {
     490                        log.error( "Exception occurred during export of sequences. Probably the user has cancelled the download." );
     491                }
     492        }
     493
    447494
    448495        /**
  • trunk/grails-app/controllers/nl/tno/metagenomics/StudyController.groovy

    r12 r13  
    2020                        lastSynchronized: synchronizationService.lastFullSynchronization ]
    2121        }
     22       
     23        def exportAsExcel = {
     24                render "To be implemented"
     25        }
    2226
    2327}
  • trunk/grails-app/controllers/nl/tno/metagenomics/integration/ActionController.groovy

    r12 r13  
    33
    44class ActionController {
    5         def fastaService
    6        
    7         /**
    8          * Exports data in fasta format
    9          */
    10         def fasta = {
    11                 def entity = params.entity;
    12                 def tokens = params.tokens;
    13                
    14                 if( !tokens ) {
    15                         def message = "No entity tokens given"
    16                         response.setStatus( 400, message)
    17                         render message;
    18                         return;
    19                 }
    20                
    21                 if( tokens instanceof String )
    22                         tokens = [tokens]
    23                
    24                 def assaySamples = [];
    25                 def name = "testfile";
    26                
    27                 switch( params.entity ) {
    28                         case "Study":
    29                                 // Determine which assaySamples to export
    30                                 tokens.each { token ->
    31                                         def study = Study.findByStudyToken( token );
    32                                         if( study )
    33                                                 assaySamples += study.assay*.assaySamples.flatten()
    34                                 }
    35                                 break;
    36                         case "Assay":
    37                                 // Determine which assaySamples to export
    38                                 tokens.each { token ->
    39                                         def assay = Assay.findByAssayToken( token );
    40                                         if( assay )
    41                                                 assaySamples += assay.assaySamples
    42                                 }
    43                                 break;
    44                         case "Sample":
    45                                 // Determine which assaySamples to export
    46                                 tokens.each { token ->
    47                                         def sample = Sample.findBySampleToken( token );
    48                                         if( sample )
    49                                                 assaySamples += sample.assaySamples
    50                                 }
    51                                 break;
    52                         default:
    53                                 def message = "Specified entity can't be handled by this module."
    54                                 response.setStatus( 400, message)
    55                                 render message;
    56                                 return;
    57                 }
    58                
    59                 // Export the sequences and quality scores
    60                 response.setHeader "Content-disposition", "attachment; filename=${name}.zip"
    61                 fastaService.export( assaySamples.unique(), response.getOutputStream(), name );
    62                 response.outputStream.flush();
    63         }
     5
    646}
  • trunk/grails-app/controllers/nl/tno/metagenomics/integration/RestController.groovy

    r12 r13  
    373373                        switch( entity ) {
    374374                                case "Study":
    375                                         actions[ entity ] = [ [ name: "excel", description: "Export as excel" ] ]
     375                                        actions[ entity ] = [ [ name: "excel", description: "Export as excel", url: createLink( controller: "study", action: "exportAsExcel", absolute: true ) ] ]
    376376                                        break;
    377377                                case "Assay":
    378                                         actions[ entity ] = [ [ name: "fasta", description: "Export as fasta" ] ]
     378                                        actions[ entity ] = [ [ name: "fasta", description: "Export as fasta", url: createLink( controller: "assay", action: "exportAsFasta", absolute: true ) ] ]
    379379                                        break;
    380380                                case "Sample":
    381                                         actions[ entity ] = [ [ name: "fasta", description: "Export as fasta" ] ]
     381                                        actions[ entity ] = [ [ name: "fasta", description: "Export as fasta", url: createLink( controller: "sample", action: "exportAsFasta", absolute: true ) ] ]
    382382                                        break;
    383383                                default:
  • trunk/grails-app/services/nl/tno/metagenomics/FastaService.groovy

    r12 r13  
    33import java.io.BufferedWriter;
    44import java.io.File;
     5import java.io.Writer;
    56import java.util.ArrayList;
    67import java.util.zip.ZipEntry
     
    1112        def fileService
    1213        def fuzzySearchService
     14        def sampleExcelService
    1315
    1416        static transactional = true
     
    428430
    429431                                // Save the tag for exporting
    430                                 tags << [assaySampleId: assaySample.id, sampleName: assaySample.sample.name, assayName: assaySample.assay.name, tag: tag]
     432                                tags << [assaySampleId: assaySample.id, sampleName: assaySample.sample.name, assayName: assaySample.assay.name, studyName: assaySample.assay.study.name, tag: tag]
    431433                        }
    432434                }
     
    481483                }
    482484               
     485                // Export a tab delimited file with tags
     486                zipFile.putNextEntry( new ZipEntry( name + "_sample-tag.tab" ) );
     487                exportSampleTagFile( tags, zipWriter );
     488                zipWriter.flush();
     489                zipFile.closeEntry();
     490
     491                // Export an excel file with information about the samples
     492                zipFile.putNextEntry( new ZipEntry( name + ".xls" ) );
     493                sampleExcelService.exportExcelSampleData( assaySamples, tags, zipFile );
     494                zipFile.closeEntry();
     495               
    483496                zipFile.close();
    484 
    485                 // Export a tab delimited file with tags
    486                 //exportSampleTagFile( tags );
     497        }
     498       
     499        /**
     500         * Creates a tab delimited file with two columns and column headers "Sequence" and "Samplename"
     501         * @param tags          Map with newly created tags
     502         * @param zipWriter     Writer to write the data to
     503         */
     504        protected void exportSampleTagFile( List tags, Writer zipWriter ) {
     505                zipWriter.write( "Sequence" + "\t" + "Samplename" + "\n" );
     506               
     507                // Check whether the sample names are unique. If they aren't, the assay and study names
     508                // are appended to the sample name
     509                def sampleNames = tags*.sampleNames;
     510                if( sampleNames.unique().size() < sampleNames.size() ) {
     511                        tags.each {
     512                                it.uniqueName = it.sampleName + " (" + it.assayName + " / " + it.studyName + ")";
     513                        }
     514                } else {
     515                        tags.each {
     516                                it.uniqueName = it.sampleName;
     517                        }
     518                }
     519               
     520                tags.each {
     521                        zipWriter.write( it.tag + "\t" + it.uniqueName + "\n" );
     522                }
    487523        }
    488524
  • trunk/grails-app/services/nl/tno/metagenomics/SampleExcelService.groovy

    r9 r13  
    11package nl.tno.metagenomics
     2
     3import org.springframework.web.context.request.RequestContextHolder;
    24
    35class SampleExcelService {
    46        def excelService
    57        def fuzzySearchService
     8        def gscfService
    69       
    710    static transactional = true
     
    1518        def possibleFields = [sampleNameName, runName, tagNameName, tagSequenceName, oligoNumberName]
    1619       
    17     def downloadSampleExcel( def assaySamples, boolean includeRun = true ) {
     20    /**
     21     * Download a sample excel file with information about the metagenomics data of the assaySamples (i.e. all assaySample properties)
     22     * @param assaySamples      AssaySamples for which the information should be exported
     23     * @param includeRun        Whether to include a column with run name or not
     24     * @return
     25     */
     26        def downloadSampleExcel( def assaySamples, boolean includeRun = true ) {
    1827                def sheetIndex = 0;
    1928               
     
    5059                excelService.autoSizeColumns( wb, sheetIndex, 0..2)
    5160
    52                 return wb;             
     61                return wb;
    5362    }
    5463       
     64        /**
     65         * Parses a given excel file and tries to match the column names with assaySample properties
     66         * @param file 
     67         * @return
     68         */
    5569        def parseTagsExcel( File file, boolean includeRun = true ) {
    5670                def sheetIndex = 0
     
    8195        }
    8296       
     97        /**
     98         * Updates given assay samples with data from the excel file
     99         * @param matchColumns          Indicated which columns from the excel file should go into which field of the assaySample
     100         * @param possibleFields        List with possible fields to enter
     101         * @param file                          Excel file with the data
     102         * @param assaySamples          Assay Samples to be updated
     103         * @return
     104         */
    83105        def updateTagsByExcel( def matchColumns, def possibleFields, File file, def assaySamples ) {
    84106                def sheetIndex = 0
     
    187209                }
    188210        }
     211       
     212        /**
     213         * Exports all known data about the samples to an excel file
     214         * @param assaySamples  Assaysamples to export information about
     215         * @param tags                  Tags associated with the assay samples
     216         * @param stream                Outputstream to write the data to       
     217         * @return
     218         */
     219        def exportExcelSampleData( List<AssaySample> assaySamples, def tags, OutputStream stream ) {
     220                if( assaySamples == null )
     221                        assaySamples = []
     222
     223                // Gather data from GSCF.
     224                def sampleTokens = assaySamples*.sample.unique()*.sampleToken;
     225                def sessionToken = RequestContextHolder.currentRequestAttributes().getSession().sessionToken
     226                def gscfData
     227                try {
     228                        gscfData = gscfService.getSamples( sessionToken, sampleTokens );
     229                } catch( Exception e ) {
     230                        log.error "Exception occurred while fetching sample data from gscf: " + e.getMessage();
     231                        return;
     232                }
     233               
     234                // Determine which fields to show from the GSCF data
     235                def gscfFields = []
     236                def subjectFields = []
     237                def eventFields = []
     238                def moduleFields = [ "Sample name", "Assay name", "Study name", "Run name", "# sequences", "Artificial tag sequence", "Original tag sequence", "Tag name", "Oligo number" ]
     239                gscfData.each { sample ->
     240                        sample.each { key, value ->
     241                                if( key == "subjectObject" ) {
     242                                        value.each { subjectKey, subjectValue ->
     243                                                if( subjectValue && !value.isNull( subjectKey ) && !subjectFields.contains( subjectKey ) )
     244                                                        subjectFields << subjectKey
     245                                        }
     246                                } else if( key == "eventObject" ) {
     247                                        value.each { eventKey, eventValue ->
     248                                                if( eventValue && !value.isNull( eventKey ) && !eventFields.contains( eventKey ) )
     249                                                        eventFields << eventKey
     250                                        }
     251                                } else if( value && !sample.isNull( key ) && !gscfFields.contains( key ) ) {
     252                                        gscfFields << key
     253                                }
     254                        }
     255                }
     256               
     257                // Handle specific fields and names in GSCF
     258                def fields = handleSpecificFields( [ "module": moduleFields, "gscf": gscfFields, "subject": subjectFields, "event": eventFields ] );
     259
     260                // Put the module data in the right format (and sorting the samples by name)
     261                def data = []
     262                assaySamples.toList().sort { it.sample.name }.each { assaySample ->
     263                        // Lookup the tag for this assaySample
     264                        def currentTag = tags.find { it.assaySampleId == assaySample.id };
     265                       
     266                        // First add the module data
     267                        def row = [
     268                                assaySample.sample.name,
     269                                assaySample.assay.name,
     270                                assaySample.assay.study.name,
     271                                assaySample.run?.name,
     272                                assaySample.numSequences(),
     273                                currentTag?.tag,
     274                                assaySample.tagName,
     275                                assaySample.tagSequence,
     276                                assaySample.oligoNumber
     277                        ]
     278                       
     279                        // Afterwards add the gscfData including subject and event data
     280                        def gscfRow = gscfData.find { it.sampleToken == assaySample.sample.sampleToken };
     281                        if( gscfRow ) {
     282                                fields[ "names" ][ "gscf" ].each { field ->
     283                                        row << prepare( gscfRow, field );
     284                                }
     285                                fields[ "names" ][ "subject" ].each { field ->
     286                                        row << prepare( gscfRow.optJSONObject( "subjectObject" ), field );
     287                                }
     288                                fields[ "names" ][ "event" ].each { field ->
     289                                        row << prepare( gscfRow.optJSONObject( "eventObject" ), field );
     290                                }
     291                        }
     292                       
     293                        data << row;
     294                       
     295                }
     296               
     297                // Create excel file
     298                def sheetIndex = 0;
     299                       
     300                // Create an excel sheet
     301                def wb = excelService.create();
     302
     303                // Put the headers on the first row
     304                excelService.writeHeader( wb, fields[ "descriptions" ][ "all" ], sheetIndex );
     305                excelService.writeData( wb, data, sheetIndex, 1 );
     306
     307                // Auto resize columns
     308                excelService.autoSizeColumns( wb, sheetIndex, 0..fields[ "names" ][ "all" ].size()-1)
     309
     310                // Write the data to the output stream
     311                wb.write( stream );
     312        }
     313       
     314        protected String prepare( def object, def fieldName ) {
     315                if( object.isNull( fieldName ) )
     316                        return "";
     317               
     318                // If the field is a object, return the 'name' property
     319                def obj = object.optJSONObject( fieldName )
     320                if( obj )
     321                        return obj.optString( "name" )
     322                else
     323                        return object.optString( fieldName );
     324        }
     325       
     326        protected handleSpecificFields( def inputFields ) {
     327                def fields = [
     328                        "names": [
     329                                "all": []
     330                        ],
     331                        "descriptions": [
     332                                "all": []
     333                        ]
     334                ]
     335               
     336                inputFields.each { key, value ->
     337                        def names = [];
     338                        def descriptions = []
     339                        switch( key ) {
     340                                case "gscf":
     341                                        value.each {
     342                                                if( it != "sampleToken" && it != "name" ) {
     343                                                        names << it;
     344                                                        if( it == "startTime" )
     345                                                                descriptions << "Event start time";
     346                                                        else
     347                                                                descriptions << it
     348                                                }
     349                                        }
     350                                        break;
     351                                case "subject":
     352                                        value.each {
     353                                                if( it != "name" ) {
     354                                                        names << it;
     355                                                        descriptions << it
     356                                                }
     357                                        }
     358                                        break;
     359                                case "event":
     360                                        value.each {
     361                                                if( it != "startTime" ) {
     362                                                        names << it;
     363                                                        descriptions << it
     364                                                }
     365                                        }
     366                                        break;
     367                                default:
     368                                        names = value; descriptions = value;
     369                                        break;
     370                        }
     371                       
     372                        fields[ "names" ][ key ] = names;
     373                        fields[ "names" ][ "all" ] += names;
     374                        fields[ "descriptions" ][ key ] = descriptions;
     375                        fields[ "descriptions" ][ "all" ] += descriptions;
     376                }       
     377               
     378                return fields;
     379
     380        }
     381       
    189382}
  • trunk/grails-app/services/nl/tno/metagenomics/files/FileService.groovy

    r12 r13  
    100100
    101101                if( !uploadDir )
    102                 uploadDir = "fileuploads"
     102                        uploadDir = "fileuploads"
    103103
    104104                return absolutePath( uploadDir );
  • trunk/grails-app/services/nl/tno/metagenomics/integration/GscfService.groovy

    r9 r13  
    1212 */
    1313class GscfService {
    14 
    1514        def config = ConfigurationHolder.config
    1615
    1716        static transactional = true
    18 
    19 
    2017
    2118        /**
     
    5148                return "${config.gscf.baseURL}/study/showByToken/" + studyToken
    5249        }
    53        
    54         /**
    55         * Returns the url to add a new study in GSCF
    56         *
    57         * @return                       URL to redirect the user to
    58         */
    59    public String urlAddStudy( String studyToken ) {
    60            return config.gscf.baseURL + config.gscf.addStudyPath
    61    }
    62 
    63    /**
    64   * Retrieves the currently logged in user from GSCF
    65   *
    66   * @param sessionToken String
    67   *
    68   * @return Map
    69   */
    70   public Map getUser(String sessionToken) {
    71           def user = [:]
    72           this.callGSCF(sessionToken, "getUser").each {
    73                   user[ it.key ] = it.value;
    74           }
    75           return user
    76   }
    77    
     50
     51        /**
     52         * Returns the url to add a new study in GSCF
     53         *
     54         * @return                      URL to redirect the user to
     55         */
     56        public String urlAddStudy( String studyToken ) {
     57                return config.gscf.baseURL + config.gscf.addStudyPath
     58        }
     59
     60        /**
     61        * Retrieves the currently logged in user from GSCF
     62        *
     63        * @param sessionToken String
     64        *
     65        * @return Map
     66        */
     67        public Map getUser(String sessionToken) {
     68                def user = [:]
     69                this.callGSCF(sessionToken, "getUser").each {
     70                        user[ it.key ] = it.value;
     71                }
     72                return user
     73        }
     74
    7875        /**
    7976         * Retrieve a list of Studies from the GSCF
     
    8784        }
    8885
    89        
     86
    9087        /**
    9188         * Retrieve a list of Studies from the GSCF
     
    116113                } catch( ResourceNotFoundException e ) {
    117114                        throw new ResourceNotFoundException( "Study with token " + studyToken + " not found in GSCF" )
    118                         throw e // Other exceptions are thrown as they appear
    119                 }
     115                } // Other exceptions are thrown as they appear
    120116
    121117                // Return only the first element of the list, since we are only interested in one study
    122                 if( list.size() == 0 ) {
     118                if( list?.size() ) {
     119                        return list[0];
     120                } else {
    123121                        return []
    124                 } else {
    125                         return list[0];
    126122                }
    127123        }
     
    172168        }
    173169
    174         /**
    175          * Retrieve a list of Samples from the GSCF
    176          *
    177          * @param sessionToken String
    178          * @param assay Assay (parameter assay is optional, only used when you want to limit the list of samples of an Assay within a Study)
    179          *
    180          * @return ArrayList
    181          */
    182         public ArrayList getSamples(String sessionToken, String assayToken) throws NotAuthorizedException, ResourceNotFoundException {
    183                 // Samples of a Study limited to a single Assay
    184                 try {
    185                         return this.callGSCF(sessionToken, "getSamples", ["assayToken":assayToken])
    186                 } catch( NotAuthorizedException e ) {
    187                         throw new NotAuthorizedException( "User is not authorized to access study for assay with token " + assayToken )
    188                 } catch( ResourceNotFoundException e ) {1
    189                         throw new ResourceNotFoundException( "Assay with token " + assayToken + " not found in GSCF" )
    190                 } // Other exceptions are thrown as they appear
    191 
    192         }
    193 
     170       
    194171        /**
    195172         * Retrieve a single Sample from the GSCF
    196          * 
     173         *
    197174         * @param sessionToken String
    198175         * @param assay Assay
    199          * @param sample Sample 
    200          * 
    201          * @return ArrayList 
     176         * @param sample Sample
     177         *
     178         * @return ArrayList
    202179         */
    203180        public def getSample(String sessionToken, String assayToken, String sampleToken) throws NotAuthorizedException, ResourceNotFoundException {
     
    219196
    220197        }
     198       
     199        /**
     200         * Retrieve a list of Samples from the GSCF
     201         *
     202         * @param sessionToken String
     203         * @param assay Assay (parameter assay is optional, only used when you want to limit the list of samples of an Assay within a Study)
     204         *
     205         * @return ArrayList
     206         */
     207        public ArrayList getSamples(String sessionToken, String assayToken) throws NotAuthorizedException, ResourceNotFoundException {
     208                // Samples of a Study limited to a single Assay
     209                try {
     210                        return this.callGSCF(sessionToken, "getSamples", ["assayToken":assayToken])
     211                } catch( NotAuthorizedException e ) {
     212                        throw new NotAuthorizedException( "User is not authorized to access study for assay with token " + assayToken )
     213                } catch( ResourceNotFoundException e ) {1
     214                        throw new ResourceNotFoundException( "Assay with token " + assayToken + " not found in GSCF" )
     215                } // Other exceptions are thrown as they appear
     216
     217        }
     218
     219        /**
     220         * Retrieve a list of samples from the GSCF
     221         *
     222         * @param sessionToken String
     223         * @param sampleTokens List of sampleTokens
     224         *
     225         * @return ArrayList
     226         */
     227        public def getSamples(String sessionToken, List sampleTokens) throws NotAuthorizedException, ResourceNotFoundException {
     228                def list
     229
     230                try {
     231                        list = this.callGSCF(sessionToken, "getSamples", ["sampleToken": sampleTokens])
     232                } catch( NotAuthorizedException e ) {
     233                        throw new NotAuthorizedException( "User is not authorized to access samples" )
     234                } catch( ResourceNotFoundException e ) {
     235                        throw new ResourceNotFoundException( "Samples with token " + sampleTokens + " not found in GSCF" )
     236                } // Other exceptions are thrown as they appear
     237
     238                // Return only the first element of the list, since we are only interested in one sample
     239                return list
     240        }
     241
    221242
    222243        /**
     
    273294
    274295                // concat all Rest Call specific arguments behind addr
    275                 restParams.each {parameter -> 
     296                restParams.each {parameter ->
    276297                        // If a list is given as value, the parameter should show up multiple times
    277298                        if( parameter.value instanceof Collection ) {
     
    281302                        } else {
    282303                                addr += "&${parameter.key}=" + parameter.value.toString().encodeAsURL()
    283                         } 
     304                        }
    284305                }
    285306
  • trunk/grails-app/services/nl/tno/metagenomics/integration/SynchronizationService.groovy

    r12 r13  
    261261                        // Can't retrieve data. Maybe sessionToken has expired or invalid. Anyway, stop
    262262                        // synchronizing and return null
     263                        e.printStackTrace()
    263264                        log.error( "Exception occurred when fetching study " + study.studyToken + ": " + e.getMessage() )
    264265                        throw new Exception( "Error while fetching study " + study.studyToken, e)
  • trunk/grails-app/views/assay/index.gsp

    r12 r13  
    1212                </g:if>
    1313                <g:else>
    14                         <table class="paginate">
     14                        <form id="assayForm">
     15                        </form>
     16                        <table id="assays" class="paginate">
    1517                                <thead>
    1618                                        <tr>
     19                                                <th class="nonsortable"></th>
    1720                                                <th>Assay</th>
    1821                                                <th>Study</th>
     
    2629                                                        <g:each in="${study.assays}" var="assay">
    2730                                                                <tr>
     31                                                                        <td><g:checkBox name="ids" value="${assay.id}" checked="${false}" /></td>
     32                                                               
    2833                                                                        <td><g:link controller="assay" action="show" id="${assay.id}">${assay.name}</g:link></td>
    2934                                                                        <td><a href="${study.viewUrl()}">${study.name}</a></td>
     
    4348                                </tbody>
    4449                        </table>
     50                        <p class="options">
     51                                <a class="fasta" href="#" onClick="submitPaginatedForm( $( '#assayForm' ), '<g:createLink action="exportAsFasta" />', '#assays', 'Please select an assay to export' ); return false;">Export as fasta</a>
     52                        </p>
     53                       
    4554                </g:else>                       
    4655        </body>
  • trunk/grails-app/views/assay/show.gsp

    r12 r13  
    100100                        </tbody>
    101101                </table>
     102                <p class="options">
     103                        <g:if test="${editable}">
     104                                <a class="editAssociation" onClick="showEnterTagsDialog();" href="#">Edit sample data</a>
     105                        </g:if>
     106                        <g:else>
     107                                <a class="editAssociation disabled" onClick="return false;" href="#">Edit sample data</a>
     108                        </g:else>
     109                        <g:if test="${!editable || assay.runs == null || assay.runs.size() == 0}">
     110                                <a class="addSequences disabled" onClick="return false;" href="#">Add sequence files</a>
     111                        </g:if>
     112                        <g:else>
     113                                <a class="addSequences" onClick="showAddFilesDialog();" href="#">Add sequence files</a>
     114                        </g:else>
     115                </p>
     116
    102117                <g:if test="${editable}">
    103                         <input type="button" value="Edit sample data" onClick="showEnterTagsDialog();">
    104                         <input type="button" value="Add sequence files" onClick="showAddFilesDialog();" <g:if test="${assay.runs == null || assay.runs.size() == 0}">disabled="disabled"</g:if>>
    105118                        <g:render template="enterTagsDialog" model="[assay: assay, sortedAssaySamples: assaySamples]" />
    106119                        <g:render template="addFilesDialog" model="[assay: assay]" />
     
    162175                </table>
    163176        </g:else>
     177        <p class="options">
     178                <g:if test="${editable}">
     179                        <a class="addAssociation" onClick="showAddRunDialog();" href="#">Add run</a>
     180                </g:if>
     181                <g:else>
     182                        <a class="addAssociation disabled" onClick="return false;" href="#">Add run</a>
     183                </g:else>
     184        </p>
     185
    164186        <g:if test="${editable}">
    165                 <input type="button" value="Add run" onClick="showAddRunDialog();">
    166187                <g:render template="addRunDialog" model="[assay: assay]" />
    167188                <div id="editRunDialog" class="dialog"></div>
  • trunk/grails-app/views/layouts/main.gsp

    r7 r13  
    88                <link rel="stylesheet" href="${resource(dir: 'css', file: session.style + '.css')}"/>
    99                <link rel="stylesheet" href="${resource(dir: 'css', file: 'login_panel.css')}"/>
     10                <link rel="stylesheet" href="${resource(dir: 'css', file: 'buttons.css')}"/>
    1011                <link rel="shortcut icon" href="${resource(dir: 'images', file: 'favicon.ico')}" type="image/x-icon"/>
    1112               
     
    2122                </script>
    2223                <script type="text/javascript" src="${resource(dir: 'js', file: 'paginate.js')}"></script>
     24                <script type="text/javascript" src="${resource(dir: 'js', file: 'forms.js')}"></script>
    2325               
    2426                <g:layoutHead/>
  • trunk/grails-app/views/run/index.gsp

    r7 r13  
    2525                </g:if>
    2626                <g:else>
    27                         <table class="paginate">
     27                        <form id="runForm">
     28                        </form>
     29                        <table id="runs" class="paginate">
    2830                                <thead>
    2931                                        <tr>
     32                                                <th class="nonsortable"></th>
    3033                                                <th>Run</th>
    3134                                                <th># samples</th>
     
    3639                                        <g:each in="${runs}" var="run">
    3740                                                <tr>
     41                                                        <td><g:checkBox name="ids" value="${run.id}" checked="${false}" /></td>
    3842                                                        <td><g:link controller="run" action="show" id="${run.id}">${run.name}</g:link></td>
    3943                                                        <td>${run.assaySamples?.size()}</td>
     
    4347                                </tbody>
    4448                        </table>
     49                        <p class="options">
     50                                <a class="add" href="#" onClick="showAddRunDialog(); return false;">Add run</a>
     51                                <a class="fasta" href="#" onClick="submitPaginatedForm( $( '#runForm' ), '<g:createLink action="exportAsFasta" />', '#runs', 'Please select a run to export' ); return false;">Export as fasta</a>
     52                        </p>
    4553                </g:else>
    4654                <g:render template="addRunDialog" />
  • trunk/grails-app/views/run/show.gsp

    r12 r13  
    5656                <label>Machine</label>: ${run.machine}<br />
    5757                <label>Parameters</label>: <g:uploadedFile value="${run.parameterFile}" /><br />
    58                 <a href="#" onClick="showEditRunDialog( ${run.id} ); return false;">Edit run</a>
    5958        </div>
    6059        <div class="blok_data">
     
    7978                <label># files</label>: ${run.numFiles()}<br />
    8079        </div>
     80        <p class="options">
     81                <a class="edit" href="#" onClick="showEditRunDialog( ${run.id} ); return false;">Edit run</a>
     82        </p>
    8183       
    8284        <!-- Samples -->
     
    137139                </table>
    138140                <g:if test="${editable}">
    139                         <% def writableAssaySamples = assaySamples.findAll { it.assay.study.canWrite( session.user ) } %>
     141                        <p class="options">
     142                                <% def writableAssaySamples = assaySamples.findAll { it.assay.study.canWrite( session.user ) } %>
     143                                <a class="addAssociation" onClick="showAddSamplesDialog();" href="#">Add samples</a>
     144
     145                                <g:if test="${writableAssaySamples.size() > 0}">
     146                                        <a class="editAssociation" onClick="showEnterTagsDialog();" href="#">Edit sample data</a>
     147                                </g:if>
     148                                <g:else>
     149                                        <a class="editAssociation disabled" onClick="return false;" href="#">Edit sample data</a>
     150                                </g:else>
     151
     152                                <g:if test="${writableAssaySamples.size() == 0 || !run.assays?.size()}">
     153                                        <a class="addSequences disabled" onClick="return false;" href="#">Add sequence files</a>
     154                                </g:if>
     155                                <g:else>
     156                                        <a class="addSequences" onClick="showAddFilesDialog();" href="#">Add sequence files</a>
     157                                </g:else>
     158
     159                        </p>
    140160                        <g:if test="${writableAssaySamples.size() > 0}">
    141                                 <input type="button" value="Edit sample data" onClick="showEnterTagsDialog();">
    142                                 <input type="button" value="Add sequence files" onClick="showAddFilesDialog();" <g:if test="${run.assays == null || run.assays.size() == 0}">disabled="disabled"</g:if>>
    143161                                <g:render template="enterTagsDialog" model="[run: run, writableAssaySamples: writableAssaySamples]" />
    144162                                <g:render template="addFilesDialog" model="[run: run]" />
    145163                        </g:if>
    146                         <g:else>
    147                                 <input type="button" value="Edit sample data" disabled="disabled">
    148                                 <input type="button" value="Add sequence files" disabled="disabled">
    149                         </g:else>
    150                         <input type="button" value="Add sample" onClick="showAddSamplesDialog();">
     164                       
    151165                        <g:render template="addSamplesDialog" model="[run: run]" />
     166                       
    152167                </g:if>
    153168                <div id="showSampleDialog" class="dialog"></div>
     
    208223        <% def writableAssays = otherAssays.findAll { it.study.canWrite( session.user ) } %>
    209224        <g:if test="${editable}">
     225                <p class="options">
     226                        <g:if test="${writableAssays.size() > 0}">
     227                                <a class="addAssociation" href="#" onClick="showAddAssayDialog();">Add assay</a>
     228                        </g:if>
     229                        <g:else>
     230                                <a class="addAssociation disabled" href="#" onClick="return false;">Add assay</a>
     231                        </g:else>
     232                </p>
    210233                <g:if test="${writableAssays.size() > 0}">
    211                         <input type="button" value="Add assay" onClick="showAddAssayDialog();" />
    212234                        <g:render template="addAssayDialog" model="[run: run, writableAssays: writableAssays]" />
    213235                </g:if>
    214                 <g:else>
    215                         <input type="button" value="Add assay" disabled="disabled" />
    216                 </g:else>
    217236        </g:if>
    218237        <div id="showRunDialog" class="dialog"></div>
Note: See TracChangeset for help on using the changeset viewer.