source: trunk/grails-app/services/dbnp/importer/ImporterService.groovy @ 634

Last change on this file since 634 was 634, checked in by tabma, 11 years ago
  • improved Excel cell type detection again, decimal values in an integer cell are parsed correctly with the test dataset
  • Property svn:keywords set to Date Author Rev
File size: 11.9 KB
Line 
1/**
2 * Importer service
3 *
4 * The importer service handles the import of tabular, comma delimited and Excel format
5 * based files.
6 *
7 * @package     importer
8 * @author      t.w.abma@umcutrecht.nl
9 * @since       20100126
10 *
11 * Revision information:
12 * $Rev: 634 $
13 * $Author: tabma $
14 * $Date: 2010-07-01 13:03:03 +0000 (do, 01 jul 2010) $
15 */
16
17package dbnp.importer
18import org.apache.poi.hssf.usermodel.*
19import org.apache.poi.poifs.filesystem.POIFSFileSystem
20import org.apache.poi.ss.usermodel.DataFormatter
21
22import dbnp.studycapturing.TemplateFieldType
23import dbnp.studycapturing.Template
24import dbnp.studycapturing.Study
25import dbnp.studycapturing.Subject
26import dbnp.studycapturing.Event
27
28import dbnp.studycapturing.Sample
29
30import dbnp.data.Term
31
32class ImporterService {
33
34    boolean transactional = true
35
36    /**
37    * @param is input stream representing the (workbook) resource
38    * @return high level representation of the workbook
39    */
40    HSSFWorkbook getWorkbook(InputStream is) {
41        POIFSFileSystem fs = new POIFSFileSystem(is)
42        HSSFWorkbook    wb = new HSSFWorkbook(fs);
43        return wb;
44    }
45
46    /**
47     * @param wb high level representation of the workbook
48     * @param sheetindex sheet to use within the workbook
49     * @return header representation as a MappingColumn hashmap
50     */
51    def getHeader(HSSFWorkbook wb, int sheetindex, theEntity=null){
52
53        def sheet = wb.getSheetAt(sheetindex)
54        def datamatrix_start = sheet.getFirstRowNum() + 1
55        //def header = []
56        def header = [:]
57        def df = new DataFormatter()
58        def property = new String()
59
60        for (HSSFCell c: sheet.getRow(datamatrix_start)) {
61            def index   =   c.getColumnIndex()
62            def datamatrix_celltype = sheet.getRow(datamatrix_start).getCell(index).getCellType()
63            def datamatrix_celldata = df.formatCellValue(sheet.getRow(datamatrix_start).getCell(index))
64            def headercell = sheet.getRow(sheet.getFirstRowNum()).getCell(index)
65            def tft = TemplateFieldType.STRING //default templatefield type
66
67            // Check for every celltype, currently redundant code, but possibly this will be
68            // a piece of custom code for every cell type like specific formatting
69               
70            switch (datamatrix_celltype) {
71                    case HSSFCell.CELL_TYPE_STRING:
72                            //parse cell value as double
73                            def doubleBoolean = true
74                            def fieldtype = TemplateFieldType.STRING
75
76                            // is this string perhaps a double?
77                            try {                               
78                                formatValue(datamatrix_celldata, TemplateFieldType.DOUBLE)
79                            } catch (NumberFormatException nfe) { doubleBoolean = false }
80                            finally {                           
81                                if (doubleBoolean) fieldtype = TemplateFieldType.DOUBLE
82                            }
83
84                            header[index] = new dbnp.importer.MappingColumn(name:df.formatCellValue(headercell),
85                                                                            templatefieldtype:fieldtype,
86                                                                            index:index,
87                                                                            entity:theEntity,
88                                                                            property:property);
89
90                            break
91                    case HSSFCell.CELL_TYPE_NUMERIC:
92                            def fieldtype = TemplateFieldType.INTEGER
93                            def doubleBoolean = true
94                            def integerBoolean = true
95
96                            // is this cell really an integer?
97                            try {                               
98                                Integer.valueOf(datamatrix_celldata)
99                            } catch (NumberFormatException nfe) { integerBoolean = false }
100                            finally {                           
101                                if (integerBoolean) fieldtype = TemplateFieldType.INTEGER
102                            }
103
104                            // it's not an integer, perhaps a double?
105                            if (!integerBoolean)
106                                try {
107                                    formatValue(datamatrix_celldata, TemplateFieldType.DOUBLE)
108                                } catch (NumberFormatException nfe) { doubleBoolean = false }
109                                finally {
110                                    if (doubleBoolean) fieldtype = TemplateFieldType.DOUBLE
111                                }
112
113                            if (HSSFDateUtil.isCellDateFormatted(c)) fieldtype = TemplateFieldType.DATE
114                           
115                            header[index] = new dbnp.importer.MappingColumn(name:df.formatCellValue(headercell),
116                                                                            templatefieldtype:fieldtype,
117                                                                            index:index,
118                                                                            entity:theEntity,
119                                                                            property:property);
120                            break
121                    case HSSFCell.CELL_TYPE_BLANK:
122                            header[index] = new dbnp.importer.MappingColumn(name:df.formatCellValue(headercell),
123                                                                            templatefieldtype:TemplateFieldType.STRING,
124                                                                            index:index,
125                                                                            entity:theEntity,
126                                                                            property:property);
127                            break
128                    default:
129                            header[index] = new dbnp.importer.MappingColumn(name:df.formatCellValue(headercell),
130                                                                            templatefieldtype:TemplateFieldType.STRING,
131                                                                            index:index,
132                                                                            entity:theEntity,
133                                                                            property:property);
134                            break
135            } // end of switch
136        } // end of cell loop
137        return header
138    }
139
140    /**
141     * This method is meant to return a matrix of the rows and columns
142     * used in the preview
143     *
144     * @param wb workbook object
145     * @param sheetindex sheet index used
146     * @param rows amount of rows returned
147     * @return two dimensional array (matrix) of HSSFCell objects
148     */
149
150    HSSFCell[][] getDatamatrix(HSSFWorkbook wb, int sheetindex, int count) {
151        def sheet = wb.getSheetAt(sheetindex)
152        def rows  = []
153        def df = new DataFormatter()
154        def datamatrix_start = 1
155
156        // walk through all rows
157        (count <= sheet.getLastRowNum()) ?
158        ((datamatrix_start+sheet.getFirstRowNum())..count).each { rowindex ->
159            def row = []
160
161            // walk through every cell
162            for (HSSFCell c: sheet.getRow(rowindex))
163                row.add(c)
164                //row.add(df.formatCellValue(c))
165            rows.add(row)
166        } : 0
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 the database
199    * by using mapping information
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, HSSFWorkbook wb, int sheetindex, int rowindex, mcmap) {
211        def sheet = wb.getSheetAt(sheetindex)
212        def table = []
213
214       
215        // walk through all rows       
216        (rowindex..sheet.getLastRowNum()).each { i ->
217            table.add(createRecord(template_id, sheet.getRow(i), mcmap))
218        }
219
220        /*table.each {
221            it.each { entity ->
222                entity.giveFields().each { field ->
223                    print field.name + ":" + entity.getFieldValue(field.name) + "/"
224                }
225                println
226            }
227        }*/
228
229        return table   
230    }
231   
232     /**
233                     // start transaction
234                        def transaction = sessionFactory.getCurrentSession().beginTransaction()
235                              // persist data to the database
236                                try {
237                                   // commit transaction
238                                        println "commit"
239                                        transaction.commit()
240                                        success()
241                                } catch (Exception e) {
242                                        // rollback
243                                        // stacktrace in flash scope
244                                        flash.debug = e.getStackTrace()
245
246                                        println "rollback"
247                                        transaction.rollback()
248                                        error()
249                                }
250      */
251
252    /**
253     * Method to store a matrix containing the entities in a record like structure. Every row in the table
254     * contains one or more entity objects (which contain fields with values). So actually a row represents
255     * a record with fields from one or more different entities.
256     *
257     * @param study entity Study
258     * @param datamatrix two dimensional array containing entities with values read from Excel file     *
259     */   
260    def saveDatamatrix(Study study, datamatrix) {
261        study.refresh()
262       
263        datamatrix.each { record ->
264            record.each { entity ->
265                switch (entity.getClass()) {
266                    case Study   :  print "Persisting Study `" + entity.title + "`: "
267                                    persistEntity(entity)
268                                    break
269                    case Subject :  print "Persisting Subject `" + entity.name + "`: "
270                                    persistEntity(entity)
271                                    study.addToSubjects(entity)
272                                    break
273                    case Event   :  print "Persisting Event `" + entity.eventdescription + "`: "
274                                    persistEntity(entity)
275                                    break
276                    case Sample  :  print "Persisting Sample `" + entity.name +"`: "
277                                    persistEntity(entity)
278                                    break
279                    default      :  println "Skipping persisting of `" + entity.getclass() +"`"
280                                    break
281                }
282            }
283        }
284    }
285
286    /**
287     * Method to persist entities into the database
288     * Checks whether entity already exists (based on identifier column 'name')
289     *
290     * @param entity entity object like Study, Subject, Protocol et cetera
291     *
292     */
293    def persistEntity(entity) { 
294        if (entity.save(flush:true)) {  //.merge?           
295        } else entity.errors.allErrors.each {
296                println it
297        }
298    }
299
300    /**
301     * This method creates a record (array) containing entities with values
302     *
303     * @param template_id template identifier
304     * @param excelrow POI based Excel row containing the cells
305     * @param mcmap map containing MappingColumn objects
306     */
307    def createRecord(template_id, HSSFRow excelrow, mcmap) {
308        def df = new DataFormatter()
309        def template = Template.get(template_id)
310        def record = [] 
311       
312        def study = new Study(template:template)
313        def subject = new Subject(template:template)
314        def event = new Event(template:template)
315        def sample = new Sample(template:template)
316
317        for (HSSFCell cell: excelrow) {     
318            def mc = mcmap[cell.getColumnIndex()]
319            def value = formatValue(df.formatCellValue(cell), mc.templatefieldtype)
320
321            switch(mc.entity) {
322                case Study      :   (record.any {it.getClass()==mc.entity}) ? 0 : record.add(study)                                 
323                                    study.setFieldValue(mc.property, value)
324                                    break
325                case Subject    :   (record.any {it.getClass()==mc.entity}) ? 0 : record.add(subject)
326                                    subject.setFieldValue(mc.property, value)                               
327                                    break
328                case Event      :   (record.any {it.getClass()==mc.entity}) ? 0 : record.add(event)                                 
329                                    event.setFieldValue(mc.property, value)
330                                    break
331                case Sample     :   (record.any {it.getClass()==mc.entity}) ? 0 : record.add(sample)                               
332                                    sample.setFieldValue(mc.property, value)
333                                    break
334                case Object     :   // don't import
335                                    break
336            } // end switch
337        } // end for
338
339        return record
340    }
341
342    /**
343    * Method to parse a value conform a specific type
344    * @param value string containing the value
345    * @return object corresponding to the TemplateFieldType
346    */
347    def formatValue(String value, TemplateFieldType type) throws NumberFormatException {
348            switch (type) {
349                case TemplateFieldType.STRING       :   return value.trim()
350                case TemplateFieldType.TEXT         :   return value.trim()
351                case TemplateFieldType.INTEGER      :   return Integer.valueOf(value)
352                case TemplateFieldType.FLOAT        :   return Float.valueOf(value.replace(",","."));
353                case TemplateFieldType.DOUBLE       :   return Double.valueOf(value.replace(",","."));
354                case TemplateFieldType.STRINGLIST   :   return value.trim()
355                case TemplateFieldType.ONTOLOGYTERM :   return value.trim()
356                case TemplateFieldType.DATE         :   return value
357                default                             :   return value
358            }
359    }
360}
Note: See TracBrowser for help on using the repository browser.