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

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