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

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