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

Last change on this file since 204 was 204, checked in by duh, 11 years ago
  • temporary commit
  • Property svn:keywords set to
    Date
    Author
    Rev
File size: 9.7 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: 204 $
19 * $Author: duh $
20 * $Date: 2010-02-18 09:41:10 +0000 (do, 18 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: 'Templates'],   // templates
50                                [title: 'Study'],               // study
51                                [title: 'Subjects'],    // subjects
52                                [title: 'Groups'],              // groups
53                                [title: 'Events'],              // events
54                                [title: 'Samples'],             // samples
55                                [title: 'Protocols'],   // protocols
56                                [title: 'Assays'],              // assays
57                                [title: 'Done']                 // finish page
58                        ]
59
60                }
61
62                // render the main wizard page which immediately
63                // triggers the 'next' action (hence, the main
64                // page dynamically renders the study template
65                // and makes the flow jump to the study logic)
66                mainPage {
67                        render(view: "/wizard/index")
68                        onRender {
69                                flow.page = 1
70                        }
71                        on("next").to "templates"
72                }
73
74                // select the templates to use for this study
75                templates {
76                        render(view: "_templates")
77                        onRender {
78                                flow.page = 1
79                        }
80                        on("next") {
81                                // if we don't have a study, instantiate a study with dummy values
82                                if (!flow.study) {
83                                        flow.study = new Study(
84                                                title: "my study",
85                                                code: "",
86                                                ecCode: "",
87                                                researchQuestion: "",
88                                                description: "",
89                                                startDate: new Date()
90                                        )
91                                }
92
93                                // assign template to study
94                                flow.study.template = Template.findByName(params.get('template'));
95
96                                // validate study
97                                if (flow.study.validate()) {
98                                        success()
99                                } else {
100                                        // validation failed, feedback errors
101                                        flash.errors = new LinkedHashMap()
102                                        this.appendErrors(flow.study, flash.errors)
103                                        error()
104                                }
105                        }.to "study"
106                }
107
108                // render and handle the study page
109                study {
110                        render(view: "_study")
111                        onRender {
112                                flow.page = 2
113                        }
114                        on("previous") {
115                                flash.errors = new LinkedHashMap()
116
117                                if (this.handleStudy(flow, flash, params)) {
118                                        success()
119                                } else {
120                                        error()
121                                }
122                        }.to "templates"
123                        on("next") {
124                                flash.errors = new LinkedHashMap()
125
126                                if (this.handleStudy(flow, flash, params)) {
127                                        success()
128                                } else {
129                                        error()
130                                }
131                        }.to "subjects"
132                }
133
134                // render and handle subjects page
135                subjects {
136                        render(view: "_subjects")
137                        onRender {
138                                flow.page = 3
139
140                                if (!flow.subjects) {
141                                        flow.subjects = []
142                                }
143                        }
144                        on("add") {
145                                // fetch species by name (as posted by the form)
146                                def speciesTerm = Term.findByName(params.addSpecies)
147
148                                // add x subject of species y
149                                (params.addNumber as int).times {
150                                        def increment = flow.subjects.size()
151                                        flow.subjects[increment] = new Subject(
152                                                name: 'Subject ' + (increment + 1),
153                                                species: speciesTerm,
154                                                template: flow.study.template
155                                        )
156                                }
157                        }.to "subjects"
158                        on("next") {
159                                flash.errors = new LinkedHashMap()
160
161                                // check if we have at least one subject
162                                // and check form data
163                                if (flow.subjects.size() < 1) {
164                                        // append error map
165                                        this.appendErrorMap(['subjects': 'You need at least to create one subject for your study'], flash.errors)
166                                        error()
167                                } else if (!this.handleSubjects(flow, flash, params)) {
168                                        error()
169                                } else {
170                                        success()
171                                }
172                        }.to "groups"
173                        on("previous") {
174                                flash.errors = new LinkedHashMap()
175
176                                // handle form data
177                                if (!this.handleSubjects(flow, flash, params)) {
178                                        error()
179                                } else {
180                                        success()
181                                }
182                        }.to "study"
183                }
184
185                // render and handle group page
186                groups {
187                        render(view: "_groups")
188                        onRender {
189                                flow.page = 4
190
191                                if (!flow.groups) {
192                                        flow.groups = []
193                                }
194                        }
195                        on("add") {
196                                def increment = flow.groups.size()
197                                flow.groups[increment] = new SubjectGroup(params)
198                        }.to "groups"
199                        on("next") {
200                                // TODO
201                        }.to "groups"
202                        on("previous") {
203                                // TODO
204                        }.to "subjects"
205                }
206
207                // render page three
208                events {
209                        render(view: "_events")
210                        onRender {
211                                flow.page = 5
212                        }
213                        on("previous") {
214                                // TODO
215                        }.to "subjects"
216                        on("next") {
217                                // TODO
218                        }.to "samples"
219                }
220
221                // render page three
222                samples {
223                        render(view: "_samples")
224                        onRender {
225                                flow.page = 6
226                        }
227                        on("previous") {
228                                // TODO
229                        }.to "events"
230                        on("next") {
231                                // TODO
232                        }.to "protocols"
233                }
234
235                // render page three
236                protocols {
237                        render(view: "_protocols")
238                        onRender {
239                                flow.page = 7
240                        }
241                        on("previous") {
242                                // TODO
243                        }.to "samples"
244                        on("next") {
245                                // TODO
246                        }.to "assays"
247                }
248
249                // render page three
250                assays {
251                        render(view: "_assays")
252                        onRender {
253                                flow.page = 8
254                        }
255                        on("previous") {
256                                // TODO
257                        }.to "protocols"
258                        on("next") {
259                                // TODO
260                        }.to "done"
261                }
262
263                // render page three
264                done {
265                        render(view: "_done")
266                        onRender {
267                                flow.page = 9
268                        }
269                        on("previous") {
270                                // TODO
271                        }.to "assays"
272                }
273        }
274
275        /**
276         * re-usable code for handling study form data in a web flow
277         * @param Map LocalAttributeMap (the flow scope)
278         * @param Map localAttributeMap (the flash scope)
279         * @param Map GrailsParameterMap (the flow parameters = form data)
280         * @returns boolean
281         */
282        def handleStudy(flow, flash, params) {
283                // create study instance if we have none
284                if (!flow.study) flow.study = new Study();
285
286                // create date instance from date string?
287                // @see WizardTagLibrary::dateElement{...}
288                if (params.get('startDate')) {
289                        params.startDate = new Date().parse("d/M/yyyy", params.get('startDate').toString())
290                }
291
292                // if a template is selected, get template instance
293                if (params.get('template')) {
294                        params.template = Template.findByName(params.get('template'))
295                }
296
297                // update study instance with parameters
298                params.each() {key, value ->
299                        if (flow.study.hasProperty(key)) {
300                                flow.study.setProperty(key, value);
301                        }
302                }
303
304                // validate study
305                if (flow.study.validate()) {
306                        return true
307                } else {
308                        // validation failed, feedback errors
309                        flash.errors = new LinkedHashMap()
310                        this.appendErrors(flow.study, flash.errors)
311                        return false
312                }
313        }
314
315        /**
316         * re-usable code for handling subject form data in a web flow
317         * @param Map LocalAttributeMap (the flow scope)
318         * @param Map localAttributeMap (the flash scope)
319         * @param Map GrailsParameterMap (the flow parameters = form data)
320         * @returns boolean
321         */
322        def handleSubjects(flow, flash, params) {
323                def names = new LinkedHashMap();
324                def errors = false;
325                def id = 0;
326
327                // iterate through subjects
328                flow.subjects.each() {
329                        // store subject properties
330                        def name = params.get('subject_' + id + '_name')
331                        it.name = params.get('subject_' + id + '_name')
332                        it.species = Term.findByName(params.get('subject_' + id + '_species'))
333
334                        // remember name and check for duplicates
335                        if (!names[it.name]) {
336                                names[it.name] = [count: 1, first: 'subject_' + id + '_name']
337                        } else {
338                                // duplicate name found, set error flag
339                                names[it.name]['count']++
340
341                                // second occurence?
342                                if (names[it.name]['count'] == 2) {
343                                        // yeah, also mention the first
344                                        // occurrence in the error message
345                                        this.appendErrorMap([[names[it.name]['first']]: 'The subject name needs to be unique!'], flash.errors)
346                                }
347
348                                // add to error map
349                                this.appendErrorMap([['subject_' + id + '_name']: 'The subject name needs to be unique!'], flash.errors)
350                                errors = true
351                        }
352
353                        // clear lists
354                        def stringList = new LinkedHashMap();
355                        def intList = new LinkedHashMap();
356                        def floatList = new LinkedHashMap();
357                        def termList = new LinkedHashMap();
358
359                        // get all template fields
360                        flow.study.template.subjectFields.each() {
361                                // valid type?
362                                if (!it.type) throw new NoSuchFieldException("Field name ${fieldName} not recognized")
363
364                                // get value
365                                def value = params.get('subject_' + id + '_' + it.name);
366                                if (value) {
367                                        // add to template parameters
368                                        switch (it.type) {
369                                                case 'STRINGLIST':
370                                                        stringList[it.name] = value
371                                                        break;
372                                                case 'INTEGER':
373                                                        intList[it.name] = value
374                                                        break;
375                                                case 'FLOAT':
376                                                        floatList[it.name] = value
377                                                        break;
378                                                default:
379                                                        // unsupported type?
380                                                        throw new NoSuchFieldException("Field type ${it.type} not recognized")
381                                                        break;
382                                        }
383                                }
384                        }
385
386                        // set field data
387                        it.templateStringFields = stringList
388                        it.templateIntegerFields = intList
389                        it.templateFloatFields = floatList
390                        it.templateTermFields = termList
391
392                        // validate subject
393                        if (!it.validate()) {
394                                errors = true
395                                println id + ' :: ' + it.errors.getAllErrors()
396                                this.appendErrors(it, flash.errors)
397                        }
398
399                        id++;
400                }
401
402                return !errors
403        }
404
405        /**
406         * transform domain class validation errors into a human readable
407         * linked hash map
408         * @param object validated domain class
409         * @returns object  linkedHashMap
410         */
411        def getHumanReadableErrors(object) {
412                def errors = new LinkedHashMap()
413
414                object.errors.getAllErrors().each() {
415                        errors[it.getArguments()[0]] = it.getDefaultMessage()
416                }
417
418                return errors
419        }
420
421        /**
422         * append errors of a particular object to a map
423         * @param object
424         * @param map linkedHashMap
425         * @void
426         */
427        def appendErrors(object, map) {
428                this.appendErrorMap(this.getHumanReadableErrors(object), map)
429        }
430
431        /**
432         * append errors of one map to another map
433         * @param map linkedHashMap
434         * @param map linkedHashMap
435         * @void
436         */
437        def appendErrorMap(map, mapToExtend) {
438                map.each() {key, value ->
439                        mapToExtend[key] = value
440                }
441        }
442}
Note: See TracBrowser for help on using the repository browser.