Changeset 1838


Ignore:
Timestamp:
May 11, 2011, 6:16:26 PM (12 years ago)
Author:
s.h.sikkema@…
Message:

First version of study inferring in simple wizard (does not support editing yet and assumes species == Homo sapiens)

Location:
trunk/grails-app
Files:
2 edited

Legend:

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

    r1834 r1838  
    3737         */
    3838        def index = {
    39                 if( params.id )
    40                         redirect( action: "simpleWizard", id: params.id );
    41                 else
    42                         redirect( action: "simpleWizard" );
     39//              if( params.id )
     40//                      redirect( action: "simpleWizard", id: params.id );
     41//              else
     42//                      redirect( action: "simpleWizard" );
     43        redirect action: 'simpleWizard', params: params
    4344        }
    4445
     
    4849                                flow.study = getStudyFromRequest( params )
    4950                                if (!flow.study) retrievalError()
    50                                
     51
     52                flow.inferDesign = params.inferDesign
    5153                                // Search for studies
    5254                                flow.studies = Study.giveWritableStudies( authenticationService.getLoggedInUser(), 100 )
     
    817819                        failedCells: failedcells
    818820                ];
    819        
    820                 // loop through all entities to validate them and add them to failedcells if an error occurs
    821                 def numInvalidEntities = 0;
    822                 def errors = [];
    823 
    824                 // Add all samples
    825                 table.each { record ->
    826                         record.each { entity ->
    827                                 if( entity ) {
    828                                         // Determine entity class and add a parent. Add the entity to the study
    829                                         def preferredIdentifier = importerService.givePreferredIdentifier( entity.class );
    830                                         def equalClosure = { it.getIdentifier() == entity.getIdentifier() }
    831                                         def entityName = entity.class.name[ entity.class.name.lastIndexOf( "." ) + 1 .. -1 ]
    832 
    833                                         entity.parent = study
    834                                        
    835                                         switch( entity.class ) {
    836                                                 case Sample:
    837                                                         if( !study.samples?.find( equalClosure ) ) {
    838                                                                 study.addToSamples( entity );
    839                                                         }
    840                                                        
    841                                                         // If an eventgroup is created, add it to the study
    842                                                         // The eventgroup must have a unique name, but the user shouldn't be bothered with it
    843                                                         // Add 'group ' + samplename and it that is not unique, add a number to it
    844                                                         if( entity.parentEventGroup ) {
    845                                                                 study.addToEventGroups( entity.parentEventGroup )
    846 
    847                                                                 entity.parentEventGroup.name = "Group " + entity.name
    848                                                                 while( !entity.parentEventGroup.validate() ) {
    849                                                                         //entity.parentEventGroup.getErrors().each { println it }
    850                                                                         entity.parentEventGroup.name += "" + Math.floor( Math.random() * 100 )
    851                                                                 }
    852                                                         }
    853                                                        
    854                                                         break;
    855                                                 case Subject:
    856                                                         if( !study.samples?.find( equalClosure ) ) {
    857                                                                
    858                                                                 if( preferredIdentifier ) {
    859                                                                         // Subjects without a name should just be called 'subject'
    860                                                                         if( !entity.getFieldValue( preferredIdentifier.name ) )
    861                                                                                 entity.setFieldValue( preferredIdentifier.name, "Subject" );
    862                                                                
    863                                                                         // Subjects should have unique names; if the user has entered the same name multiple times,
    864                                                                         // the subject will be renamed
    865                                                                         def baseName = entity.getFieldValue( preferredIdentifier.name )
    866                                                                         def counter = 2;
    867                                                                        
    868                                                                         while( study.subjects?.find { it.getFieldValue( preferredIdentifier.name ) == entity.getFieldValue( preferredIdentifier.name ) } ) {
    869                                                                                 entity.setFieldValue( preferredIdentifier.name, baseName + " (" + counter++ + ")" )
    870                                                                         }
    871                                                                 }
    872                                                                
    873                                                                 study.addToSubjects( entity );
    874                                                        
    875                                                         }
    876                                                        
    877                                                         break;
    878                                                 case Event:
    879                                                         if( !study.events?.find( equalClosure ) ) {
    880                                                                 study.addToEvents( entity );
    881                                                         }
    882                                                         break;
    883                                                 case SamplingEvent:
    884                                                         // Sampling events have a 'sampleTemplate' value, which should be filled by the
    885                                                         // template that is chosen for samples.
    886                                                         if( !entity.getFieldValue( 'sampleTemplate' ) ) {
    887                                                                 entity.setFieldValue( 'sampleTemplate', flow.sampleForm.template.Sample.name )
    888                                                         }
    889                                                
    890                                                         if( !study.samplingEvents?.find( equalClosure ) ) {
    891                                                                 study.addToSamplingEvents( entity );
    892                                                         }
    893                                                         break;
    894                                         }
    895                                        
    896                                         if (!entity.validate()) {
    897                                                 numInvalidEntities++;
    898                                                
    899                                                 // Add this field to the list of failed cells, in order to give the user feedback
    900                                                 failedcells = addNonValidatingCells( failedcells, entity, flow )
    901        
    902                                                 // Also create a full list of errors
    903                                                 def currentErrors = getHumanReadableErrors( entity )
    904                                                 if( currentErrors ) {
    905                                                         currentErrors.each {
    906                                                                 errors += "(" + entityName + ") " + it.value;
    907                                                         }
    908                                                 }
    909                                         }
    910                                 }
    911                         }
    912                 }
     821
     822        // loop through all entities to validate them and add them to failedcells if an error occurs
     823        def numInvalidEntities = 0;
     824        def errors = [];
     825
     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 );
     945                                }
     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 )
     957                                    }
     958                                }
     959
     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                                }
     1014                            }
     1015                        }
     1016                    }
     1017                }
     1018            }
     1019        }
    9131020
    9141021                flow.imported.numInvalidEntities = numInvalidEntities + failedcells?.size();
     
    12311338                return errors
    12321339        }
    1233 
    1234     // TEMPORARY ACTION TO TEST CORRECT STUDY DESIGN INFERRING: SHOULD BE REMOVED WHEN DONE
    1235     def testMethod = {
    1236 
    1237         ////////////////////////////////////////////////////////////////////////
    1238         // Functionality mimics intermediate results from simple wizard       //
    1239         ////////////////////////////////////////////////////////////////////////
    1240 
    1241         def data = [//['sample name',         'subject',         'timepoint'],
    1242                     [ '97___N_151_HAKA_1',    'N_151_HAKA',    '0w'],
    1243                     [ '98___N_163_QUJO_3',    'N_163_QUJO',    '2w'],
    1244                     [ '99___N_151_HAKA_2',    'N_151_HAKA',    '1w'],
    1245                     ['100___N_163_QUJO_4',    'N_163_QUJO',    '3w'],
    1246                     ['101___N_151_HAKA_3',    'N_151_HAKA',    '2w'],
    1247                     ['102___N_163_QUJO_2',    'N_163_QUJO',    '1w'],
    1248                     ['103___U_031_SMGI_1',    'U_031_SMGI',    '0w'],
    1249                     ['104___U_031_SMGI_3',    'U_031_SMGI',    '2w'],
    1250                     ['105___N_163_QUJO_1',    'N_163_QUJO',    '0w'],
    1251                     ['106___U_031_SMGI_4',    'U_031_SMGI',    '3w'],
    1252                     ['107___N_151_HAKA_4',    'N_151_HAKA',    '3w'],
    1253                     ['108___U_031_SMGI_2',    'U_031_SMGI',    '1w'],
    1254                     ['109___N_021_THAA_2',    'N_021_THAA',    '1w'],
    1255                     ['110___U_029_DUJA_2',    'U_029_DUJA',    '1w'],
    1256                     ['111___U_029_DUJA_3',    'U_029_DUJA',    '2w'],
    1257                     ['112___N_021_THAA_3',    'N_021_THAA',    '2w'],
    1258                     ['113___U_029_DUJA_4',    'U_029_DUJA',    '3w'],
    1259                     ['114___N_045_SNSU_1',    'N_045_SNSU',    '0w'],
    1260                     ['115___N_021_THAA_1',    'N_021_THAA',    '0w'],
    1261                     ['116___N_045_SNSU_2',    'N_045_SNSU',    '1w'],
    1262                     ['117___N_045_SNSU_3',    'N_045_SNSU',    '2w'],
    1263                     ['118___N_045_SNSU_4',    'N_045_SNSU',    '3w'],
    1264                     ['119___N_021_THAA_4',    'N_021_THAA',    '3w'],
    1265                     ['120___U_029_DUJA_1',    'U_029_DUJA',    '0w'],
    1266                     ['121___U_060_BRGE_3',    'U_060_BRGE',    '2w'],
    1267                     ['122___N_018_WIHA_1',    'N_018_WIHA',    '0w'],
    1268                     ['123___N_022_HUCA_3',    'N_022_HUCA',    '2w'],
    1269                     ['124___N_022_HUCA_2',    'N_022_HUCA',    '1w']]
    1270 
    1271         def sampleTemplate = Template.findByName ('Human blood sample')
    1272         def subjectTemplate = Template.findByName ('Human')
    1273         def samplingEventTemplate = Template.findByName ('Blood extraction')
    1274 //        def eventTemplate = Template.findByName ('Diet treatment')
    1275 
    1276 
    1277         // Table is a collection of records. A records contains entities of type
    1278         // Sample, Subject, and SamplingEvent. This mimics the output of
    1279         // importerService.importOrUpdateDataBySampleIdentifier
    1280         def table = data.collect { row ->
    1281 
    1282             [       new Sample( name: row[0], template: sampleTemplate),
    1283                     new Subject(name: row[1], template: subjectTemplate),
    1284                     new SamplingEvent(template: samplingEventTemplate).setFieldValue('startTime', row[2])
    1285 //                    new Event(template: eventTemplate)
    1286             ]
    1287 
    1288         }
    1289 
    1290         ////////////////////////////////////////////////////////////////////////
    1291         // Functionality below should be inserted into simple wizard
    1292         // We'll assume entities with pre-existing preferred identifiers have
    1293         // already been loaded from the db so that updating will work.
    1294         ////////////////////////////////////////////////////////////////////////
    1295 
    1296         def inferStudyDesign = { study ->
    1297 
    1298             // Check for duplicate samples
    1299             def samples = table.collect{it[0]}
    1300 
    1301             def uniques     = [] as Set
    1302             def duplicates  = [] as Set
    1303 
    1304             // this approach separates the unique from the duplicate entries
    1305             samples*.name.each {
    1306                 uniques.add(it) || duplicates.add(it)
    1307             }
    1308 
    1309             duplicates.each{ duplicateName ->
    1310                 samples.findAllByName(duplicateName).each{ sample ->
    1311                     numInvalidEntities++
    1312                     failedcells = addNonValidatingCells(failedcells, sample, flow)
    1313                     errors += "(Sample) duplicate name: $duplicateName"
    1314                 }
    1315             }
    1316 
    1317             // A closure that returns a sub list of entities from a list that have
    1318             // unique values of a property indicated by propertyName
    1319             def uniqueEntitiesByProperty = { entities, propertyName ->
    1320 
    1321                entities*."$propertyName".unique().collect { uniquePropertyValue ->
    1322 
    1323                     entities.find{ it."$propertyName" == uniquePropertyValue }
    1324 
    1325                 }
    1326             }
    1327 
    1328             // collect unique subjects and sampling events from table
    1329             def uniqueSubjects =
    1330                 uniqueEntitiesByProperty(table.collect{it[1]}, 'name')
    1331             def uniqueSamplingEvents =
    1332                 uniqueEntitiesByProperty(table.collect{it[2]}, 'startTime')
    1333 
    1334             // create an event group for each unique sampling event (not much of a group, is it ...)
    1335             uniqueSamplingEvents.each{
    1336                 new EventGroup(name: "Sampling_${it.name}_${it.startTime}").addToSamplingEvents(it)
    1337                 study.addToEventGroups eventGroup
    1338             }
    1339 
    1340             def addToCollectionIfNecessary = { parent, collectionName, entity, propertyName ->
    1341 
    1342                 if (!parent.'collectionName'.find{it.'propertyName' == entity.'propertyName'})
    1343                     parent."addTo${collectionName.toUpperCase()}" entity
    1344 
    1345             }
    1346 
    1347             table.each{ record ->
    1348 
    1349                 Sample sample = record[0]
    1350 
    1351                 // gather all sample related entities
    1352                 def correspondingSamplingEvent  = uniqueSamplingEvents.findByStartTime(record[2].startTime)
    1353                 def correspondingSubject        = uniqueSubjects.findByName(record[1].name)
    1354                 def correspondingEventGroup     = correspondingSamplingEvent.eventGroup
    1355 
    1356                 correspondingSamplingEvent.addToSamples sample
    1357                 correspondingSubject.addToSamples       sample
    1358                 correspondingEventGroup.addToSamples    sample
    1359 
    1360                 if (!correspondingEventGroup.subjects.find{it.name == correspondingSubject.name})
    1361                     correspondingEventGroup.addToSubjects correspondingSubject
    1362 
    1363                 study.addToSamples sample
    1364             }
    1365         }
    1366 
    1367         // inferStudyDesign(study)
    1368 
    1369         println 'hoi'
    1370         render 'bla'
    1371 
    1372     }
    13731340}
  • trunk/grails-app/views/common/_topnav.gsp

    r1829 r1838  
    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>
    1718            <li><g:link controller="gdtImporter" action="index">A part of the study design</g:link></li>
    1819            <li><g:link controller="gdtImporter" action="index">A list of studies (choose Study)</g:link></li>
Note: See TracChangeset for help on using the changeset viewer.