root/trunk/grails-app/controllers/dbnp/studycapturing/WizardController.groovy @ 722

Revision 722, 38.5 KB (checked in by duh, 4 years ago)

- disabled the check that you need to add at least 1 sampling event by request of Kees

  • Property svn:keywords set to Date Author Rev
Line 
1package dbnp.studycapturing
2
3import dbnp.data.*
4
5// Grails convertors is imported in order to create JSON objects
6import grails.converters.*
7
8
9/**
10 * Wizard Controler
11 *
12 * The wizard controller handles the handeling of pages and data flow
13 * through the study capturing wizard.
14 *
15 * @author Jeroen Wesbeek
16 * @since 20100107
17 * @package studycapturing
18 *
19 * Revision information:
20 * $Rev$
21 * $Author$
22 * $Date$
23 */
24class WizardController {
25        /**
26         * index method, redirect to the webflow
27         * @void
28         */
29        def index = {
30                def jump = [:]
31
32                // allow quickjumps to:
33                //      edit a study    : /wizard?jump=edit&id=1
34                //      create a study  : /wizard?jump=create
35                if (params.get('jump')) {
36                        switch (params.get('jump')) {
37                                case 'create':
38                                        jump = [
39                                            action: 'create'
40                                        ]
41                                        break
42                                case 'edit':
43                                        jump = [
44                                            action      : 'edit',
45                                                id              : params.get('id')
46                                        ]
47                                        break
48                                default:
49                                        break
50                        }
51                }
52
53                // store in session
54                session.jump = jump
55
56                /**
57                 * Do you believe it in your head?
58                 * I can go with the flow
59                 * Don't say it doesn't matter (with the flow) matter anymore
60                 * I can go with the flow (I can go)
61                 * Do you believe it in your head?
62                 */
63                redirect(action: 'pages')
64        }
65
66        /**
67         * WebFlow definition
68         * @see http://grails.org/WebFlow
69         * @void
70         */
71        def pagesFlow = {
72                // start the flow
73                onStart {
74                        // define flow variables
75                        flow.page = 0
76                        flow.pages = [
77                                //[title: 'Templates'],                 // templates
78                                [title: 'Start'],                               // load or create a study
79                                [title: 'Study'],                               // study
80                                [title: 'Subjects'],                    // subjects
81                                [title: 'Events'],                              // events and event grouping
82                                [title: 'Groups'],                              // groups
83                                [title: 'Samples'],                             // samples
84                                [title: 'Confirmation'],                // confirmation page
85                                [title: 'Done']                                 // finish page
86                        ]
87                        flow.jump = session.jump
88                        success()
89                }
90
91                // render the main wizard page which immediately
92                // triggers the 'next' action (hence, the main
93                // page dynamically renders the study template
94                // and makes the flow jump to the study logic)
95                mainPage {
96                        render(view: "/wizard/index")
97                        onRender {
98                                flow.page = 1
99                                success()
100                        }
101                        on("next").to "handleJump"
102                }
103
104                // handle the jump parameter
105                //
106                // I came to get down [2x]
107                // So get out your seats and jump around
108                // Jump around [3x]
109                // Jump up Jump up and get down
110                // Jump [18x]
111                handleJump {
112                        action {
113                                if (flow.jump && flow.jump.action == 'edit' && flow.jump.id) {
114                                        // load study
115                                        if (this.loadStudy(flow, flash, [studyid:flow.jump.id])) {
116                                                toStudyPage()
117                                        } else {
118                                                toStartPage()
119                                        }
120                                } else if (flow.jump && flow.jump.action == 'create') {
121                                        toStudyPage()
122                                } else {
123                                        toStartPage()
124                                }
125                        }
126                        on("toStartPage").to "start"
127                        on("toStudyPage").to "study"
128                }
129
130                // create or modify a study
131                start {
132                        render(view: "_start")
133                        onRender {
134                                flow.page = 1
135                                success()
136                        }
137                        on("next") {
138                                // clean the flow scope
139                                flow.remove('study')
140                                flow.remove('subjects')
141                                flow.remove('subjectTemplates')
142                                flow.remove('event')
143                                flow.remove('events')
144                                flow.remove('eventGroups')
145                                flow.remove('eventTemplates')
146                                flow.remove('samples')
147                                flow.remove('sampleTemplates')
148
149                                // set 'quicksave' variable to false
150                                flow.quickSave = false
151                        }.to "study"
152                        on("modify").to "modify"
153                        on("import").to "redirectToImport"
154                }
155
156                // redirect to the import wizard
157                redirectToImport {
158                        render(view: "_redirect")
159                        onRender {
160                                flash.uri = "/importer/index"
161                        }
162                        on("next").to "start"
163                }
164
165                // load a study to modify
166                modify {
167                        render(view: "_modify")
168                        onRender {
169                                flow.page = 1
170                                flash.cancel = true
171                                success()
172                        }
173                        on("cancel") {
174                                flow.study = null
175
176                                success()
177                        }.to "start"
178                        on("next") {
179                                // load study
180                                if (this.loadStudy(flow, flash, params)) {
181                                        success()
182                                } else {
183                                        error()
184                                }
185                        }.to "study"
186                }
187
188                // render and handle the study page
189                study {
190                        render(view: "_study")
191                        onRender {
192                                flow.page = 2
193                                success()
194                        }
195                        on("refresh") {
196                                println ".refreshing...."
197                                flash.values = params
198
199                                // handle study data
200                                this.handleStudy(flow, flash, params)
201
202                                // force refresh of the template
203                                if (flow.study.template) {
204                                        flow.study.template.refresh()
205                                }
206
207                                // remove errors as we don't want any warnings now
208                                flash.errors = [:]
209
210                                success()
211                        }.to "study"
212            on("switchTemplate") {
213                                flash.values = params
214
215                                // handle study data
216                                this.handleStudy(flow, flash, params)
217
218                                // force refresh of the template
219                                if (flow.study.template) {
220                                        flow.study.template.refresh()
221                                }
222
223                                // remove errors as we don't want any warnings now
224                                flash.errors = [:]
225
226                                success()
227                        }.to "study"
228                        on("previous") {
229                                flash.errors = [:]
230
231                                // handle the study
232                                this.handleStudy(flow, flash, params)
233
234                                // reset errors
235                                flash.errors = [:]
236
237                                success()
238                        }.to "start"
239                        on("next") {
240                                flash.errors = [:]
241
242                                if (this.handleStudy(flow, flash, params)) {
243                                        success()
244                                } else {
245                                        error()
246                                }
247                        }.to "subjects"
248                        on("quickSave") {
249                                flash.errors = [:]
250
251                                if (this.handleStudy(flow, flash, params)) {
252                                        success()
253                                } else {
254                                        error()
255                                }
256                        }.to "waitForSave"
257                }
258
259                // render and handle subjects page
260                subjects {
261                        render(view: "_subjects")
262                        onRender {
263                                flow.page = 3
264
265                                if (!flow.subjects) {
266                                        flow.subjects = [:]
267                                        flow.subjectTemplates = [:]
268                                }
269
270                                if (!flash.values) flash.values = [addNumber:1]
271
272                                success()
273                        }
274                        on("refresh") {
275                                flash.values = params
276
277                                // refresh templates
278                                flow.subjectTemplates.each() {
279                                        it.value.template.refresh()
280                                }
281
282                                success()
283                        }.to "subjects"
284                        on("add") {
285                                flash.errors = [:]
286
287                                // handle subjects
288                                this.handleSubjects(flow, flash, params)
289
290                                flash.errors = [:]
291                                flash.values = params
292
293                                def speciesTerm = Term.findByName(params.species)
294                                def subjectTemplateName = params.get('template')
295                                def subjectTemplate = Template.findByName(subjectTemplateName)
296
297                                // got a species and a subjectTemplate?
298                                if (speciesTerm && subjectTemplate) {
299                                        // add this subject template to the subject template array
300                                        if (!flow.subjectTemplates[subjectTemplateName]) {
301                                                flow.subjectTemplates[subjectTemplateName] = [
302                                                        name: subjectTemplateName,
303                                                        template: subjectTemplate,
304                                                        subjects: [:]
305                                                ]
306                                        }
307
308                                        // add x subjects of species y
309                                        (params.addNumber as int).times {
310                                                def increment = (flow.subjects.size()) ? (flow.subjects.keySet().max() + 1) : 0
311                                                def subject = new Subject(
312                                                        name: 'Subject ' + (increment + 1),
313                                                        species: speciesTerm,
314                                                        template: subjectTemplate
315                                                )
316
317                                                // instantiate a new Subject
318                                                flow.subjects[increment] = subject
319
320                                                // add the subject to the study
321                                                flow.study.addToSubjects( subject )
322
323                                                // and remember the subject id with the template
324                                                def subjectsSize = (flow.subjectTemplates[subjectTemplateName].subjects.size()) ? (flow.subjectTemplates[subjectTemplateName].subjects.keySet().max() + 1) : 0
325                                                flow.subjectTemplates[subjectTemplateName].subjects[subjectsSize] = increment
326                                        }
327
328                                        success()
329                                } else {
330                                        // add feedback
331                                        if (!speciesTerm) this.appendErrorMap(['species': 'You need to select a species, or add one if it is not yet present'], flash.errors)
332                                        if (!subjectTemplate) this.appendErrorMap(['template': 'You need to select a template, or add one if it is not yet present'], flash.errors)
333
334                                        error()
335                                }
336                        }.to "subjects"
337                        on("delete") {
338                                // handle subjects
339                                this.handleSubjects(flow, flash, params)
340
341                                flash.errors = [:]
342                                def delete = params.get('do') as int
343
344                                // remove subject
345                                if (flow.subjects[ delete ] && flow.subjects[ delete ] instanceof Subject) {
346                                        // from study as well
347                                        flow.study.removeFromSubjects( flow.subjects[ delete ] )
348
349                                        // remove subject from templates
350                                        flow.subjectTemplates.each() { templateName, templateData ->
351                                                templateData.subjects.values().remove( delete )
352                                        }
353
354                                        // remove templates that contain no subject
355                                        flow.subjectTemplates.find{!it.value.subjects.size()}.each{ flow.subjectTemplates.remove( it.key ) }
356
357                                        // remove subject altogether
358                                        flow.subjects.remove( delete )
359                                }
360                        }.to "subjects"
361                        on("previous") {
362                                flash.errors = [:]
363
364                                // handle form data
365                                if (!this.handleSubjects(flow, flash, params)) {
366                                        error()
367                                } else {
368                                        success()
369                                }
370                        }.to "study"
371                        on("next") {
372                                flash.errors = [:]
373
374                                // check form data
375                                if (!this.handleSubjects(flow, flash, params)) {
376                                        error()
377                                } else {
378                                        success()
379                                }
380                        }.to "events"
381                        on("quickSave") {                               
382                                flash.errors = [:]
383
384                                // check form data
385                                if (!this.handleSubjects(flow, flash, params)) {
386                                        error()
387                                } else {
388                                        success()
389                                }
390                        }.to "waitForSave"
391                }
392
393                // render events page
394                events {
395                        render(view: "_events")
396                        onRender {
397                                flow.page = 4
398
399                                if (!flow.event) {
400                                        flow.event                      = new Event()
401                                        flow.events                     = [:]
402                                        flow.eventGroups        = [ new EventGroup(name: 'Group 1') ]
403                                        flow.eventTemplates     = [:]
404
405                                        // add initial eventGroup to study
406                                        flow.study.addToEventGroups( flow.eventGroups[ 0 ] )
407                                } else if (!flash.values) {
408                                        // set flash.values.templateType based on the event instance
409                                        flash.values = [:]
410                                        flash.values.templateType = (flow.event instanceof Event) ? 'event' : 'sample'
411                                }
412                                success()
413                        }
414                        on("clear") {
415                                flow.remove('event')
416                                success()
417                        }.to "events"
418                        on("switchTemplate") {
419                                flash.values = params
420
421                                // handle study data
422                                this.handleEvents(flow, flash, params)
423
424                                // refresh event templates
425                                flow.eventTemplates.each() {
426                                        it.value.template.refresh()
427                                }
428
429                                // refresh flow template
430                                if (flow.event.template) flow.event.template.refresh()
431
432                                // remove errors as we don't want any warnings now
433                                flash.errors = [:]
434                        }.to "events"
435                        on("refresh") {
436                                flash.values = params
437
438                                // handle study data
439                                this.handleEvents(flow, flash, params)
440
441                                // refresh templates
442                                flow.eventTemplates.each() {
443                                        it.value.template.refresh()
444                                }
445
446                                // refresh flow template
447                                if (flow.event.template) flow.event.template.refresh()
448
449                                // remove errors as we don't want any warnings now
450                                flash.errors = [:]
451                        }.to "events"
452                        on("add") {
453                                flash.values                    = params
454                                def eventTemplateName   = (params.get('eventType') == 'event') ? params.get('eventTemplate') : params.get('sampleTemplate')
455                                def eventTemplate               = Template.findByName(eventTemplateName)
456
457                                // handle study data
458                                this.handleEvents(flow, flash, params)
459
460                                // validate event object
461                                if (flow.event.validate()) {
462                                        // add this event template to the event template array
463                                        if (!flow.eventTemplates[ eventTemplateName ]) {
464                                                flow.eventTemplates[ eventTemplateName ] = [
465                                                        name: eventTemplateName,
466                                                        template: eventTemplate,
467                                                        events: []
468                                                ]
469                                        }
470
471                                        // it validated! Duplicate the event object...
472                                        def newEvent    = flow.event
473                                        def increment   = flow.events.size()
474
475                                        // ...store it in the events map in the flow scope...
476                                        flow.events[ increment ] = newEvent
477
478                                        // ...add it to the study...
479                                        if (newEvent instanceof SamplingEvent) {
480                                                flow.study.addToSamplingEvents( newEvent )
481                                        } else {
482                                                flow.study.addToEvents( newEvent )
483                                        }
484
485                                        // ...and 'reset' the event object in the flow scope
486                                        flow.event = new Event(template: newEvent.template)
487                                       
488                                        // remember the event id with the template
489                                        def eventSize = flow.eventTemplates[ eventTemplateName ]['events'].size()
490                                        flow.eventTemplates[ eventTemplateName ]['events'][ eventSize ] = increment
491
492                                        success()
493                                } else {
494                                        // it does not validate, show error feedback
495                                        flash.errors = [:]
496                                        this.appendErrors(flow.event, flash.errors)
497                                        error()
498                                }
499                        }.to "events"
500                        on("deleteEvent") {
501                                flash.values = params
502                                def delete = params.get('do') as int
503
504                                // handle event groupings
505                                this.handleEventGrouping(flow, flash, params)
506
507                                // remove event
508                                if (flow.events[ delete ] && flow.events[ delete ] instanceof Event) {
509                                        // remove it from the study
510                                        flow.study.removeFromEvents( flow.events[ delete ] )
511
512                                        // remove it from the map
513                                        flow.events.remove(delete)
514                                        flow.eventTemplates.each() { eventTemplate ->
515                                                eventTemplate.value.events = eventTemplate.value.events.minus(delete)
516                                        }
517
518                                        // find eventTemplates without events
519                                flow.eventTemplates.find { eventTemplate ->
520                                                eventTemplate.value.events.size() < 1
521                                        }.each() {
522                                                // remove eventTemplate
523                                                flow.eventTemplates.remove( it.value.name )
524                                        }
525                                }
526
527                                success()
528                        }.to "events"
529                        on("addEventGroup") {
530                                flash.values = params
531                               
532                                // handle study data
533                                this.handleEvents(flow, flash, params)
534
535                                // handle event groupings
536                                this.handleEventGrouping(flow, flash, params)
537
538                                def increment = flow.eventGroups.size()
539                                def groupName = "Group " + (increment + 1)
540
541                                // check if group name exists
542                                def nameExists = true
543                                def u = 0
544
545                                // make sure a unique name is generated
546                                while (nameExists) {
547                                        u++
548                                        def count = 0
549                                       
550                                        flow.eventGroups.each() {
551                                                if (it.name == groupName) {
552                                                        groupName = "Group " + (increment + 1) + "," + u
553                                                } else {
554                                                        count++
555                                                }
556                                        }
557
558                                        nameExists = !(count == flow.eventGroups.size())
559                                }
560
561                                // remember eventGroup
562                                flow.eventGroups[ increment ] = new EventGroup( name: groupName )
563
564                                // and add the group to the study
565                                flow.study.addToEventGroups( flow.eventGroups[ increment ] )
566
567                                success()
568                        }.to "events"
569                        on("deleteEventGroup") {
570                                flash.values = params
571                                def delete = params.get('do') as int
572
573                                // handle event groupings
574                                this.handleEventGrouping(flow, flash, params)
575
576                                // remove the group with this specific id
577                                if (flow.eventGroups[delete] && flow.eventGroups[delete] instanceof EventGroup) {
578                                        // remove the eventGroup from the study
579                                        flow.study.removeFromEventGroups( flow.eventGroups[ delete ] )
580
581                                        // remove this eventGroup
582                                        flow.eventGroups.remove(delete)
583                                }
584
585                                success()
586                        }.to "events"
587                        on("previous") {
588                                // handle event groupings
589                                this.handleEventGrouping(flow, flash, params)
590                        }.to "subjects"
591                        on("next") {
592                                flash.values = params
593                                flash.errors = [:]
594
595                                // handle study data
596                                /* turned off by request of Kees / Leny / Jildau
597                                if (!flow.eventTemplates.find { eventTemplate -> eventTemplate.value.template.entity == SamplingEvent }) {
598                                        // append error map
599                                        this.appendErrorMap(['events': 'You need to create at least one sampling event for your study'], flash.errors)
600                                        error()                                         
601                                } else
602                                */
603                                if (this.handleEvents(flow, flash, params)) {
604                                        success()
605                                } else {
606                                        error()
607                                }
608                        }.to "groups"
609                        on("quickSave") {
610                                flash.values = params
611                                flash.errors = [:]
612
613                                // handle study data
614                                if (!flow.eventTemplates.find { eventTemplate -> eventTemplate.value.template.entity == SamplingEvent }) {
615                                        // append error map
616                                        this.appendErrorMap(['events': 'You need to create at least one sampling event for your study'], flash.errors)
617                                        error()
618                                } else if (this.handleEvents(flow, flash, params)) {
619                                        success()
620                                } else {
621                                        error()
622                                }
623                        }.to "waitForSave"
624                }
625
626                // groups page
627                groups {
628                        render(view: "_groups")
629                        onRender {
630                                flow.page = 5
631                                success()
632                        }
633                        on("previous") {
634                                this.handleSubjectGrouping(flow, flash, params)
635                                success()
636                        }.to "events"
637                        on("next") {
638                                this.handleSubjectGrouping(flow, flash, params)
639                                flash.check = true
640                                success()
641                        }.to "samples"
642                        on("quickSave") {
643                                this.handleSubjectGrouping(flow, flash, params)
644                                success()
645                        }.to "waitForSave"
646                }
647
648                // sample 'previous' page with warning
649                samplePrevious {
650                        render(view: "_samples_previous_warning")
651                        onRender {
652                                flow.page = 6
653
654                                // TEMPORARY FIX TO REMOVE ALL SAMPLES AND REGENERATE THEM
655                                // THEN USER BROWSED BACK
656                                println ".removing samples from study"
657
658                                // remove samples from study
659                                flow.samples.each() {
660                                        flow.study.removeFromSamples(it.sample)
661                                }
662
663                                // remove samples from flow
664                                flow.remove('samples')
665                                // END FIX
666                        }
667                        on("next").to "samples"
668                        on("previous").to "groups"
669                }
670
671                // samples page
672                samples {
673                        render(view: "_samples")
674                        onRender {
675                                flow.page = 6
676
677                                // iterate through eventGroups
678                                if (!flow.samples) {
679                                        println ".generating samples"
680                                        flow.samplesWithTemplate = 0
681                                        flow.samples = []
682                                        flow.sampleTemplates = [:]
683                                        flow.eventGroups.each() { eventGroup ->
684                                                // iterate through events
685                                                eventGroup.events.each() { event ->
686                                                        if (event.isSamplingEvent()) {
687                                                                def eventName = this.ucwords(event.template.name)
688
689                                                                // iterate through subjects
690                                                                eventGroup.subjects.each() { subject ->
691                                                                        def sampleName = (this.ucwords(subject.name) + '_' + eventName + '_' + new RelTime(event.startTime).toString()).replaceAll("([ ]{1,})", "")
692                                                                        def incrementor = flow.samples.size()
693
694                                                                        flow.samples[ incrementor ] = [
695                                                                                sample: new Sample(
696                                                                                        parent: flow.study,
697                                                                                        parentSubject: subject,
698                                                                                        parentEvent: event,
699                                                                                        name: sampleName
700                                                                                ),
701                                                                                name: sampleName,
702                                                                                eventGroup: eventGroup,
703                                                                                event: event,
704                                                                                subject: subject
705                                                                        ]
706
707                                                                        // and add this sample to the study
708                                                                        flow.study.addToSamples( flow.samples[ incrementor ].sample )
709                                                                }
710                                                        }
711                                                }
712                                        }
713                                } else if (flash.check) {
714                                        println "CHECKING SAMPLE CONSISTENCY"
715                                        // check the consistency of the samples
716                                        flow.samples.each() { sampleData ->
717                                                println sampleData
718                                                println sampleData.event.template
719                                        }
720                                }
721
722                                success()
723                        }
724                        on("switchTemplate") {
725                                //println params
726                                handleSamples(flow, flash, params)
727
728                                // ignore errors
729                                flash.errors = [:]
730                               
731                                succes()
732                        }.to "samples"
733                        on("refresh") {
734                                println ".refresh ${flow.sampleTemplates.size()} sample templates (${flow.samples.size()} samples present)"
735
736                                // refresh templates
737                                flow.sampleTemplates.each() {
738                                        println ".refresh template ["+it.value.name+"]"
739                                        it.value.template.refresh()
740                                        println "  --> fields: "+it.value.template.fields
741                                }
742
743                                // handle samples
744                                handleSamples(flow, flash, params)
745
746                                // ignore errors
747                                flash.errors = [:]
748
749                                success()
750                        }.to "samples"
751                        on("regenerate") {
752                                println ".removing 'samples' and 'sampleTemplates' from the flowscope, triggering regeneration of the samples..."
753                                flow.samples.each() {
754                                        flow.study.removeFromSamples( it.sample )
755                                }
756                                flow.remove('samples')
757                                flow.remove('sampleTemplates')
758                                println flow.study.samples
759                                success()
760                        }.to "samples"
761                        on("previous") {
762                                // handle samples
763                                handleSamples(flow, flash, params)
764
765                                // ignore errors
766                                flash.errors = [:]
767
768                                success()
769                        }.to "samplePrevious"
770                        on("next") {
771                                flash.values = params
772                                flash.errors = [:]
773
774// for now, development only!
775if (grails.util.GrailsUtil.environment == "development") {
776                                // do all samples have a template assigned?
777                                if (flow.samplesWithTemplate < flow.samples.size()) {
778                                        // handle samples
779                                        this.handleSamples(flow, flash, params)
780
781                                        // ignore errors
782                                        flash.errors = [:]
783                                       
784                                        // add error
785                                        this.appendErrorMap(['samples': 'you need to select a template for each sample'], flash.errors)
786
787                                        error()
788                                } else if (this.handleSamples(flow, flash, params)) {
789                                        success()
790                                } else {
791                                        error()
792                                }
793} else {
794        success()
795}
796                        }.to "confirm"
797                        on("quickSave") {
798                                // handle samples
799                                if (handleSamples(flow, flash, params)) {
800                                        success()
801                                } else {
802                                        error()
803                                }
804                        }.to "waitForSave"
805                }
806
807                // confirmation
808                confirm {
809                        render(view: "_confirmation")
810                        onRender {
811                                flow.page = 7
812                        }
813                        on("toStudy").to "study"
814                        on("toSubjects").to "subjects"
815                        on("toEvents").to "events"
816                        on("toGroups").to "groups"
817                        on("previous").to "samples"
818                        on("next").to "waitForSave"
819                        on("quickSave").to "waitForSave"
820                }
821
822                waitForSave {
823                        render(view: "_wait")
824                        onRender {
825                                flow.page = 8
826                        }
827                        on("next").to "save"
828                }
829
830                // store all study data
831                save {
832                        action {
833                                println "saving..."
834                                flash.errors = [:]
835
836                                // persist data to the database
837                                try {
838                                        // save study
839                                        println ".saving study"
840                                        if (!flow.study.save()) {
841                                                this.appendErrors(flow.study, flash.errors)
842                                                throw new Exception('error saving study')
843                                        }
844                                        println ".saved study "+flow.study+" (id: "+flow.study.id+")"
845
846                                        success()
847                                } catch (Exception e) {
848                                        // rollback
849                                        this.appendErrorMap(['exception': e.toString() + ', see log for stacktrace' ], flash.errors)
850
851                                        // stacktrace in flash scope
852                                        flash.debug = e.getStackTrace()
853
854                                        error()
855                                }
856                        }
857                        on("error").to "error"
858                        on(Exception).to "error"
859                        on("success").to "done"
860                }
861
862                // error storing data
863                error {
864                        render(view: "_error")
865                        onRender {
866                                flow.page = 7
867                        }
868                        on("next").to "waitForSave"
869                        on("previous").to "samples"
870                }
871
872                // render finish page
873                done {
874                        render(view: "_done")
875                        onRender {
876                                flow.page = 8
877                        }
878                        onEnd {
879                                // clean flow scope
880                                flow.clear()
881                        }
882                }
883        }
884
885        /**
886         * load a study
887         * @param Map LocalAttributeMap (the flow scope)
888         * @param Map localAttributeMap (the flash scope)
889         * @param Map GrailsParameterMap (the flow parameters = form data)
890         * @returns boolean
891         */
892        def loadStudy(flow, flash, params) {
893                // load study
894                try {
895                        // load study
896                        flow.study = (params.studyid) ? Study.findById( params.studyid ) : Study.findByTitle( params.study )
897
898                        // recreate subjects
899                        flow.subjects = [:]
900                        flow.subjectTemplates = [:]
901                        flow.study.subjects.each() { subject ->
902                                def subjectIncrement = flow.subjects.size()
903                                flow.subjects[subjectIncrement] = subject
904
905                                // add subject template?
906                                if (!flow.subjectTemplates[subject.template.name]) {
907                                        flow.subjectTemplates[subject.template.name] = [
908                                                name: subject.template.name,
909                                                template: subject.template,
910                                                subjects: [:]
911                                        ]
912                                }
913
914                                // reference subject in template
915                                flow.subjectTemplates[subject.template.name].subjects[flow.subjectTemplates[subject.template.name].subjects.size()] = subjectIncrement
916                        }
917
918                        // recreate events
919                        flow.events = [:]
920                        flow.eventGroups = []
921                        flow.eventTemplates = [:]
922                        flow.study.events.each() { event ->
923                                def eventIncrement = flow.events.size()
924                                flow.events[eventIncrement] = event
925
926                                // add event template?
927                                if (!flow.eventTemplates[event.template.name]) {
928                                        flow.eventTemplates[event.template.name] = [
929                                                name: event.template.name,
930                                                template: event.template,
931                                                events: new ArrayList()
932                                        ]
933                                }
934
935                                // reference event in template
936                                flow.eventTemplates[event.template.name].events[flow.eventTemplates[event.template.name].events.size()] = eventIncrement
937
938                                // set dummy event
939                                flow.event = event
940                        }
941
942                        // recreate sample events
943                        flow.study.samplingEvents.each() { event ->
944                                def eventIncrement = flow.events.size()
945                                flow.events[eventIncrement] = event
946
947                                // add event template?
948                                if (!flow.eventTemplates[event.template.name]) {
949                                        flow.eventTemplates[event.template.name] = [
950                                                name: event.template.name,
951                                                template: event.template,
952                                                events: new ArrayList()
953                                        ]
954                                }
955
956                                // reference event in template
957                                flow.eventTemplates[event.template.name].events[flow.eventTemplates[event.template.name].events.size()] = eventIncrement
958
959                                // set dummy event
960                                flow.event = event
961                        }
962
963                        // recreate eventGroups
964                        flow.study.eventGroups.each() { eventGroup ->
965                                flow.eventGroups[flow.eventGroups.size()] = eventGroup
966                        }
967
968                        // set 'quicksave' variable
969                        flow.quickSave = true
970
971                        return true
972                } catch (Exception e) {
973                        // rollback
974                        this.appendErrorMap(['exception': e.toString() + ', see log for stacktrace'], flash.errors)
975
976                        return false
977                }
978        }
979
980        /**
981         * re-usable code for handling study form data in a web flow
982         * @param Map LocalAttributeMap (the flow scope)
983         * @param Map localAttributeMap (the flash scope)
984         * @param Map GrailsParameterMap (the flow parameters = form data)
985         * @returns boolean
986         */
987        def handleStudy(flow, flash, params) {
988                // create study instance if we have none
989                if (!flow.study) flow.study = new Study()
990
991                // create date instance from date string?
992                // @see WizardTagLibrary::dateElement{...}
993                if (params.get('startDate')) {
994                        params.startDate = new Date().parse("d/M/yyyy", params.get('startDate').toString())
995                } else {
996                        params.remove('startDate')
997                }
998
999                // if a template is selected, get template instance
1000                def template = params.remove('template')
1001                if (template instanceof String && template.size() > 0) {
1002                        flow.study.template = Template.findByName(template)
1003                } else if (template instanceof Template) {
1004                        flow.study.template = template
1005                }
1006
1007                // iterate through fields
1008                if (flow.study.template) {
1009                        flow.study.giveFields().each() {
1010                                flow.study.setFieldValue(it.name, params.get(it.escapedName()))
1011                        }
1012                }
1013
1014                // handle Publications and Contacts
1015                handlePublications(flow, flash, params)
1016                handleContacts(flow, flash, params)
1017
1018                // validate study
1019                if (flow.study.validate()) {
1020                        return true
1021                } else {
1022                        // validation failed, feedback errors
1023                        flash.errors = [:]
1024                        this.appendErrors(flow.study, flash.errors)
1025                        return false
1026                }
1027        }
1028
1029        /**
1030         * re-usable code for handling publications form data in a web flow
1031         * @param Map LocalAttributeMap (the flow scope)
1032         * @param Map localAttributeMap (the flash scope)
1033         * @param Map GrailsParameterMap (the flow parameters = form data)
1034         * @returns boolean
1035         */
1036        def handlePublications(flow, flash, params) {
1037                // create study instance if we have none
1038                if (!flow.study) flow.study = new Study()
1039                if (!flow.study.publications) flow.study.publications = []
1040
1041                // Check the ids of the pubblications that should be attached
1042                // to this study. If they are already attached, keep 'm. If
1043                // studies are attached that are not in the selected (i.e. the
1044                // user deleted them), remove them
1045                def publicationIDs = params.get('publication_ids')
1046                if (publicationIDs) {
1047                        // Find the individual IDs and make integers
1048                        publicationIDs = publicationIDs.split(',').collect { Integer.parseInt(it, 10) }
1049
1050                        // First remove the publication that are not present in the array
1051                        flow.study.publications.removeAll { publication -> !publicationIDs.find { id -> id == publication.id } }
1052
1053                        // Add those publications not yet present in the database
1054                        publicationIDs.each { id ->
1055                                if (!flow.study.publications.find { publication -> id == publication.id }) {
1056                                        def publication = Publication.get(id)
1057                                        if (publication) {
1058                                                flow.study.addToPublications(publication)
1059                                        } else {
1060                                                println('.publication with ID ' + id + ' not found in database.')
1061                                        }
1062                                }
1063                        }
1064
1065                } else {
1066                        println('.no publications selected.')
1067                        flow.study.publications.clear()
1068                }
1069
1070        }
1071
1072        /**
1073         * re-usable code for handling contacts form data in a web flow
1074         * @param Map LocalAttributeMap (the flow scope)
1075         * @param Map localAttributeMap (the flash scope)
1076         * @param Map GrailsParameterMap (the flow parameters = form data)
1077         * @return boolean
1078         */
1079        def handleContacts(flow, flash, params) {
1080                // create study instance if we have none
1081                if (!flow.study) flow.study = new Study()
1082                if (!flow.study.persons) flow.study.persons = []
1083
1084                // Check the ids of the contacts that should be attached
1085                // to this study. If they are already attached, keep 'm. If
1086                // studies are attached that are not in the selected (i.e. the
1087                // user deleted them), remove them
1088
1089                // Contacts are saved as [person_id]-[role_id]
1090                def contactIDs = params.get('contacts_ids')
1091                if (contactIDs) {
1092                        // Find the individual IDs and make integers
1093                        contactIDs = contactIDs.split(',').collect {
1094                                def parts = it.split('-')
1095                                return [person: Integer.parseInt(parts[0]), role: Integer.parseInt(parts[1])]
1096                        }
1097
1098                        // First remove the contacts that are not present in the array
1099                        flow.study.persons.removeAll {
1100                                studyperson -> !contactIDs.find { ids -> (ids.person == studyperson.person.id) && (ids.role == studyperson.role.id) }
1101                        }
1102
1103                        // Add those contacts not yet present in the database
1104                        contactIDs.each { ids ->
1105                                if (!flow.study.persons.find { studyperson -> (ids.person == studyperson.person.id) && (ids.role == studyperson.role.id) }) {
1106                                        def person = Person.get(ids.person)
1107                                        def role = PersonRole.get(ids.role)
1108                                        if (person && role) {
1109                                                // Find a studyperson object with these parameters
1110                                                def studyPerson = StudyPerson.findAll().find { studyperson -> studyperson.person.id == person.id && studyperson.role.id == role.id }
1111
1112                                                // If if does not yet exist, save the example
1113                                                if (!studyPerson) {
1114                                                        studyPerson = new StudyPerson(
1115                                                                person: person,
1116                                                                role: role
1117                                                        )
1118                                                        studyPerson.save(flush: true)
1119                                                }
1120
1121                                                flow.study.addToPersons(studyPerson)
1122                                        } else {
1123                                                println('.person ' + ids.person + ' or Role ' + ids.role + ' not found in database.')
1124                                        }
1125                                }
1126                        }
1127                } else {
1128                        println('.no persons selected.')
1129                        flow.study.persons.clear()
1130                }
1131
1132        }
1133
1134        /**
1135         * re-usable code for handling subject form data in a web flow
1136         * @param Map LocalAttributeMap (the flow scope)
1137         * @param Map localAttributeMap (the flash scope)
1138         * @param Map GrailsParameterMap (the flow parameters = form data)
1139         * @return boolean
1140         */
1141        def handleSubjects(flow, flash, params) {
1142                def names = [:]
1143                def errors = false
1144                def id = 0
1145
1146                // iterate through subject templates
1147                flow.subjectTemplates.each() { subjectTemplate ->
1148                        // iterate through subjects
1149                        subjectTemplate.value.subjects.each() { subjectIncrement, subjectId ->
1150                                // iterate through fields (= template fields and domain properties)
1151                                flow.subjects[ subjectId ].giveFields().each() { subjectField ->
1152                                        // set the field
1153                                        flow.subjects[ subjectId ].setFieldValue(
1154                                                subjectField.name,
1155                                                params.get( 'subject_' + subjectId + '_' + subjectField.escapedName() )
1156                                        )
1157                                }
1158
1159                                // validate subject
1160                                if (!flow.subjects[ subjectId ].validate()) {
1161                                        errors = true
1162                                        this.appendErrors(flow.subjects[ subjectId ], flash.errors, 'subject_' + subjectId + '_')
1163                                }
1164                        }
1165                }
1166
1167                return !errors
1168        }
1169
1170        /**
1171         * re-usable code for handling event form data in a web flow
1172         * @param Map LocalAttributeMap (the flow scope)
1173         * @param Map localAttributeMap (the flash scope)
1174         * @param Map GrailsParameterMap (the flow parameters = form data)
1175         * @return boolean
1176         */
1177        def handleEvents(flow, flash, params) {
1178                def errors = false
1179                def template = null
1180
1181                // handle the type of event
1182                if (params.eventType == 'event') {
1183                        flow.event = new Event()
1184                        template = params.remove('eventTemplate')
1185                } else if (params.eventType == 'sample') {
1186                        flow.event = new SamplingEvent()
1187                        template = params.remove('sampleTemplate')
1188                }
1189
1190                // if a template is selected, get template instance
1191                if (template instanceof String && template.size() > 0) {
1192                        params.template = Template.findByName(template)
1193                } else if (template instanceof Template) {
1194                        params.template = template
1195                } else {
1196                        params.template = null
1197                }
1198
1199                // set template
1200                if (params.template) flow.event.template = params.template
1201
1202                // update event instance with parameters
1203                flow.event.giveFields().each() { eventField ->
1204                        flow.event.setFieldValue(eventField.name, params[ eventField.escapedName() ])   
1205                }
1206
1207                // handle event objects
1208                flow.eventTemplates.each() { eventTemplate ->
1209                        // iterate through events
1210                        eventTemplate.getValue().events.each() { eventId ->
1211                                // iterate through template fields
1212                                flow.events[ eventId ].giveFields().each() { eventField ->
1213                                        if ( params.containsKey( 'event_' + eventId + '_' + eventField.escapedName() ) ) {
1214                                                flow.events[ eventId ].setFieldValue(eventField.name, params.get( 'event_' + eventId + '_' + eventField.escapedName() ) )
1215                                        }
1216                                }
1217
1218                                // validate event
1219                                if (!flow.events[ eventId ].validate()) {
1220                                        errors = true
1221                                        this.appendErrors(flow.events[ eventId ], flash.errors, 'event_' + eventId + '_')
1222                                }
1223                        }
1224                }
1225
1226                // handle event grouping
1227                handleEventGrouping(flow, flash, params)
1228
1229                return !errors
1230        }
1231
1232        /**
1233         * re-usable code for handling event grouping in a web flow
1234         * @param Map LocalAttributeMap (the flow scope)
1235         * @param Map localAttributeMap (the flash scope)
1236         * @param Map GrailsParameterMap (the flow parameters = form data)
1237         * @return boolean
1238         */
1239        def handleEventGrouping(flow, flash, params) {
1240                // walk through eventGroups
1241                def g = 0
1242                flow.eventGroups.each() { eventGroup ->
1243                        def e = 0
1244
1245                        // reset events
1246                        eventGroup.events = new HashSet()
1247
1248                        // iterate through events
1249                        flow.events.each() {
1250                                if (params.get('event_' + e + '_group_' + g) == 'on') {
1251                                        eventGroup.addToEvents(it.value)
1252                                }
1253                                e++
1254                        }
1255                        g++
1256                }
1257        }
1258
1259        /**
1260         * re-usable code for handling subject grouping in a web flow
1261         * @param Map LocalAttributeMap (the flow scope)
1262         * @param Map localAttributeMap (the flash scope)
1263         * @param Map GrailsParameterMap (the flow parameters = form data)
1264         * @return boolean
1265         */
1266        def handleSubjectGrouping(flow, flash, params) {
1267                // iterate through event groups
1268                def g = 0
1269                flow.eventGroups.each() { eventGroup ->
1270                        // reset subjects
1271                        eventGroup.subjects = new HashSet()
1272
1273                        // iterate through subjects
1274                        flow.subjects.each() { subjectId, subject ->
1275                                // is this combination set?
1276                                if (params.get('subject_' + subjectId + '_group_' + g) != null) {
1277                                        eventGroup.addToSubjects(subject)
1278                                }
1279                        }
1280
1281                        g++
1282                }
1283        }
1284
1285
1286        /**
1287         * re-usable code for handling samples
1288         * @param Map LocalAttributeMap (the flow scope)
1289         * @param Map localAttributeMap (the flash scope)
1290         * @param Map GrailsParameterMap (the flow parameters = form data)
1291         * @return boolean
1292         */
1293        def handleSamples(flow, flash, params) {
1294                flash.errors = [:]
1295                def errors = false             
1296                def id = 0
1297
1298                // iterate through samples
1299                flow.samples.each() { sampleData ->
1300                        def sample = sampleData.sample
1301                        def sampleTemplateName = params.get('template_'+id)
1302                        def oldSampleTemplateName = sampleData.sample.template.toString()
1303
1304                        // has the sample template for this sample changed
1305                        if (sampleTemplateName && sampleTemplateName.size() > 0 && oldSampleTemplateName != sampleTemplateName) {
1306                                // yes, has the template changed?
1307                                println ".changing template for sample ${id} to ${sampleTemplateName}"
1308
1309                                // decrease previous template count
1310                                if (oldSampleTemplateName && flow.sampleTemplates[ oldSampleTemplateName ]) {
1311                                        flow.sampleTemplates[ oldSampleTemplateName ].count--
1312
1313                                        if (flow.sampleTemplates[ oldSampleTemplateName ].count < 1) {
1314                                                // no samples left, remove template altogether
1315                                                flow.sampleTemplates.remove( oldSampleTemplateName )
1316                                        }
1317                                } else {
1318                                        // increate main template counter?
1319                                        flow.samplesWithTemplate++
1320                                }
1321
1322                                // increase current template count
1323                                if (!flow.sampleTemplates[ sampleTemplateName ]) {
1324                                        flow.sampleTemplates[ sampleTemplateName ] = [
1325                                                name            : sampleTemplateName,
1326                                                template        : Template.findByName( sampleTemplateName ),
1327                                                count           : 1
1328                                        ]
1329                                } else {
1330                                        // increase count
1331                                        flow.sampleTemplates[ sampleTemplateName ].count++
1332                                }
1333
1334                                // change template
1335                                sampleData.sample.template = flow.sampleTemplates[ sampleTemplateName ].template
1336                        }
1337
1338                        // handle values
1339                        sampleData.sample.giveFields().each() { sampleField ->
1340                                if ( params.containsKey( 'sample_'+id+'_'+sampleField.escapedName() ) ) {
1341                                        sampleData.sample.setFieldValue( sampleField.name, params.get( 'sample_'+id+'_'+sampleField.escapedName() ) )
1342                                }
1343                        }
1344
1345                        // validate sample
1346                        if (!sampleData.sample.validate()) {
1347                                errors = true
1348                                this.appendErrors(sampleData.sample, flash.errors, 'sample_' + id + '_' )
1349                        }
1350
1351                        // increase counter
1352                        id++
1353                }
1354
1355                return !errors
1356        }
1357
1358        /**
1359         * groovy / java equivalent of php's ucwords function
1360         *
1361         * Capitalize all first letters of seperate words
1362         *
1363         * @param String
1364         * @return String
1365         */
1366        def ucwords(String text) {
1367                def newText = ''
1368
1369                // change case to lowercase
1370                text = text.toLowerCase()
1371
1372                // iterate through words
1373                text.split(" ").each() {
1374                        newText += it[0].toUpperCase() + it.substring(1) + " "
1375                }
1376
1377                return newText.substring(0, newText.size()-1)
1378        }
1379
1380        /**
1381         * return the object from a map of objects by searching for a name
1382         * @param String name
1383         * @param Map map of objects
1384         * @return Object
1385         */
1386        def getObjectByName(name, map) {
1387                def result = null
1388                map.each() {
1389                        if (it.name == name) {
1390                                result = it
1391                        }
1392                }
1393
1394                return result
1395        }
1396
1397        /**
1398         * transform domain class validation errors into a human readable
1399         * linked hash map
1400         * @param object validated domain class
1401         * @return object  linkedHashMap
1402         */
1403        def getHumanReadableErrors(object) {
1404                def errors = [:]
1405                object.errors.getAllErrors().each() {
1406                        def message = it.toString()
1407
1408                        //errors[it.getArguments()[0]] = it.getDefaultMessage()
1409                        errors[it.getArguments()[0]] = message.substring(0, message.indexOf(';'))
1410                }
1411
1412                return errors
1413        }
1414
1415        /**
1416         * append errors of a particular object to a map
1417         * @param object
1418         * @param map linkedHashMap
1419         * @void
1420         */
1421        def appendErrors(object, map) {
1422                this.appendErrorMap(this.getHumanReadableErrors(object), map)
1423        }
1424
1425        def appendErrors(object, map, prepend) {
1426                this.appendErrorMap(this.getHumanReadableErrors(object), map, prepend)
1427        }
1428
1429        /**
1430         * append errors of one map to another map
1431         * @param map linkedHashMap
1432         * @param map linkedHashMap
1433         * @void
1434         */
1435        def appendErrorMap(map, mapToExtend) {
1436                map.each() {key, value ->
1437                        mapToExtend[key] = ['key': key, 'value': value, 'dynamic': false]
1438                }
1439        }
1440
1441        def appendErrorMap(map, mapToExtend, prepend) {
1442                map.each() {key, value ->
1443                        mapToExtend[prepend + key] = ['key': key, 'value': value, 'dynamic': true]
1444                }
1445        }
1446
1447        /**
1448         * Parses a RelTime string and returns a nice human readable string
1449         *
1450         * @return Human Readable string or a HTTP response code 400 on error
1451         */
1452        def ajaxParseRelTime = {
1453                if (params.reltime == null) {
1454                        response.status = 400
1455                        render('reltime parameter is expected')
1456                }
1457
1458                try {
1459                        def reltime = RelTime.parseRelTime(params.reltime)
1460                        render reltime.toPrettyString()
1461                } catch (IllegalArgumentException e) {
1462                        response.status = 400
1463                        render(e.getMessage())
1464                }
1465        }
1466
1467        /**
1468         * Proxy for searching PubMed articles (or other articles from the Entrez DB).
1469         *
1470         * This proxy is needed because it is not allowed to fetch XML directly from a different
1471         * domain using javascript. So we have the javascript call a function on our own domain
1472         * and the proxy will fetch the data from Entrez
1473         *
1474         * @since       20100609
1475         * @param       _utility        The name of the utility, without the complete path. Example: 'esearch.fcgi'
1476         * @return      XML
1477         */
1478        def entrezProxy = {
1479                // Remove unnecessary parameters
1480                params.remove( "action" )
1481                params.remove( "controller" )
1482
1483                def url = "http://eutils.ncbi.nlm.nih.gov/entrez/eutils";
1484                def util = params.remove( "_utility" )
1485                def paramString = params.collect { k, v -> k + '=' + v.encodeAsURL() }.join( '&' );
1486
1487                def fullUrl = url + '/' + util + '?' + paramString;
1488
1489                // Return the output of the request
1490                // render fullUrl;
1491                render(
1492                    text:           new URL( fullUrl ).getText(),
1493                    contentType:    "text/xml",
1494                    encoding:       "UTF-8"
1495                );
1496        }
1497}
Note: See TracBrowser for help on using the browser.