Changeset 1608

Show
Ignore:
Timestamp:
09-03-11 15:56:26 (3 years ago)
Author:
robert@…
Message:

Fixed bug with hibernate and transactional services in the simple wizard

Location:
trunk/grails-app
Files:
11 added
11 removed
2 modified

Legend:

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

    r1591 r1608  
    1515package dbnp.studycapturing 
    1616 
     17import org.apache.poi.ss.usermodel.DataFormatter 
    1718import org.dbnp.gdt.* 
    1819import grails.plugins.springsecurity.Secured 
     
    2728        def fileService 
    2829        def importerService 
    29         def gdtService 
     30        def gdtService = new GdtService() 
    3031 
    3132        /** 
     
    3334         */ 
    3435        def index = { 
    35                 redirect( action: "study" ); 
     36                if( params.id ) 
     37                        redirect( action: "simpleWizard", id: params.id ); 
     38                else 
     39                        redirect( action: "simpleWizard" ); 
    3640        } 
    3741 
    38         /** 
    39          * Shows the study page 
    40          */ 
    41         def study = { 
    42                 // Retrieve the correct study 
    43                 Study study = getStudyInWizard( params ); 
    44  
    45                 // If no study is found in the wizard, this is the entry page of the 
    46                 // wizard. Retrieve the study from request parameters 
    47                 if( !study ) { 
    48                         study = getStudyFromRequest( params ); 
    49  
    50                         // Some error might have occurred during the retrieval of the study 
    51                         if( !study ) { 
    52                                 redirect( controller: 'simpleWizard', action: 'study' ); 
    53                                 return; 
    54                         } 
    55  
    56                         session.simpleWizard = [ study: study ] 
    57                 } 
    58  
    59                 def event = getEvent(params); 
    60  
    61                 // If any event on this page is triggered, we should save the entered data. 
    62                 // If no event is triggered, the user gets here from another page. In that case, 
    63                 // we don't set the values 
    64                 if( event ) { 
    65                         // Only continue to the next or previous page if the information entered is correct 
    66                         if( handleStudy( study, params ) ) { 
    67                                 // Now determine what action to perform 
    68                                 // If the user clicks next, the study should be validated 
    69                                 if( event == "next" && validateObject( study ) ) { 
    70                                         if( study.samples?.size() ) { 
    71                                                 // This wizard can only handle simple studies. If the 
    72                                                 // study is too complex, this wizard doesn't provide the 
    73                                                 // possibility to edit samples/subjects etc. 
    74                                                 if( checkStudySimplicity( study ) ) { 
    75                                                         toPage( "existingSamples" ); 
    76                                                 } else { 
    77                                                         toPage( "complexStudy" ); 
    78                                                 } 
    79                                         } else { 
    80                                                 toPage( "samples" ); 
    81                                         } 
    82                                          
    83                                         return; 
    84                                 } 
    85                         } 
    86                 } 
    87  
    88                 // Give the study to the user 
    89                 [ study: study ] 
    90         } 
    91          
    92         /** 
    93          * Shows a page to mention that the study being edited is too complex 
    94          */ 
    95         def complexStudy = { 
    96                 // Retrieve the correct study 
    97                 study = getStudyInWizard( params ); 
    98                 if( !study ) { 
    99                         redirect( controller: 'simpleWizard', action: 'study' ); 
    100                         return; 
    101                 } 
    102  
    103                 def event = getEvent(params); 
    104  
    105                 // If any event on this page is triggered 
    106                 if( event ) { 
    107                         // Now determine what action to perform 
    108                         if( event == "save" ) { 
    109                                 toPage( "save" ); 
    110                                 return; 
    111                         } else if( event == "previous" ) { 
    112                                 toPage( "study" ); 
    113                                 return; 
    114                         } 
    115                 } 
    116  
    117                 // Give the study and other data to the user 
    118                 [ study: study ] 
    119         } 
    120  
    121         /** 
    122          * Shows the samples page 
    123          */ 
    124         def existingSamples = { 
    125                 // Retrieve the correct study 
    126                 study = getStudyInWizard( params ); 
    127                 if( !study ) { 
    128                         redirect( controller: 'simpleWizard', action: 'study' ); 
    129                         return; 
    130                 } 
    131  
    132                 def event = getEvent(params); 
    133  
    134                 // If any event on this page is triggered, we should save the entered data. 
    135                 // If no event is triggered, the user gets here from another page. In that case, 
    136                 // we don't set the values 
    137                 if( event ) { 
    138                         // Now determine what action to perform 
    139                         if( event == "next" && handleExistingSamples( study, params )) { 
     42        def simpleWizardFlow = { 
     43                entry { 
     44                        action{ 
     45                                flow.study = getStudyFromRequest( params ) 
     46                                if (!flow.study) retrievalError() 
     47                        } 
     48                        on("retrievalError").to "handleError" 
     49                        on("success").to "study" 
     50                } 
     51 
     52                study { 
     53                        on("next") { 
     54                                handleStudy( flow.study, params ) 
     55                                if( !validateObject( flow.study ) ) 
     56                                        error() 
     57                        }.to "decisionState" 
     58                        on("refresh") { handleStudy( flow.study, params ); }.to "study" 
     59                        on( "success" ) { handleStudy( flow.study, params ) }.to "study" 
     60                } 
     61 
     62                decisionState { 
     63                        action { 
     64                                // Create data in the flow 
     65                                flow.templates = [ 
     66                                                        'Sample': Template.findAllByEntity( Sample.class ), 
     67                                                        'Subject': Template.findAllByEntity( Subject.class ), 
     68                                                        'Event': Template.findAllByEntity( Event.class ), 
     69                                                        'SamplingEvent': Template.findAllByEntity( SamplingEvent.class ) 
     70                                ]; 
     71                                flow.encodedEntity = [ 
     72                                                        'Sample': gdtService.encryptEntity( Sample.class.name ), 
     73                                                        'Subject': gdtService.encryptEntity( Subject.class.name ), 
     74                                                        'Event': gdtService.encryptEntity( Event.class.name ), 
     75                                                        'SamplingEvent': gdtService.encryptEntity( SamplingEvent.class.name ) 
     76                                                ] 
     77 
     78                                if (flow.study.samples) 
     79                                        checkStudySimplicity(flow.study) ? existingSamples() : complexStudy() 
     80                                else 
     81                                        samples() 
     82                        } 
     83                        on ("existingSamples").to "startExistingSamples" 
     84                        on ("complexStudy").to "complexStudy" 
     85                        on ("samples").to "samples" 
     86                } 
     87                 
     88                startExistingSamples { 
     89                        action { 
     90                                def records = importerService.getRecords( flow.study ); 
     91                                flow.records = records 
     92                                flow.templateCombinations = records.templateCombination.unique() 
     93                                success(); 
     94                        } 
     95                        on( "success" ).to "existingSamples" 
     96                } 
     97 
     98                existingSamples { 
     99                        on("next") { 
     100                                handleExistingSamples( flow.study, params, flow ) ? success() : error() 
     101                        }.to "startAssays" 
     102                        on("previous").to "study" 
     103                        on("update") { 
     104                                handleExistingSamples( flow.study, params, flow ) ? success() : error() 
     105                        }.to "samples" 
     106 
     107                        on("skip").to "startAssays" 
     108                } 
     109 
     110                complexStudy { 
     111                        on("save").to "save" 
     112                        on("previous").to "study" 
     113                } 
     114 
     115                samples { 
     116                        on("next") { 
     117                                handleSamples( flow.study, params, flow ) ? success() : error () 
     118                                 
     119                                // Add domain fields for all entities 
     120                                flow.domainFields = [:] 
     121                                 
     122                                flow.templates.each {  
     123                                        if( it.value ) { 
     124                                                flow.domainFields[ it.key ] = it.value[0].entity.giveDomainFields(); 
     125                                        } 
     126                                } 
     127 
     128                        }.to "columns" 
     129                        on("previous").to "returnFromSamples" 
     130                        on("study").to "study" 
     131                        on("skip").to "startAssays" 
     132                } 
     133 
     134                returnFromSamples { 
     135                        action { 
     136                                flow.study.samples ? existingSamples() : study(); 
     137                        } 
     138                        on( "existingSamples" ).to "startExistingSamples" 
     139                        on( "study" ).to "study" 
     140                } 
     141                 
     142                columns { 
     143                        on( "next" ) { 
     144                                handleColumns( flow.study, params, flow ) ? success() : error() 
     145                        }.to "checkImportedEntities" 
     146                        on( "previous" ).to "samples"  
     147                } 
     148                 
     149                checkImportedEntities { 
     150                        action { 
    140151                                // Only continue to the next page if the information entered is correct 
    141                                 toPage( "assays" ); 
    142                                 return; 
    143                         } else if( event == "previous" ) { 
    144                                 // The user may go to the previous page, even if none of the data entered is OK. 
    145                                 toPage( "study" ); 
    146                                 return; 
    147                         } else if( event == "update" && handleExistingSamples( study, params ) ) { 
    148                                 // The user may update the samples using excel. Before that, the sample date should be saved 
    149                                 toPage( "samples" ); 
    150                                 return; 
    151                         } else if( event == "skip" ) { 
    152                                 // The user may skip the complete samples page 
    153                                 toPage( "assays" ); 
    154                                 return; 
    155                         } 
    156                 } 
    157  
    158                 // Give the study and other data to the user 
    159                 def records = importerService.getRecords( study ); 
    160  
    161                 [ study: study, 
    162                         records: records, 
    163                         templateCombinations: records.templateCombination.unique(), 
    164                         templates: [  
    165                                 'Sample': Template.findAllByEntity( Sample.class ), 
    166                                 'Subject': Template.findAllByEntity( Subject.class ), 
    167                                 'Event': Template.findAllByEntity( Event.class ), 
    168                                 'SamplingEvent': Template.findAllByEntity( SamplingEvent.class ) 
    169                         ], 
    170                         encodedEntity: [  
    171                                 'Sample': gdtService.encryptEntity( Sample.class.name ),  
    172                                 'Subject': gdtService.encryptEntity( Subject.class.name ),  
    173                                 'Event': gdtService.encryptEntity( Event.class.name ),  
    174                                 'SamplingEvent': gdtService.encryptEntity( SamplingEvent.class.name ) 
    175                         ], 
    176                         existingSampleForm: session.simpleWizard.existingSampleForm  
    177                 ] 
    178         } 
    179  
    180         /** 
    181          * Shows the samples page 
    182          */ 
    183         def samples = { 
    184                 // Retrieve the correct study 
    185                 study = getStudyInWizard( params ); 
    186                 if( !study ) { 
    187                         redirect( controller: 'simpleWizard', action: 'study' ); 
    188                         return; 
    189                 } 
    190  
    191                 def event = getEvent(params); 
    192  
    193                 // If any event on this page is triggered, we should save the entered data. 
    194                 // If no event is triggered, the user gets here from another page. In that case, 
    195                 // we don't set the values 
    196                 if( event ) { 
    197                         // Now determine what action to perform 
    198                         if( event == "next" && handleSamples( study, params )) { 
    199                                 // Only continue to the next page if the information entered is correct 
    200                                 toPage( "columns" ); 
    201                                 return; 
    202                         } else if( event == "previous" ) { 
    203                                 // The user may go to the previous page, even if none of the data entered is OK. 
    204                                 if( study.samples?.size() ) 
    205                                         toPage( "existingSamples" ); 
    206                                 else 
    207                                         toPage( "study" ); 
    208  
    209                                 return; 
    210                         } else if( event == "skip" ) { 
    211                                 // The user may skip the complete samples page 
    212                                 toPage( "assays" ); 
    213                                 return; 
    214                         } 
    215                 } 
    216  
    217                 // Give the study and other data to the user 
    218                 [ study: study,  
    219                         templates: [  
    220                                 'Sample': Template.findAllByEntity( Sample.class ), 
    221                                 'Subject': Template.findAllByEntity( Subject.class ), 
    222                                 'Event': Template.findAllByEntity( Event.class ), 
    223                                 'SamplingEvent': Template.findAllByEntity( SamplingEvent.class ) 
    224                         ], 
    225                         encodedEntity: [  
    226                                 'Sample': gdtService.encryptEntity( Sample.class.name ),  
    227                                 'Subject': gdtService.encryptEntity( Subject.class.name ),  
    228                                 'Event': gdtService.encryptEntity( Event.class.name ),  
    229                                 'SamplingEvent': gdtService.encryptEntity( SamplingEvent.class.name ) 
    230                         ], 
    231                         sampleForm: session.simpleWizard.sampleForm ] 
    232         } 
    233  
    234         /** 
    235          * Shows the columns page 
    236          */ 
    237         def columns = { 
    238                 // Retrieve the correct study 
    239                 study = getStudyInWizard( params ); 
    240                 if( !study ) { 
    241                         redirect( controller: 'simpleWizard', action: 'study' ); 
    242                         return; 
    243                 } 
    244  
    245                 def event = getEvent(params); 
    246  
    247                 // If any event on this page is triggered, we should save the entered data. 
    248                 // If no event is triggered, the user gets here from another page. In that case, 
    249                 // we don't set the values 
    250                 if( event ) { 
    251                         // Now determine what action to perform 
    252                         if( event == "next" && handleColumns( study, params ) ) { 
    253                                  
    254                                 // Only continue to the next page if the information entered is correct 
    255                                 if( session.simpleWizard.imported.numInvalidEntities > 0 ) { 
    256                                         toPage( "missingFields" ); 
     152                                if( flow.imported.numInvalidEntities > 0 ) { 
     153                                        missingFields(); 
    257154                                } else { 
    258155                                        // The import of the excel file has finished. Now delete the excelfile 
    259                                         if( session.simpleWizard.sampleForm.importFile ) 
    260                                                 fileService.delete( session.simpleWizard.sampleForm.importFile ); 
    261  
    262                                         session.simpleWizard.sampleForm = null 
    263  
    264                                         toPage( "assays" ); 
    265                                 } 
    266                                 return; 
    267                         } else if( event == "previous" ) { 
    268                                 // The user may go to the previous page, even if the data is not correct 
    269                                 toPage( "samples" ); 
    270                                 return; 
    271                         } 
    272                 } 
    273                  
    274                 def templates = [:];  
    275                 def domainFields = [:] 
    276  
    277                 session.simpleWizard.sampleForm.templateId.each {  
    278                         templates[ it.key ] = it.value ? Template.get( it.value ) : null; 
    279                         if( it.value ) { 
    280                                 domainFields[ it.key ] = templates[ it.key ].entity.giveDomainFields(); 
    281                         } 
    282                 }  
    283                  
    284                 // Give the study and other data to the user 
    285                 [ study: study, 
    286                                         filename: session.simpleWizard.sampleForm.importFile, 
    287                                         templates: templates, 
    288                                         domainFields: domainFields, 
    289                                         excel: session.simpleWizard.excel] 
     156                                        if( flow.excel.filename ) 
     157                                                fileService.delete( flow.excel.filename ); 
     158         
     159                                        flow.sampleForm = null 
     160         
     161                                        assays(); 
     162                                } 
     163                        } 
     164                        on( "missingFields" ).to "missingFields" 
     165                        on( "assays" ).to "startAssays"  
     166                } 
     167                 
     168                missingFields { 
     169                        on( "next" ) { 
     170                                if( !handleMissingFields( flow.study, params, flow ) ) { 
     171                                        return error(); 
     172                                } 
     173                                 
     174                                // The import of the excel file has finished. Now delete the excelfile 
     175                                if( flow.excel.filename ) 
     176                                        fileService.delete( flow.excel.filename ); 
     177 
     178                                flow.sampleForm = null 
     179                                 
     180                                success(); 
     181                        }.to "startAssays" 
     182                        on( "previous" ) { 
     183                                // The user goes back to the previous page, so the already imported entities 
     184                                // (of which some gave an error) should be removed again. 
     185                                // Add all samples 
     186                                flow.imported.data.each { record -> 
     187                                        record.each { entity -> 
     188                                                if( entity ) { 
     189                                                        switch( entity.class ) { 
     190                                                                case Sample:    flow.study.removeFromSamples( entity ); break; 
     191                                                                case Subject:   flow.study.removeFromSubjects( entity ); break; 
     192                                                                case Event:             flow.study.removeFromEvents( entity ); break; 
     193                                                                case SamplingEvent:     flow.study.removeFromSamplingEvents( entity ); break; 
     194                                                        } 
     195                                                } 
     196                                        } 
     197                                } 
     198                                 
     199                                success(); 
     200                        }.to "columns" 
     201                } 
     202                 
     203                startAssays { 
     204                        action { 
     205                                if( !flow.assay )  
     206                                        flow.assay = new Assay( parent: flow.study ); 
     207                                         
     208                                success(); 
     209                        } 
     210                        on( "success" ).to "assays" 
     211                } 
     212                 
     213                assays { 
     214                        on( "next" ) {  
     215                                handleAssays( flow.assay, params, flow ); 
     216                                if( !validateObject( flow.assay ) ) 
     217                                        error(); 
     218                         }.to "overview" 
     219                        on( "skip" ) { 
     220                                // In case the user has created an assay before he clicked 'skip', it should only be kept if it 
     221                                // existed before this step 
     222                                if( flow.assay != null && !flow.assay.id ) { 
     223                                        flow.remove( "assay" ) 
     224                                } 
     225 
     226                         }.to "overview" 
     227                        on( "previous" ).to "returnFromAssays" 
     228                        on("refresh") { handleAssays( flow.assay, params, flow ); success() }.to "assays" 
     229                } 
     230 
     231                returnFromAssays { 
     232                        action { 
     233                                flow.study.samples ? existingSamples() : samples(); 
     234                        } 
     235                        on( "existingSamples" ).to "existingSamples" 
     236                        on( "samples" ).to "samples" 
     237                } 
     238                 
     239                overview {  
     240                        on( "save" ).to "saveStudy"  
     241                        on( "previous" ).to "startAssays" 
     242                } 
     243                 
     244                saveStudy { 
     245                        action { 
     246                                if( flow.assay && !flow.study.assays?.contains( flow.assay ) ) { 
     247                                        flow.study.addToAssays( flow.assay ); 
     248                                } 
     249                                 
     250                                if( flow.study.save( flush: true ) ) { 
     251                                        // Make sure all samples are attached to all assays 
     252                                        flow.study.assays.each { assay -> 
     253                                                def l = []+ assay.samples; 
     254                                                l.each { sample -> 
     255                                                        if( sample ) 
     256                                                                assay.removeFromSamples( sample ); 
     257                                                } 
     258                                                assay.samples?.clear(); 
     259                 
     260                                                flow.study.samples.each { sample -> 
     261                                                        assay.addToSamples( sample ) 
     262                                                } 
     263                                        } 
     264                         
     265                                        flash.message = "Your study is succesfully saved."; 
     266                                         
     267                                        finish(); 
     268                                } else { 
     269                                        flash.error = "An error occurred while saving your study: <br />" 
     270                                        flow.study.getErrors().each { flash.error += it.toString() + "<br />"} 
     271                                         
     272                                        // Remove the assay from the study again, since it is still available 
     273                                        // in the session 
     274                                        if( flow.assay ) { 
     275                                                flow.study.removeFromAssays( flow.assay ); 
     276                                                flow.assay.parent = flow.study; 
     277                                        } 
     278                                         
     279                                        overview(); 
     280                                } 
     281                        } 
     282                        on( "finish" ).to "finish" 
     283                        on( "overview" ).to "overview" 
     284                } 
     285                 
     286                finish() 
     287                 
     288                handleError{ 
     289                        redirect action: "errorPage" 
     290                } 
    290291        } 
    291292 
    292293        /** 
    293          * Shows the page where missing fields can be filled in 
     294         * Retrieves the required study from the database or return an empty Study object if 
     295         * no id is given 
     296         * 
     297         * @param params        Request parameters with params.id being the ID of the study to be retrieved 
     298         * @return                      A study from the database or an empty study if no id was given 
    294299         */ 
    295         def missingFields = { 
    296                 // Retrieve the correct study 
    297                 study = getStudyInWizard( params ); 
    298                 if( !study ) { 
    299                         redirect( controller: 'simpleWizard', action: 'study' ); 
    300                         return; 
    301                 } 
    302  
    303                 def event = getEvent(params); 
    304  
    305                 // If any event on this page is triggered, we should save the entered data. 
    306                 // If no event is triggered, the user gets here from another page. In that case, 
    307                 // we don't set the values 
    308                 if( event ) { 
    309                         // Now determine what action to perform 
    310                         if( event == "next" && handleMissingFields( study, params ) ) { 
    311                                 if( session.simpleWizard.imported.numInvalidEntities == 0 ) { 
    312                                         // Only continue to the next page if the information entered is correct 
    313  
    314                                         // The import of the excel file has finished. Now delete the excelfile 
    315                                         if( session.simpleWizard.sampleForm.importFile ) { 
    316                                                 fileService.delete( session.simpleWizard.sampleForm.importFile ); 
    317                                         } 
    318                                         session.simpleWizard.sampleForm = null 
    319  
    320                                         toPage( "assays" ); 
    321                                         return; 
    322                                 } 
    323                         } else if( event == "previous" ) { 
    324                                 // THe user may go to the previous page, even if the data is not correct 
    325                                 toPage( "columns" ); 
    326                                 return; 
    327                         } 
    328                 } 
    329  
    330                 // If any errors have occurred during validation, show them to the user. However, 
    331                 // the same error might have occurred for multiple entities. For that reason, 
    332                 // we only show unique errors 
    333                 def rules 
    334                 if( session.simpleWizard.imported.errors ) { 
    335                         rules = session.simpleWizard.imported.errors*.values().flatten().unique().join( "<br /> \n" ); 
    336                 } 
    337  
    338                 // Create the right format for showing the data 
    339                 def records = []; 
    340                 session.simpleWizard.imported.data.each { row -> 
    341                         def record = [:]; 
    342                         row.each { object -> 
    343                                 // Attach template to session 
    344                                 if( object.template ) 
    345                                         attachTemplate( object.template ); 
    346                                  
    347                                 def entityName = object.class.name[ object.class.name.lastIndexOf( '.' ) + 1 .. -1 ] 
    348                                 record[ entityName ] = object; 
    349                         } 
    350                         records << record; 
    351                 } 
    352                  
    353                 // Give the study and other data to the user 
    354                 println "Imported: " + session.simpleWizard.imported.failedCells; 
    355                  
    356                 [ study: study, imported: session.simpleWizard.imported, records: records, rules: rules ] 
     300        protected Study getStudyFromRequest( def params ) { 
     301                int id = params.int( "id" ); 
     302 
     303                if( !id ) { 
     304                        return new Study( title: "New study", owner: authenticationService.getLoggedInUser() ); 
     305                } 
     306 
     307                Study s = Study.get( id ); 
     308 
     309                if( !s ) { 
     310                        flash.error = "No study found with given id"; 
     311                        return null; 
     312                } 
     313                if( !s.canWrite( authenticationService.getLoggedInUser() ) ) { 
     314                        flash.error = "No authorization to edit this study." 
     315                        return null; 
     316                } 
     317 
     318                return s 
    357319        } 
    358320 
    359321        /** 
    360          * Shows the assay page 
    361          */ 
    362         def assays = { 
    363                 // Retrieve the correct study 
    364                 Study study = getStudyInWizard( params ); 
    365                 if( !study ) { 
    366                         redirect( controller: 'simpleWizard', action: 'study' ); 
    367                         return; 
    368                 } 
    369  
    370                 Assay assay = getAssayInWizard( study ); 
    371  
    372                 def event = getEvent(params); 
    373  
    374                 // If any event on this page is triggered, we should save the entered data. 
    375                 // If no event is triggered, the user gets here from another page. In that case, 
    376                 // we don't set the values 
    377                 if( event ) { 
    378                         // Only continue to the next or previous page if the information entered is correct 
    379                         if( event == "skip" ) { 
    380                                 // The user may skip the complete assay page 
    381  
    382                                 // In case the user has created an assay before, it should only be kept if it 
    383                                 // existed before this step 
    384                                 if( session.simpleWizard.assay != null && !session.simpleWizard.assay.id ) { 
    385                                         session.simpleWizard.remove( "assay" ) 
    386                                 } 
    387  
    388                                 toPage( "overview" ); 
    389                                 return; 
    390                         } else if( handleAssays( assay, params ) ) { 
    391                                 // Now determine what action to perform 
    392                                 if( event == "next" && validateObject( assay ) ) { 
    393                                         toPage( "overview" ); 
    394                                         return; 
    395                                 } else if( event == "previous" ) { 
    396                                         if( study.samples?.size() ) 
    397                                                 toPage( "existingSamples" ) 
    398                                         else 
    399                                                 toPage( "samples" ); 
    400  
    401                                         return; 
    402                                 } 
    403                         } 
    404                 } 
    405  
    406                 // Give the study to the user 
    407                 [ study: study, wizardAssay: assay ] 
    408         } 
    409  
    410         /** 
    411          * Shows an overview of the entered study 
    412          */ 
    413         def overview = { 
    414                 // Retrieve the correct study 
    415                 Study study = getStudyInWizard( params ); 
    416                 if( !study ) { 
    417                         redirect( controller: 'simpleWizard', action: 'study' ); 
    418                         return; 
    419                 } 
    420  
    421                 Assay assay = getAssayInWizard(); 
    422  
    423                 def event = getEvent(params); 
    424  
    425                 // If any event on this page is triggered, we should save the entered data. 
    426                 // If no event is triggered, the user gets here from another page. In that case, 
    427                 // we don't set the values 
    428                 if( event ) { 
    429                         // Now determine what action to perform 
    430                         if( event == "save" ) { 
    431                                 toPage( "save" ); 
    432                                 return; 
    433                         } else if( event == "previous" ) { 
    434                                 toPage( "assays" ) 
    435                                 return; 
    436                         } 
    437                 } 
    438  
    439                 // Give the study to the user 
    440                 [ study: study, wizardAssay: assay ] 
    441         } 
    442  
    443         def save = { 
    444                 // Retrieve the correct study 
    445                 Study study = getStudyInWizard( params ); 
    446                 if( !study ) { 
    447                         redirect( controller: 'simpleWizard', action: 'study' ); 
    448                         return; 
    449                 } 
    450  
    451                 def event = getEvent(params); 
    452  
    453                 if( event && event == "previous" ) { 
    454                         toPage( "assays" ); 
    455                         return; 
    456                 } 
    457                  
    458                 Assay newAssay = getAssayInWizard(); 
    459  
    460                 if( newAssay && !study.assays?.contains( newAssay ) ) { 
    461                         study.addToAssays( newAssay ); 
    462                 } 
    463  
    464                 //attachAndValidateEntities( study ); 
    465                  
    466                 // Save the study. The study must be merged if it is loaded from 
    467                 // the database before, since otherwise it will raise 'lazy initialization' errors 
    468                 // The validation is done in the other steps, so it is skipped here. 
    469                 def success 
    470                  
    471                 if( 
    472                 study.id ? 
    473                 study.merge( validate: false, flush: true ) : 
    474                 study.save( flush: true ) 
    475                 ) { 
    476                         // Make sure all samples are attached to all assays 
    477                         study.assays.each { assay -> 
    478                                 def l = []+ assay.samples; 
    479                                 l.each { sample -> 
    480                                         if( sample ) 
    481                                                 assay.removeFromSamples( sample ); 
    482                                 } 
    483                                 assay.samples?.clear(); 
    484  
    485                                 study.samples.each { sample -> 
    486                                         assay.addToSamples( sample ) 
    487                                 } 
    488                         } 
    489  
    490                         // Clear session 
    491                         session.simpleWizard = null; 
    492  
    493                         flash.message = "Your study is succesfully saved."; 
    494                         success = true 
    495                 } else { 
    496                         flash.error = "An error occurred while saving your study"; 
    497  
    498                         study.getErrors().each { println it } 
    499  
    500                         // Remove the assay from the study again, since it is still available 
    501                         // in the session 
    502                         if( newAssay ) { 
    503                                 study.removeFromAssays( newAssay ); 
    504                                 newAssay.parent = study; 
    505                         } 
    506  
    507                         //validateObject( study ); 
    508                         success = false 
    509                 } 
    510  
    511                 // Give the study to the user 
    512                 [ study: study, success: success ] 
    513         } 
    514  
    515         /**  
    516322         * Handles study input 
    517323         * @param study         Study to update 
     
    552358                return true 
    553359        } 
    554  
    555         /** 
    556          * Handles the editing of existing samples 
    557          * @param study         Study to update 
    558          * @param params                Request parameter map 
    559          * @return                      True if everything went OK, false otherwise. An error message is put in flash.error 
    560          */ 
    561         def handleExistingSamples( study, params ) { 
    562                 session.simpleWizard.existingSampleForm = params 
    563                 flash.validationErrors = [:]; 
    564  
    565                 def errors = false; 
    566                  
    567                 // iterate through objects; set field values and validate the object 
    568                 def eventgroups = study.samples.parentEventGroup.findAll { it } 
    569                 def events; 
    570                 if( !eventgroups ) 
    571                         events = [] 
    572                 else 
    573                         events = eventgroups.events?.getAt(0); 
    574                  
    575                 def objects = [  
    576                         'Sample': study.samples,  
    577                         'Subject': study.samples.parentSubject.findAll { it },  
    578                         'SamplingEvent': study.samples.parentEvent.findAll { it },  
    579                         'Event': events.flatten().findAll { it } 
    580                 ];       
    581                 objects.each { 
    582                         def type = it.key; 
    583                         def entities = it.value; 
    584                          
    585                         entities.each { entity -> 
    586                                 // iterate through entity fields 
    587                                 entity.giveFields().each() { field -> 
    588                                         def value = params.get( type.toLowerCase() + '_' + entity.getIdentifier() + '_' + field.escapedName()) 
    589  
    590                                         // set field value; name cannot be set to an empty value 
    591                                         if (field.name != 'name' || value) { 
    592                                                 log.info "setting "+field.name+" to "+value 
    593                                                 entity.setFieldValue(field.name, value) 
    594                                         } 
    595                                 } 
    596                                  
    597                                 // has the template changed?  
    598                                 def templateName = params.get(type.toLowerCase() + '_' + entity.getIdentifier() + '_template') 
    599                                 if (templateName && entity.template?.name != templateName) { 
    600                                         entity.template = Template.findByName(templateName) 
    601                                 } 
    602          
    603                                 // validate sample 
    604                                 if (!entity.validate()) { 
    605                                         errors = true; 
    606                                         flash.validationErrors << getHumanReadableErrors( entity ) 
    607                                 } 
    608                         } 
    609                 } 
    610  
    611                 return !errors 
    612         } 
     360         
     361        /** 
     362        * Handles the editing of existing samples 
     363        * @param study          Study to update 
     364        * @param params         Request parameter map 
     365        * @return                       True if everything went OK, false otherwise. An error message is put in flash.error 
     366        */ 
     367   def handleExistingSamples( study, params, flow ) { 
     368           flash.validationErrors = []; 
     369 
     370           def errors = false; 
     371            
     372           // iterate through objects; set field values and validate the object 
     373           def eventgroups = study.samples.parentEventGroup.findAll { it } 
     374           def events; 
     375           if( !eventgroups ) 
     376                   events = [] 
     377           else 
     378                   events = eventgroups.events?.getAt(0); 
     379            
     380           def objects = [ 
     381                   'Sample': study.samples, 
     382                   'Subject': study.samples.parentSubject.findAll { it }, 
     383                   'SamplingEvent': study.samples.parentEvent.findAll { it }, 
     384                   'Event': events.flatten().findAll { it } 
     385           ]; 
     386           objects.each { 
     387                   def type = it.key; 
     388                   def entities = it.value; 
     389                    
     390                   entities.each { entity -> 
     391                           // iterate through entity fields 
     392                           entity.giveFields().each() { field -> 
     393                                   def value = params.get( type.toLowerCase() + '_' + entity.getIdentifier() + '_' + field.escapedName()) 
     394 
     395                                   // set field value; name cannot be set to an empty value 
     396                                   if (field.name != 'name' || value) { 
     397                                           log.info "setting "+field.name+" to "+value 
     398                                           entity.setFieldValue(field.name, value) 
     399                                   } 
     400                           } 
     401                            
     402                           // has the template changed? 
     403                           def templateName = params.get(type.toLowerCase() + '_' + entity.getIdentifier() + '_template') 
     404                           if (templateName && entity.template?.name != templateName) { 
     405                                   entity.template = Template.findByName(templateName) 
     406                           } 
     407    
     408                           // validate sample 
     409                           if (!entity.validate()) { 
     410                                   errors = true; 
     411                                    
     412                                   def entityName = entity.class.name[ entity.class.name.lastIndexOf( "." ) + 1 .. -1 ] 
     413                                   getHumanReadableErrors( entity ).each { 
     414                                                flash.validationErrors << [ key: it.key, value: "(" + entityName + ") " + it.value ]; 
     415                                   } 
     416                           } 
     417                   } 
     418           } 
     419 
     420           return !errors 
     421   } 
    613422 
    614423        /** 
     
    618427         * @return                      True if everything went OK, false otherwise. An error message is put in flash.error 
    619428         */ 
    620         def handleSamples( study, params ) { 
     429        def handleSamples( study, params, flow ) { 
    621430                def filename = params.get( 'importfile' ); 
    622431 
     
    629438                else if( filename[0..8] == 'existing*' ) 
    630439                        filename = filename[9..-1] 
    631                  
     440 
    632441                def sampleTemplateId  = params.long( 'sample_template_id' ) 
    633442                def subjectTemplateId  = params.long( 'subject_template_id' ) 
    634443                def eventTemplateId  = params.long( 'event_template_id' ) 
    635444                def samplingEventTemplateId  = params.long( 'samplingEvent_template_id' ) 
    636                  
     445 
    637446                // These fields have been removed from the form, so will always contain 
    638447                // their default value. The code however remains like this for future use. 
     
    642451 
    643452                // Save form data in session 
    644                 session.simpleWizard.sampleForm = [ 
     453                flow.sampleForm = [ 
    645454                                        importFile: filename, 
    646                                         templateId: [  
     455                                        templateId: [ 
    647456                                                'Sample': sampleTemplateId, 
    648457                                                'Subject': subjectTemplateId, 
     
    650459                                                'SampingEvent': samplingEventTemplateId 
    651460                                        ], 
     461                                        template: [ 
     462                                                'Sample': sampleTemplateId ? Template.get( sampleTemplateId ) : null, 
     463                                                'Subject': subjectTemplateId ? Template.get( subjectTemplateId ) : null, 
     464                                                'Event': eventTemplateId ? Template.get( eventTemplateId ) : null, 
     465                                                'SampingEvent': samplingEventTemplateId ? Template.get( samplingEventTemplateId ) : null 
     466                                        ], 
    652467                                        sheetIndex: sheetIndex, 
    653468                                        dataMatrixStart: dataMatrixStart, 
     
    661476                        return false 
    662477                } 
    663  
     478                 
    664479                def importedfile = fileService.get( filename ) 
    665480                def workbook 
     
    693508                def importerDataMatrix; 
    694509 
    695                 try { 
     510                try {            
    696511                        importerHeader = importerService.getHeader(workbook, 
    697512                                        sheetIndex - 1,                 // 0 == first sheet 
     
    699514                                        dataMatrixStart - 1,    // 0 == first row 
    700515                                        Sample.class) 
    701  
     516                 
    702517                        importerDataMatrix = importerService.getDatamatrix( 
    703518                                        workbook, 
     
    716531                } 
    717532 
     533                // Match excel columns with template fields 
     534                def fieldNames = []; 
     535                flow.sampleForm.template.each { template -> 
     536                        if( template.value ) { 
     537                                def fields = template.value.entity.giveDomainFields() + template.value.getFields(); 
     538                                fields.each { field -> 
     539                                        if( !field.entity ) 
     540                                                field.entity = template.value.entity 
     541                                                 
     542                                        fieldNames << field 
     543                                } 
     544                        } 
     545                } 
     546                importerHeader.each { mc -> 
     547                        def bestfit = importerService.mostSimilar( mc.name, fieldNames, 0.8); 
     548                        if( bestfit ) { 
     549                                // Remove this fit from the list 
     550                                fieldNames.remove( bestfit ); 
     551                                 
     552                                mc.entityclass = bestfit.entity 
     553                                mc.property = bestfit.name 
     554                        } 
     555                } 
     556                 
    718557                // Save read excel data into session 
    719                 session.simpleWizard.excel = [ 
    720                                         workbook: workbook, 
     558                def dataMatrix = []; 
     559                def df = new DataFormatter(); 
     560                importerDataMatrix.each { 
     561                        dataMatrix << it.collect{ it ? df.formatCellValue(it) : "" } 
     562                } 
     563                 
     564                flow.excel = [ 
     565                                        filename: filename, 
    721566                                        sheetIndex: sheetIndex, 
    722567                                        dataMatrixStart: dataMatrixStart, 
     
    724569                                        data: [ 
    725570                                                header: importerHeader, 
    726                                                 dataMatrix: importerDataMatrix 
     571                                                dataMatrix: dataMatrix 
    727572                                        ] 
    728573                                ] 
     
    731576        } 
    732577 
     578         
    733579        /** 
    734580         * Handles the matching of template fields with excel columns by the user 
     
    736582         * @param params        Request parameter map 
    737583         * @return                      True if everything went OK, false otherwise. An error message is put in flash.error 
    738          *                                      The field session.simpleWizard.imported.numInvalidEntities reflects the number of  
     584         *                                      The field session.simpleWizard.imported.numInvalidEntities reflects the number of 
    739585         *                                      entities that have errors, and should be fixed before saving. The errors for those entities 
    740586         *                                      are saved into session.simpleWizard.imported.errors 
    741587         */ 
    742         def handleColumns( study, params ) { 
     588        def handleColumns( study, params, flow ) { 
    743589                // Find actual Template object from the chosen template name 
    744590                def templates = [:]; 
    745                 session.simpleWizard.sampleForm.templateId.each { 
     591                flow.sampleForm.templateId.each { 
    746592                        templates[ it.key ] = it.value ? Template.get( it.value ) : null; 
    747593                } 
    748594                 
    749                 def headers = session.simpleWizard.excel.data.header; 
     595                def headers = flow.excel.data.header; 
    750596 
    751597                if( !params.matches ) { 
     
    796642                println "Importing samples for study " + study + " (" + study.id + ")"; 
    797643                 
     644                def importedfile = fileService.get( flow.excel.filename ) 
     645                def workbook 
     646                if (importedfile.exists()) { 
     647                        try { 
     648                                workbook = importerService.getWorkbook(new FileInputStream(importedfile)) 
     649                        } catch (Exception e) { 
     650                                log.error ".simple study wizard could not load file: " + e 
     651                                flash.error = "The given file doesn't seem to be an excel file. Please provide an excel file for entering samples."; 
     652                                return false 
     653                        } 
     654                } else { 
     655                        log.error ".simple study wizard no file given"; 
     656                        flash.error = "No file was given. Please provide an excel file for entering samples."; 
     657                        return false; 
     658                } 
     659 
     660                if( !workbook ) { 
     661                        log.error ".simple study wizard could not load file into a workbook" 
     662                        flash.error = "The given file doesn't seem to be an excel file. Please provide an excel file for entering samples."; 
     663                        return false 
     664                } 
     665                 
     666                 
    798667                def imported = importerService.importOrUpdateDataBySampleIdentifier(templates, 
    799                                 session.simpleWizard.excel.workbook, 
    800                                 session.simpleWizard.excel.sheetIndex - 1, 
    801                                 session.simpleWizard.excel.dataMatrixStart - 1, 
    802                                 session.simpleWizard.excel.data.header, 
    803                                 study, 
    804                                 true                    // Also create entities for which no data is imported but where templates were chosen  
     668                                workbook, 
     669                                flow.excel.sheetIndex - 1, 
     670                                flow.excel.dataMatrixStart - 1, 
     671                                flow.excel.data.header, 
     672                                flow.study, 
     673                                true                    // Also create entities for which no data is imported but where templates were chosen 
    805674                ); 
    806675 
     
    808677                def failedcells = imported.failedCells 
    809678 
    810                 session.simpleWizard.imported = [ 
     679                flow.imported = [ 
    811680                        data: table, 
    812681                        failedCells: failedcells 
    813682                ]; 
    814  
     683         
    815684                // loop through all entities to validate them and add them to failedcells if an error occurs 
    816685                def numInvalidEntities = 0; 
     
    821690                        record.each { entity -> 
    822691                                if( entity ) { 
    823                                         // Determine entity class and add a parent 
     692                                        // Determine entity class and add a parent. Add the entity to the study 
    824693                                        def preferredIdentifier = importerService.givePreferredIdentifier( entity.class ); 
    825694                                        def equalClosure = { it.getFieldValue( preferredIdentifier.name ) == entity.getFieldValue( preferredIdentifier.name ) } 
    826          
     695                                        def entityName = entity.class.name[ entity.class.name.lastIndexOf( "." ) + 1 .. -1 ] 
     696 
    827697                                        entity.parent = study 
    828698                                         
     
    854724                                                 
    855725                                                // Add this field to the list of failed cells, in order to give the user feedback 
    856                                                 failedcells = addNonValidatingCells( failedcells, entity ) 
     726                                                failedcells = addNonValidatingCells( failedcells, entity, flow ) 
    857727         
    858728                                                // Also create a full list of errors 
    859                                                 errors += getHumanReadableErrors( entity ); 
    860                                         } 
    861                                 } 
    862                         } 
    863                 } 
    864  
    865                 session.simpleWizard.imported.numInvalidEntities = numInvalidEntities + failedcells?.size(); 
    866                 session.simpleWizard.imported.errors = errors; 
     729                                                def currentErrors = getHumanReadableErrors( entity ) 
     730                                                if( currentErrors ) { 
     731                                                        currentErrors.each { 
     732                                                                errors += "(" + entityName + ") " + it.value; 
     733                                                        } 
     734                                                } 
     735                                        } 
     736                                } 
     737                        } 
     738                } 
     739 
     740                flow.imported.numInvalidEntities = numInvalidEntities + failedcells?.size(); 
     741                flow.imported.errors = errors; 
    867742 
    868743                return true 
    869744        } 
    870  
     745         
    871746        /** 
    872747         * Handles the update of the edited fields by the user 
     
    878753         *                                      are saved into session.simpleWizard.imported.errors 
    879754         */ 
    880         def handleMissingFields( study, params ) { 
     755        def handleMissingFields( study, params, flow ) { 
    881756                def numInvalidEntities = 0; 
    882757                def errors = []; 
    883758 
    884759                // Check which fields failed previously 
    885                 def failedCells = session.simpleWizard.imported.failedCells 
    886  
    887                 session.simpleWizard.imported.data.each { table -> 
     760                def failedCells = flow.imported.failedCells 
     761                def newFailedCells = []; 
     762 
     763                flow.imported.data.each { table -> 
    888764                        table.each { entity -> 
    889765                                def invalidFields = 0 
     766                                def failed = new ImportRecord(); 
     767                                def entityName = entity.class.name[ entity.class.name.lastIndexOf( "." ) + 1 .. -1 ] 
     768                                 
    890769 
    891770                                // Set the fields for this entity by retrieving values from the params 
     
    896775                                                // If the value '#invalidterm' is chosen, the user hasn't fixed anything, so this field is still incorrect 
    897776                                                invalidFields++; 
     777                                                 
     778                                                // store the mapping column and value which failed 
     779                                                def identifier = entityName.toLowerCase() + "_" + entity.getIdentifier() + "_" + fieldName 
     780                                                def mcInstance = new MappingColumn() 
     781                                                failed.addToImportcells(new ImportCell(mappingcolumn: mcInstance, value: params[ fieldName ], entityidentifier: identifier)) 
    898782                                        } else { 
    899783                                                if( field.type == org.dbnp.gdt.TemplateFieldType.ONTOLOGYTERM || field.type == org.dbnp.gdt.TemplateFieldType.STRINGLIST ) { 
     
    907791                                        } 
    908792                                } 
    909  
    910                                 // Determine entity class and add a parent 
    911                                 entity.parent = study; 
    912  
     793                                 
    913794                                // Try to validate the entity now all fields have been set. If it fails, return an error 
    914795                                if (!entity.validate() || invalidFields) { 
     
    916797 
    917798                                        // Add this field to the list of failed cells, in order to give the user feedback 
    918                                         failedCells = addNonValidatingCells( failedCells, entity ) 
     799                                        failedCells = addNonValidatingCellsToImportRecord( failed, entity, flow ) 
    919800 
    920801                                        // Also create a full list of errors 
    921                                         errors += getHumanReadableErrors( entity ); 
     802                                        def currentErrors = getHumanReadableErrors( entity ) 
     803                                        if( currentErrors ) { 
     804                                                currentErrors.each { 
     805                                                        errors += "(" + entityName + ") " + it.value; 
     806                                                } 
     807                                        } 
     808                                         
     809                                        newFailedCells << failed; 
    922810                                } else { 
    923811                                        importerService.removeFailedCell( failedCells, entity ) 
     
    926814                } // end of table 
    927815 
    928                 session.simpleWizard.imported.numInvalidEntities = numInvalidEntities; 
    929                 session.simpleWizard.imported.errors = errors; 
    930  
    931                 return true 
     816                flow.imported.failedCells = newFailedCells 
     817                flow.imported.numInvalidEntities = numInvalidEntities; 
     818                flow.imported.errors = errors; 
     819 
     820                return numInvalidEntities == 0 
    932821        } 
    933  
    934         /** 
    935          * Handles assay input 
    936          * @param study         Study to update 
    937          * @param params                Request parameter map 
    938          * @return                      True if everything went OK, false otherwise. An error message is put in flash.error 
    939          */ 
    940         def handleAssays( assay, params ) { 
    941                 // did the study template change? 
    942                 if (params.get('template') && assay.template?.name != params.get('template')) { 
    943                         // set the template 
    944                         assay.template = Template.findByName(params.remove('template')) 
    945                 } 
    946  
    947                 // does the study have a template set? 
    948                 if (assay.template && assay.template instanceof Template) { 
    949                         // yes, iterate through template fields 
    950                         assay.giveFields().each() { 
    951                                 // and set their values 
    952                                 assay.setFieldValue(it.name, params.get(it.escapedName())) 
    953                         } 
    954                 } 
    955  
    956                 // Save the assay in session 
    957                 session.simpleWizard.assay = assay; 
    958  
    959                 return true 
    960         } 
     822         
     823        /** 
     824        * Handles assay input 
     825        * @param study          Study to update 
     826        * @param params         Request parameter map 
     827        * @return                       True if everything went OK, false otherwise. An error message is put in flash.error 
     828        */ 
     829   def handleAssays( assay, params, flow ) { 
     830           // did the study template change? 
     831           if (params.get('template') && assay.template?.name != params.get('template')) { 
     832                   // set the template 
     833                   assay.template = Template.findByName(params.remove('template')) 
     834           } 
     835 
     836           // does the study have a template set? 
     837           if (assay.template && assay.template instanceof Template) { 
     838                   // yes, iterate through template fields 
     839                   assay.giveFields().each() { 
     840                           // and set their values 
     841                           assay.setFieldValue(it.name, params.get(it.escapedName())) 
     842                   } 
     843           } 
     844 
     845           return true 
     846   } 
     847         
    961848         
    962849        /** 
    963850         * Checks whether the given study is simple enough to be edited using this controller. 
    964          *  
    965          * The study is simple enough if the samples, subjects, events and samplingEvents can be  
    966          * edited as a flat table. That is:  
     851         * 
     852         * The study is simple enough if the samples, subjects, events and samplingEvents can be 
     853         * edited as a flat table. That is: 
    967854         *              - Every subject belongs to 0 or 1 eventgroup 
    968855         *              - Every eventgroup belongs to 0 or 1 sample 
    969856         *              - Every eventgroup has 0 or 1 subjects, 0 or 1 event and 0 or 1 samplingEvents 
    970857         *              - If a sample belongs to an eventgroup: 
    971          *                      - If that eventgroup has a samplingEvent, that same samplingEvent must also be  
     858         *                      - If that eventgroup has a samplingEvent, that same samplingEvent must also be 
    972859         *                              the sampling event that generated this sample 
    973860         *                      - If that eventgroup has a subject, that same subject must also be the subject 
    974861         *                              from whom the sample was taken 
    975          *  
     862         * 
    976863         * @param study         Study to check 
    977864         * @return                      True if the study can be edited by this controller, false otherwise 
     
    979866        def checkStudySimplicity( study ) { 
    980867                def simplicity = true; 
    981                  
     868 
    982869                if( !study ) 
    983870                        return false 
     
    990877                                        simplicity = false; 
    991878                                } 
    992                                  
     879 
    993880                                // Check whether this eventgroup only belongs to (max) 1 sample 
    994881                                def numSamples = 0; 
    995882                                study.samples.each { sample -> 
    996                                         // If no id is given for the eventGroup, it has been entered in this wizard, but  
     883                                        // If no id is given for the eventGroup, it has been entered in this wizard, but 
    997884                                        // not yet saved. In that case, it is always OK 
    998885                                        if( eventGroup.id && sample.parentEventGroup?.id == eventGroup.id ) 
    999886                                                numSamples++; 
    1000887                                } 
    1001                                  
    1002                                 if( numSamples > 1 ) {  
     888 
     889                                if( numSamples > 1 ) { 
    1003890                                        flash.message = "One or more eventgroups belong to multiple samples." 
    1004891                                        simplicity = false; 
    1005892                                } 
    1006893                        } 
    1007                          
     894 
    1008895                        if( !simplicity ) return false; 
    1009896 
     
    1018905                                                        numEventGroups++ 
    1019906                                        } 
    1020                                          
     907 
    1021908                                        if( numEventGroups > 1 ) { 
    1022909                                                flash.message = "One or more subjects belong to multiple eventgroups." 
     
    1027914 
    1028915                        if( !simplicity ) return false; 
    1029                          
     916 
    1030917                        // Check whether the samples that belong to an eventgroup have the right parentObjects 
    1031918                        study.samples.each { sample -> 
     
    1039926                                                } 
    1040927                                        } 
    1041                                          
     928 
    1042929                                        // If no id is given for the sampling event, it has been entered in this wizard, but 
    1043930                                        // not yet saved. In that case, it is always OK 
     
    1050937                                } 
    1051938                        } 
    1052                          
     939 
    1053940                        if( !simplicity ) return false; 
    1054941                } 
    1055                  
     942 
    1056943                return simplicity; 
    1057944        } 
    1058945 
    1059         def attachAndValidateEntities( def study ) { 
    1060                 if( !session.simpleWizard?.imported?.data )  
    1061                         return 
    1062                           
    1063                 def table = session.simpleWizard.imported.data 
    1064                 def numInvalidEntities = 0; 
    1065                  
    1066                 // Add all samples 
    1067                 table.each { record -> 
    1068                         println record*.class 
    1069                         record.each { entity -> 
    1070                                 if( entity ) { 
    1071                                         if( entity.validate() ) { 
    1072                                                 println "Saving: " + entity + " (" + entity.class.name + ")" 
    1073                                                 println study.samples; 
    1074                                                 println study.subjects; 
    1075                                                  
    1076                                                 // Determine entity class and add a parent 
    1077                                                 def preferredIdentifier = importerService.givePreferredIdentifier( entity.class ); 
    1078                                                 def equalClosure = { it.getFieldValue( preferredIdentifier.name ) == entity.getFieldValue( preferredIdentifier.name ) } 
    1079                  
    1080                                                 //entity.parent = study 
    1081                                                 switch( entity.class ) { 
    1082                                                         case Sample: 
    1083                                                                 if( preferredIdentifier && !study.samples?.find( equalClosure ) ) { 
    1084                                                                         study.addToSamples( entity ); 
    1085                                                                 } 
    1086                                                                 break; 
    1087                                                         case Subject: 
    1088                                                                 if( preferredIdentifier && !study.subjects?.find( equalClosure ) ) { 
    1089                                                                         study.addToSubjects( entity ); 
    1090                                                                 } 
    1091                                                                 break; 
    1092                                                         case Event: 
    1093                                                                 if( preferredIdentifier && !study.events?.find( equalClosure ) ) { 
    1094                                                                         study.addToEvents( entity ); 
    1095                                                                 } 
    1096                                                                 break; 
    1097                                                         case SamplingEvent: 
    1098                                                                 if( preferredIdentifier && !study.samplingEvents?.find( equalClosure ) ) { 
    1099                                                                         study.addToSamplingEvents( entity ); 
    1100                                                                 } 
    1101                                                                 break; 
    1102                                                 } 
    1103                                                  
    1104                                                 entity.save(); 
    1105                                                  
    1106                                         } else { 
    1107                                                 numInvalidEntities++; 
    1108                                         } 
    1109                                 } 
    1110                         } 
    1111                 } 
    1112  
    1113                 return numInvalidEntities == 0; 
     946         
     947        /** 
     948         * Adds all fields of this entity that have given an error when validating to the failedcells list 
     949         * @param failedcells   Current list of ImportRecords 
     950         * @param entity                Entity to check. The entity must have been validated before 
     951         * @return                              Updated list of ImportRecords 
     952         */ 
     953        protected def addNonValidatingCells( failedcells, entity, flow ) { 
     954                // Add this entity and the fields with an error to the failedCells list 
     955                ImportRecord failedRecord = addNonValidatingCellsToImportRecord( new ImportRecord(), entity, flow ); 
     956 
     957                failedcells.add( failedRecord ); 
     958 
     959                return failedcells 
    1114960        } 
    1115                  
     961         
     962        /** 
     963        * Adds all fields of this entity that have given an error when validating to the failedcells list 
     964        * @param failedcells    Current list of ImportRecords 
     965        * @param entity         Entity to check. The entity must have been validated before 
     966        * @return                               Updated list of ImportRecords 
     967        */ 
     968   protected def addNonValidatingCellsToImportRecord( failedRecord, entity, flow ) { 
     969           entity.getErrors().getFieldErrors().each { error -> 
     970                   String field = error.getField(); 
     971                    
     972                   def mc = importerService.findMappingColumn( flow.excel.data.header, field ); 
     973                   def mcInstance = new MappingColumn( name: field, entityClass: Sample.class, index: -1, property: field.toLowerCase(), templateFieldType: entity.giveFieldType( field ) ); 
     974 
     975                   // Create a clone of the mapping column 
     976                   if( mc ) { 
     977                           mcInstance.properties = mc.properties 
     978                   } 
     979 
     980                   failedRecord.addToImportcells( new ImportCell(mappingcolumn: mcInstance, value: error.getRejectedValue(), entityidentifier: importerService.getFieldNameInTableEditor( entity, field ) ) ) 
     981           } 
     982            
     983           return failedRecord 
     984   } 
     985 
     986         
     987        /** 
     988        * Checks an excel workbook whether the given sheetindex and rownumbers are correct 
     989        * @param workbook                       Excel workbook to read 
     990        * @param sheetIndex             1-based sheet index for the sheet to read (1=first sheet) 
     991        * @param headerRow                      1-based row number for the header row (1=first row) 
     992        * @param dataMatrixStart        1-based row number for the first data row (1=first row) 
     993        * @return                                       True if the sheet index and row numbers are correct. 
     994        */ 
     995   protected boolean excelChecks( def workbook, int sheetIndex, int headerRow, int dataMatrixStart ) { 
     996           // Perform some basic checks on the excel file. These checks should be performed by the importerservice 
     997           // in a perfect scenario. 
     998           if( sheetIndex > workbook.getNumberOfSheets() ) { 
     999                   log.error ".simple study wizard Sheet index is too high: " + sheetIndex + " / " + workbook.getNumberOfSheets(); 
     1000                   flash.error = "Your excel sheet contains too few excel sheets. The provided excel sheet has only " + workbook.getNumberOfSheets() + " sheet(s)."; 
     1001                   return false 
     1002           } 
     1003 
     1004           def sheet = workbook.getSheetAt(sheetIndex - 1); 
     1005           def firstRowNum = sheet.getFirstRowNum(); 
     1006           def lastRowNum = sheet.getLastRowNum(); 
     1007           def numRows = lastRowNum - firstRowNum + 1; 
     1008 
     1009           if( headerRow > numRows  ) { 
     1010                   log.error ".simple study wizard Header row number is incorrect: " + headerRow + " / " + numRows; 
     1011                   flash.error = "Your excel sheet doesn't contain enough rows (" + numRows + "). Please provide an excel sheet with one header row and data below"; 
     1012                   return false 
     1013           } 
     1014 
     1015           if( dataMatrixStart > numRows  ) { 
     1016                   log.error ".simple study wizard Data row number is incorrect: " + dataMatrixStart + " / " + numRows; 
     1017                   flash.error = "Your excel sheet doesn't contain enough rows (" + numRows + "). Please provide an excel sheet with one header row and data below"; 
     1018                   return false 
     1019           } 
     1020 
     1021           return true; 
     1022   } 
     1023         
    11161024        /** 
    11171025         * Validates an object and puts human readable errors in validationErrors variable 
     
    11251033                } 
    11261034                return true; 
    1127         } 
    1128  
    1129         /** 
    1130          * Adds all fields of this entity that have given an error when validating to the failedcells list 
    1131          * @param failedcells   Current list of ImportRecords 
    1132          * @param entity                Entity to check. The entity must have been validated before 
    1133          * @return                              Updated list of ImportRecords 
    1134          */ 
    1135         protected def addNonValidatingCells( failedcells, entity ) { 
    1136                 // Add this entity and the fields with an error to the failedCells list 
    1137                 ImportRecord failedRecord = new ImportRecord(); 
    1138  
    1139                 entity.getErrors().getFieldErrors().each { error -> 
    1140                         String field = error.getField(); 
    1141                          
    1142                         def mc = importerService.findMappingColumn( session.simpleWizard.excel.data.header, field ); 
    1143                         def mcInstance = new MappingColumn( name: field, entityClass: Sample.class, index: -1, property: field.toLowerCase(), templateFieldType: entity.giveFieldType( field ) ); 
    1144  
    1145                         // Create a clone of the mapping column 
    1146                         if( mc ) { 
    1147                                 mcInstance.properties = mc.properties 
    1148                         } 
    1149  
    1150                         failedRecord.addToImportcells( new ImportCell(mappingcolumn: mcInstance, value: error.getRejectedValue(), entityidentifier: importerService.getFieldNameInTableEditor( entity, field ) ) ) 
    1151                 } 
    1152                 failedcells.add( failedRecord ); 
    1153  
    1154                 return failedcells 
    1155         } 
    1156  
    1157  
    1158         /** 
    1159          * Checks an excel workbook whether the given sheetindex and rownumbers are correct      
    1160          * @param workbook                      Excel workbook to read 
    1161          * @param sheetIndex            1-based sheet index for the sheet to read (1=first sheet) 
    1162          * @param headerRow                     1-based row number for the header row (1=first row) 
    1163          * @param dataMatrixStart       1-based row number for the first data row (1=first row) 
    1164          * @return                                      True if the sheet index and row numbers are correct. 
    1165          */ 
    1166         protected boolean excelChecks( def workbook, int sheetIndex, int headerRow, int dataMatrixStart ) { 
    1167                 // Perform some basic checks on the excel file. These checks should be performed by the importerservice 
    1168                 // in a perfect scenario. 
    1169                 if( sheetIndex > workbook.getNumberOfSheets() ) { 
    1170                         log.error ".simple study wizard Sheet index is too high: " + sheetIndex + " / " + workbook.getNumberOfSheets(); 
    1171                         flash.error = "Your excel sheet contains too few excel sheets. The provided excel sheet has only " + workbook.getNumberOfSheets() + " sheet(s)."; 
    1172                         return false 
    1173                 } 
    1174  
    1175                 def sheet = workbook.getSheetAt(sheetIndex - 1); 
    1176                 def firstRowNum = sheet.getFirstRowNum(); 
    1177                 def lastRowNum = sheet.getLastRowNum(); 
    1178                 def numRows = lastRowNum - firstRowNum + 1; 
    1179  
    1180                 if( headerRow > numRows  ) { 
    1181                         log.error ".simple study wizard Header row number is incorrect: " + headerRow + " / " + numRows; 
    1182                         flash.error = "Your excel sheet doesn't contain enough rows (" + numRows + "). Please provide an excel sheet with one header row and data below"; 
    1183                         return false 
    1184                 } 
    1185  
    1186                 if( dataMatrixStart > numRows  ) { 
    1187                         log.error ".simple study wizard Data row number is incorrect: " + dataMatrixStart + " / " + numRows; 
    1188                         flash.error = "Your excel sheet doesn't contain enough rows (" + numRows + "). Please provide an excel sheet with one header row and data below"; 
    1189                         return false 
    1190                 } 
    1191  
    1192                 return true; 
    1193         } 
    1194  
    1195         /** 
    1196          * Redirects the user to the page with the given name 
    1197          * @param action 
    1198          */ 
    1199         protected void toPage( String action ) { 
    1200                 println "Redirecting to: " + action; 
    1201                 redirect( action: action, params: [ "wizard": true ] ); 
    1202         } 
    1203  
    1204         /** 
    1205          * Returns the event that is specified by the user form 
    1206          * @param params 
    1207          * @return 
    1208          */ 
    1209         protected String getEvent( def params ) { 
    1210                 return params.get( 'event' ); 
    1211         } 
    1212  
    1213         /** 
    1214          * Retrieves the required study from the database or return an empty Study object if  
    1215          * no id is given 
    1216          *  
    1217          * @param params        Request parameters with params.id being the ID of the study to be retrieved 
    1218          * @return                      A study from the database or an empty study if no id was given 
    1219          */ 
    1220         protected Study getStudyFromRequest( def params ) { 
    1221                 int id = params.int( "id" ); 
    1222  
    1223                 if( !id ) { 
    1224                         return new Study( title: "New study", owner: authenticationService.getLoggedInUser() ); 
    1225                 } 
    1226  
    1227                 Study s = Study.get( id ); 
    1228  
    1229                 if( !s ) { 
    1230                         flash.error = "No study found with given id"; 
    1231                         return null; 
    1232                 } 
    1233                 if( !s.canWrite( authenticationService.getLoggedInUser() ) ) { 
    1234                         flash.error = "No authorization to edit this study." 
    1235                         return null; 
    1236                 } 
    1237  
    1238                 return s 
    1239         } 
    1240  
    1241         /** 
    1242          * Attach a template to the session 
    1243          * @param t 
    1244          * @return 
    1245          */ 
    1246         protected attachTemplate( Template t ) { 
    1247                 if( t && !t.isAttached() ) { 
    1248                         t.attach(); 
    1249  
    1250                         t.fields.each { field -> 
    1251                                 if( field && !field.isAttached() ) 
    1252                                         field.attach(); 
    1253          
    1254                                 field.listEntries?.each { entry -> 
    1255                                         if( entry && !entry.isAttached() ) 
    1256                                                 entry.attach(); 
    1257                                 } 
    1258                                 field.ontologies?.each { entry -> 
    1259                                         if( entry && !entry.isAttached() ) 
    1260                                                 entry.attach(); 
    1261                                 } 
    1262                         } 
    1263                 } 
    1264         } 
    1265          
    1266         /** 
    1267          * Retrieves the study that is saved in the wizard,  
    1268          *  
    1269          * @param params        Request parameters 
    1270          * @return                      The found study object, or null if no study object is found 
    1271          */ 
    1272         protected Study getStudyInWizard( def params ) { 
    1273                 if( params.wizard && session.simpleWizard && session.simpleWizard.study ) { 
    1274                         // The user came here by clicking previous or a link on another page. Use the existing study 
    1275                         Study s = session.simpleWizard.study; 
    1276  
    1277                         if( s.id && !s.isAttached() ) { 
    1278                                 s.attach(); 
    1279                         } 
    1280                          
    1281                         s.samples?.each { 
    1282                                 if( it && it.id && !it.isAttached() ) 
    1283                                         it.attach(); 
    1284                                           
    1285                                 attachTemplate( it.template ) 
    1286                         } 
    1287                         s.subjects?.each {  
    1288                                 if( it && it.id && !it.isAttached() ) 
    1289                                         it.attach(); 
    1290                                           
    1291                                 attachTemplate( it.template ) 
    1292                         } 
    1293                         s.events?.each {  
    1294                                 if( it && it.id && !it.isAttached() ) 
    1295                                         it.attach(); 
    1296                                           
    1297                                 attachTemplate( it.template ) 
    1298                         } 
    1299  
    1300                         s.samplingEvents?.each { 
    1301                                 if( it && it.id && !it.isAttached() ) 
    1302                                         it.attach(); 
    1303                                           
    1304                                 attachTemplate( it.template ) 
    1305                         } 
    1306                         s.assays?.each {  
    1307                                 if( it && it.id && !it.isAttached() ) 
    1308                                         it.attach(); 
    1309                                           
    1310                                 attachTemplate( it.template ) 
    1311                         } 
    1312  
    1313                         return s; 
    1314                 } else { 
    1315                         // The user didn't get here from the wizard or no study is found 
    1316                         return null; 
    1317                 } 
    1318         } 
    1319  
    1320         /** 
    1321          * Retrieves the assay to edit in the wizard 
    1322          * @param s             Study that the assay will be in 
    1323          * @return              Assay object (may be empty) 
    1324          */ 
    1325         protected Assay getAssayInWizard( Study study = null ) { 
    1326                 if( session.simpleWizard && session.simpleWizard.assay ) { 
    1327                         Assay a = session.simpleWizard.assay; 
    1328  
    1329                         if( a.id && !a.isAttached() ) { 
    1330                                 a.attach(); 
    1331  
    1332                                 attachTemplate( a.template ); 
    1333                         } 
    1334  
    1335                         return a; 
    1336                 } else if( study ) { 
    1337                         // The user came on the assay page for the first time 
    1338                         if( study.assays?.size() ) { 
    1339                                 def assay = study.assays[0]; 
    1340  
    1341                                 return assay; 
    1342                         } else { 
    1343                                 return new Assay( parent: study ); 
    1344                         } 
    1345                 } else { 
    1346                         return null; 
    1347                 } 
    13481035        } 
    13491036 
  • trunk/grails-app/services/dbnp/importer/ImporterService.groovy

    r1603 r1608  
    2323        def authenticationService 
    2424 
    25         static transactional = true 
     25        static transactional = false 
    2626 
    2727        /** 
     
    339339                        importedEntities = importedRows.flatten().findAll { it.class == dbnp.studycapturing.Sample }.unique(); 
    340340 
    341                 def importedSample = findEntityInImportedEntities( dbnp.studycapturing.Sample, excelRow, mcmap, importedEntities, df ) 
    342                 def imported = retrieveEntitiesBySample( importedSample ); 
     341                def importedSample = null // findEntityInImportedEntities( dbnp.studycapturing.Sample, excelRow, mcmap, importedEntities, df ) 
     342                def imported = [] // retrieveEntitiesBySample( importedSample ); 
    343343                 
    344344                for( entity in entities ) { 
     
    675675         * Retrieves a mapping column from a list based on the given fieldname 
    676676         * @param mappingColumns                List of mapping columns 
    677          * @param fieldName                     Field name to find 
    678          * @return                                      Mapping column if a column is found, null otherwise 
     677         * @param fieldName                             Field name to find 
     678         * @return                                              Mapping column if a column is found, null otherwise 
    679679         */ 
    680680        def findMappingColumn( mappingColumns, String fieldName ) {