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

Last change on this file since 534 was 534, checked in by tabma, 11 years ago
  • rewrote MappingColumn? (and underlying methods) to have a String property instead of TemplateField?
  • improved Excel-column detection to importer service
  • Property svn:keywords set to Date Author Rev
File size: 11.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: 534 $
13 * $Author: tabma $
14 * $Date: 2010-06-04 14:00:31 +0000 (vr, 04 jun 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=0){
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 headercell = sheet.getRow(sheet.getFirstRowNum()).getCell(index)
64
65            // Check for every celltype, currently redundant code, but possibly this will be
66            // a piece of custom code for every cell type like specific formatting
67               
68            switch (datamatrix_celltype) {
69                    case HSSFCell.CELL_TYPE_STRING:
70                            //parse cell value as double
71                            def parsable = true
72                            def templatefieldtype = TemplateFieldType.STRING
73
74                            // is this string perhaps a double?
75                            try {
76                                formatValue(c.getStringCellValue(), TemplateFieldType.DOUBLE)
77                            } catch (NumberFormatException nfe) { parsable = false }
78                            finally {
79                                if (parsable) templatefieldtype = TemplateFieldType.DOUBLE
80                            }
81
82                            header[index] = new dbnp.importer.MappingColumn(name:df.formatCellValue(headercell),
83                                                                            templatefieldtype:templatefieldtype,
84                                                                            index:index,
85                                                                            entity:theEntity,
86                                                                            property:property);
87                            break
88                    case HSSFCell.CELL_TYPE_NUMERIC:                   
89                            if (HSSFDateUtil.isCellDateFormatted(c)) {
90                                header[index] = new dbnp.importer.MappingColumn(name:df.formatCellValue(headercell),
91                                                                                templatefieldtype:TemplateFieldType.DATE,
92                                                                                index:index,
93                                                                                entity:theEntity,
94                                                                                property:property)
95                            }
96                            else
97                                header[index] = new dbnp.importer.MappingColumn(name:df.formatCellValue(headercell),
98                                                                                templatefieldtype:TemplateFieldType.INTEGER,
99                                                                                index:index,
100                                                                                entity:theEntity,
101                                                                                property:property);
102                            break
103                    case HSSFCell.CELL_TYPE_BLANK:
104                            header[index] = new dbnp.importer.MappingColumn(name:df.formatCellValue(headercell),
105                                                                            templatefieldtype:TemplateFieldType.STRING,
106                                                                            index:index,
107                                                                            entity:theEntity,
108                                                                            property:property);
109                            break
110                    default:
111                            header[index] = new dbnp.importer.MappingColumn(name:df.formatCellValue(headercell),
112                                                                            templatefieldtype:TemplateFieldType.STRING,
113                                                                            index:index,
114                                                                            entity:theEntity,
115                                                                            property:property);
116                            break
117            }
118        }
119        return header
120    }
121
122    /**
123     * This method is meant to return a matrix of the rows and columns
124     * used in the preview
125     *
126     * @param wb workbook object
127     * @param sheetindex sheet index used
128     * @param rows amount of rows returned
129     * @return two dimensional array (matrix) of HSSFCell objects
130     */
131
132    HSSFCell[][] getDatamatrix(HSSFWorkbook wb, int sheetindex, int count) {
133        def sheet = wb.getSheetAt(sheetindex)
134        def rows  = []
135        def df = new DataFormatter()
136        def datamatrix_start = 1
137
138        // walk through all rows
139        (count <= sheet.getLastRowNum()) ?
140        ((datamatrix_start+sheet.getFirstRowNum())..count).each { rowindex ->
141            def row = []
142
143            // walk through every cell
144            for (HSSFCell c: sheet.getRow(rowindex))
145                row.add(c)
146                //row.add(df.formatCellValue(c))
147            rows.add(row)
148        } : 0
149
150        return rows
151    }
152
153    /**
154    * This method will move a file to a new location.
155    *
156    * @param file File object to move
157    * @param folderpath folder to move the file to
158    * @param filename (new) filename to give
159    * @return if file has been moved succesful, the new path and filename will be returned, otherwise an empty string will be returned
160    */
161    def moveFile(File file, String folderpath, String filename) {
162        try {
163                def rnd = ""; //System.currentTimeMillis()
164                file.transferTo(new File(folderpath, rnd+filename))
165                return folderpath + filename
166            } catch(Exception exception) {
167                log.error "File move error, ${exception}"
168                return ""
169                }
170    }
171
172    /**
173    * @return random numeric value
174    */
175    def random = {
176            return System.currentTimeMillis() + Runtime.runtime.freeMemory()
177        }
178
179    /**
180    * Method to read data from a workbook and to import data into the database
181    * by using mapping information
182    *
183    * @param template_id template identifier to use fields from
184    * @param wb POI horrible spreadsheet formatted workbook object
185    * @param mcmap linked hashmap (preserved order) of MappingColumns
186    * @param sheetindex sheet to use when using multiple sheets
187    * @param rowindex first row to start with reading the actual data (NOT the header)
188    * @return two dimensional array containing records (with entities)
189    *
190    * @see dbnp.importer.MappingColumn
191    */
192    def importdata(template_id, HSSFWorkbook wb, int sheetindex, int rowindex, mcmap) {
193        def sheet = wb.getSheetAt(sheetindex)
194        def table = []
195
196       
197        // walk through all rows       
198        (rowindex..sheet.getLastRowNum()).each { i ->
199            table.add(createRecord(template_id, sheet.getRow(i), mcmap))
200        }
201
202        /*table.each {
203            it.each { entity ->
204                entity.giveFields().each { field ->
205                    print field.name + ":" + entity.getFieldValue(field.name) + "/"
206                }
207                println
208            }
209        }*/
210
211        return table   
212    }
213   
214     /**
215                     // start transaction
216                        def transaction = sessionFactory.getCurrentSession().beginTransaction()
217                              // persist data to the database
218                                try {
219                                   // commit transaction
220                                        println "commit"
221                                        transaction.commit()
222                                        success()
223                                } catch (Exception e) {
224                                        // rollback
225                                        // stacktrace in flash scope
226                                        flash.debug = e.getStackTrace()
227
228                                        println "rollback"
229                                        transaction.rollback()
230                                        error()
231                                }
232      */
233
234    /**
235     * Method to store a matrix containing the entities in a record like structure. Every row in the table
236     * contains one or more entity objects (which contain fields with values). So actually a row represents
237     * a record with fields from one or more different entities.
238     *
239     * @param study entity Study
240     * @param datamatrix two dimensional array containing entities with values read from Excel file     *
241     */   
242    def saveDatamatrix(Study study, datamatrix) {
243        study.refresh()
244       
245        datamatrix.each { record ->
246            record.each { entity ->
247                switch (entity.getClass()) {
248                    case Study   :  print "Persisting Study `" + entity.title + "`: "
249                                    persistEntity(entity)
250                                    break
251                    case Subject :  print "Persisting Subject `" + entity.name + "`: "
252                                    persistEntity(entity)
253                                    study.addToSubjects(entity)
254                                    break
255                    case Event   :  print "Persisting Event `" + entity.eventdescription + "`: "
256                                    persistEntity(entity)
257                                    break
258                    case Sample  :  print "Persisting Sample `" + entity.name +"`: "
259                                    persistEntity(entity)
260                                    break
261                    default      :  println "Skipping persisting of `" + entity.getclass() +"`"
262                                    break
263                }
264            }
265        }
266    }
267
268    /**
269     * Method to persist entities into the database
270     * Checks whether entity already exists (based on identifier column 'name')
271     *
272     * @param entity entity object like Study, Subject, Protocol et cetera
273     *
274     */
275    def persistEntity(entity) { 
276        if (entity.save(flush:true)) {  //.merge?           
277        } else entity.errors.allErrors.each {
278                println it
279        }
280    }
281
282    /**
283     * This method creates a record (array) containing entities with values
284     *
285     * @param template_id template identifier
286     * @param excelrow POI based Excel row containing the cells
287     * @param mcmap map containing MappingColumn objects
288     */
289    def createRecord(template_id, HSSFRow excelrow, mcmap) {
290        def df = new DataFormatter()
291        def template = Template.get(template_id)
292        def record = [] 
293       
294        def study = new Study(title:"New study", template:template)
295        def subject = new Subject(name:"New subject", species:Term.findByName("Homo sapiens"), template:template)
296        def event = new Event(eventdescription:"New event", template:template)
297        def sample = new Sample(name:"New sample", template:template)
298
299        for (HSSFCell cell: excelrow) {     
300            def mc = mcmap[cell.getColumnIndex()]
301            def value = formatValue(df.formatCellValue(cell), mc.templatefieldtype)
302
303            switch(mc.entity) {
304                case Study      :   (record.any {it.getClass()==mc.entity}) ? 0 : record.add(study)                                 
305                                    study.setFieldValue(mc.property, value)
306                                    break
307                case Subject    :   (record.any {it.getClass()==mc.entity}) ? 0 : record.add(subject)                               
308                                    subject.setFieldValue(mc.property, value)                               
309                                    break
310                case Event      :   (record.any {it.getClass()==mc.entity}) ? 0 : record.add(event)                                 
311                                    event.setFieldValue(mc.property, value)
312                                    break
313                case Sample     :   (record.any {it.getClass()==mc.entity}) ? 0 : record.add(sample)                               
314                                    sample.setFieldValue(mc.property, value)
315                                    break
316                case Object     :   // don't import
317                                    break
318            } // end switch
319        } // end for
320
321        return record
322    }
323
324    /**
325    * Method to parse a value conform a specific type
326    * @param value string containing the value
327    * @return object corresponding to the TemplateFieldType
328    */
329    def formatValue(String value, TemplateFieldType type) throws NumberFormatException {
330            switch (type) {
331                case TemplateFieldType.STRING       :   return value.trim()
332                case TemplateFieldType.TEXT         :   return value.trim()
333                case TemplateFieldType.INTEGER      :   return Integer.valueOf(value.replaceAll("[^0-9]",""))
334                case TemplateFieldType.FLOAT        :   return Float.valueOf(value.replace(",","."));
335                case TemplateFieldType.DOUBLE       :   return Double.valueOf(value.replace(",","."));
336                case TemplateFieldType.STRINGLIST   :   return value.trim()
337                case TemplateFieldType.ONTOLOGYTERM :   return value.trim()
338                case TemplateFieldType.DATE         :   return value
339                default                             :   return value
340            }
341    }
342}
Note: See TracBrowser for help on using the repository browser.