Changeset 2156


Ignore:
Timestamp:
Jan 30, 2012, 3:41:51 PM (10 years ago)
Author:
m.s.vanvliet@…
Message:

Added support for exporting meta-data from a module.
When meta-data is present it will restructure the export format to include the measurement meta-data (including the properties)

Location:
trunk/grails-app
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/grails-app/controllers/dbnp/studycapturing/AssayController.groovy

    r2120 r2156  
    5959                                // interpret the params set and gather the data
    6060                                flow.rowData = collectAssayData(flow.assay, fieldMapSelection, measurementTokens, flow.assay.samples)
     61                               
     62                                // save the measurementTokes to the session for use later
     63                                session.measurementTokens = measurementTokens
    6164
    6265                                // remember the selected file type
     
    101104                compileExportData {
    102105                        on ("ok"){
    103 
     106                                session.assay = flow.assay
    104107                                session.rowData = flow.rowData
    105108                                session.exportFileType = flow.exportFileType
     
    168171                def measurementTokens = JSON.parse((String) params.measurementTokens)
    169172
    170                 println fieldMapSelection
    171                 println measurementTokens
    172 
    173173                // Check accessibility
    174174                def consumer = "galaxy"
    175                 def remoteUser = authenticationService.getRemotelyLoggedInUser( consumer, params.sessionToken )
     175                def remoteUser = authenticationService.getRemotelyLoggedInUsergetLoggedInUser( consumer, params.sessionToken )
    176176                if( !remoteUser ) {
    177177                        response.status = 401
     
    218218                if (!(session.rowData && session.exportFileType))
    219219                        redirect(action: 'assayExportFlow')
     220                       
     221                def remoteUser = authenticationService.getLoggedInUser()
     222                if( !remoteUser ) {
     223                        response.status = 401
     224                        render "You must be logged in"
     225                        return
     226                }
    220227
    221228                // process requested output file type
     
    241248                response.setContentType("application/octet-stream")
    242249                try {
    243 
     250                       
     251                        //merge data with metadata if possible
     252                        def metadata = assayService.requestModuleMeasurementMetaDatas(session.assay, session.measurementTokens, remoteUser) ?: null
     253                        session.rowData = assayService.mergeModuleDataWithMetadata(session.rowData, metadata)
     254                               
    244255                        assayService.exportRowWiseDataToCSVFile(session.rowData, response.outputStream, outputDelimiter, locale)
    245256
    246257                        // clear the data from the session
    247258                        session.removeAttribute('rowData')
     259                        session.removeAttribute('measurementTokens')
    248260                        session.removeAttribute('exportFileType')
    249261
  • trunk/grails-app/services/dbnp/studycapturing/AssayService.groovy

    r2120 r2156  
    6363                                        'Sampling Event Data' :     getUsedTemplateFields( samples*."parentEvent".unique() ),
    6464                                        'Sample Data' :             getUsedTemplateFields( samples ),
    65                                         'Event Group' :             [[name: 'name', comment: 'Name of Event Group', displayName: 'name']],
     65                                        'Event Group' :             [
     66                                                [name: 'name', comment: 'Name of Event Group', displayName: 'name']
     67                                        ],
    6668                                        'Module Measurement Data':  moduleMeasurements,
    6769                                        'Module Error':             moduleError
     
    186188                }
    187189
    188                 def moduleError = '', moduleMeasurementData = [:]
     190                def moduleError = '', moduleMeasurementData = [:], moduleMeasurementMetaData = [:]
    189191
    190192                if (measurementTokens) {
    191193
    192194                        try {
    193                                 moduleMeasurementData = requestModuleMeasurements(assay, measurementTokens, samples, remoteUser)
     195                                moduleMeasurementData           = requestModuleMeasurements(assay, measurementTokens, samples, remoteUser)
    194196                        } catch (e) {
    195                                 moduleMeasurementData = ['error' : ['Module error, module not available or unknown assay'] * samples.size() ]
     197                                moduleMeasurementData = ['error' : [
     198                                                'Module error, module not available or unknown assay']
     199                                        * samples.size() ]
    196200                                e.printStackTrace()
    197201                                moduleError =  e.message
    198202                        }
    199 
    200                 }
    201 
    202                 [   'Subject Data' :            getFieldValues(samples, fieldMap['Subject Data'], 'parentSubject'),
    203                         'Sampling Event Data' :     getFieldValues(samples, fieldMap['Sampling Event Data'], 'parentEvent'),
    204                         'Sample Data' :             getFieldValues(samples, fieldMap['Sample Data']),
    205                         'Event Group' :             eventFieldMap,
    206                         'Module Measurement Data' : moduleMeasurementData,
    207                         'Module Error' :            moduleError
    208                 ]
     203                }
     204
     205                [   'Subject Data' :                            getFieldValues(samples, fieldMap['Subject Data'], 'parentSubject'),
     206                                        'Sampling Event Data' :                 getFieldValues(samples, fieldMap['Sampling Event Data'], 'parentEvent'),
     207                                        'Sample Data' :                         getFieldValues(samples, fieldMap['Sample Data']),
     208                                        'Event Group' :                         eventFieldMap,
     209                                        'Module Measurement Data' :             moduleMeasurementData,
     210                                        'Module Error' :                        moduleError
     211                                ]
    209212        }
    210213
     
    225228                        def value = assay.parent.getFieldValue( it.name )
    226229                        if( value )
    227                                 studyData[ it.name ] = [value] * numValues
     230                                studyData[ it.name ] = [value]* numValues
    228231                }
    229232
     
    249252                        def value = assay.getFieldValue( it.name )
    250253                        if( value )
    251                                 assayData[ it.name ] = [value] * numValues
     254                                assayData[ it.name ] = [value]* numValues
    252255                }
    253256
     
    304307                def tokenString = ''
    305308
    306                 inputMeasurementTokens.each{
    307                         tokenString+="&measurementToken=${it.encodeAsURL()}"
    308                 }
    309 
     309                inputMeasurementTokens.each{ tokenString+="&measurementToken=${it.encodeAsURL()}" }
     310
     311                /* Contact module to fetch measurement data */
    310312                def path = moduleUrl + "/rest/getMeasurementData/query"
    311 
    312313                def query = "assayToken=$assay.assayUUID$tokenString"
    313314
    314315                if (samples) {
    315                          query += '&' + samples*.sampleUUID.collect { "sampleToken=$it" }.join('&')
     316                        query += '&' + samples*.sampleUUID.collect { "sampleToken=$it" }.join('&')
    316317                }
    317318
     
    373374
    374375                return map;
     376        }
     377
     378        /**
     379         * Retrieves module measurement meta data through a rest call to the module
     380         *
     381         * @param assay                         Assay for which the module measurements should be retrieved
     382         * @param measurementTokens     List with the names of the fields to be retrieved. Format: [ 'measurementName1', 'measurementName2' ]
     383         * @return
     384         */
     385        def requestModuleMeasurementMetaDatas(assay, inputMeasurementTokens, SecUser remoteUser = null) {
     386
     387                def measurementTokenMetaData = [:]
     388
     389                def moduleUrl = assay.module.url
     390
     391                def tokenString = ''
     392                inputMeasurementTokens.each{ tokenString+="&measurementToken=${it.encodeAsURL()}" }
     393
     394                def pathMeasurementMetaData = moduleUrl + "/rest/getMeasurementMetaData/"
     395                def queryMeasurementMetaData = "assayToken=$assay.assayUUID$tokenString"
     396
     397                try {
     398                        moduleCommunicationService.callModuleMethod(moduleUrl, pathMeasurementMetaData, queryMeasurementMetaData, "POST", remoteUser).each { metaDataArray ->
     399                                if (metaDataArray['name']){
     400                                        measurementTokenMetaData[metaDataArray['name']] = metaDataArray //convert list to an associative array
     401                                }
     402                        }
     403                } catch (e) {
     404                        e.printStackTrace()
     405                        throw new Exception("An error occured while trying to get the measurement meta data from the $assay.module.name. \
     406                        This means the module containing the measurement data is not available right now. Please try again \
     407                        later or notify the system administrator if the problem persists. URL: $path?$query.")
     408                }
     409
     410                return measurementTokenMetaData
     411        }
     412
     413        /**
     414         * Injects meta-data(s) into rowData before creating a CSV or tab
     415         */
     416        def mergeModuleDataWithMetadata(data, metadata = null){
     417
     418                if (metadata == null){
     419                        return data
     420                }
     421
     422                //check if there is meta-data available for the features in the data
     423                if (!(data[1].intersect(metadata.keySet()))){
     424                        return data
     425                }
     426               
     427                //find out where the measurements start in the data
     428                def addLabelsAtColumnPosition = null
     429                data[0].eachWithIndex { cat, i ->
     430                        if (cat == 'Module Measurement Data'){
     431                                addLabelsAtColumnPosition = i
     432                        }
     433                }
     434
     435                //check if we have a position to inject the labels
     436                if (addLabelsAtColumnPosition == null){
     437                        return data //returning data as we were unable to find any measurements. Or at least where the start.
     438                }
     439
     440                //find out all (unique) feature properties
     441                def featureProperties = []
     442                metadata.values().each { featureProperties += it.keySet() }
     443                featureProperties = featureProperties.unique()
     444
     445                //prepare the additional rows with meta-data
     446                def additionalRows = []
     447                featureProperties.each { mdfp ->
     448                        def addRow = []
     449                        (1..addLabelsAtColumnPosition).each {
     450                                //add some empty fields before the labels of the meta-data properties
     451                                addRow.add(null)
     452                        }
     453                        addRow.add(mdfp)
     454                        data[1].eachWithIndex { feature, i ->
     455                                if (i >= addLabelsAtColumnPosition) { addRow.add(metadata[feature][mdfp]) }
     456                        }
     457                        additionalRows.add(addRow)
     458                }
     459
     460                //add the additional row and add a null to the feature label column in the data rows
     461                if (additionalRows){
     462                        def tempData = []
     463                        data.eachWithIndex { row, iRow ->
     464                               
     465                                //this is an existing row (not meta-data), so we add a null under the feature label column
     466                                def tempR = []
     467                                row.eachWithIndex { rowElement, iRowElement ->
     468                                        if ((iRow != 0) && (iRowElement == addLabelsAtColumnPosition)){
     469                                                tempR.add(null) //add an empty element under the meta-data label
     470                                        }
     471                                        tempR.add(rowElement)
     472                                }
     473                                tempData.add(tempR)
     474
     475                                //as the additional rows have already been formatted in the correct way we only have to add them under the row that holds the categories
     476                                if (iRow == 1){
     477                                        additionalRows.each { additionalRow ->
     478                                                tempData.add(additionalRow)
     479                                        }
     480                                }
     481                        }
     482                        //overwrite data with the tempRowData
     483                        data = tempData
     484                }
     485
     486                return data
    375487        }
    376488
     
    457569                                        } else {
    458570                                                // Append empty string for each entity if the field doesn't exist
    459                                                 categoryValues[ field ] += [""] * numValues[ idx ]
     571                                                categoryValues[ field ] += [""]* numValues[ idx ]
    460572                                        }
    461573                                }
     
    519631                // A map with keys being the sampleIds, and the values are the indices of that sample in the values list
    520632                def idMap = [:]
    521                
     633
    522634                // A map with the key being an index in the value list, and the value is the index the values should be copied to
    523635                def convertMap = [:]
     
    534646                        }
    535647                }
    536                
     648
    537649                /*
    538650                 * Example output:
     
    544656                 * loop through the values and remove the row that has been merged.
    545657                 */
    546                
    547                 convertMap.sort { a, b -> b.key <=> a.key }.each { 
     658
     659                convertMap.sort { a, b -> b.key <=> a.key }.each {
    548660                        def row = it.key;
    549661                        def mergeWith = it.value;
    550                        
     662
    551663                        if( row != mergeWith ) {
    552664                                // Combine the data on row [row] with the data on row [mergeWith]
    553                                
    554                                 mergedData.each { 
     665
     666                                mergedData.each {
    555667                                        def cat = it.key; def fields = it.value;
    556668                                        fields.each { fieldData ->
    557                                                 def fieldName = fieldData.key; 
     669                                                def fieldName = fieldData.key;
    558670                                                def fieldValues = fieldData.value;
    559                                                
     671
    560672                                                // If one of the fields to merge is empty, use the other one
    561673                                                // Otherwise the values should be the same (e.g. study, subject, sample data)
    562674                                                fieldValues[ mergeWith ] = ( fieldValues[ mergeWith ] == null || fieldValues[ mergeWith ] == "" ) ? fieldValues[ row ] : fieldValues[ mergeWith ]
    563                                                
     675
    564676                                                // Remove the row from this list
    565677                                                fieldValues.remove( row );
     
    568680                        }
    569681                }
    570                
     682
    571683                // Remove sample id if required
    572684                if( removeIdColumn )
    573685                        mergedData[ "Sample Data" ].remove( "id" );
    574                
     686
    575687                return mergedData
    576688        }
     
    605717                if (columnData.every { it.value.every { it.value instanceof ArrayList } }) {
    606718
    607                         def headers = [[],[]]
     719                        def headers = [[], []]
    608720
    609721                        columnData.each { category ->
     
    613725                                        // put category keys into first row separated by null values
    614726                                        // wherever there are > 1 columns per category
    615                                         headers[0] += [category.key] + [null] * (category.value.size() - 1)
     727                                        headers[0] += [category.key]+ [null]* (category.value.size() - 1)
    616728
    617729                                        // put non-category column headers into 2nd row
     
    691803
    692804                outputStream << rowData.collect { row ->
     805
    693806                        row.collect{
    694807
     
    761874                                        def value = rowData[ri][ci]
    762875
    763                                         value = (value instanceof Number | value?.class in [boolean.class, String.class, Date.class]) ? value : value?.toString()
     876                                        value = (value instanceof Number | value?.class in [
     877                                                boolean.class,
     878                                                String.class,
     879                                                Date.class
     880                                        ]) ? value : value?.toString()
    764881
    765882                                        // write the value (or an empty String if null) to the cell
Note: See TracChangeset for help on using the changeset viewer.