Changeset 1864

Show
Ignore:
Timestamp:
23-05-11 16:36:00 (3 years ago)
Author:
s.h.sikkema@…
Message:

in exporter: module error display improvements (ie. display on screen but still able to export); removed changes to simplewizard; removed unnecessary imports; should handle 'null' parent subject from samples correctly; should handle non number values from modules correctly; deselects module measurements in case of module error/no measurements; removed obsolete entry in topnav

Location:
trunk/grails-app
Files:
9 modified

Legend:

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

    r1830 r1864  
    139139                                flow.fieldMap = assayService.collectAssayTemplateFields(flow.assay) 
    140140 
     141                flash.errorMessage = flow.fieldMap.remove('ModuleError') 
    141142                                flow.measurementTokens = flow.fieldMap.remove('Module Measurement Data') 
    142143                        }.to "selectFields" 
     
    147148                selectFields { 
    148149                        on ("submit"){ 
     150                 
    149151                                def fieldMapSelection = [:] 
    150152 
     
    173175                // collect the assay data according to user selecting 
    174176                                def assayData           = assayService.collectAssayData(flow.assay, fieldMapSelection, measurementTokens) 
     177 
     178                flash.errorMessage      = assayData.remove('ModuleError') 
     179 
    175180                                flow.rowData            = assayService.convertColumnToRowStructure(assayData) 
    176181 
  • trunk/grails-app/controllers/dbnp/studycapturing/SimpleWizardController.groovy

    r1838 r1864  
    3737         */ 
    3838        def index = { 
    39 //              if( params.id ) 
    40 //                      redirect( action: "simpleWizard", id: params.id ); 
    41 //              else 
    42 //                      redirect( action: "simpleWizard" ); 
    4339        redirect action: 'simpleWizard', params: params 
    4440        } 
     
    5046                                if (!flow.study) retrievalError() 
    5147 
    52                 flow.inferDesign = params.inferDesign 
    5348                                // Search for studies 
    5449                                flow.studies = Study.giveWritableStudies( authenticationService.getLoggedInUser(), 100 ) 
     
    824819        def errors = []; 
    825820 
    826         if (flow.inferDesign) { 
    827  
    828             println 'Entered infer design...' 
    829  
    830             // find the indices of the classes of interest in the records 
    831             def sampleIdx           = table[0].findIndexOf{it.class.name == 'dbnp.studycapturing.Sample'} 
    832             def samplingEventIdx    = table[0].findIndexOf{it.class.name == 'dbnp.studycapturing.SamplingEvent'} 
    833             def subjectIdx          = table[0].findIndexOf{it.class.name == 'dbnp.studycapturing.Subject'} 
    834  
    835             // Check for duplicate samples 
    836             def samples = table.collect{it[sampleIdx]} 
    837  
    838             def uniques     = [] as Set 
    839             def duplicates  = [] as Set 
    840  
    841             // this approach separates the unique from the duplicate entries 
    842             samples*.name.each { 
    843                 uniques.add(it) || duplicates.add(it) 
    844             } 
    845  
    846             duplicates.each{ duplicateName -> 
    847                 samples.findAll{it.name == duplicateName}.each{ sample -> 
    848                     numInvalidEntities++ 
    849                     failedcells = addNonValidatingCells(failedcells, sample, flow) 
    850                     errors += "(Sample) duplicate name: $duplicateName" 
    851                 } 
    852             } 
    853  
    854             // A closure that returns a sub list of entities from a list that have 
    855             // unique values of a property indicated by propertyName 
    856             def uniqueEntitiesByProperty = { entities, propertyName -> 
    857  
    858                entities*."$propertyName".unique().collect { uniquePropertyValue -> 
    859  
    860                     entities.find{ it."$propertyName" == uniquePropertyValue } 
    861  
    862                 } 
    863             } 
    864  
    865             def addToCollectionIfNonexistent = { parent, collectionName, entity, propertyName -> 
    866  
    867                 if (!parent[collectionName].find{it[propertyName] == entity[propertyName]}) 
    868                     parent."addTo${collectionName.capitalize()}" entity 
    869  
    870             } 
    871  
    872             // collect unique subjects and sampling events from table 
    873             def uniqueSubjects = 
    874                 uniqueEntitiesByProperty(table.collect{it[subjectIdx]}, 'name') 
    875             uniqueSubjects.each{ 
    876                 addToCollectionIfNonexistent study, 'subjects', it, 'name' 
    877                 it.species = Term.findByName('Homo sapiens') 
    878             } 
    879  
    880             def uniqueSamplingEvents = 
    881                 uniqueEntitiesByProperty(table.collect{it[samplingEventIdx]}, 'startTime') 
    882             uniqueSamplingEvents.each{ 
    883                 it.setFieldValue( 'sampleTemplate', flow.sampleForm.template.Sample.name ) 
    884             } 
    885  
    886             // create an event group for each unique sampling event (not much of a group, is it ...) 
    887             def eventGroups = uniqueSamplingEvents.collect{ 
    888  
    889                 def eventGroupName = "Sampling_${it.sampleTemplate.name}_${new RelTime(it.startTime).toString()}" 
    890  
    891                 def eventGroup = study.eventGroups.find{it.name == eventGroupName} ?: //EventGroup.findByParentAndName(study, eventGroupName) ?: 
    892                     new EventGroup(name: eventGroupName) 
    893  
    894                 eventGroup.addToSamplingEvents it 
    895  
    896                 if (!study.eventGroups.find{it == eventGroup}) 
    897                     study.addToEventGroups eventGroup 
    898  
    899                 if (!it.parent) study.addToSamplingEvents it 
    900  
    901                 println eventGroup.name 
    902  
    903                 eventGroup 
    904  
    905             } 
    906  
    907             table.each{ record -> 
    908  
    909                 Sample sample = record[sampleIdx] 
    910  
    911                 // gather all sample related entities 
    912                 def correspondingSamplingEvent  = uniqueSamplingEvents.find {it.startTime == record[samplingEventIdx].startTime} 
    913                 def correspondingSubject        = uniqueSubjects.find{it.name == record[subjectIdx].name} 
    914                 def correspondingEventGroup     = eventGroups.find{correspondingSamplingEvent in it.samplingEvents} 
    915  
    916                 addToCollectionIfNonexistent correspondingSamplingEvent, 'samples', sample, 'name' 
    917  
    918                 sample.parentSubject = correspondingSubject 
    919  
    920                 correspondingEventGroup.addToSamplingEvents correspondingSamplingEvent 
    921  
    922                 if (!correspondingEventGroup.subjects.find{it.name == correspondingSubject.name}) 
    923                     correspondingEventGroup.addToSubjects correspondingSubject 
    924  
    925                 addToCollectionIfNonexistent study, 'samples', sample, 'name' 
    926  
    927             } 
    928  
    929         } else { 
    930             // Add all samples 
    931             table.each { record -> 
    932                 record.each { entity -> 
    933                     if( entity ) { 
    934                         // Determine entity class and add a parent. Add the entity to the study 
    935                         def preferredIdentifier = importerService.givePreferredIdentifier( entity.class ); 
    936                         def equalClosure = { it.getIdentifier() == entity.getIdentifier() } 
    937                         def entityName = entity.class.name[ entity.class.name.lastIndexOf( "." ) + 1 .. -1 ] 
    938  
    939                         entity.parent = study 
    940  
    941                         switch( entity.class ) { 
    942                             case Sample: 
    943                                 if( !study.samples?.find( equalClosure ) ) { 
    944                                     study.addToSamples( entity ); 
     821        // Add all samples 
     822        table.each { record -> 
     823            record.each { entity -> 
     824                if( entity ) { 
     825                    // Determine entity class and add a parent. Add the entity to the study 
     826                    def preferredIdentifier = importerService.givePreferredIdentifier( entity.class ); 
     827                    def equalClosure = { it.getIdentifier() == entity.getIdentifier() } 
     828                    def entityName = entity.class.name[ entity.class.name.lastIndexOf( "." ) + 1 .. -1 ] 
     829 
     830                    entity.parent = study 
     831 
     832                    switch( entity.class ) { 
     833                        case Sample: 
     834                            if( !study.samples?.find( equalClosure ) ) { 
     835                                study.addToSamples( entity ); 
     836                            } 
     837 
     838                            // If an eventgroup is created, add it to the study 
     839                            // The eventgroup must have a unique name, but the user shouldn't be bothered with it 
     840                            // Add 'group ' + samplename and it that is not unique, add a number to it 
     841                            if( entity.parentEventGroup ) { 
     842                                study.addToEventGroups( entity.parentEventGroup ) 
     843 
     844                                entity.parentEventGroup.name = "Group " + entity.name 
     845                                while( !entity.parentEventGroup.validate() ) { 
     846                                    //entity.parentEventGroup.getErrors().each { println it } 
     847                                    entity.parentEventGroup.name += "" + Math.floor( Math.random() * 100 ) 
    945848                                } 
    946  
    947                                 // If an eventgroup is created, add it to the study 
    948                                 // The eventgroup must have a unique name, but the user shouldn't be bothered with it 
    949                                 // Add 'group ' + samplename and it that is not unique, add a number to it 
    950                                 if( entity.parentEventGroup ) { 
    951                                     study.addToEventGroups( entity.parentEventGroup ) 
    952  
    953                                     entity.parentEventGroup.name = "Group " + entity.name 
    954                                     while( !entity.parentEventGroup.validate() ) { 
    955                                         //entity.parentEventGroup.getErrors().each { println it } 
    956                                         entity.parentEventGroup.name += "" + Math.floor( Math.random() * 100 ) 
     849                            } 
     850 
     851                            break; 
     852                        case Subject: 
     853                            if( !study.samples?.find( equalClosure ) ) { 
     854 
     855                                if( preferredIdentifier ) { 
     856                                    // Subjects without a name should just be called 'subject' 
     857                                    if( !entity.getFieldValue( preferredIdentifier.name ) ) 
     858                                        entity.setFieldValue( preferredIdentifier.name, "Subject" ); 
     859 
     860                                    // Subjects should have unique names; if the user has entered the same name multiple times, 
     861                                    // the subject will be renamed 
     862                                    def baseName = entity.getFieldValue( preferredIdentifier.name ) 
     863                                    def counter = 2; 
     864 
     865                                    while( study.subjects?.find { it.getFieldValue( preferredIdentifier.name ) == entity.getFieldValue( preferredIdentifier.name ) } ) { 
     866                                        entity.setFieldValue( preferredIdentifier.name, baseName + " (" + counter++ + ")" ) 
    957867                                    } 
    958868                                } 
    959869 
    960                                 break; 
    961                             case Subject: 
    962                                 if( !study.samples?.find( equalClosure ) ) { 
    963  
    964                                     if( preferredIdentifier ) { 
    965                                         // Subjects without a name should just be called 'subject' 
    966                                         if( !entity.getFieldValue( preferredIdentifier.name ) ) 
    967                                             entity.setFieldValue( preferredIdentifier.name, "Subject" ); 
    968  
    969                                         // Subjects should have unique names; if the user has entered the same name multiple times, 
    970                                         // the subject will be renamed 
    971                                         def baseName = entity.getFieldValue( preferredIdentifier.name ) 
    972                                         def counter = 2; 
    973  
    974                                         while( study.subjects?.find { it.getFieldValue( preferredIdentifier.name ) == entity.getFieldValue( preferredIdentifier.name ) } ) { 
    975                                             entity.setFieldValue( preferredIdentifier.name, baseName + " (" + counter++ + ")" ) 
    976                                         } 
    977                                     } 
    978  
    979                                     study.addToSubjects( entity ); 
    980  
    981                                 } 
    982  
    983                                 break; 
    984                             case Event: 
    985                                 if( !study.events?.find( equalClosure ) ) { 
    986                                     study.addToEvents( entity ); 
    987                                 } 
    988                                 break; 
    989                             case SamplingEvent: 
    990                                 // Sampling events have a 'sampleTemplate' value, which should be filled by the 
    991                                 // template that is chosen for samples. 
    992                                 if( !entity.getFieldValue( 'sampleTemplate' ) ) { 
    993                                     entity.setFieldValue( 'sampleTemplate', flow.sampleForm.template.Sample.name ) 
    994                                 } 
    995  
    996                                 if( !study.samplingEvents?.find( equalClosure ) ) { 
    997                                     study.addToSamplingEvents( entity ); 
    998                                 } 
    999                                 break; 
    1000                         } 
    1001  
    1002                         if (!entity.validate()) { 
    1003                             numInvalidEntities++; 
    1004  
    1005                             // Add this field to the list of failed cells, in order to give the user feedback 
    1006                             failedcells = addNonValidatingCells( failedcells, entity, flow ) 
    1007  
    1008                             // Also create a full list of errors 
    1009                             def currentErrors = getHumanReadableErrors( entity ) 
    1010                             if( currentErrors ) { 
    1011                                 currentErrors.each { 
    1012                                     errors += "(" + entityName + ") " + it.value; 
    1013                                 } 
     870                                study.addToSubjects( entity ); 
     871 
     872                            } 
     873 
     874                            break; 
     875                        case Event: 
     876                            if( !study.events?.find( equalClosure ) ) { 
     877                                study.addToEvents( entity ); 
     878                            } 
     879                            break; 
     880                        case SamplingEvent: 
     881                            // Sampling events have a 'sampleTemplate' value, which should be filled by the 
     882                            // template that is chosen for samples. 
     883                            if( !entity.getFieldValue( 'sampleTemplate' ) ) { 
     884                                entity.setFieldValue( 'sampleTemplate', flow.sampleForm.template.Sample.name ) 
     885                            } 
     886 
     887                            if( !study.samplingEvents?.find( equalClosure ) ) { 
     888                                study.addToSamplingEvents( entity ); 
     889                            } 
     890                            break; 
     891                    } 
     892 
     893                    if (!entity.validate()) { 
     894                        numInvalidEntities++; 
     895 
     896                        // Add this field to the list of failed cells, in order to give the user feedback 
     897                        failedcells = addNonValidatingCells( failedcells, entity, flow ) 
     898 
     899                        // Also create a full list of errors 
     900                        def currentErrors = getHumanReadableErrors( entity ) 
     901                        if( currentErrors ) { 
     902                            currentErrors.each { 
     903                                errors += "(" + entityName + ") " + it.value; 
    1014904                            } 
    1015905                        } 
  • trunk/grails-app/domain/dbnp/studycapturing/EventGroup.groovy

    r1457 r1864  
    2222        static constraints = { 
    2323                // Ensure that the event group name is unique within the study 
    24                 name(unique:['parent'])  
     24                name(unique:['parent']) 
    2525        } 
    2626} 
  • trunk/grails-app/services/dbnp/modules/ModuleCommunicationService.groovy

    r1852 r1864  
    1515package dbnp.modules 
    1616 
    17 import dbnp.studycapturing.* 
    18 import dbnp.authentication.* 
    1917import grails.converters.* 
    2018import javax.servlet.http.HttpServletResponse 
     
    7573        } 
    7674 
    77         /** 
     75    /** 
    7876         * Calls a rest method on a module 
    7977         *  
  • trunk/grails-app/services/dbnp/studycapturing/AssayService.groovy

    r1853 r1864  
    5050                } 
    5151 
     52        def moduleError = '', moduleMeasurements = [] 
     53 
     54        try { 
     55            moduleMeasurements = requestModuleMeasurementNames(assay) 
     56        } catch (e) { 
     57            moduleError = e.message 
     58        } 
     59 
    5260                def samples = assay.samples 
    5361                [               'Subject Data' :            getUsedTemplateFields( samples*."parentSubject".unique() ), 
     
    5563                                        'Sample Data' :             getUsedTemplateFields( samples ), 
    5664                                        'Event Group' :             [[name: 'name', comment: 'Name of Event Group', displayName: 'name']], 
    57  
    58                                         // If module is not reachable, only the field 'module error' is returned, and is filled later on. 
    59                                         'Module Measurement Data':  moduleCommunicationService.isModuleReachable(assay.module.url) ? requestModuleMeasurementNames(assay) : [ [ name: "Module error" ] ] 
     65                                        'Module Measurement Data':  moduleMeasurements, 
     66                    'ModuleError':              moduleError 
    6067                ] 
    6168 
     
    8390                        headerFields.inject([:]) { map, headerField -> 
    8491 
    85                                 map + [(headerField.displayName): templateEntities.collect { 
     92                                map + [(headerField.displayName): templateEntities.collect { entity -> 
    8693 
    8794                    // default to an empty string 
    8895                    def val = '' 
    8996 
    90                     def field 
    91                     try { 
    92  
    93                         val = it.getFieldValue(headerField.name) 
    94  
    95                         // Convert RelTime fields to human readable strings 
    96                         field = it.getField(headerField.name) 
    97                         if (field.type == TemplateFieldType.RELTIME) 
    98                             val = new RelTime( val as long ) 
    99  
    100                     } catch (NoSuchFieldException e) { /* pass */ } 
     97                    if (entity) { 
     98                        def field 
     99                        try { 
     100 
     101                            val = entity.getFieldValue(headerField.name) 
     102 
     103                            // Convert RelTime fields to human readable strings 
     104                            field = entity.getField(headerField.name) 
     105                            if (field.type == TemplateFieldType.RELTIME) 
     106                                val = new RelTime( val as long ) 
     107 
     108                        } catch (NoSuchFieldException e) { /* pass */ } 
     109                    } 
    101110 
    102111                    (val instanceof Number) ? val : val.toString()}] 
     
    177186                } 
    178187 
     188                def moduleError = '', moduleMeasurementData = [:] 
     189 
     190                if (measurementTokens) { 
     191 
     192            try { 
     193                moduleMeasurementData = requestModuleMeasurements(assay, measurementTokens, samples) 
     194            } catch (e) { 
     195                moduleMeasurementData = ['error' : ['Module error, module not available or unknown assay'] * samples.size() ] 
     196                moduleError =  e.message 
     197            } 
     198 
     199                } 
     200 
    179201                [       'Subject Data' :            getFieldValues(samples, fieldMap['Subject Data'], 'parentSubject'), 
    180202                                'Sampling Event Data' :     getFieldValues(samples, fieldMap['Sampling Event Data'], 'parentEvent'), 
    181203                'Sample Data' :             getFieldValues(samples, fieldMap['Sample Data']), 
    182204                'Event Group' :             eventFieldMap, 
    183  
    184                 // If module is not reachable, only the message 'module not reachable' is given for each sample 
    185                 'Module Measurement Data':  moduleCommunicationService.isModuleReachable(assay.module.url) ? 
    186                                                 ( measurementTokens ? requestModuleMeasurements(assay, measurementTokens, samples) : [:] ) : 
    187                                                 [ "Module error": [ "Module not reachable" ] * samples.size() ] 
     205                'Module Measurement Data' : moduleMeasurementData, 
     206                'ModuleError' :             moduleError 
    188207                                ] 
    189208        } 
     
    249268 
    250269                def path = moduleUrl + "/rest/getMeasurements/query" 
    251         def query = "assayToken=$assay.assayUUID" 
     270        def query = "assayToken=${assay.giveUUID()}" 
    252271        def jsonArray 
    253272 
     
    274293         * @param assay                         Assay for which the module measurements should be retrieved 
    275294         * @param measurementTokens     List with the names of the fields to be retrieved. Format: [ 'measurementName1', 'measurementName2' ] 
    276          * @param samples                       Samples for which the module 
     295         * @param samples                       Samples to collect measurements for 
    277296         * @return 
    278297         */ 
     
    329348                                                measurements << null 
    330349                                        }  else { 
    331                                                 measurements << ( moduleData[ valueIndex ] == JSONObject.NULL ? "" : moduleData[ valueIndex ].toDouble() ); 
     350 
     351                        def val 
     352                        def measurement = moduleData[ valueIndex ] 
     353 
     354                        if          (measurement == JSONObject.NULL)    val = "" 
     355                        else if     (measurement instanceof Number)     val = measurement 
     356                        else if     (measurement.isDouble())            val = measurement.toDouble() 
     357                        else val =   measurement.toString() 
     358                                                measurements << val 
    332359                                        } 
    333360                                } else { 
     
    487514                        // transpose d into row wise data and combine with header rows 
    488515                        headers + d.transpose() 
    489                 } 
     516                } else [] 
    490517 
    491518        } 
  • trunk/grails-app/taglib/dbnp/studycapturing/AssayExporterTagLib.groovy

    r1583 r1864  
    2121    def categorySelector = {attrs, body -> 
    2222 
     23        def categoryName = attrs.remove('category') 
     24        attrs['class'] = 'category' 
    2325        out << '<div class="element">' 
    24         out << g.checkBox(name: attrs.ref, value: true, class: 'category') 
    25         out << attrs.category 
     26        out << g.checkBox(attrs) 
     27        out << categoryName 
    2628        out << '</div>' 
    2729 
  • trunk/grails-app/views/assay/assayExport/compileExportData.gsp

    r1841 r1864  
    1414  <body> 
    1515 
    16   <h1>Below you see a preview of the resulting file, click OK to generate it</h1> 
     16  <h1>Below you see a preview of the resulting file, click OK to download</h1> 
     17 
     18  <g:if test="${errorMessage}"> 
     19  <div class="errormessage">${errorMessage}</div> 
     20  </g:if> 
    1721 
    1822  <table> 
  • trunk/grails-app/views/assay/assayExport/selectFields.gsp

    r1841 r1864  
    4040    <h1>Select the columns that you want to be included in the resulting file</h1> 
    4141 
     42    <g:if test="${errorMessage}"> 
     43    <div class="errormessage">${errorMessage}</div> 
     44    </g:if> 
     45 
    4246    In this step you can make a selection from the available fields stored in the database related to the samples, including measurement data from a module (if available). 
    4347 
     
    4751      <g:each in="${fieldMap}" var="entry"> 
    4852 
    49           <assayExporter:categorySelector category="${entry.key}" ref="cat_${catNum}"/> 
     53          <assayExporter:categorySelector category="${entry.key}" name="cat_${catNum}" value="${true}" /> 
    5054 
    5155          <assayExporter:fieldSelectors ref="cat_${catNum}" fields="${entry.value}"/> 
     
    5559      </g:each> 
    5660 
    57       <assayExporter:categorySelector category="Measurements" ref="cat_${catNum}"/> 
     61      <assayExporter:categorySelector category="Measurements" name="cat_${catNum}" value="${measurementTokens as Boolean}" /> 
    5862      <g:select name="measurementToken" id="measurementToken" from="${measurementTokens}" value="${measurementTokens}" class="field" multiple="true" /> 
    5963      <br /><br /> 
  • trunk/grails-app/views/common/_topnav.gsp

    r1841 r1864  
    1515      <ul class="subnav"> 
    1616                <li><g:link controller="simpleWizard" action="index">A complete study with straightforward design</g:link></li> 
    17                 <li><g:link controller="simpleWizard" action="index" params="[inferDesign: true]">A complete study with inferred design</g:link></li> 
     17                %{--<li><g:link controller="simpleWizard" action="index" params="[inferDesign: true]">A complete study with inferred design</g:link></li>--}% 
    1818            <li><g:link controller="gdtImporter" action="index">A part of the study design</g:link></li> 
    1919            <li><g:link controller="gdtImporter" action="index">A list of studies (choose Study)</g:link></li>