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

Last change on this file since 1332 was 1332, checked in by t.w.abma@…, 10 years ago
  • fixed entity URL-decoding when importing the file
  • Property svn:keywords set to Author Date Rev
File size: 19.6 KB
Line 
1package dbnp.importer
2import dbnp.studycapturing.Study
3import dbnp.studycapturing.Subject
4import dbnp.studycapturing.Sample
5import dbnp.studycapturing.Event
6import dbnp.studycapturing.Template
7
8import org.apache.poi.ss.usermodel.Workbook
9
10import grails.converters.JSON
11import cr.co.arquetipos.crypto.Blowfish
12
13/**
14 * Wizard Controller
15 *
16 * @author      Jeroen Wesbeek
17 * @since       20101206
18 *
19 * Revision information:
20 * $Rev: 1332 $
21 * $Author: t.w.abma@umcutrecht.nl $
22 * $Date: 2011-01-05 10:00:01 +0000 (wo, 05 jan 2011) $
23 */
24class ImporterController {
25    // the pluginManager is used to check if the Grom
26    // plugin is available so we can 'Grom' development
27    // notifications to the unified notifications daemon
28    // (see http://www.grails.org/plugin/grom)
29    def pluginManager
30    def AuthenticationService
31    def fileService
32    def ImporterService
33       
34    /**
35     * index method, redirect to the webflow
36     * @void
37     */
38    def index = {
39        // Grom a development message
40        if (pluginManager.getGrailsPlugin('grom')) "redirecting into the webflow".grom()
41
42        // encrypt the importable entities
43        grailsApplication.config.gscf.domain.importableEntities.each {
44            it.value.encrypted =           
45            URLEncoder.encode(Blowfish.encryptBase64(
46                it.value.entity.toString().replaceAll(/^class /, ''),
47                grailsApplication.config.crypto.shared.secret
48            ))
49        }
50
51        /**
52         * Do you believe it in your head?
53         * I can go with the flow
54         * Don't say it doesn't matter (with the flow) matter anymore
55         * I can go with the flow (I can go)
56         * Do you believe it in your head?
57         */
58        redirect(action: 'pages')
59    }
60
61    /**
62     * WebFlow definition
63     * @void
64     */
65    def pagesFlow = {
66        // start the flow
67        onStart {
68            // Grom a development message
69            if (pluginManager.getGrailsPlugin('grom')) "entering the WebFlow".grom()
70
71            // define variables in the flow scope which is availabe
72            // throughout the complete webflow also have a look at
73            // the Flow Scopes section on http://www.grails.org/WebFlow
74            //
75            // The following flow scope variables are used to generate
76            // wizard tabs. Also see common/_tabs.gsp for more information
77            flow.page = 0
78            flow.pages = [
79                [title: 'Import file'],
80                [title: 'Properties'],
81                [title: 'Mappings'],
82                [title: 'Imported'],
83                [title: 'Persist']
84            ]
85            flow.cancel = true;
86            flow.quickSave = true;
87
88            success()
89        }
90
91        // render the main wizard page which immediately
92        // triggers the 'next' action (hence, the main
93        // page dynamically renders the study template
94        // and makes the flow jump to the study logic)
95        mainPage {
96            render(view: "/importer/index")
97            onRender {
98                // Grom a development message
99                if (pluginManager.getGrailsPlugin('grom')) "rendering the main Ajaxflow page (index.gsp)".grom()
100               
101                // let the view know we're in page 1
102                flow.page = 1
103                success()
104            }
105            on("next").to "pageOne"
106        }
107
108        // File import and entitie template selection page
109        pageOne {
110            render(view: "_page_one")
111            onRender {
112                log.info ".entering import wizard"
113                // Grom a development message
114                if (pluginManager.getGrailsPlugin('grom')) ".rendering the partial: pages/_page_one.gsp".grom()
115
116                flow.page = 1
117                flow.studies = Study.findAllWhere(owner:AuthenticationService.getLoggedInUser())
118                flow.importer_importableentities = grailsApplication.config.gscf.domain.importableEntities
119               
120                success()
121            }
122            on("next") {
123                if (fileImportPage(flow, params)) {                   
124                    success()
125                } else {
126                    println "field is missing"
127                    error()
128                }
129                // put your bussiness logic (if applicable) in here
130            }.to "pageTwo"
131            on("toPageTwo") {
132                // put your bussiness logic (if applicable) in here
133            }.to "pageTwo"
134            on("toPageThree") {
135                // put your bussiness logic (if applicable) in here
136            }.to "pageThree"
137            on("toPageFour") {
138                // put your bussiness logic (if applicable) in here
139            }.to "pageFour"
140            on("toPageFive") {
141                // put your bussiness logic (if applicable) in here
142                flow.page = 5
143            }.to "save"
144        }
145
146        // Property to column assignment page
147        pageTwo {           
148            render(view: "_page_two")
149            onRender {
150                log.info ".import wizard properties page"
151                // Grom a development message
152                if (pluginManager.getGrailsPlugin('grom')) ".rendering the partial: pages/_page_two.gsp".grom()
153               
154                flow.page = 2
155                success()
156            }
157            on("next") {
158                if (propertiesPage(flow, params)) {
159                    println "HIER"
160                    success()
161                } else {
162                    println "properties are wrong"
163                    error()
164                }
165            }.to "pageThree"
166            on("previous").to "pageOne"
167            on("toPageOne").to "pageOne"
168            on("toPageThree").to "pageThree"
169            on("toPageFour").to "pageFour"
170            on("toPageFive") {
171                flow.page = 5
172            }.to "save"
173        }
174
175        // Mapping page
176        pageThree {           
177            render(view: "_page_three")
178            onRender {               
179                log.info ".import wizard mapping page"
180                // Grom a development message
181                if (pluginManager.getGrailsPlugin('grom')) ".rendering the partial pages/_page_three.gsp".grom()
182
183                flow.page = 3
184                success()
185            }           
186            on("refresh") {               
187                    success()
188            }.to "pageThree"
189            on("next") {
190                if (mappingsPage(flow, params)) {
191                    success()
192                } else {
193                    log.error ".import wizard mapping error, could not validate all entities"
194                    error()                   
195                }
196            }.to "pageFour"
197            on("previous").to "pageTwo"
198            on("toPageOne").to "pageOne"
199            on("toPageTwo").to "pageTwo"
200            on("toPageFour").to "pageFour"
201            on("toPageFive") {
202                flow.page = 5
203            }.to "save"
204        }
205
206        // Imported data overview page
207        pageFour {
208            render(view: "_page_four")
209            onRender {
210                // Grom a development message
211                if (pluginManager.getGrailsPlugin('grom')) ".rendering the partial pages/_page_four.gsp".grom()
212
213                flow.page = 4
214                success()
215            }
216            on("next") {
217                if (importedPage(flow, params)) {
218                    success()
219                } else {
220                    log.error ".import wizard imported error, something went wrong showing the imported entities"
221                    error()
222                }
223                flow.page = 5
224            }.to "save"
225            on("previous").to "pageThree"
226            on("toPageOne").to "pageOne"
227            on("toPageTwo").to "pageTwo"
228            on("toPageThree").to "pageThree"
229            on("toPageFive") {
230                flow.page = 5
231            }.to "save"
232        }
233
234        // Save the imported data
235        save {
236            action {
237                // here you can validate and save the
238                // instances you have created in the
239                // ajax flow.
240                try {
241                    // Grom a development message
242                    if (pluginManager.getGrailsPlugin('grom')) ".persisting instances to the database...".grom()                   
243
244                    if (saveEntities(flow, params)) {
245                        println "succes"
246                        success()
247                    } else {
248                        log.error ".import wizard imported error, something went wrong showing the imported entities"                       
249                        //throw Exception
250                    }                   
251                } catch (Exception e) {
252                    // put your error handling logic in
253                    // here                   
254                    flow.page = 4
255                    error()
256                }
257            }
258            on("error").to "error"
259            on(Exception).to "error"
260            on("success").to "finalPage"
261        }
262
263        // render errors
264        error {
265            render(view: "_error")
266            onRender {
267               
268                // Grom a development message
269                if (pluginManager.getGrailsPlugin('grom')) ".rendering the partial pages/_error.gsp".grom()
270
271                // set page to 4 so that the navigation
272                // works (it is disabled on the final page)
273                flow.page = 4
274            }
275            on("next").to "save"
276            on("previous").to "pageFour"
277            on("toPageOne").to "pageOne"
278            on("toPageTwo").to "pageTwo"
279            on("toPageThree").to "pageThree"
280            on("toPageFour").to "pageFour"
281            on("toPageFive").to "save"
282
283        }
284
285        // last wizard page
286        finalPage {
287            render(view: "_final_page")
288            onRender {
289                // Grom a development message
290                if (pluginManager.getGrailsPlugin('grom')) ".rendering the partial pages/_final_page.gsp".grom()
291                               
292                success()
293            }
294        }
295    }
296
297    /**
298     * Return templates which belong to a certain entity type
299     *
300     * @param entity entity name string (Sample, Subject, Study et cetera)
301     * @return JSON object containing the found templates
302     */
303    def ajaxGetTemplatesByEntity = {       
304        def entityName = Blowfish.decryptBase64(           
305            URLDecoder.decode(params.entity),
306            grailsApplication.config.crypto.shared.secret
307        )
308
309        //def entityClass = grailsApplication.config.gscf.domain.importableEntities.get(params.entity).entity
310        def entityClass = entityName
311
312        // fetch all templates for a specific entity
313        def templates = Template.findAllByEntity(Class.forName(entityClass, true, this.getClass().getClassLoader()))
314
315        // render as JSON
316        render templates as JSON
317    }
318
319    /**
320     * Handle the file import page.
321     *
322     * @param Map LocalAttributeMap (the flow scope)
323     * @param Map GrailsParameterMap (the flow parameters = form data)
324     * @returns boolean true if correctly validated, otherwise false
325     */
326    boolean fileImportPage(flow, params) {
327        def importedfile = fileService.get(params['importfile'])
328        //fileService.delete(YourFile)
329
330        if (params.entity && params.template_id && importedfile.exists()) {
331            // create a workbook instance of the file
332            session.importer_workbook = ImporterService.getWorkbook(new FileInputStream(importedfile))
333           
334            def selectedentities = []
335           
336            def entityName = Blowfish.decryptBase64(
337                URLDecoder.decode(params.entity),
338                grailsApplication.config.crypto.shared.secret
339            )
340           
341            def entityClass = Class.forName(entityName, true, this.getClass().getClassLoader())
342           
343            // Initialize some session variables
344            //flow.importer_workbook = wb // workbook object must be serialized for this to work
345            flow.importer_study = Study.get(params.study.id.toInteger())           
346
347             // Is the current logged in user allowed to write to this study?
348             if (flow.importer_study.canWrite(AuthenticationService.getLoggedInUser())) {
349                flow.importer_template_id = params.template_id
350                flow.importer_sheetindex = params.sheetindex.toInteger() -1 // 0 == first sheet
351                flow.importer_datamatrix_start = params.datamatrix_start.toInteger() -1 // 0 == first row
352                flow.importer_headerrow = params.headerrow.toInteger()
353
354                // Get the header from the Excel file using the arguments given in the first step of the wizard
355                flow.importer_header = ImporterService.getHeader(session.importer_workbook,
356                    flow.importer_sheetindex,
357                    flow.importer_headerrow,
358                    flow.importer_datamatrix_start,
359                    entityClass)
360
361                // Initialize 'selected entities', used to show entities above the columns
362                flow.importer_header.each {
363                    selectedentities.add([name:entityName, columnindex:it.key.toInteger()])
364                }
365               
366                flow.importer_selectedentities = selectedentities               
367
368                session.importer_datamatrix = ImporterService.getDatamatrix(
369                            session.importer_workbook, flow.importer_header,
370                            flow.importer_sheetindex,
371                            flow.importer_datamatrix_start,
372                            5)
373             
374                flow.importer_templates = Template.get(flow.importer_template_id)
375                flow.importer_allfieldtypes = "true"
376            } // end of if
377            /*else {
378                render (template:"common/error",
379                    model:[error:"Wrong permissions: you are not allowed to write to the study you selected (${flow.importer_study})."])
380            }*/
381
382            return true
383        }
384    }
385
386    /**
387     * Handle the property mapping page.
388     *
389     * @param Map LocalAttributeMap (the flow scope)
390     * @param Map GrailsParameterMap (the flow parameters = form data)
391     * @returns boolean true if correctly validated, otherwise false
392     */
393    boolean propertiesPage (flow, params) {
394        // Find actual Template object from the chosen template name
395        def template = Template.get(flow.importer_template_id)
396
397        params.columnproperty.index.each { columnindex, property ->
398            // Create an actual class instance of the selected entity with the selected template
399            // This should be inside the closure because in some cases in the advanced importer, the fields can have different target entities
400            def entityClass = Class.forName(flow.importer_header[columnindex.toInteger()].entity.getName(), true, this.getClass().getClassLoader())
401            def entityObj = entityClass.newInstance(template:template)
402
403            // Store the selected property for this column into the column map for the ImporterService
404            flow.importer_header[columnindex.toInteger()].property = property
405
406            // Look up the template field type of the target TemplateField and store it also in the map
407            flow.importer_header[columnindex.toInteger()].templatefieldtype = entityObj.giveFieldType(property)
408
409            // Is a "Don't import" property assigned to the column?
410            flow.importer_header[columnindex.toInteger()].dontimport = (property=="dontimport") ? true : false
411
412            //if it's an identifier set the mapping column true or false
413            entityObj.giveFields().each {
414                (it.preferredIdentifier && (it.name==property)) ? flow.importer_header[columnindex.toInteger()].identifier = true : false
415            }
416        }
417
418        // Import the workbook and store the table with entity records and store the failed cells
419        def (table, failedcells) = ImporterService.importData(flow.importer_template_id,
420                                                              session.importer_workbook,
421                                                              flow.importer_sheetindex,
422                                                              flow.importer_datamatrix_start,
423                                                              flow.importer_header)
424
425        flow.importer_importeddata = table
426        flow.importer_failedcells = failedcells
427        return true
428    }
429
430    /**
431     * Handle the mapping page.
432     *
433     * @param Map LocalAttributeMap (the flow scope)
434     * @param Map GrailsParameterMap (the flow parameters = form data)
435     * @returns boolean true if correctly validated, otherwise false
436     */
437    boolean mappingsPage(flow,params) {       
438        flow.importer_invalidentities = 0       
439
440        flow.importer_importeddata.each { table ->
441            table.each { entity ->
442                def invalidontologies = 0
443
444                // Set the fields for this entity by retrieving values from the params
445                entity.giveFields().each { field ->
446                        // field of type ontology and value "#invalidterm"?
447                        if (field.type == dbnp.studycapturing.TemplateFieldType.ONTOLOGYTERM &&
448                            params["entity_" + entity.getIdentifier() + "_" + field.escapedName()] == "#invalidterm"                           
449                        ) {
450                            invalidontologies++
451                        } else
452                            entity.setFieldValue (field.toString(), params["entity_" + entity.getIdentifier() + "_" + field.escapedName()])                                               
453                }
454
455                // Determine entity class and add a parent (defined as Study in first step of wizard)
456                switch (entity.getClass()) {
457                    case [Subject, Sample, Event]:   entity.parent = flow.importer_study
458                }
459
460                // Try to validate the entity now all fields have been set
461                if (!entity.validate() || invalidontologies) {
462                    flow.importer_invalidentities++
463                   
464                                        entity.errors.getAllErrors().each() {
465                                                log.error ".import wizard imported validation error:" + it
466                                        }
467                } else {                   
468                    // Valid entity, remove it from failedcells
469                    flow.importer_failedcells.each { record ->
470                        record.importcells.each { cell ->
471                            // remove the cell from the failed cells session
472                                if (cell.entityidentifier == entity.getIdentifier()) {
473                                record.removeFromImportcells(cell)
474                            }
475                        } // end of importcells
476                    } // end of failedcells
477                } // end else if
478
479            } // end of record
480        } // end of table
481
482        return (flow.importer_invalidentities == 0) ? true : false
483    } // end of method
484
485    /**
486     * Handle the imported entities page.
487     *
488     * @param Map LocalAttributeMap (the flow scope)
489     * @param Map GrailsParameterMap (the flow parameters = form data)
490     * @returns boolean true if correctly validated, otherwise false
491     */
492    boolean importedPage(flow, params) {
493        return true
494    }
495
496    boolean saveEntities(flow, params) {
497            def (validatedSuccesfully, updatedEntities, failedToPersist) = ImporterService.saveDatamatrix(flow.importer_study, flow.importer_importeddata)
498
499            flow.importer_validatedsuccesfully = validatedSuccesfully
500            flow.importer_failedtopersist = failedToPersist
501            flow.imported_updatedentities = updatedEntities
502            flow.importer_totalrows = flow.importer_importeddata.size
503            flow.importer_referer = ""
504
505            return true
506    }
507}
Note: See TracBrowser for help on using the repository browser.