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

Last change on this file since 725 was 725, checked in by keesvb, 11 years ago

enabled boolean field recognition of 'x', added SamplingEvent? field addition to ImporterService?

  • Property svn:keywords set to Date Author Rev
File size: 12.4 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: 725 $
13 * $Author: keesvb $
14 * $Date: 2010-07-27 10:07:32 +0000 (di, 27 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
31import dbnp.studycapturing.SamplingEvent
32
33class ImporterService {
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    HSSFWorkbook getWorkbook(InputStream is) {
42        POIFSFileSystem fs = new POIFSFileSystem(is)
43        HSSFWorkbook    wb = new HSSFWorkbook(fs);
44        return wb;
45    }
46
47    /**
48     * @param wb high level representation of the workbook
49     * @param sheetindex sheet to use within the workbook
50     * @return header representation as a MappingColumn hashmap
51     */
52    def getHeader(HSSFWorkbook wb, int sheetindex, theEntity=null){
53
54        def sheet = wb.getSheetAt(sheetindex)
55        def datamatrix_start = sheet.getFirstRowNum() + 2
56        def sheetrow = sheet.getRow(datamatrix_start)
57        //def header = []
58        def header = [:]
59        def df = new DataFormatter()
60        def property = new String()
61
62        //for (HSSFCell c: sheet.getRow(datamatrix_start)) {
63
64        (0..sheetrow.getLastCellNum() -1 ).each { columnindex ->
65
66            //def index =   c.getColumnIndex()
67            def datamatrix_celltype = sheet.getRow(datamatrix_start).getCell(columnindex, org.apache.poi.ss.usermodel.Row.CREATE_NULL_AS_BLANK).getCellType()
68            def datamatrix_celldata = df.formatCellValue(sheet.getRow(datamatrix_start).getCell(columnindex))
69            def datamatrix_cell     = sheet.getRow(datamatrix_start).getCell(columnindex)
70            def headercell = sheet.getRow(sheet.getFirstRowNum()).getCell(columnindex)
71            def tft = TemplateFieldType.STRING //default templatefield type
72
73            // Check for every celltype, currently redundant code, but possibly this will be
74            // a piece of custom code for every cell type like specific formatting         
75               
76            switch (datamatrix_celltype) {
77                    case HSSFCell.CELL_TYPE_STRING:
78                            //parse cell value as double
79                            def doubleBoolean = true
80                            def fieldtype = TemplateFieldType.STRING
81
82                            // is this string perhaps a double?
83                            try {
84                                formatValue(datamatrix_celldata, TemplateFieldType.DOUBLE)
85                            } catch (NumberFormatException nfe) { doubleBoolean = false }
86                            finally {
87                                if (doubleBoolean) fieldtype = TemplateFieldType.DOUBLE
88                            }
89
90                            header[columnindex] = new dbnp.importer.MappingColumn(name:df.formatCellValue(headercell),
91                                                                            templatefieldtype:fieldtype,
92                                                                            index:columnindex,
93                                                                            entity:theEntity,
94                                                                            property:property);
95
96                            break
97                    case HSSFCell.CELL_TYPE_NUMERIC:
98                            def fieldtype = TemplateFieldType.INTEGER
99                            def doubleBoolean = true
100                            def integerBoolean = true
101
102                            // is this cell really an integer?
103                            try {
104                                Integer.valueOf(datamatrix_celldata)
105                            } catch (NumberFormatException nfe) { integerBoolean = false }
106                            finally {
107                                if (integerBoolean) fieldtype = TemplateFieldType.INTEGER
108                            }
109
110                            // it's not an integer, perhaps a double?
111                            if (!integerBoolean)
112                                try {
113                                    formatValue(datamatrix_celldata, TemplateFieldType.DOUBLE)
114                                } catch (NumberFormatException nfe) { doubleBoolean = false }
115                                finally {
116                                    if (doubleBoolean) fieldtype = TemplateFieldType.DOUBLE
117                                }
118
119                            if (HSSFDateUtil.isCellDateFormatted(datamatrix_cell)) fieldtype = TemplateFieldType.DATE
120
121                            header[columnindex] = new dbnp.importer.MappingColumn(name:df.formatCellValue(headercell),
122                                                                            templatefieldtype:fieldtype,
123                                                                            index:columnindex,
124                                                                            entity:theEntity,
125                                                                            property:property);
126                            break
127                    case HSSFCell.CELL_TYPE_BLANK:
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                    default:
135                            header[columnindex] = new dbnp.importer.MappingColumn(name:df.formatCellValue(headercell),
136                                                                            templatefieldtype:TemplateFieldType.STRING,
137                                                                            index:columnindex,
138                                                                            entity:theEntity,
139                                                                            property:property);
140                            break
141            } // end of switch
142        } // end of cell loop
143        return header
144    }
145
146    /**
147     * This method is meant to return a matrix of the rows and columns
148     * used in the preview
149     *
150     * @param wb workbook object
151     * @param sheetindex sheet index used
152     * @param rows amount of rows returned
153     * @return two dimensional array (matrix) of HSSFCell objects
154     */
155
156    HSSFCell[][] getDatamatrix(HSSFWorkbook wb, header, int sheetindex, int count) {
157        def sheet = wb.getSheetAt(sheetindex)
158        def rows  = []
159        def df = new DataFormatter()
160        def datamatrix_start = 1
161
162        // walk through all rows
163        (count <= sheet.getLastRowNum()) ?
164        ((datamatrix_start+sheet.getFirstRowNum())..count).each { rowindex ->
165            def row = []
166
167            // walk through every cell
168            /*for (HSSFCell c: sheet.getRow(rowindex)) {
169                row.add(c)
170                println c.getColumnIndex() + "=" +c
171            }*/
172           
173            (0..header.size()-1).each { columnindex ->
174                def c = sheet.getRow(rowindex).getCell(columnindex, org.apache.poi.ss.usermodel.Row.CREATE_NULL_AS_BLANK)               
175                //row.add(df.formatCellValue(c))
176                row.add(c)
177                //if (c.getCellType() == c.CELL_TYPE_STRING) println "STR"+c.getStringCellValue()
178                //if (c.getCellType() == c.CELL_TYPE_NUMERIC) println "INT" +c.getNumericCellValue()
179            }
180                //row.add(df.formatCellValue(c))
181            rows.add(row)
182        } : 0
183
184        return rows
185    }
186
187    /**
188    * This method will move a file to a new location.
189    *
190    * @param file File object to move
191    * @param folderpath folder to move the file to
192    * @param filename (new) filename to give
193    * @return if file has been moved succesful, the new path and filename will be returned, otherwise an empty string will be returned
194    */
195    def moveFile(File file, String folderpath, String filename) {
196        try {
197                def rnd = ""; //System.currentTimeMillis()
198                file.transferTo(new File(folderpath, rnd+filename))
199                return folderpath + filename
200            } catch(Exception exception) {
201                log.error "File move error, ${exception}"
202                return ""
203                }
204    }
205
206    /**
207    * @return random numeric value
208    */
209    def random = {
210            return System.currentTimeMillis() + Runtime.runtime.freeMemory()
211        }
212
213    /**
214    * Method to read data from a workbook and to import data into a two dimensional
215    * array
216    *
217    * @param template_id template identifier to use fields from
218    * @param wb POI horrible spreadsheet formatted workbook object
219    * @param mcmap linked hashmap (preserved order) of MappingColumns
220    * @param sheetindex sheet to use when using multiple sheets
221    * @param rowindex first row to start with reading the actual data (NOT the header)
222    * @return two dimensional array containing records (with entities)
223    *
224    * @see dbnp.importer.MappingColumn
225    */
226    def importdata(template_id, HSSFWorkbook wb, int sheetindex, int rowindex, mcmap) {
227        def sheet = wb.getSheetAt(sheetindex)
228        def table = []
229       
230        // walk through all rows       
231        (rowindex..sheet.getLastRowNum()).each { i ->
232            table.add(createRecord(template_id, sheet.getRow(i), mcmap))
233        }
234
235        /*table.each {
236            it.each { entity ->
237                entity.giveFields().each { field ->
238                    print field.name + ":" + entity.getFieldValue(field.name) + "/"
239                }
240                println
241            }
242        }*/
243
244        return table   
245    }
246   
247    /**
248     * Method to store a matrix containing the entities in a record like structure. Every row in the table
249     * contains one or more entity objects (which contain fields with values). So actually a row represents
250     * a record with fields from one or more different entities.
251     *
252     * @param study entity Study
253     * @param datamatrix two dimensional array containing entities with values read from Excel file     *
254     */   
255    def saveDatamatrix(Study study, datamatrix) {
256        def validatedSuccesfully = 0
257        study.refresh()
258       
259        datamatrix.each { record ->
260            record.each { entity ->
261                if(entity.validate()) {
262                    switch (entity.getClass()) {
263                        case Study       :  print "Persisting Study `" + entity + "`: "
264                                            persistEntity(entity)
265                                            break
266                        case Subject     :  print "Persisting Subject `" + entity + "`: "
267                                            persistEntity(entity)
268                                            study.addToSubjects(entity)
269                                            break
270                        case Event       :  print "Persisting Event `" + entity + "`: "
271                                            persistEntity(entity)
272                                            study.addToEvents(entity)
273                                            break
274                        case Sample      :  print "Persisting Sample `" + entity +"`: "
275                                            persistEntity(entity)
276                                            study.addToSamples(entity)
277                                            break
278                        case SamplingEvent: print "Persisting SamplingEvent `" + entity + "`: "
279                                            persistEntity(entity)
280                                            study.addToSamplingEvents(entity)
281                                            break;
282                        default          :  println "Skipping persisting of `" + entity.getclass() +"`"
283                                            break
284                    } // end switch
285                    validatedSuccesfully++
286                } // end if
287            } // end record
288        } // end datamatrix
289        return validatedSuccesfully
290    }
291
292    /**
293     * Method to persist entities into the database
294     * Checks whether entity already exists (based on identifier column 'name')
295     *
296     * @param entity entity object like Study, Subject, Protocol et cetera
297     *
298     */
299    def persistEntity(entity) { 
300         if (!entity.save()) //.merge?
301            entity.errors.allErrors.each {
302            println it
303         }
304    }
305
306        /**
307         * This method creates a record (array) containing entities with values
308         *
309         * @param template_id template identifier
310         * @param excelrow POI based Excel row containing the cells
311         * @param mcmap map containing MappingColumn objects
312         */
313        def createRecord(template_id, HSSFRow excelrow, mcmap) {
314                def df = new DataFormatter()
315                def template = Template.get(template_id)
316                def record = []
317
318                def study = new Study(template: template)
319                def subject = new Subject(template: template)
320                def samplingEvent = new SamplingEvent(template: template)
321                def event = new Event(template: template)
322                def sample = new Sample(template: template)
323
324                for (HSSFCell cell: excelrow) {
325                        def mc = mcmap[cell.getColumnIndex()]
326                        def value
327
328                        // Check if column must be imported
329                        if (!mc.dontimport) {
330                                try {
331                                        value = formatValue(df.formatCellValue(cell), mc.templatefieldtype)
332                                } catch (NumberFormatException nfe) {
333                                        value = ""
334                                }
335
336                                switch (mc.entity) {
337                                        case Study: (record.any {it.getClass() == mc.entity}) ? 0 : record.add(study)
338                                                study.setFieldValue(mc.property, value)
339                                                break
340                                        case Subject: (record.any {it.getClass() == mc.entity}) ? 0 : record.add(subject)
341                                                subject.setFieldValue(mc.property, value)
342                                                break
343                                        case SamplingEvent: (record.any {it.getClass() == mc.entity}) ? 0 : record.add(samplingEvent)
344                                                samplingEvent.setFieldValue(mc.property, value)
345                                                break
346                                        case Event: (record.any {it.getClass() == mc.entity}) ? 0 : record.add(event)
347                                                event.setFieldValue(mc.property, value)
348                                                break
349                                        case Sample: (record.any {it.getClass() == mc.entity}) ? 0 : record.add(sample)
350                                                sample.setFieldValue(mc.property, value)
351                                                break
352                                        case Object:   // don't import
353                                                break
354                                } // end switch
355                        } // end
356                } // end for
357
358        return record
359    }
360
361    /**
362    * Method to parse a value conform a specific type
363    * @param value string containing the value
364    * @return object corresponding to the TemplateFieldType
365    */
366    def formatValue(String value, TemplateFieldType type) throws NumberFormatException {
367            switch (type) {
368                case TemplateFieldType.STRING       :   return value.trim()
369                case TemplateFieldType.TEXT         :   return value.trim()
370                case TemplateFieldType.INTEGER      :   return (int) Double.valueOf(value)
371                case TemplateFieldType.FLOAT        :   return Float.valueOf(value.replace(",","."));
372                case TemplateFieldType.DOUBLE       :   return Double.valueOf(value.replace(",","."));
373                case TemplateFieldType.STRINGLIST   :   return value.trim()
374                case TemplateFieldType.ONTOLOGYTERM :   return value.trim()
375                case TemplateFieldType.DATE         :   return value
376                default                             :   return value
377            }
378    }
379
380}
Note: See TracBrowser for help on using the repository browser.