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

Last change on this file since 897 was 897, checked in by keesvb, 10 years ago

fixed multiple bugs in importer, really implemented the header and start row functionality, updated Ontology setter with proper error message

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