Changeset 1559 for trunk/grails-app


Ignore:
Timestamp:
Feb 24, 2011, 5:14:08 PM (12 years ago)
Author:
s.h.sikkema@…
Message:

Assay export functionality

Location:
trunk/grails-app
Files:
3 added
3 edited

Legend:

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

    r1455 r1559  
    133133
    134134    /**
     135     * Shows a page where individual fields for the different categories (ie.
     136     * subject data, sampling events... etc.) can be selected for export
     137     *
     138     * @param params.id Assay id
     139     */
     140    def selectFields = {
     141        // receives an assay id
     142        def assay = Assay.get(params.assayId)
     143
     144        // obtain fields for each category
     145        try {
     146
     147            def fieldMap = assayService.collectAssayTemplateFields(assay)
     148           
     149        } catch (Exception e) {
     150
     151            flash.errorMessage = e.message
     152            redirect action: 'selectAssay'
     153
     154        }
     155        def measurementTokens = fieldMap.remove('Module Measurement Data')
     156
     157        flash.fieldMap = fieldMap
     158        flash.measurementTokens = measurementTokens
     159        flash.assayId = params.assayId
     160
     161        [fieldMap: fieldMap, measurementTokens: measurementTokens*.name]
     162    }
     163
     164    /**
    135165     * Exports all assay information as an Excel file.
    136166     *
    137167     * @param params.id Assay id
    138168     */
    139     def exportAssayAsExcel = {
    140 
    141         Assay assay = Assay.get(params.assayId)
     169    def compileExportData = {
     170
     171        def fieldMap = flash.fieldMap
     172        def measurementTokens = flash.measurementTokens
     173
     174        def fieldMapSelection = [:]
     175
     176        fieldMap.eachWithIndex { cat, cat_i ->
     177
     178            if (params."cat_$cat_i" == 'on') {
     179                fieldMapSelection[cat.key] = []
     180
     181                cat.value.eachWithIndex { field, field_i ->
     182
     183                    if (params."cat_${cat_i}_${field_i}" == 'on') {
     184
     185                        fieldMapSelection[cat.key] += field
     186
     187                    }
     188
     189                }
     190
     191                if (fieldMapSelection[cat.key] == []) fieldMapSelection.remove(cat.key)
     192
     193            }
     194
     195        }
     196
     197        def measurementTokensSelection = []
     198
     199        if (params."cat_4" == 'on') {
     200
     201            measurementTokensSelection = params.measurementToken == 'null' ? measurementTokens : [ name: params.measurementToken]
     202
     203        }
     204
     205        Assay assay = Assay.get(flash.assayId)
    142206
    143207        // check if assay exists
    144208        if (!assay) {
    145             flash.errorMessage = "No assay found with id: $params.assayId."
     209
     210            flash.errorMessage = flash.assayId ? "No assay found with id: ${flash.assayId}" : 'Assay has no value (null).'
    146211            redirect action: 'selectAssay'
    147212            return
     213
    148214        }
    149215
    150216        try {
    151217
    152             def assayData = assayService.collectAssayData(assay, grailsApplication.config.modules.metabolomics.url)
    153 
    154             def filename = 'export.xlsx'
    155             response.setHeader("Content-disposition", "attachment;filename=\"${filename}\"")
    156             response.setContentType("application/octet-stream")
    157 
    158             assayService.exportColumnWiseDataToExcelFile(assayData, response.outputStream)
     218            def assayData = assayService.collectAssayData(assay, fieldMapSelection, measurementTokensSelection)
     219
     220            def rowData = assayService.convertColumnToRowStructure(assayData)
     221
     222            flash.rowData = rowData
     223
     224            def assayDataPreview = rowData[0..4].collect{it[0..4]}
     225
     226
     227            [assayDataPreview: assayDataPreview]
    159228
    160229        } catch (Exception e) {
     
    165234        }
    166235    }
     236
     237    def doExport = {
     238
     239        def filename = 'export.xlsx'
     240        response.setHeader("Content-disposition", "attachment;filename=\"${filename}\"")
     241        response.setContentType("application/octet-stream")
     242        try {
     243           
     244            assayService.exportRowWiseDataToExcelFile(flash.rowData, response.outputStream)
     245
     246        } catch (Exception e) {
     247
     248            flash.errorMessage = e.message
     249            redirect action: 'selectAssay'
     250
     251        }
     252
     253
     254    }
    167255}
  • trunk/grails-app/services/dbnp/studycapturing/AssayService.groovy

    r1455 r1559  
    1616import org.apache.poi.xssf.usermodel.XSSFWorkbook
    1717import org.apache.poi.hssf.usermodel.HSSFWorkbook
    18 import grails.converters.JSON
    19 import javax.servlet.http.HttpServletResponse
    2018
    2119class AssayService {
     
    2422    def authenticationService
    2523    def moduleCommunicationService
     24
     25    /**
     26     * Collects the assay field names per category in a map as well as the
     27     * module's measurements.
     28     *
     29     * @param assay the assay for which to collect the fields
     30     * @return a map of categories as keys and field names or measurements as
     31     *  values
     32     */
     33    def collectAssayTemplateFields(assay) throws Exception {
     34
     35        def getUsedTemplateFieldNames = { templateEntities ->
     36
     37            // gather all unique and non null template fields that haves values
     38            templateEntities*.giveFields().flatten().unique().findAll{ field ->
     39
     40                field && templateEntities.any { it.fieldExists(field.name) && it.getFieldValue(field.name) }
     41
     42            }*.name
     43
     44        }
     45
     46        // check whether module is reachable
     47        if (!moduleCommunicationService.isModuleReachable(assay.module.url)) {
     48
     49            throw new Exception('Module is not reachable')
     50
     51        }
     52
     53        def samples = assay.samples
     54
     55        [   'Subject Data' :            getUsedTemplateFieldNames( samples*."parentSubject".unique() ),
     56            'Sampling Event Data' :     getUsedTemplateFieldNames( samples*."parentEvent".unique() ),
     57            'Sample Data' :             getUsedTemplateFieldNames( samples ),
     58            'Event Group' :             ['name'],
     59            'Module Measurement Data':  requestModuleMeasurementNames(assay)
     60        ]
     61
     62    }
    2663
    2764    /**
     
    3471     *
    3572     * @param assay the assay to collect data for
    36      * @consumer the module url
     73     * @fieldMap map with categories as keys and fields as values
     74     * @measurementTokens selection of measurementTokens
    3775     * @return The assay data structure as described above.
    3876     */
    39     def collectAssayData(assay, consumer) throws Exception {
    40 
    41         def path = "/rest/getMeasurementData?assayToken=$assay.assayUUID"
    42 
    43         // check whether module is reachable
    44         if (!moduleCommunicationService.isModuleReachable(consumer)) {
    45 
    46             throw new Exception('Module is not reachable')
    47 
    48         }
    49 
    50         // Gather sample meta data from GSCF
    51         def samples = assay.samples
    52 
    53         def getUsedTemplateFieldNames = { templateEntities ->
    54 
    55             // gather all unique and non null template fields that haves values
    56             templateEntities*.giveFields().flatten().unique().findAll{ field ->
    57 
    58                 field && templateEntities.any { it.fieldExists(field.name) && it.getFieldValue(field.name) }
    59 
    60             }*.name
    61 
    62         }
     77    def collectAssayData(assay, fieldMap, measurementTokens) throws Exception {
    6378
    6479        def collectFieldValuesForTemplateEntities = { templateFieldNames, templateEntities ->
     
    7893        }
    7994
    80         def getFieldValues = { templateEntities, propertyName = '' ->
     95        def getFieldValues = { templateEntities, fieldNames, propertyName = '' ->
    8196
    8297            def returnValue
     
    86101            if (propertyName == '') {
    87102
    88                 returnValue = collectFieldValuesForTemplateEntities(getUsedTemplateFieldNames(templateEntities), templateEntities)
     103                returnValue = collectFieldValuesForTemplateEntities(fieldNames, templateEntities)
    89104
    90105            } else {
     
    96111                // samples, there may be less than 10 parent subjects. Maybe
    97112                // there's only 1 parent subject. We don't want to collect field
    98                 // values for this subject 10 times ...
    99                 def fieldNames, fieldValues
     113                // values for this single subject 10 times ...
     114                def fieldValues
    100115
    101116                // we'll get the unique list of properties to make sure we're
     
    104119                def uniqueProperties = templateEntities*."$propertyName".unique()
    105120
    106                 fieldNames =    getUsedTemplateFieldNames(uniqueProperties)
    107                 fieldValues =   collectFieldValuesForTemplateEntities(fieldNames, uniqueProperties)
     121                fieldValues = collectFieldValuesForTemplateEntities(fieldNames, uniqueProperties)
    108122
    109123                // prepare a lookup hashMap to be able to map an entities'
     
    137151        }
    138152
    139         [   'Subject Data' :            getFieldValues(samples, 'parentSubject'),
    140             'Sampling Event Data' :     getFieldValues(samples, 'parentEvent'),
    141             'Sample Data' :             getFieldValues(samples),
    142             'Event Group' :             [name: samples*.parentEventGroup*.name.flatten()],
    143             'Module Measurement Data':  requestModuleMeasurements(consumer, path)]
     153        // check whether module is reachable
     154        if (!moduleCommunicationService.isModuleReachable(assay.module.url)) {
     155
     156            throw new Exception('Module is not reachable')
     157
     158        }
     159
     160        def samples = assay.samples
     161
     162        def eventFieldMap = [:]
     163
     164        // check whether event group data was requested
     165        if (fieldMap['Event Group']) {
     166
     167            def names = samples*.parentEventGroup*.name.flatten()
     168
     169            // only set name field when there's actual data
     170            if (!names.every {!it}) eventFieldMap['name'] = names
     171
     172        }
     173
     174        [   'Subject Data' :            getFieldValues(samples, fieldMap['Subject Data'], 'parentSubject'),
     175            'Sampling Event Data' :     getFieldValues(samples, fieldMap['Sampling Event Data'], 'parentEvent'),
     176            'Sample Data' :             getFieldValues(samples, fieldMap['Sample Data']),
     177            'Event Group' :             eventFieldMap,
     178            'Module Measurement Data':  measurementTokens ? requestModuleMeasurements(assay, measurementTokens) : [:]
     179        ]
     180    }
     181
     182    /**
     183     * Retrieves measurement names from the module through a rest call
     184     *
     185     * @param consumer the url of the module
     186     * @param path path of the rest call to the module
     187     * @return
     188     */
     189    def requestModuleMeasurementNames(assay) {
     190
     191        def moduleUrl = assay.module.url
     192
     193        def path = moduleUrl + "/rest/getMeasurementMetaData?assayToken=$assay.assayUUID"
     194
     195        moduleCommunicationService.callModuleRestMethodJSON(moduleUrl, path)
     196
    144197    }
    145198
     
    148201     *
    149202     * @param consumer the url of the module
    150      * @param path of the rest call to the module
     203     * @param path path of the rest call to the module
    151204     * @return
    152205     */
    153     def requestModuleMeasurements(consumer, path) {
    154 
    155         def (sampleTokens, measurementTokens, moduleData) = moduleCommunicationService.callModuleRestMethodJSON(consumer, consumer+path)
     206    def requestModuleMeasurements(assay, fields) {
     207
     208        def moduleUrl = assay.module.url
     209
     210        def tokenString = ''
     211
     212        fields.each{tokenString+="&measurementToken=${it.name.encodeAsURL()}"}
     213
     214        def path = moduleUrl + "/rest/getMeasurementData?assayToken=$assay.assayUUID" + tokenString
     215       
     216        def (sampleTokens, measurementTokens, moduleData) = moduleCommunicationService.callModuleRestMethodJSON(moduleUrl, path)
    156217
    157218        if (!sampleTokens?.size()) return []
     
    172233
    173234    /**
    174      * Export column wise data in Excel format to a stream.
    175      *
     235     * Converts column
    176236     * @param columnData multidimensional map containing column data.
    177237     * On the top level, the data must be grouped by category. Each key is the
     
    194254     * | 3          | 6         | 9         | 12        | 15        |
    195255     *
    196      * @param outputStream the stream to write to
    197      * @param useOfficeOpenXML flag to specify xlsx (standard) or xls output
    198      * @return
    199      */
    200     def exportColumnWiseDataToExcelFile(columnData, outputStream, useOfficeOpenXML = true) {
    201 
    202         def convertColumnToRowStructure = { data ->
     256     * @return row wise data
     257     */
     258    def convertColumnToRowStructure(columnData) {
    203259
    204260            // check if all columns have the dimensionality 2
    205             if (data.every { it.value.every { it.value instanceof ArrayList } }) {
     261            if (columnData.every { it.value.every { it.value instanceof ArrayList } }) {
    206262
    207263                def headers = [[],[]]
    208264
    209                 data.each { category ->
     265                columnData.each { category ->
    210266
    211267                    if (category.value.size()) {
     
    225281
    226282                // add all column wise data into 'd'
    227                 data.each { it.value.each { d << it.value } }
     283                columnData.each { it.value.each { d << it.value } }
    228284
    229285                // transpose d into row wise data and combine with header rows
     
    232288
    233289        }
     290
     291    /**
     292     * Export column wise data in Excel format to a stream.
     293     *
     294     * @param columnData Multidimensional map containing column data
     295     * @param outputStream Stream to write to
     296     * @param useOfficeOpenXML Flag to specify xlsx (standard) or xls output
     297     * @return
     298     */
     299    def exportColumnWiseDataToExcelFile(columnData, outputStream, useOfficeOpenXML = true) {
     300
    234301        // transform data into row based structure for easy writing
    235302        def rows = convertColumnToRowStructure(columnData)
     
    250317     * Export row wise data in Excel format to a stream
    251318     *
    252      * @param rowData a list of lists containing for each row all cell values
    253      * @param outputStream the stream to write to
    254      * @param useOfficeOpenXML flag to specify xlsx (standard) or xls output
     319     * @param rowData List of lists containing for each row all cell values
     320     * @param outputStream Stream to write to
     321     * @param useOfficeOpenXML Flag to specify xlsx (standard) or xls output
    255322     * @return
    256323     */
  • trunk/grails-app/views/assay/selectAssay.gsp

    r1445 r1559  
    1515        var a = eval(jsonData);
    1616        var sel = $('#'+selectID).empty();
    17        
     17
    1818        $(a).each(function(i, el){
    1919          sel.append($("<option></option>").attr("value",el.id).text(el.name))
     
    3333    ${flash.errorMessage}
    3434  </div>
    35   <g:form name="assaySelect" action="exportAssayAsExcel">
     35  %{--<g:form name="assaySelect" action="exportAssayAsExcel">--}%
     36  <g:form name="assaySelect" action="selectFields">
    3637    <g:select optionKey="id" optionValue="title" name="studyId" from="${userStudies}" id="study"
    3738      onChange="${remoteFunction(controller:'study',action:'ajaxGetAssays',params:'\'id=\'+escape(this.value)',onComplete: 'updateAssay(XMLHttpRequest.responseText, \'assay\')')}"/>
Note: See TracChangeset for help on using the changeset viewer.