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

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