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

Last change on this file since 1313 was 1313, checked in by t.w.abma@…, 10 years ago
  • removed URL-encoding of encrypted "importable entities"
  • Property svn:keywords set to Author Date Rev
File size: 19.4 KB
Line 
1package dbnp.importer
2import dbnp.studycapturing.Study
3import dbnp.studycapturing.Sample
4import dbnp.studycapturing.Event
5import dbnp.studycapturing.Template
6
7import org.apache.poi.ss.usermodel.Workbook
8
9import grails.converters.JSON
10import cr.co.arquetipos.crypto.Blowfish
11
12
13/**
14 * Wizard Controller
15 *
16 * @author      Jeroen Wesbeek
17 * @since       20101206
18 *
19 * Revision information:
20 * $Rev: 1313 $
21 * $Author: t.w.abma@umcutrecht.nl $
22 * $Date: 2010-12-22 13:08:19 +0000 (wo, 22 dec 2010) $
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            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                    success()
160                } else {
161                    println "properties are wrong"
162                    error()
163                }
164            }.to "pageThree"
165            on("previous").to "pageOne"
166            on("toPageOne").to "pageOne"
167            on("toPageThree").to "pageThree"
168            on("toPageFour").to "pageFour"
169            on("toPageFive") {
170                flow.page = 5
171            }.to "save"
172        }
173
174        // Mapping page
175        pageThree {           
176            render(view: "_page_three")
177            onRender {               
178                log.info ".import wizard mapping page"
179                // Grom a development message
180                if (pluginManager.getGrailsPlugin('grom')) ".rendering the partial pages/_page_three.gsp".grom()
181
182                flow.page = 3
183                success()
184            }           
185            on("refresh") {               
186                    success()
187            }.to "pageThree"
188            on("next") {
189                if (mappingsPage(flow, params)) {
190                    success()
191                } else {
192                    log.error ".import wizard mapping error, could not validate all entities"
193                    error()                   
194                }
195            }.to "pageFour"
196            on("previous").to "pageTwo"
197            on("toPageOne").to "pageOne"
198            on("toPageTwo").to "pageTwo"
199            on("toPageFour").to "pageFour"
200            on("toPageFive") {
201                flow.page = 5
202            }.to "save"
203        }
204
205        // Imported data overview page
206        pageFour {
207            render(view: "_page_four")
208            onRender {
209                // Grom a development message
210                if (pluginManager.getGrailsPlugin('grom')) ".rendering the partial pages/_page_four.gsp".grom()
211
212                flow.page = 4
213                success()
214            }
215            on("next") {
216                if (importedPage(flow, params)) {
217                    success()
218                } else {
219                    log.error ".import wizard imported error, something went wrong showing the imported entities"
220                    error()
221                }
222                flow.page = 5
223            }.to "save"
224            on("previous").to "pageThree"
225            on("toPageOne").to "pageOne"
226            on("toPageTwo").to "pageTwo"
227            on("toPageThree").to "pageThree"
228            on("toPageFive") {
229                flow.page = 5
230            }.to "save"
231        }
232
233        // Save the imported data
234        save {
235            action {
236                // here you can validate and save the
237                // instances you have created in the
238                // ajax flow.
239                try {
240                    // Grom a development message
241                    if (pluginManager.getGrailsPlugin('grom')) ".persisting instances to the database...".grom()                   
242
243                    if (saveEntities(flow, params)) {
244                        println "succes"
245                        success()
246                    } else {
247                        log.error ".import wizard imported error, something went wrong showing the imported entities"                       
248                        //throw Exception
249                    }                   
250                } catch (Exception e) {
251                    // put your error handling logic in
252                    // here                   
253                    flow.page = 4
254                    error()
255                }
256            }
257            on("error").to "error"
258            on(Exception).to "error"
259            on("success").to "finalPage"
260        }
261
262        // render errors
263        error {
264            render(view: "_error")
265            onRender {
266               
267                // Grom a development message
268                if (pluginManager.getGrailsPlugin('grom')) ".rendering the partial pages/_error.gsp".grom()
269
270                // set page to 4 so that the navigation
271                // works (it is disabled on the final page)
272                flow.page = 4
273            }
274            on("next").to "save"
275            on("previous").to "pageFour"
276            on("toPageOne").to "pageOne"
277            on("toPageTwo").to "pageTwo"
278            on("toPageThree").to "pageThree"
279            on("toPageFour").to "pageFour"
280            on("toPageFive").to "save"
281
282        }
283
284        // last wizard page
285        finalPage {
286            render(view: "_final_page")
287            onRender {
288                // Grom a development message
289                if (pluginManager.getGrailsPlugin('grom')) ".rendering the partial pages/_final_page.gsp".grom()
290                               
291                success()
292            }
293        }
294    }
295
296    /**
297     * Return templates which belong to a certain entity type
298     *
299     * @param entity entity name string (Sample, Subject, Study et cetera)
300     * @return JSON object containing the found templates
301     */
302    def ajaxGetTemplatesByEntity = {       
303        def entityName = Blowfish.decryptBase64(           
304            params.entity,
305            grailsApplication.config.crypto.shared.secret
306        )
307
308        //def entityClass = grailsApplication.config.gscf.domain.importableEntities.get(params.entity).entity
309        def entityClass = entityName
310
311        // fetch all templates for a specific entity
312        def templates = Template.findAllByEntity(Class.forName(entityClass, true, this.getClass().getClassLoader()))
313
314        // render as JSON
315        render templates as JSON
316    }
317
318    /**
319     * Handle the file import page.
320     *
321     * @param Map LocalAttributeMap (the flow scope)
322     * @param Map GrailsParameterMap (the flow parameters = form data)
323     * @returns boolean true if correctly validated, otherwise false
324     */
325    boolean fileImportPage(flow, params) {
326        def importedfile = fileService.get(params['importfile'])
327        //fileService.delete(YourFile)
328
329        if (params.entity && params.template_id && importedfile.exists()) {
330            // create a workbook instance of the file
331            session.importer_workbook = ImporterService.getWorkbook(new FileInputStream(importedfile))
332           
333            def selectedentities = []
334           
335            def entityName = Blowfish.decryptBase64(
336                params.entity,
337                grailsApplication.config.crypto.shared.secret
338            )
339           
340            def entityClass = Class.forName(entityName, true, this.getClass().getClassLoader())
341           
342            // Initialize some session variables
343            //flow.importer_workbook = wb // workbook object must be serialized for this to work
344            flow.importer_study = Study.get(params.study.id.toInteger())           
345
346             // Is the current logged in user allowed to write to this study?
347             if (flow.importer_study.canWrite(AuthenticationService.getLoggedInUser())) {
348                flow.importer_template_id = params.template_id
349                flow.importer_sheetindex = params.sheetindex.toInteger() -1 // 0 == first sheet
350                flow.importer_datamatrix_start = params.datamatrix_start.toInteger() -1 // 0 == first row
351                flow.importer_headerrow = params.headerrow.toInteger()
352
353                // Get the header from the Excel file using the arguments given in the first step of the wizard
354                flow.importer_header = ImporterService.getHeader(session.importer_workbook,
355                    flow.importer_sheetindex,
356                    flow.importer_headerrow,
357                    flow.importer_datamatrix_start,
358                    entityClass)
359
360                // Initialize 'selected entities', used to show entities above the columns
361                flow.importer_header.each {
362                    selectedentities.add([name:entityName, columnindex:it.key.toInteger()])
363                }
364               
365                flow.importer_selectedentities = selectedentities               
366
367                session.importer_datamatrix = ImporterService.getDatamatrix(
368                            session.importer_workbook, flow.importer_header,
369                            flow.importer_sheetindex,
370                            flow.importer_datamatrix_start,
371                            5)
372
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    }
428
429    /**
430     * Handle the mapping page.
431     *
432     * @param Map LocalAttributeMap (the flow scope)
433     * @param Map GrailsParameterMap (the flow parameters = form data)
434     * @returns boolean true if correctly validated, otherwise false
435     */
436    boolean mappingsPage(flow,params) {       
437        flow.importer_invalidentities = 0       
438
439        flow.importer_importeddata.each { table ->
440            table.each { entity ->
441                def invalidontologies = 0
442
443                // Set the fields for this entity by retrieving values from the params
444                entity.giveFields().each { field ->
445                        // field of type ontology and value "#invalidterm"?
446                        if (field.type == dbnp.studycapturing.TemplateFieldType.ONTOLOGYTERM &&
447                            params["entity_" + entity.getIdentifier() + "_" + field.escapedName()] == "#invalidterm"                           
448                        ) {
449                            invalidontologies++
450                        } else
451                            entity.setFieldValue (field.toString(), params["entity_" + entity.getIdentifier() + "_" + field.escapedName()])                                               
452                }
453
454                // Determine entity class and add a parent (defined as Study in first step of wizard)
455                switch (entity.getClass()) {
456                    case [Study, Sample, Event]:   entity.parent = flow.importer_study
457                }
458
459                // Try to validate the entity now all fields have been set
460                if (!entity.validate() || invalidontologies) {
461                    flow.importer_invalidentities++
462                   
463                                        entity.errors.getAllErrors().each() {
464                                                log.error ".import wizard imported validation error:" + it
465                                        }
466                } else {                   
467                    // Valid entity, remove it from failedcells
468                    flow.importer_failedcells.each { record ->
469                        record.importcells.each { cell ->
470                            // remove the cell from the failed cells session
471                                if (cell.entityidentifier == entity.getIdentifier()) {
472                                record.removeFromImportcells(cell)
473                            }
474                        } // end of importcells
475                    } // end of failedcells
476                } // end else if
477
478            } // end of record
479        } // end of table
480
481        return (flow.importer_invalidentities == 0) ? true : false
482    } // end of method
483
484    /**
485     * Handle the imported entities page.
486     *
487     * @param Map LocalAttributeMap (the flow scope)
488     * @param Map GrailsParameterMap (the flow parameters = form data)
489     * @returns boolean true if correctly validated, otherwise false
490     */
491    boolean importedPage(flow, params) {
492        return true
493    }
494
495    boolean saveEntities(flow, params) {
496            def (validatedSuccesfully, updatedEntities, failedToPersist) = ImporterService.saveDatamatrix(flow.importer_study, flow.importer_importeddata)
497
498            flow.importer_validatedsuccesfully = validatedSuccesfully
499            flow.importer_failedtopersist = failedToPersist
500            flow.imported_updatedentities = updatedEntities
501            flow.importer_totalrows = flow.importer_importeddata.size
502            flow.importer_referer = ""
503
504            return true
505    }
506}
Note: See TracBrowser for help on using the repository browser.