root/trunk/grails-app/controllers/dbnp/importer/ImporterController.groovy @ 1171

Revision 1171, 14.1 KB (checked in by t.w.abma@…, 3 years ago)

- added add / modify to date template chooser
- added some JS to handle select boxes, when choosing study the study chooser dropdown disappears
- added encryption to select box, necessary to let the template add/remove popup work correctly
- added "encrypted" variable to Config, used by the date template dropdown

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