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

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