source: trunk/grails-app/controllers/dbnp/studycapturing/WizardController.groovy @ 189

Last change on this file since 189 was 189, checked in by duh, 10 years ago
  • previous commits broke application bootstrapping:
    • fixed some domain classes where nullable and / or blank were not correctly implemented
    • commented out the PPSH study which is still buggy
  • improved wizard data handeling, error reporting and javascript
  • Property svn:keywords set to
    Date
    Author
    Rev
File size: 7.1 KB
Line 
1package dbnp.studycapturing
2
3import dbnp.studycapturing.*
4import dbnp.data.*
5import grails.converters.*
6
7/**
8 * Wizard Controler
9 *
10 * The wizard controller handles the handeling of pages and data flow
11 * through the study capturing wizard.
12 *
13 * @author Jeroen Wesbeek
14 * @since 20100107
15 * @package studycapturing
16 *
17 * Revision information:
18 * $Rev: 189 $
19 * $Author: duh $
20 * $Date: 2010-02-10 16:35:11 +0000 (wo, 10 feb 2010) $
21 */
22class WizardController {
23        /**
24         * index method, redirect to the webflow
25         * @void
26         */
27        def index = {
28                /**
29                 * Do you believe it in your head?
30                 * I can go with the flow
31                 * Don't say it doesn't matter (with the flow) matter anymore
32                 * I can go with the flow (I can go)
33                 * Do you believe it in your head?
34                 */
35                redirect(action: 'pages')
36        }
37
38        /**
39         * WebFlow definition
40         * @see http://grails.org/WebFlow
41         * @void
42         */
43        def pagesFlow = {
44                // start the flow
45                onStart {
46                        // define flow variables
47                        flow.page = 0
48                        flow.pages = [
49                                [title: 'Study'],               // study
50                                [title: 'Subjects'],    // subjects
51                                [title: 'Groups'],              // groups
52                                [title: 'Form elements demo page']
53                        ]
54
55                }
56
57                // render the main wizard page which immediately
58                // triggers the 'next' action (hence, the main
59                // page dynamically renders the study template
60                // and makes the flow jump to the study logic)
61                mainPage {
62                        render(view: "/wizard/index")
63                        onRender {
64                                flow.page = 1
65                        }
66                        on("next").to "study"
67                }
68
69                // render and handle the study page
70                study {
71                        render(view: "_study")
72                        onRender {
73                                flow.page = 1
74                        }
75                        on("next") {
76                                // create date instance from date string?
77                                // @see WizardTagLibrary::dateElement{...}
78                                if (params.get('startDate')) {
79                                        params.startDate = new Date().parse("d/M/yyyy", params.get('startDate').toString())
80                                }
81
82                                // if a template is selected, get template instance
83                                if (params.get('template')) {
84                                        params.template = Template.findByName(params.get('template'))
85                                }
86
87                                // create a study instance
88                                flow.study = new Study(params)
89
90                                // validate study
91                                if (flow.study.validate()) {
92                                        success()
93                                } else {
94                                        // validation failed, feedback errors
95                                        flash.errors = new LinkedHashMap()
96                                        this.appendErrors(flow.study, flash.errors)
97                                        error()
98                                }
99                        }.to "subjects"
100                }
101
102                // render and handle subjects page
103                subjects {
104                        render(view: "_subjects")
105                        onRender {
106                                flow.page = 2
107
108                                if (!flow.subjects) {
109                                        flow.subjects = []
110                                }
111                        }
112                        on("add") {
113                                // fetch species by name (as posted by the form)
114                                def speciesTerm = Term.findByName(params.addSpecies)
115
116                                // add x subject of species y
117                                (params.addNumber as int).times {
118                                        def increment = flow.subjects.size()
119                                        flow.subjects[increment] = new Subject(
120                                                name: 'Subject ' + (increment + 1),
121                                                species: speciesTerm,
122                                                template: flow.study.template
123                                        )
124                                }
125                        }.to "subjects"
126                        on("next") {
127                                flash.errors = new LinkedHashMap()
128
129                                // handle form data
130                                if (!this.handleSubjects(flow, flash, params)) {
131                                        error()
132                                } else {
133                                        success()
134                                }
135                        }.to "groups"
136                        on("previous") {
137                                flash.errors = new LinkedHashMap()
138
139                                // handle form data
140                                if (!this.handleSubjects(flow, flash, params)) {
141                                        error()
142                                } else {
143                                        success()
144                                }
145                        }.to "study"
146                }
147
148                // render and handle group page
149                groups {
150                        render(view: "_groups")
151                        onRender {
152                                flow.page = 3
153
154                                if (!flow.groups) {
155                                        flow.groups = []
156                                }
157                        }
158                        on("add") {
159                                def increment = flow.groups.size()
160                                flow.groups[increment] = new SubjectGroup(params)
161                        }.to "groups"
162                        on("next") {
163                                // TODO
164                        }.to "groups"
165                        on("previous") {
166                                // TODO
167                        }.to "subjects"
168                }
169
170                // render page three
171                demo {
172                        render(view: "_three")
173                        onRender {
174                                flow.page = 4
175                        }
176                        on("previous") {
177                                // TODO
178                        }.to "groups"
179                }
180        }
181
182        /**
183         * re-usable code for handling subject form data in a web flow
184         * @param Map   LocalAttributeMap (the flow scope)
185         * @param Map   localAttributeMap (the flash scope)
186         * @param Map   GrailsParameterMap (the flow parameters = form data)
187         * @returns boolean
188         */
189        def handleSubjects(flow, flash, params) {
190                if (flow.subjects.size() < 1) {
191                        return false
192                } else {
193                        def names = new LinkedHashMap();
194                        def errors = false;
195                        def id = 0;
196
197                        // iterate through subjects
198                        flow.subjects.each() {
199                                // store subject properties
200                                def name = params.get('subject_' + id + '_name')
201                                it.name = params.get('subject_' + id + '_name')
202                                it.species = Term.findByName(params.get('subject_' + id + '_species'))
203
204                                // remember name and check for duplicates
205                                if (!names[ it.name ]) {
206                                        names[ it.name ] = [count: 1, first: 'subject_' + id + '_name'] 
207                                } else {
208                                        // duplicate name found, set error flag
209                                        names[ it.name ]['count']++
210
211                                        // second occurence?
212                                        if (names[ it.name ]['count'] == 2) {
213                                                // yeah, also mention the first
214                                                // occurrence in the error message
215                                                this.appendErrorMap([[names[ it.name ]['first']]: 'The subject name needs to be unique!'], flash.errors)
216                                        }
217
218                                        // add to error map
219                                        this.appendErrorMap([['subject_' + id + '_name']: 'The subject name needs to be unique!'], flash.errors)
220                                        errors = true
221                                }
222
223                                // clear lists
224                                def stringList  = new LinkedHashMap();
225                                def intList             = new LinkedHashMap();
226                                def floatList   = new LinkedHashMap();
227                                def termList    = new LinkedHashMap();
228
229                                // get all template fields
230                                flow.study.template.subjectFields.each() {
231                                        // valid type?
232                                        if (!it.type) throw new NoSuchFieldException("Field name ${fieldName} not recognized")
233
234                                        // get value
235                                        def value = params.get('subject_' + id + '_' + it.name);
236                                        if (value) {
237                                                // add to template parameters
238                                                switch (it.type) {
239                                                        case 'STRINGLIST':
240                                                                stringList[it.name]     = value
241                                                                break;
242                                                        case 'INTEGER':
243                                                                intList[it.name]        = value
244                                                                break;
245                                                        case 'FLOAT':
246                                                                floatList[it.name]      = value
247                                                                break;
248                                                        default:
249                                                                // unsupported type?
250                                                                throw new NoSuchFieldException("Field type ${it.type} not recognized")
251                                                                break;
252                                                }
253                                        }
254                                }
255
256                                // set field data
257                                it.templateStringFields         = stringList
258                                it.templateIntegerFields        = intList
259                                it.templateFloatFields          = floatList
260                                it.templateTermFields           = termList
261
262                                // validate subject
263                                if (!it.validate()) {
264                                        errors = true
265                                        println id + ' :: ' + it.errors.getAllErrors()
266                                        this.appendErrors(it, flash.errors)
267                                }
268
269                                id++;
270                        }
271
272                        return !errors
273                }
274        }
275
276        /**
277         * transform domain class validation errors into a human readable
278         * linked hash map
279         * @param object validated domain class
280         * @returns object  linkedHashMap
281         */
282        def getHumanReadableErrors(object) {
283                def errors = new LinkedHashMap()
284
285                object.errors.getAllErrors().each() {
286                        errors[it.getArguments()[0]] = it.getDefaultMessage()
287                }
288
289                return errors
290        }
291
292        /**
293         * append errors of a particular object to a map
294         * @param object
295         * @param map linkedHashMap
296         * @void
297         */
298        def appendErrors(object, map) {
299                this.appendErrorMap(this.getHumanReadableErrors(object), map)
300        }
301
302        /**
303         * append errors of one map to another map
304         * @param map linkedHashMap
305         * @param map linkedHashMap
306         * @void
307         */
308        def appendErrorMap(map, mapToExtend) {
309                map.each() {key, value ->
310                        mapToExtend[key] = value
311                }
312        }
313}
Note: See TracBrowser for help on using the repository browser.