Changeset 1417

Show
Ignore:
Timestamp:
20-01-11 16:10:50 (3 years ago)
Author:
work@…
Message:

- did some extreme programming with Tjeerd to fix issues in the importer where it either failed to finish, or failed to save template fields. Turns out it was due to the fact the method in the service was not static

Location:
trunk/grails-app
Files:
2 modified

Legend:

Unmodified
Added
Removed
  • trunk/grails-app/controllers/dbnp/importer/ImporterController.groovy

    r1416 r1417  
    9292                [title: 'Properties'], 
    9393                [title: 'Mappings'], 
    94                 [title: 'Imported'], 
     94                //[title: 'Imported'], 
    9595                [title: 'Persist'] 
    9696            ] 
     
    212212                    error() 
    213213                } 
    214             }.to "pageFour" 
     214            }.to "save" 
    215215            on("previous").to "pageTwo" 
    216216        } 
     
    248248 
    249249                    if (saveEntities(flow, params)) { 
     250                                        //if (ImporterService.saveDatamatrix(flow.importer_study, flow.importer_importeddata)) { 
    250251                        log.error ".import wizard succesfully persisted all entities"                         
    251                         def session = sessionFactory.getCurrentSession() 
    252                         session.clear()                         
     252                        //def session = sessionFactory.getCurrentSession() 
     253                        //session.clear() 
    253254                        success() 
    254255                    } else {                         
  • trunk/grails-app/services/dbnp/importer/ImporterService.groovy

    r1416 r1417  
    55 * based files. 
    66 * 
    7  * @package     importer 
    8  * @author      t.w.abma@umcutrecht.nl 
    9  * @since       20100126 
     7 * @package importer 
     8 * @author t.w.abma@umcutrecht.nl 
     9 * @since 20100126 
    1010 * 
    1111 * Revision information: 
     
    1616 
    1717package dbnp.importer 
     18 
    1819import org.apache.poi.ss.usermodel.* 
    1920import org.apache.poi.xssf.usermodel.XSSFCell 
     
    2627import dbnp.studycapturing.Event 
    2728import dbnp.studycapturing.Sample 
     29import dbnp.data.Term 
     30import org.apache.commons.lang.RandomStringUtils 
    2831 
    2932class ImporterService { 
    30     def AuthenticationService 
    31  
    32     boolean transactional = true 
    33  
    34     /** 
    35     * @param is input stream representing the (workbook) resource 
    36     * @return high level representation of the workbook 
    37     */ 
    38     Workbook getWorkbook(InputStream is) { 
    39         WorkbookFactory.create(is) 
    40     } 
    41  
    42     /** 
    43      * @param wb high level representation of the workbook 
    44      * @param sheetindex sheet to use within the workbook 
    45      * @return header representation as a MappingColumn hashmap 
    46      */ 
    47     def getHeader(Workbook wb, int sheetindex, int headerrow, int datamatrix_start, theEntity=null) { 
    48         def sheet = wb.getSheetAt(sheetindex) 
    49         def sheetrow = sheet.getRow(datamatrix_start) 
    50         //def header = [] 
    51         def header = [:] 
    52         def df = new DataFormatter() 
    53         def property = new String() 
    54  
    55         //for (Cell c: sheet.getRow(datamatrix_start)) { 
    56  
    57         (0..sheetrow.getLastCellNum() -1 ).each { columnindex -> 
    58  
    59             //def index =   c.getColumnIndex() 
    60             def datamatrix_celltype = sheet.getRow(datamatrix_start).getCell(columnindex,Row.CREATE_NULL_AS_BLANK).getCellType() 
    61             def datamatrix_celldata = df.formatCellValue(sheet.getRow(datamatrix_start).getCell(columnindex)) 
    62             def datamatrix_cell     = sheet.getRow(datamatrix_start).getCell(columnindex)            
    63         def headercell = sheet.getRow(headerrow-1+sheet.getFirstRowNum()).getCell(columnindex) 
    64             def tft = TemplateFieldType.STRING //default templatefield type 
    65  
    66         // Check for every celltype, currently redundant code, but possibly this will be 
    67             // a piece of custom code for every cell type like specific formatting           
    68                  
    69             switch (datamatrix_celltype) { 
    70                 case Cell.CELL_TYPE_STRING: 
    71                     //parse cell value as double 
    72                     def doubleBoolean = true 
    73                     def fieldtype = TemplateFieldType.STRING 
    74  
    75                     // is this string perhaps a double? 
    76                     try { 
    77                         formatValue(datamatrix_celldata, TemplateFieldType.DOUBLE) 
    78                     } catch (NumberFormatException nfe) { doubleBoolean = false } 
    79                     finally { 
    80                         if (doubleBoolean) fieldtype = TemplateFieldType.DOUBLE 
    81                     } 
    82  
    83                     header[columnindex] = new dbnp.importer.MappingColumn(name:df.formatCellValue(headercell), 
    84                                                                             templatefieldtype:fieldtype, 
    85                                                                             index:columnindex, 
    86                                                                             entity:theEntity, 
    87                                                                             property:property); 
    88  
    89                     break 
    90                 case Cell.CELL_TYPE_NUMERIC: 
    91                     def fieldtype = TemplateFieldType.LONG 
    92                     def doubleBoolean = true 
    93                     def longBoolean = true 
    94  
    95                     // is this cell really an integer? 
    96                     try { 
    97                         Long.valueOf(datamatrix_celldata) 
    98                     } catch (NumberFormatException nfe) { longBoolean = false } 
    99                     finally { 
    100                         if (longBoolean) fieldtype = TemplateFieldType.LONG 
    101                     } 
    102  
    103                     // it's not an long, perhaps a double? 
    104                     if (!longBoolean) 
    105                     try { 
    106                                     formatValue(datamatrix_celldata, TemplateFieldType.DOUBLE) 
    107                                 } catch (NumberFormatException nfe) { doubleBoolean = false } 
    108                                 finally { 
    109                                     if (doubleBoolean) fieldtype = TemplateFieldType.DOUBLE 
    110                                 } 
    111  
    112                     if (DateUtil.isCellDateFormatted(datamatrix_cell)) fieldtype = TemplateFieldType.DATE 
    113  
    114                     header[columnindex] = new dbnp.importer.MappingColumn(name:df.formatCellValue(headercell), 
    115                                                                             templatefieldtype:fieldtype, 
    116                                                                             index:columnindex, 
    117                                                                             entity:theEntity, 
    118                                                                             property:property); 
    119                     break 
    120                 case Cell.CELL_TYPE_BLANK: 
    121                     header[columnindex] = new dbnp.importer.MappingColumn(name:df.formatCellValue(headercell), 
    122                                                                             templatefieldtype:TemplateFieldType.STRING, 
    123                                                                             index:columnindex, 
    124                                                                             entity:theEntity, 
    125                                                                             property:property); 
    126                     break 
    127                 default: 
    128                     header[columnindex] = new dbnp.importer.MappingColumn(name:df.formatCellValue(headercell), 
    129                                                                             templatefieldtype:TemplateFieldType.STRING, 
    130                                                                             index:columnindex, 
    131                                                                             entity:theEntity, 
    132                                                                             property:property); 
    133                     break 
    134                 } // end of switch 
    135         } // end of cell loop 
    136         return header 
    137     } 
    138  
    139     /** 
    140      * This method is meant to return a matrix of the rows and columns 
    141      * used in the preview 
    142      * 
    143      * @param wb workbook object 
    144      * @param sheetindex sheet index used 
    145      * @param rows amount of rows returned 
    146      * @return two dimensional array (matrix) of Cell objects 
    147      */ 
    148  
    149     Object[][] getDatamatrix(Workbook wb, header, int sheetindex, int datamatrix_start, int count) { 
    150         def sheet = wb.getSheetAt(sheetindex) 
    151         def rows  = [] 
    152         def df = new DataFormatter() 
    153  
    154     count = (count < sheet.getLastRowNum()) ? count : sheet.getLastRowNum() 
    155  
    156         // walk through all rows         
    157         ((datamatrix_start+sheet.getFirstRowNum())..count).each { rowindex -> 
    158             def row = [] 
    159  
    160             (0..header.size()-1).each { columnindex -> 
    161             def c = sheet.getRow(rowindex).getCell(columnindex, Row.CREATE_NULL_AS_BLANK)             
    162             row.add(c) 
    163             } 
    164       
    165             rows.add(row)      
    166         } 
    167  
    168         return rows 
    169     } 
    170  
    171     /** 
    172     * This method will move a file to a new location. 
    173     * 
    174     * @param file File object to move 
    175     * @param folderpath folder to move the file to 
    176     * @param filename (new) filename to give 
    177     * @return if file has been moved succesful, the new path and filename will be returned, otherwise an empty string will be returned 
    178     */ 
    179     def moveFile(File file, String folderpath, String filename) { 
    180         try { 
    181                 def rnd = ""; //System.currentTimeMillis() 
    182                 file.transferTo(new File(folderpath, rnd+filename)) 
    183                 return folderpath + filename 
    184             } catch(Exception exception) { 
    185                 log.error "File move error, ${exception}" 
    186                 return "" 
    187                 } 
    188     } 
    189  
    190     /** 
    191     * @return random numeric value 
    192     */ 
    193     def random = { 
    194             return System.currentTimeMillis() + Runtime.runtime.freeMemory() 
    195         } 
    196  
    197     /** 
    198     * Method to read data from a workbook and to import data into a two dimensional 
    199     * array 
    200     * 
    201     * @param template_id template identifier to use fields from 
    202     * @param wb POI horrible spreadsheet formatted workbook object 
    203     * @param mcmap linked hashmap (preserved order) of MappingColumns 
    204     * @param sheetindex sheet to use when using multiple sheets 
    205     * @param rowindex first row to start with reading the actual data (NOT the header) 
    206     * @return two dimensional array containing records (with entities) 
    207     *  
    208     * @see dbnp.importer.MappingColumn 
    209     */ 
    210     def importData(template_id, Workbook wb, int sheetindex, int rowindex, mcmap) { 
    211         def sheet = wb.getSheetAt(sheetindex) 
    212     def template = Template.get(template_id) 
    213         def table = [] 
    214     def failedcells = [] // list of records 
    215          
    216         // walk through all rows and fill the table with records 
    217         (rowindex..sheet.getLastRowNum()).each { i ->        
    218             // Create an entity record based on a row read from Excel and store the cells which failed to be mapped 
    219             def (record, failed) = createRecord(template, sheet.getRow(i), mcmap) 
    220  
    221             // Add record with entity and its values to the table 
    222             table.add(record) 
    223  
    224             // If failed cells have been found, add them to the failed cells list             
    225             if (failed?.importcells?.size()>0) failedcells.add(failed) 
    226         } 
    227  
    228         return [table,failedcells] 
    229     } 
    230  
    231     /** Method to put failed cells back into the datamatrix. Failed cells are cell values 
    232      * which could not be stored in an entity (e.g. Humu Supiuns in an ontology field). 
    233      * Empty corrections should not be stored 
    234      * 
    235      * @param datamatrix two dimensional array containing entities and possibly also failed cells 
    236      * @param failedcells list with maps of failed cells in [mappingcolumn, cell] format 
    237      * @param correctedcells map of corrected cells in [cellhashcode, value] format 
    238      **/ 
    239     def saveCorrectedCells(datamatrix, failedcells, correctedcells) {        
    240          
    241         // Loop through all failed cells (stored as 
    242         failedcells.each { record -> 
    243             record.value.importcells.each { cell -> 
    244  
    245                   // Get the corrected value 
    246                   def correctedvalue = correctedcells.find { it.key.toInteger() == cell.getIdentifier()}.value 
    247  
    248                   // Find the record in the table which the mappingcolumn belongs to 
    249                   def tablerecord = datamatrix.find { it.hashCode() == record.key } 
    250  
    251                   // Loop through all entities in the record and correct them if necessary 
    252                   tablerecord.each { rec -> 
    253                       rec.each { entity -> 
    254                             try { 
    255                                 // Update the entity field 
    256                                 entity.setFieldValue(cell.mappingcolumn.property, correctedvalue) 
    257                                 //println "Adjusted " + cell.mappingcolumn.property + " to " + correctedvalue 
    258                             } 
    259                             catch (Exception e) { 
    260                                 //println "Could not map corrected ontology: " + cell.mappingcolumn.property + " to " + correctedvalue 
    261                             } 
    262                       } 
    263                   } // end of table record 
    264                } // end of cell record 
    265             } // end of failedlist 
    266     } 
    267     
    268     /** 
    269      * Method to store a matrix containing the entities in a record like structure. Every row in the table 
    270      * contains one or more entity objects (which contain fields with values). So actually a row represents 
    271      * a record with fields from one or more different entities. 
    272      * 
    273      * @param study entity Study 
    274      * @param datamatrix two dimensional array containing entities with values read from Excel file 
    275      */     
    276     def saveDatamatrix(Study study, datamatrix) { 
    277         def validatedSuccesfully = 0 
    278     def entitystored = null 
    279      
    280     // Study passed? Sync data 
    281     if (study!=null) study.refresh() 
    282  
    283     //study.subjects.each { it.refresh() } 
    284          
    285         // go through the data matrix, read every record and validate the entity and try to persist it 
    286         datamatrix.each { record -> 
    287             record.each { entity ->                 
    288                         switch (entity.getClass()) { 
    289                         case Study       :  log.info "Persisting Study `" + entity + "`: " 
    290                                                 entity.owner = AuthenticationService.getLoggedInUser()                                               
    291                                                 persistEntity(entity)                                                     
    292                                         break 
    293                         case Subject :  log.info "Persisting Subject `" + entity + "`: " 
    294  
    295                                         // is the current entity not already in the database? 
    296                                         //entitystored = isEntityStored(entity) 
    297                                                  
    298                                         // this entity is new, so add it to the study 
    299                                         //if (entitystored==null) 
    300                                         study.addToSubjects(entity) 
    301                                          
    302                                         /*else { // existing entity, so update it 
    303                                             updateEntity(entitystored, entity) 
    304                                             updatedentities.add(entity) 
    305                                         }*/ 
    306      
    307                                         break 
    308                         case Event       :  log.info "Persisting Event `" + entity + "`: "                                       
    309                                         study.addToEvents(entity)                                       
    310                                         break 
    311                         case Sample      :  log.info "Persisting Sample `" + entity +"`: "                                       
    312                                                  
    313                                         // is this sample validatable (sample name unique for example?)                                         
    314                                         study.addToSamples(entity)                                         
    315                                          
    316                                         break 
    317                     case SamplingEvent: log.info "Persisting SamplingEvent `" + entity + "`: "                                     
    318                                         study.addToSamplingEvents(entity)                                        
    319                                         break 
    320                         default          :  log.info "Skipping persisting of `" + entity.getclass() +"`"                                         
    321                                         break 
    322                         } // end switch 
    323             } // end record 
    324         } // end datamatrix 
    325  
    326  
    327     if (study!=null) study.save(failOnError:true) 
    328         //persistEntity(study) 
    329      
    330         //return [validatedSuccesfully, updatedentities, failedtopersist] 
    331     //return [0,0,0] 
    332     } 
    333  
    334     /** 
    335      * Check whether an entity already exist. A unique field in the entity is 
    336      * used to check whether the instantiated entity (read from Excel) is new. 
    337      * If the entity is found in the database it will be returned as is. 
    338      * 
    339      * @param entity entity object like a Study, Subject, Sample et cetera 
    340      * @return entity if found, otherwise null 
    341      */ 
    342     def isEntityStored(entity) { 
    343             switch (entity.getClass()) { 
    344                         case Study          :  return Study.findByCode(entity.code) 
    345                                                break 
    346                         case Subject        :  return Subject.findByParentAndName(entity.parent, entity.name) 
    347                                                break 
    348                         case Event          :  break 
    349                         case Sample         : 
    350                                                break 
    351                         case SamplingEvent  :  break 
    352                         default             :  // unknown entity 
    353                                                return null 
    354             } 
    355     } 
    356  
    357     /** 
    358      * Find the entity and update the fields. The entity is an instance 
    359      * read from Excel. This method looks in the database for the entity 
    360      * having the same identifier. If it has found the same entity 
    361      * already in the database, it will update the record. 
    362      * 
    363      * @param entitystored existing record in the database to update 
    364      * @param entity entity read from Excel 
    365      */ 
    366     def updateEntity(entitystored, entity) { 
    367         switch (entity.getClass()) { 
    368                         case Study          :  break 
    369                         case Subject        :  entitystored.properties = entity.properties 
    370                                                entitystored.save() 
    371                                                break 
    372                         case Event          :  break 
    373                         case Sample         :  break 
    374                         case SamplingEvent  :  break 
    375                         default             :  // unknown entity 
    376                                                return null 
    377         } 
    378     } 
    379  
    380     /** 
    381      * Method to persist entities into the database 
    382      * Checks whether entity already exists (based on identifier column 'name') 
    383      *  
    384      * @param entity entity object like Study, Subject, Protocol et cetera 
    385      *  
    386      */ 
    387     boolean persistEntity(entity) { 
    388             log.info ".import wizard persisting ${entity}" 
    389  
    390          try {           
    391                 entity.save(flush:true) 
    392                 return true 
    393  
    394          } catch (Exception e) { 
    395              def session = sessionFactory.currentSession 
    396              session.setFlushMode(org.hibernate.FlushMode.MANUAL) 
    397              log.error ".import wizard, failed to save entity:\n" + org.apache.commons.lang.exception.ExceptionUtils.getRootCauseMessage(e) 
    398          } 
    399  
    400          return true 
    401          } 
     33        def AuthenticationService 
     34 
     35        boolean transactional = true 
     36 
     37        /** 
     38         * @param is input stream representing the (workbook) resource 
     39         * @return high level representation of the workbook 
     40         */ 
     41        Workbook getWorkbook(InputStream is) { 
     42                WorkbookFactory.create(is) 
     43        } 
     44 
     45        /** 
     46         * @param wb high level representation of the workbook 
     47         * @param sheetindex sheet to use within the workbook 
     48         * @return header representation as a MappingColumn hashmap 
     49         */ 
     50        def getHeader(Workbook wb, int sheetindex, int headerrow, int datamatrix_start, theEntity = null) { 
     51                def sheet = wb.getSheetAt(sheetindex) 
     52                def sheetrow = sheet.getRow(datamatrix_start) 
     53                //def header = [] 
     54                def header = [:] 
     55                def df = new DataFormatter() 
     56                def property = new String() 
     57 
     58                //for (Cell c: sheet.getRow(datamatrix_start)) { 
     59 
     60                (0..sheetrow.getLastCellNum() - 1).each { columnindex -> 
     61 
     62                        //def index     =   c.getColumnIndex() 
     63                        def datamatrix_celltype = sheet.getRow(datamatrix_start).getCell(columnindex, Row.CREATE_NULL_AS_BLANK).getCellType() 
     64                        def datamatrix_celldata = df.formatCellValue(sheet.getRow(datamatrix_start).getCell(columnindex)) 
     65                        def datamatrix_cell = sheet.getRow(datamatrix_start).getCell(columnindex) 
     66                        def headercell = sheet.getRow(headerrow - 1 + sheet.getFirstRowNum()).getCell(columnindex) 
     67                        def tft = TemplateFieldType.STRING //default templatefield type 
     68 
     69                        // Check for every celltype, currently redundant code, but possibly this will be 
     70                        // a piece of custom code for every cell type like specific formatting 
     71 
     72                        switch (datamatrix_celltype) { 
     73                                case Cell.CELL_TYPE_STRING: 
     74                                        //parse cell value as double 
     75                                        def doubleBoolean = true 
     76                                        def fieldtype = TemplateFieldType.STRING 
     77 
     78                                        // is this string perhaps a double? 
     79                                        try { 
     80                                                formatValue(datamatrix_celldata, TemplateFieldType.DOUBLE) 
     81                                        } catch (NumberFormatException nfe) { doubleBoolean = false } 
     82                                        finally { 
     83                                                if (doubleBoolean) fieldtype = TemplateFieldType.DOUBLE 
     84                                        } 
     85 
     86                                        header[columnindex] = new dbnp.importer.MappingColumn(name: df.formatCellValue(headercell), 
     87                                                templatefieldtype: fieldtype, 
     88                                                index: columnindex, 
     89                                                entity: theEntity, 
     90                                                property: property); 
     91 
     92                                        break 
     93                                case Cell.CELL_TYPE_NUMERIC: 
     94                                        def fieldtype = TemplateFieldType.LONG 
     95                                        def doubleBoolean = true 
     96                                        def longBoolean = true 
     97 
     98                                        // is this cell really an integer? 
     99                                        try { 
     100                                                Long.valueOf(datamatrix_celldata) 
     101                                        } catch (NumberFormatException nfe) { longBoolean = false } 
     102                                        finally { 
     103                                                if (longBoolean) fieldtype = TemplateFieldType.LONG 
     104                                        } 
     105 
     106                                        // it's not an long, perhaps a double? 
     107                                        if (!longBoolean) 
     108                                                try { 
     109                                                        formatValue(datamatrix_celldata, TemplateFieldType.DOUBLE) 
     110                                                } catch (NumberFormatException nfe) { doubleBoolean = false } 
     111                                                finally { 
     112                                                        if (doubleBoolean) fieldtype = TemplateFieldType.DOUBLE 
     113                                                } 
     114 
     115                                        if (DateUtil.isCellDateFormatted(datamatrix_cell)) fieldtype = TemplateFieldType.DATE 
     116 
     117                                        header[columnindex] = new dbnp.importer.MappingColumn(name: df.formatCellValue(headercell), 
     118                                                templatefieldtype: fieldtype, 
     119                                                index: columnindex, 
     120                                                entity: theEntity, 
     121                                                property: property); 
     122                                        break 
     123                                case Cell.CELL_TYPE_BLANK: 
     124                                        header[columnindex] = new dbnp.importer.MappingColumn(name: df.formatCellValue(headercell), 
     125                                                templatefieldtype: TemplateFieldType.STRING, 
     126                                                index: columnindex, 
     127                                                entity: theEntity, 
     128                                                property: property); 
     129                                        break 
     130                                default: 
     131                                        header[columnindex] = new dbnp.importer.MappingColumn(name: df.formatCellValue(headercell), 
     132                                                templatefieldtype: TemplateFieldType.STRING, 
     133                                                index: columnindex, 
     134                                                entity: theEntity, 
     135                                                property: property); 
     136                                        break 
     137                        } // end of switch 
     138                } // end of cell loop 
     139                return header 
     140        } 
     141 
     142        /** 
     143         * This method is meant to return a matrix of the rows and columns 
     144         * used in the preview 
     145         * 
     146         * @param wb workbook object 
     147         * @param sheetindex sheet index used 
     148         * @param rows amount of rows returned 
     149         * @return two dimensional array (matrix) of Cell objects 
     150         */ 
     151        Object[][] getDatamatrix(Workbook wb, header, int sheetindex, int datamatrix_start, int count) { 
     152                def sheet = wb.getSheetAt(sheetindex) 
     153                def rows = [] 
     154                def df = new DataFormatter() 
     155 
     156                count = (count < sheet.getLastRowNum()) ? count : sheet.getLastRowNum() 
     157 
     158                // walk through all rows 
     159                ((datamatrix_start + sheet.getFirstRowNum())..count).each { rowindex -> 
     160                        def row = [] 
     161 
     162                        (0..header.size() - 1).each { columnindex -> 
     163                                def c = sheet.getRow(rowindex).getCell(columnindex, Row.CREATE_NULL_AS_BLANK) 
     164                                row.add(c) 
     165                        } 
     166 
     167                        rows.add(row) 
     168                } 
     169 
     170                return rows 
     171        } 
     172 
     173        /** 
     174         * This method will move a file to a new location. 
     175         * 
     176         * @param file File object to move 
     177         * @param folderpath folder to move the file to 
     178         * @param filename (new) filename to give 
     179         * @return if file has been moved succesful, the new path and filename will be returned, otherwise an empty string will be returned 
     180         */ 
     181        def moveFile(File file, String folderpath, String filename) { 
     182                try { 
     183                        def rnd = ""; //System.currentTimeMillis() 
     184                        file.transferTo(new File(folderpath, rnd + filename)) 
     185                        return folderpath + filename 
     186                } catch (Exception exception) { 
     187                        log.error "File move error, ${exception}" 
     188                        return "" 
     189                } 
     190        } 
     191 
     192        /** 
     193         * @return random numeric value 
     194         */ 
     195        def random = { 
     196                return System.currentTimeMillis() + Runtime.runtime.freeMemory() 
     197        } 
     198 
     199        /** 
     200         * Method to read data from a workbook and to import data into a two dimensional 
     201         * array 
     202         * 
     203         * @param template_id template identifier to use fields from 
     204         * @param wb POI horrible spreadsheet formatted workbook object 
     205         * @param mcmap linked hashmap (preserved order) of MappingColumns 
     206         * @param sheetindex sheet to use when using multiple sheets 
     207         * @param rowindex first row to start with reading the actual data (NOT the header) 
     208         * @return two dimensional array containing records (with entities) 
     209         * 
     210         * @see dbnp.importer.MappingColumn 
     211         */ 
     212        def importData(template_id, Workbook wb, int sheetindex, int rowindex, mcmap) { 
     213                def sheet = wb.getSheetAt(sheetindex) 
     214                def template = Template.get(template_id) 
     215                def table = [] 
     216                def failedcells = [] // list of records 
     217 
     218                // walk through all rows and fill the table with records 
     219                (rowindex..sheet.getLastRowNum()).each { i -> 
     220                        // Create an entity record based on a row read from Excel and store the cells which failed to be mapped 
     221                        def (record, failed) = createRecord(template, sheet.getRow(i), mcmap) 
     222 
     223                        // Add record with entity and its values to the table 
     224                        table.add(record) 
     225 
     226                        // If failed cells have been found, add them to the failed cells list 
     227                        if (failed?.importcells?.size() > 0) failedcells.add(failed) 
     228                } 
     229 
     230                return [table, failedcells] 
     231        } 
     232 
     233        /** Method to put failed cells back into the datamatrix. Failed cells are cell values 
     234         * which could not be stored in an entity (e.g. Humu Supiuns in an ontology field). 
     235         * Empty corrections should not be stored 
     236         * 
     237         * @param datamatrix two dimensional array containing entities and possibly also failed cells 
     238         * @param failedcells list with maps of failed cells in [mappingcolumn, cell] format 
     239         * @param correctedcells map of corrected cells in [cellhashcode, value] format 
     240         * */ 
     241        def saveCorrectedCells(datamatrix, failedcells, correctedcells) { 
     242 
     243                // Loop through all failed cells (stored as 
     244                failedcells.each { record -> 
     245                        record.value.importcells.each { cell -> 
     246 
     247                                // Get the corrected value 
     248                                def correctedvalue = correctedcells.find { it.key.toInteger() == cell.getIdentifier()}.value 
     249 
     250                                // Find the record in the table which the mappingcolumn belongs to 
     251                                def tablerecord = datamatrix.find { it.hashCode() == record.key } 
     252 
     253                                // Loop through all entities in the record and correct them if necessary 
     254                                tablerecord.each { rec -> 
     255                                        rec.each { entity -> 
     256                                                try { 
     257                                                        // Update the entity field 
     258                                                        entity.setFieldValue(cell.mappingcolumn.property, correctedvalue) 
     259                                                        //println "Adjusted " + cell.mappingcolumn.property + " to " + correctedvalue 
     260                                                } 
     261                                                catch (Exception e) { 
     262                                                        //println "Could not map corrected ontology: " + cell.mappingcolumn.property + " to " + correctedvalue 
     263                                                } 
     264                                        } 
     265                                } // end of table record 
     266                        } // end of cell record 
     267                } // end of failedlist 
     268        } 
     269 
     270        /** 
     271         * Method to store a matrix containing the entities in a record like structure. Every row in the table 
     272         * contains one or more entity objects (which contain fields with values). So actually a row represents 
     273         * a record with fields from one or more different entities. 
     274         * 
     275         * @param study entity Study 
     276         * @param datamatrix two dimensional array containing entities with values read from Excel file 
     277         */ 
     278        static saveDatamatrix(Study study, datamatrix) { 
     279                def validatedSuccesfully = 0 
     280                def entitystored = null 
     281 
     282                // Study passed? Sync data 
     283                if (study != null) study.refresh() 
     284 
     285                // go through the data matrix, read every record and validate the entity and try to persist it 
     286                datamatrix.each { record -> 
     287                        record.each { entity -> 
     288                                switch (entity.getClass()) { 
     289                                        case Study: println "Persisting Study `" + entity + "`: " 
     290                                                entity.owner = AuthenticationService.getLoggedInUser() 
     291                                                persistEntity(entity) 
     292                                                break 
     293                                        case Subject: println "Persisting Subject `" + entity + "`: " 
     294 
     295                                                // is the current entity not already in the database? 
     296                                                //entitystored = isEntityStored(entity) 
     297 
     298                                                // this entity is new, so add it to the study 
     299                                                //if (entitystored==null) 
     300 
     301                                                study.addToSubjects(entity) 
     302 
     303                                                break 
     304                                        case Event: println "Persisting Event `" + entity + "`: " 
     305                                                study.addToEvents(entity) 
     306                                                break 
     307                                        case Sample: println "Persisting Sample `" + entity + "`: " 
     308 
     309                                                // is this sample validatable (sample name unique for example?) 
     310                                                study.addToSamples(entity) 
     311 
     312                                                break 
     313                                        case SamplingEvent: println "Persisting SamplingEvent `" + entity + "`: " 
     314                                                study.addToSamplingEvents(entity) 
     315                                                break 
     316                                        default: println "Skipping persisting of `" + entity.getclass() + "`" 
     317                                                break 
     318                                } // end switch 
     319                        } // end record 
     320                } // end datamatrix 
     321 
     322                // validate study 
     323                if (study.validate()) { 
     324                        if (!study.save(flush: true)) { 
     325                                //this.appendErrors(flow.study, flash.wizardErrors) 
     326                                throw new Exception('error saving study') 
     327                        } 
     328                } else { 
     329                        throw new Exception('study does not validate') 
     330                } 
     331 
     332                //persistEntity(study) 
     333 
     334                //return [validatedSuccesfully, updatedentities, failedtopersist] 
     335                //return [0,0,0] 
     336                return true 
     337        } 
     338 
     339        /** 
     340         * Check whether an entity already exist. A unique field in the entity is 
     341         * used to check whether the instantiated entity (read from Excel) is new. 
     342         * If the entity is found in the database it will be returned as is. 
     343         * 
     344         * @param entity entity object like a Study, Subject, Sample et cetera 
     345         * @return entity if found, otherwise null 
     346         */ 
     347        def isEntityStored(entity) { 
     348                switch (entity.getClass()) { 
     349                        case Study: return Study.findByCode(entity.code) 
     350                                break 
     351                        case Subject: return Subject.findByParentAndName(entity.parent, entity.name) 
     352                                break 
     353                        case Event: break 
     354                        case Sample: 
     355                                break 
     356                        case SamplingEvent: break 
     357                        default:  // unknown entity 
     358                                return null 
     359                } 
     360        } 
     361 
     362        /** 
     363         * Find the entity and update the fields. The entity is an instance 
     364         * read from Excel. This method looks in the database for the entity 
     365         * having the same identifier. If it has found the same entity 
     366         * already in the database, it will update the record. 
     367         * 
     368         * @param entitystored existing record in the database to update 
     369         * @param entity entity read from Excel 
     370         */ 
     371        def updateEntity(entitystored, entity) { 
     372                switch (entity.getClass()) { 
     373                        case Study: break 
     374                        case Subject: entitystored.properties = entity.properties 
     375                                entitystored.save() 
     376                                break 
     377                        case Event: break 
     378                        case Sample: break 
     379                        case SamplingEvent: break 
     380                        default:  // unknown entity 
     381                                return null 
     382                } 
     383        } 
     384 
     385        /** 
     386         * Method to persist entities into the database 
     387         * Checks whether entity already exists (based on identifier column 'name') 
     388         * 
     389         * @param entity entity object like Study, Subject, Protocol et cetera 
     390         * 
     391         */ 
     392        boolean persistEntity(entity) { 
     393                println ".import wizard persisting ${entity}" 
     394 
     395                try { 
     396                        entity.save(flush: true) 
     397                        return true 
     398 
     399                } catch (Exception e) { 
     400                        def session = sessionFactory.currentSession 
     401                        session.setFlushMode(org.hibernate.FlushMode.MANUAL) 
     402                        log.error ".import wizard, failed to save entity:\n" + org.apache.commons.lang.exception.ExceptionUtils.getRootCauseMessage(e) 
     403                } 
     404 
     405                return true 
     406        } 
    402407 
    403408        /** 
     
    407412         * @param excelrow POI based Excel row containing the cells 
    408413         * @param mcmap map containing MappingColumn objects 
    409     * @return list of entities and list of failed cells 
     414        * @return list of entities and list of failed cells 
    410415         */ 
    411416        def createRecord(template, Row excelrow, mcmap) { 
    412                 def df = new DataFormatter()             
    413         def tft = TemplateFieldType 
     417                def df = new DataFormatter() 
     418                def tft = TemplateFieldType 
    414419                def record = [] // list of entities and the read values 
    415         def failed = new ImportRecord() // map with entity identifier and failed mappingcolumn 
     420                def failed = new ImportRecord() // map with entity identifier and failed mappingcolumn 
    416421 
    417422                // Initialize all possible entities with the chosen template 
     
    426431                        // get the MappingColumn information of the current cell 
    427432                        def mc = mcmap[cell.getColumnIndex()] 
    428                         def value                         
     433                        def value 
    429434 
    430435                        // Check if column must be imported 
    431                         if (mc!=null) if (!mc.dontimport) { 
     436                        if (mc != null) if (!mc.dontimport) { 
    432437                                try { 
    433438                                        value = formatValue(df.formatCellValue(cell), mc.templatefieldtype) 
     
    436441                                } 
    437442 
    438                 try { 
    439                     // which entity does the current cell (field) belong to? 
    440                     switch (mc.entity) { 
    441                         case    Study: // does the entity already exist in the record? If not make it so. 
    442                                 (record.any {it.getClass() == mc.entity}) ? 0 : record.add(study) 
    443                                 study.setFieldValue(mc.property, value) 
    444                                 break 
    445                         case    Subject: (record.any {it.getClass() == mc.entity}) ? 0 : record.add(subject) 
    446                                 subject.setFieldValue(mc.property, value) 
    447                                 break 
    448                         case    SamplingEvent: (record.any {it.getClass() == mc.entity}) ? 0 : record.add(samplingEvent) 
    449                                 samplingEvent.setFieldValue(mc.property, value) 
    450                                 break 
    451                         case    Event: (record.any {it.getClass() == mc.entity}) ? 0 : record.add(event) 
    452                                 event.setFieldValue(mc.property, value) 
    453                                 break 
    454                         case    Sample: (record.any {it.getClass() == mc.entity}) ? 0 : record.add(sample) 
    455                                 sample.setFieldValue(mc.property, value) 
    456                                 break 
    457                         case    Object:   // don't import 
    458                                 break 
    459                     } // end switch 
    460                 } catch (Exception iae) { 
    461                     log.error ".import wizard error could not set property `" + mc.property + "` to value `" + value + "`" 
    462                     // store the mapping column and value which failed 
    463                     def identifier 
    464  
    465                     switch (mc.entity) { 
    466                         case Study: identifier = study.getIdentifier() 
    467                         break 
    468                         case Subject: identifier = subject.getIdentifier() 
    469                         break 
    470                         case SamplingEvent: identifier = samplingEvent.getIdentifier() 
    471                         break 
    472                         case Event: identifier = event.getIdentifier() 
    473                         break 
    474                         case Sample: identifier = sample.getIdentifier() 
    475                         break 
    476                         case Object:   // don't import 
    477                         break 
    478                     } 
    479                                      
    480                     def mcInstance = new MappingColumn() 
    481                     mcInstance.properties = mc.properties 
    482                     failed.addToImportcells(new ImportCell(mappingcolumn:mcInstance, value:value, entityidentifier:identifier)) 
    483                 } 
     443                                try { 
     444                                        // which entity does the current cell (field) belong to? 
     445                                        switch (mc.entity) { 
     446                                                case Study: // does the entity already exist in the record? If not make it so. 
     447                                                        (record.any {it.getClass() == mc.entity}) ? 0 : record.add(study) 
     448                                                        study.setFieldValue(mc.property, value) 
     449                                                        break 
     450                                                case Subject: (record.any {it.getClass() == mc.entity}) ? 0 : record.add(subject) 
     451                                                        subject.setFieldValue(mc.property, value) 
     452                                                        break 
     453                                                case SamplingEvent: (record.any {it.getClass() == mc.entity}) ? 0 : record.add(samplingEvent) 
     454                                                        samplingEvent.setFieldValue(mc.property, value) 
     455                                                        break 
     456                                                case Event: (record.any {it.getClass() == mc.entity}) ? 0 : record.add(event) 
     457                                                        event.setFieldValue(mc.property, value) 
     458                                                        break 
     459                                                case Sample: (record.any {it.getClass() == mc.entity}) ? 0 : record.add(sample) 
     460                                                        sample.setFieldValue(mc.property, value) 
     461                                                        break 
     462                                                case Object:   // don't import 
     463                                                        break 
     464                                        } // end switch 
     465                                } catch (Exception iae) { 
     466                                        log.error ".import wizard error could not set property `" + mc.property + "` to value `" + value + "`" 
     467                                        // store the mapping column and value which failed 
     468                                        def identifier 
     469 
     470                                        switch (mc.entity) { 
     471                                                case Study: identifier = study.getIdentifier() 
     472                                                        break 
     473                                                case Subject: identifier = subject.getIdentifier() 
     474                                                        break 
     475                                                case SamplingEvent: identifier = samplingEvent.getIdentifier() 
     476                                                        break 
     477                                                case Event: identifier = event.getIdentifier() 
     478                                                        break 
     479                                                case Sample: identifier = sample.getIdentifier() 
     480                                                        break 
     481                                                case Object:   // don't import 
     482                                                        break 
     483                                        } 
     484 
     485                                        def mcInstance = new MappingColumn() 
     486                                        mcInstance.properties = mc.properties 
     487                                        failed.addToImportcells(new ImportCell(mappingcolumn: mcInstance, value: value, entityidentifier: identifier)) 
     488                                } 
    484489                        } // end 
    485490                } // end for 
    486         // a failed column means that using the entity.setFieldValue() threw an exception         
    487         return [record, failed] 
    488     } 
    489  
    490     /** 
    491     * Method to parse a value conform a specific type 
    492     * @param value string containing the value 
    493     * @return object corresponding to the TemplateFieldType 
    494     */ 
    495    def formatValue(String value, TemplateFieldType type) throws NumberFormatException { 
    496             switch (type) { 
    497             case TemplateFieldType.STRING           :  return value.trim() 
    498             case TemplateFieldType.TEXT         :  return value.trim() 
    499             case TemplateFieldType.LONG         :  return (long) Double.valueOf(value) 
    500             //case TemplateFieldType.FLOAT          :   return Float.valueOf(value.replace(",",".")); 
    501             case TemplateFieldType.DOUBLE           :   return Double.valueOf(value.replace(",",".")); 
    502             case TemplateFieldType.STRINGLIST   :  return value.trim() 
    503             case TemplateFieldType.ONTOLOGYTERM :  return value.trim() 
    504             case TemplateFieldType.DATE         :  return value 
    505             default                             :  return value 
    506             } 
    507     } 
    508  
    509     // classes for fuzzy string matching 
    510     // <FUZZY MATCHING> 
    511     static def similarity(l_seq, r_seq, degree=2) { 
    512         def l_histo = countNgramFrequency(l_seq, degree) 
    513         def r_histo = countNgramFrequency(r_seq, degree) 
    514  
    515         dotProduct(l_histo, r_histo) / 
    516         Math.sqrt(dotProduct(l_histo, l_histo) * 
    517             dotProduct(r_histo, r_histo)) 
    518     } 
    519  
    520     static def countNgramFrequency(sequence, degree) { 
    521         def histo = [:] 
    522         def items = sequence.size() 
    523  
    524         for (int i = 0; i + degree <= items; i++) 
    525         { 
    526             def gram = sequence[i..<(i + degree)] 
    527             histo[gram] = 1 + histo.get(gram, 0) 
    528         } 
    529         histo 
    530     } 
    531  
    532     static def dotProduct(l_histo, r_histo) { 
    533         def sum = 0 
    534         l_histo.each { key, value -> 
    535             sum = sum + l_histo[key] * r_histo.get(key, 0) 
    536         } 
    537         sum 
    538     } 
    539  
    540     static def stringSimilarity (l_str, r_str, degree=2) { 
    541          
    542         similarity(l_str.toString().toLowerCase().toCharArray(), 
    543             r_str.toString().toLowerCase().toCharArray(), 
    544             degree) 
    545     } 
    546  
    547     static def mostSimilar(pattern, candidates, threshold=0) { 
    548         def topScore = 0 
    549         def bestFit = null 
    550  
    551         candidates.each { candidate -> 
    552             def score = stringSimilarity(pattern, candidate) 
    553             if (score > topScore) { 
    554                 topScore = score 
    555                 bestFit = candidate 
    556             } 
    557         } 
    558  
    559         if (topScore < threshold) 
    560         bestFit = null 
    561  
    562         bestFit 
    563     } 
    564     // </FUZZY MATCHING> 
     491                // a failed column means that using the entity.setFieldValue() threw an exception 
     492                return [record, failed] 
     493        } 
     494 
     495        /** 
     496        * Method to parse a value conform a specific type 
     497        * @param value string containing the value 
     498        * @return object corresponding to the TemplateFieldType 
     499        */ 
     500        def formatValue(String value, TemplateFieldType type) throws NumberFormatException { 
     501                switch (type) { 
     502                        case TemplateFieldType.STRING: return value.trim() 
     503                        case TemplateFieldType.TEXT: return value.trim() 
     504                        case TemplateFieldType.LONG: return (long) Double.valueOf(value) 
     505                //case TemplateFieldType.FLOAT      :   return Float.valueOf(value.replace(",",".")); 
     506                        case TemplateFieldType.DOUBLE: return Double.valueOf(value.replace(",", ".")); 
     507                        case TemplateFieldType.STRINGLIST: return value.trim() 
     508                        case TemplateFieldType.ONTOLOGYTERM: return value.trim() 
     509                        case TemplateFieldType.DATE: return value 
     510                        default: return value 
     511                } 
     512        } 
     513 
     514        // classes for fuzzy string matching 
     515        // <FUZZY MATCHING> 
     516 
     517        static def similarity(l_seq, r_seq, degree = 2) { 
     518                def l_histo = countNgramFrequency(l_seq, degree) 
     519                def r_histo = countNgramFrequency(r_seq, degree) 
     520 
     521                dotProduct(l_histo, r_histo) / 
     522                        Math.sqrt(dotProduct(l_histo, l_histo) * 
     523                                dotProduct(r_histo, r_histo)) 
     524        } 
     525 
     526        static def countNgramFrequency(sequence, degree) { 
     527                def histo = [:] 
     528                def items = sequence.size() 
     529 
     530                for (int i = 0; i + degree <= items; i++) { 
     531                        def gram = sequence[i..<(i + degree)] 
     532                        histo[gram] = 1 + histo.get(gram, 0) 
     533                } 
     534                histo 
     535        } 
     536 
     537        static def dotProduct(l_histo, r_histo) { 
     538                def sum = 0 
     539                l_histo.each { key, value -> 
     540                        sum = sum + l_histo[key] * r_histo.get(key, 0) 
     541                } 
     542                sum 
     543        } 
     544 
     545        static def stringSimilarity(l_str, r_str, degree = 2) { 
     546 
     547                similarity(l_str.toString().toLowerCase().toCharArray(), 
     548                        r_str.toString().toLowerCase().toCharArray(), 
     549                        degree) 
     550        } 
     551 
     552        static def mostSimilar(pattern, candidates, threshold = 0) { 
     553                def topScore = 0 
     554                def bestFit = null 
     555 
     556                candidates.each { candidate -> 
     557                        def score = stringSimilarity(pattern, candidate) 
     558                        if (score > topScore) { 
     559                                topScore = score 
     560                                bestFit = candidate 
     561                        } 
     562                } 
     563 
     564                if (topScore < threshold) 
     565                        bestFit = null 
     566 
     567                bestFit 
     568        } 
     569        // </FUZZY MATCHING> 
    565570 
    566571}