source: trunk/grails-app/controllers/dbnp/importer/ImporterController.groovy @ 1165

Last change on this file since 1165 was 1165, checked in by t.w.abma@…, 10 years ago
  • removed INTEGER/FLOAT support in the Import Wizard controller and replaced them with LONG/DOUBLE
  • Property svn:keywords set to Author Date Rev
File size: 13.3 KB
Line 
1/**
2 * Importer controller
3 *
4 * The importer controller handles the uploading of tabular, comma delimited and Excel format
5 * based files. When uploaded a preview is shown of the data and the user can adjust the column
6 * type. Data in cells which don't correspond to the specified column type will be represented as "#error".
7 *
8 * The importer controller catches the actions and consecutively performs the
9 * logic behind it.
10 *
11 * @package     importer
12 * @author      t.w.abma@umcutrecht.nl
13 * @since       20100126
14 *
15 * Revision information:
16 * $Rev: 1165 $
17 * $Author: t.w.abma@umcutrecht.nl $
18 * $Date: 2010-11-18 10:21:53 +0000 (do, 18 nov 2010) $
19 */
20
21package dbnp.importer
22
23import dbnp.studycapturing.Template
24import dbnp.studycapturing.Study
25
26import dbnp.studycapturing.TemplateFieldType
27import grails.converters.JSON
28import org.apache.poi.ss.usermodel.Workbook
29import grails.plugins.springsecurity.Secured
30
31@Secured(['IS_AUTHENTICATED_REMEMBERED'])
32class ImporterController {
33    def ImporterService
34    def AuthenticationService
35
36    /**
37     * Default page
38     **/
39
40    def index = {
41        // should do a check what is in the url, strip it?
42        session.import_referer = params.redirectTo
43        render(view:"index_simple",
44               model:[studies:Study.findAllWhere(owner:AuthenticationService.getLoggedInUser()),
45               entities: grailsApplication.config.gscf.domain.importableEntities])
46    }
47
48    def simpleWizard = {
49        //render(view:"index_simple", model:[studies:Study.findAllWhere(owner:AuthenticationService.getLoggedInUser()), entities: grailsApplication.config.gscf.domain.importableEntities])
50    }
51
52    def advancedWizard = {
53        //render(view:"index_advanced", model:[templates:Template.list()])
54    }
55
56    /**
57    * This method will move the uploaded file to a temporary path and send the header
58    * and the first n rows to the preview
59    * @param importfile uploaded file to import
60    * @param study.id study identifier
61    */
62    def upload_advanced = {
63        def wb = handleUpload('importfile')
64
65        session.importer_header = ImporterService.getHeader(wb, 0)
66        session.importer_study = Study.get(params.study.id.toInteger())
67        session.importer_template_id = params.template_id
68        session.importer_workbook = wb
69
70        render (view:"step1_advanced", model:[header:session.importer_header, datamatrix:ImporterService.getDatamatrix(wb, session.importer_header, 0, 5)])
71    }
72
73    /**
74    * This method will move the uploaded file to a temporary path and send the header
75    * and the rows to the postview
76    *
77    * @param importfile uploaded file to import
78    * @param entity string representation of the entity chosen
79    */
80    def upload_simple = {
81        def wb = handleUpload('importfile')
82        def selectedentities = []
83        def entity = grailsApplication.config.gscf.domain.importableEntities.get(params.entity).entity
84        def entityClass = Class.forName(entity, true, this.getClass().getClassLoader())
85
86        // Initialize some session variables
87        session.importer_workbook = wb
88        session.importer_study = Study.get(params.study.id.toInteger())
89       
90        // Is the current logged in user allowed to write to this study?
91        if (session.importer_study.canWrite(AuthenticationService.getLoggedInUser())) {
92            session.importer_template_id = params.template_id
93            session.importer_sheetindex = params.sheetindex.toInteger() -1 // 0 == first sheet
94            session.importer_datamatrix_start = params.datamatrix_start.toInteger() -1 // 0 == first row
95            session.importer_headerrow = params.headerrow.toInteger()
96
97            // Get the header from the Excel file using the arguments given in the first step of the wizard
98            session.importer_header = ImporterService.getHeader(wb,
99                                                                session.importer_sheetindex,
100                                                                session.importer_headerrow,
101                                                                session.importer_datamatrix_start,
102                                                                entityClass)
103       
104            // Initialize 'selected entities', used to show entities above the columns
105            session.importer_header.each {
106                selectedentities.add([name:params.entity, columnindex:it.key.toInteger()])
107            }
108
109            def templates = Template.get(session.importer_template_id)
110
111            render(view:"step2_simple", model:[entities: selectedentities,
112                                        header:session.importer_header,
113                                        datamatrix:ImporterService.getDatamatrix(
114                                        wb, session.importer_header,
115                                        session.importer_sheetindex,
116                                        session.importer_datamatrix_start,
117                                        5),
118                                        templates:templates])
119        } // end of if
120        else {
121            render (template:"common/error", 
122                    model:[error:"Wrong permissions: you are not allowed to write to the study you selected (${session.importer_study})."])
123        }
124    }
125
126    /**
127     * This method handles a file being uploaded and storing it in a temporary directory
128     * and returning a workbook
129     *
130     * @param formfilename name used for the file field in the form
131     * @return workbook object reference
132     */
133    private Workbook handleUpload(formfilename) {
134
135        def downloadedfile = request.getFile(formfilename);
136        def tempfile = new File(System.getProperty('java.io.tmpdir') + File.separatorChar + System.currentTimeMillis() + ".nmcdsp")
137        downloadedfile.transferTo(tempfile)
138
139        return ImporterService.getWorkbook(new FileInputStream(tempfile))
140    }
141
142    /**
143     * Method to save the missing properties.
144     *
145     * @param entity entity class we are using (dbnp.studycapturing.Subject etc.)
146     */
147
148    def saveMissingProperties = {
149       
150        session.importer_importeddata.each { table ->
151            table.each { entity ->
152                entity.giveFields().each { field ->                 
153                    entity.setFieldValue (field.toString(), params["entity_" + entity.hashCode() + "_" + field.escapedName()])
154                }               
155            }
156        }
157
158        // a new ontology term was added, so stay at the current step otherwise go to the next step
159        if (params.updatefield) render(view:"step3_simple", model:[datamatrix:session.importer_importeddata])
160            else render(view:"step3", model:[datamatrix:session.importer_importeddata])
161    }
162
163    /*
164     * Store the corrected cells back into the datamatrix. Be sure to check
165     * if the corrected ontology is not blank. If so, it should keep
166     * the original value which was read from the Excel file.
167     *
168     * @param cell array of cells with corrected cells (ontologies)
169     *
170    */
171    def saveCorrectedCells = {
172        def correctedcells = [:]
173
174        // Loop through the form with cell fields and values
175        params.cell.index.each { cellhash, value ->
176            correctedcells.put(cellhash, value)
177        }
178
179        // Store the corrected cells back into the datamatrix
180        ImporterService.saveCorrectedCells(
181                    session.importer_importeddata,
182                    session.importer_failedcells,
183                    correctedcells)
184
185        render(view:"step3_simple", model:[datamatrix:session.importer_importeddata])
186
187    }
188
189    /**
190    * User has assigned all entities and templatefieldtypes to the columns and continues to the next step (assigning properties to columns)
191    * All information of the columns is stored in a session as MappingColumn object
192    *
193    * @param entities list of entities and columns it has been assigned to (columnindex.entitytype)
194    * @param templatefieldtype list of celltypes and columns it has been assigned to (columnindex:templatefieldtype format)
195    * @return properties page
196    *
197    * @see celltype: http://poi.apache.org/apidocs/org/apache/poi/ss/usermodel/Cell.html
198    */
199    def savePreview = {
200        def tft = null 
201        def identifiercolumnindex = (params.identifier!=null) ? params.identifier.toInteger() : -1
202        def selectedentities = []
203
204        // loop all entities and see which column has been assigned which entitytype
205        // and build an array containing the selected entities
206        params.entity.index.each { columnindex, entityname ->
207            def _entity = [name:entityname,columnindex:columnindex.toInteger()]
208            selectedentities.add(_entity)
209        }
210
211        params.templatefieldtype.index.each { columnindex, _templatefieldtype ->
212            switch (_templatefieldtype) {
213                case "STRING"       : tft = TemplateFieldType.STRING
214                                      break
215                case "TEXT"         : tft = TemplateFieldType.TEXT
216                                      break
217                case "LONG"         : tft = TemplateFieldType.LONG
218                                      break
219                case "DOUBLE"       : tft = TemplateFieldType.DOUBLE
220                                      break
221                case "STRINGLIST"   : tft = TemplateFieldType.STRINGLIST
222                                      break
223                case "ONTOLOGYTERM" : tft = TemplateFieldType.ONTOLOGYTERM
224                                      break
225                case "DATE"         : tft = TemplateFieldType.DATE
226                                      break
227                default: break
228            }
229           
230            // Set the templatefield type for this column
231            session.importer_header[columnindex.toInteger()].templatefieldtype = tft
232        }
233
234        // Detect the entity type
235        params.entity.index.each { columnindex, entityname ->
236            Class clazz = null
237
238            switch (entityname) {
239                case "Study"    : clazz = dbnp.studycapturing.Study
240                        break
241                case "Subject"  : clazz = dbnp.studycapturing.Subject
242                        break
243                case "Event"    : clazz = dbnp.studycapturing.Event
244                        break
245                case "Protocol" : clazz = dbnp.studycapturing.Protocol
246                        break
247                case "Sample"   : clazz = dbnp.studycapturing.Sample
248                        break
249                default: clazz = Object
250                        break
251            }
252
253            // Store properties for this column
254            session.importer_header[columnindex.toInteger()].identifier = (columnindex.toInteger() == identifiercolumnindex) ? true : false
255            session.importer_header[columnindex.toInteger()].index = columnindex.toInteger()
256            session.importer_header[columnindex.toInteger()].entity = clazz
257        }
258
259        // currently only one template is used for all entities
260        // TODO: show template fields per entity
261       
262        def templates = Template.get(session.importer_template_id)
263
264        render(view:"step2", model:[entities:selectedentities, header:session.importer_header, templates:templates])
265    }
266   
267    /**
268     * Method which stores the properties set per column and then imports the data.
269     *
270     * @param columnproperty array of columns containing index and property (represented as a String)
271    *
272    */
273    def saveProperties = {       
274
275        // Find actual Template object from the chosen template name
276        def template = Template.get(session.importer_template_id)
277
278        params.columnproperty.index.each { columnindex, property ->
279
280                // Create an actual class instance of the selected entity with the selected template
281                // This should be inside the closure because in some cases in the advanced importer, the fields can have different target entities
282                def entityClass = Class.forName(session.importer_header[columnindex.toInteger()].entity.getName(), true, this.getClass().getClassLoader())
283                def entityObj = entityClass.newInstance(template:template)
284
285                // Store the selected property for this column into the column map for the ImporterService
286                session.importer_header[columnindex.toInteger()].property = property
287
288                // Look up the template field type of the target TemplateField and store it also in the map
289                session.importer_header[columnindex.toInteger()].templatefieldtype = entityObj.giveFieldType(property)
290
291                // Is a "Don't import" property assigned to the column?
292                session.importer_header[columnindex.toInteger()].dontimport = (property=="dontimport") ? true : false
293
294                //if it's an identifier set the mapping column true or false
295                entityObj.giveFields().each {
296                    (it.preferredIdentifier && (it.name==property)) ? session.importer_header[columnindex.toInteger()].identifier = true : false
297                }
298        }
299
300        // Import the workbook and store the table with entity records and store the failed cells
301        def (table, failedcells) = ImporterService.importData(session.importer_template_id, session.importer_workbook, session.importer_sheetindex, session.importer_datamatrix_start, session.importer_header)
302
303        session.importer_importeddata = table       
304        session.importer_failedcells = failedcells
305
306        if (failedcells.size()!=0)
307            render(view:"step2a_simple", model:[failedcells:session.importer_failedcells])
308        else {
309            if (params.layout=="horizontal")
310                render(view:"step3_simple", model:[datamatrix:session.importer_importeddata])
311            else if (params.layout=="vertical")
312                render(view:"step3", model:[datamatrix:session.importer_importeddata])
313        }
314    }
315
316    /**
317     * Method which saves the data matrix to the database
318     */
319    def savePostview = {
320        def validatedSuccesfully = ImporterService.saveDatamatrix(session.importer_study, session.importer_importeddata)
321        render(view:"step4", model:[validatedSuccesfully:validatedSuccesfully, totalrows:session.importer_importeddata.size, referer: session.import_referer])
322    }
323
324    /**
325    * Return templates which belong to a certain entity type
326    *
327    * @param entity entity name string (Sample, Subject, Study et cetera)
328    * @return JSON object containing the found templates
329    */
330    def ajaxGetTemplatesByEntity = {
331        def entityClass = grailsApplication.config.gscf.domain.importableEntities.get(params.entity).entity
332       
333
334        // fetch all templates for a specific entity
335        def templates = Template.findAllByEntity(Class.forName(entityClass, true, this.getClass().getClassLoader()))   
336
337        // render as JSON
338        render templates as JSON
339    }
340
341    def refresh = {
342       // params
343    }
344}
Note: See TracBrowser for help on using the repository browser.