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

Revision 664, 39.0 KB (checked in by duh, 4 years ago)

- fixed event page issue
- moved 'addTo*' method calls to their respective actions in the webflow and added 'removeTo*' method calls to make sure deleted items get removed as expected
- still cannot save due to compass / searchable exceptions...

  • 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                                if (!flow.eventTemplates.find { eventTemplate -> eventTemplate.value.template.entity == SamplingEvent }) {
597                                        // append error map
598                                        this.appendErrorMap(['events': 'You need to create at least one sampling event for your study'], flash.errors)
599                                        error()                                         
600                                } else if (this.handleEvents(flow, flash, params)) {
601                                        success()
602                                } else {
603                                        error()
604                                }
605                        }.to "groups"
606                        on("quickSave") {
607                                flash.values = params
608                                flash.errors = [:]
609
610                                // handle study data
611                                if (!flow.eventTemplates.find { eventTemplate -> eventTemplate.value.template.entity == SamplingEvent }) {
612                                        // append error map
613                                        this.appendErrorMap(['events': 'You need to create at least one sampling event for your study'], flash.errors)
614                                        error()
615                                } else if (this.handleEvents(flow, flash, params)) {
616                                        success()
617                                } else {
618                                        error()
619                                }
620                        }.to "waitForSave"
621                }
622
623                // groups page
624                groups {
625                        render(view: "_groups")
626                        onRender {
627                                flow.page = 5
628                                success()
629                        }
630                        on("previous") {
631                                this.handleSubjectGrouping(flow, flash, params)
632                                success()
633                        }.to "events"
634                        on("next") {
635                                this.handleSubjectGrouping(flow, flash, params)
636                                flash.check = true
637                                success()
638                        }.to "samples"
639                        on("quickSave") {
640                                this.handleSubjectGrouping(flow, flash, params)
641                                success()
642                        }.to "waitForSave"
643                }
644
645                // sample 'previous' page with warning
646                samplePrevious {
647                        render(view: "_samples_previous_warning")
648                        onRender {
649                                flow.page = 6
650                        }
651                        on("next").to "samples"
652                        on("previous").to "groups"
653                }
654
655                // samples page
656                samples {
657                        render(view: "_samples")
658                        onRender {
659                                flow.page = 6
660
661                                // iterate through eventGroups
662                                if (!flow.samples) {
663                                        flow.samplesWithTemplate = 0
664                                        flow.samples = []
665                                        flow.sampleTemplates = [:]
666                                        flow.eventGroups.each() { eventGroup ->
667                                                // iterate through events
668                                                eventGroup.events.each() { event ->
669                                                        if (event.isSamplingEvent()) {
670                                                                def eventName = this.ucwords(event.template.name)
671
672                                                                // iterate through subjects
673                                                                eventGroup.subjects.each() { subject ->
674                                                                        def sampleName = (this.ucwords(subject.name) + '_' + eventName + '_' + new RelTime(event.startTime).toString()).replaceAll("([ ]{1,})", "")
675                                                                        def incrementor = flow.samples.size()
676
677                                                                        flow.samples[ incrementor ] = [
678                                                                                sample: new Sample(
679                                                                                        parentSubject: subject,
680                                                                                        parentEvent: event,
681                                                                                        name: sampleName
682                                                                                ),
683                                                                                name: sampleName,
684                                                                                eventGroup: eventGroup,
685                                                                                event: event,
686                                                                                subject: subject
687                                                                        ]
688
689                                                                        // and add this sample to the study
690                                                                        flow.study.addToSamples( flow.samples[ incrementor ].sample )
691                                                                }
692                                                        }
693                                                }
694                                        }
695                                } else if (flash.check) {
696                                        println "CHECKING SAMPLE CONSISTENCY"
697                                        // check the consistency of the samples
698                                        flow.samples.each() { sampleData ->
699                                                println sampleData
700                                                println sampleData.event.template
701                                        }
702                                }
703
704                                success()
705                        }
706                        on("switchTemplate") {
707                                handleSamples(flow, flash, params)
708
709                                // ignore errors
710                                flash.errors = [:]
711                               
712                                succes()
713                        }.to "samples"
714                        on("refresh") {
715                                println ".refresh ${flow.sampleTemplates.size()} sample templates (${flow.samples.size()} samples present)"
716
717                                // refresh templates
718                                flow.sampleTemplates.each() {
719                                        println ".refresh template ["+it.value.name+"]"
720                                        it.value.template.refresh()
721                                        println "  --> fields: "+it.value.template.fields
722                                }
723
724                                // handle samples
725                                handleSamples(flow, flash, params)
726
727                                // ignore errors
728                                flash.errors = [:]
729
730                                success()
731                        }.to "samples"
732                        on("regenerate") {
733                                println ".removing 'samples' and 'sampleTemplates' from the flowscope, triggering regeneration of the samples..."
734                                flow.remove('samples')
735                                flow.remove('sampleTemplates')
736                                success()
737                        }.to "samples"
738                        on("previous") {
739                                // handle samples
740                                handleSamples(flow, flash, params)
741
742                                // ignore errors
743                                flash.errors = [:]
744
745                                success()
746                        }.to "samplePrevious"
747                        on("next") {
748                                flash.values = params
749                                flash.errors = [:]
750
751                                // do all samples have a template assigned?
752                                if (flow.samplesWithTemplate < flow.samples.size()) {
753                                        // handle samples
754                                        this.handleSamples(flow, flash, params)
755
756                                        // ignore errors
757                                        flash.errors = [:]
758                                       
759                                        // add error
760                                        this.appendErrorMap(['samples': 'you need to select a template for each sample'], flash.errors)
761
762                                        error()
763                                } else if (this.handleSamples(flow, flash, params)) {
764                                        success()
765                                } else {
766                                        error()
767                                }
768                        }.to "confirm"
769                        on("quickSave") {
770                                // handle samples
771                                if (handleSamples(flow, flash, params)) {
772                                        success()
773                                } else {
774                                        error()
775                                }
776                        }.to "waitForSave"
777                }
778
779                // confirmation
780                confirm {
781                        render(view: "_confirmation")
782                        onRender {
783                                flow.page = 7
784                        }
785                        on("toStudy").to "study"
786                        on("toSubjects").to "subjects"
787                        on("toEvents").to "events"
788                        on("toGroups").to "groups"
789                        on("previous").to "samples"
790                        on("next").to "waitForSave"
791                        on("quickSave").to "waitForSave"
792                }
793
794                waitForSave {
795                        render(view: "_wait")
796                        onRender {
797                                flow.page = 8
798                        }
799                        on("next").to "save"
800                }
801
802                // store all study data
803                save {
804                        action {
805                                println "saving..."
806                                flash.errors = [:]
807
808                                // persist data to the database
809                                try {
810                                        println ".saving wizard data..."
811
812                                        // add events to study
813                                        /*
814                                        println ".add events to study"
815                                        flow.events.each() { event ->
816                                                if (event instanceof SamplingEvent) {
817                                                        // only add this sampling event if it is not yet
818                                                        // linked to this study
819                                                        if (!flow.study.samplingEvents.find { e -> (e == event) }) {
820                                                                flow.study.addToSamplingEvents( event.value )
821                                                        }
822                                                } else {
823                                                        // only add it if it does not yet exist
824                                                        if (!flow.study.events.find { e -> (e == event) }) {
825                                                                flow.study.addToEvents( event.value )
826                                                        }
827                                                }
828                                        }
829
830                                        // add subjects to study
831                                        println ".add subjects to study"
832                                        flow.subjects.each() { subjectId, subject ->
833                                                // add subject to study if it is not yet in this study
834                                                if (!flow.study.subjects.find { s -> (s == subject) }) {
835                                                        flow.study.addToSubjects(subject)
836                                                }
837                                        }
838
839                                        // add eventGroups to study
840                                        println ".add eventGroups to study"
841                                        flow.eventGroups.each() { eventGroup ->
842                                                // only add the eventGroup if it is not yet linked
843                                                if (!flow.study.eventGroups.find { e -> (e == eventGroup) }) {
844                                                        flow.study.addToEventGroups(eventGroup)
845                                                }
846                                        }
847                                        */
848
849                                        // save study
850                                        println ".saving study"
851                                        if (!flow.study.save(flush:true)) {
852                                                this.appendErrors(flow.study, flash.errors)
853                                                throw new Exception('error saving study')
854                                        }
855                                        println ".saved study "+flow.study+" (id: "+flow.study.id+")"
856
857                                        success()
858                                } catch (Exception e) {
859                                        // rollback
860                                        this.appendErrorMap(['exception': e.toString() + ', see log for stacktrace' ], flash.errors)
861
862                                        // stacktrace in flash scope
863                                        flash.debug = e.getStackTrace()
864
865                                        error()
866                                }
867                        }
868                        on("error").to "error"
869                        on(Exception).to "error"
870                        on("success").to "done"
871                }
872
873                // error storing data
874                error {
875                        render(view: "_error")
876                        onRender {
877                                flow.page = 7
878                        }
879                        on("next").to "waitForSave"
880                        on("previous").to "samples"
881                }
882
883                // render finish page
884                done {
885                        render(view: "_done")
886                        onRender {
887                                flow.page = 8
888                        }
889                        onEnd {
890                                // clean flow scope
891                                flow.clear()
892                        }
893                }
894        }
895
896        /**
897         * load a study
898         * @param Map LocalAttributeMap (the flow scope)
899         * @param Map localAttributeMap (the flash scope)
900         * @param Map GrailsParameterMap (the flow parameters = form data)
901         * @returns boolean
902         */
903        def loadStudy(flow, flash, params) {
904                // load study
905                try {
906                        // load study
907                        flow.study = (params.studyid) ? Study.findById( params.studyid ) : Study.findByTitle( params.study )
908
909                        // recreate subjects
910                        flow.subjects = [:]
911                        flow.subjectTemplates = [:]
912                        flow.study.subjects.each() { subject ->
913                                def subjectIncrement = flow.subjects.size()
914                                flow.subjects[subjectIncrement] = subject
915
916                                // add subject template?
917                                if (!flow.subjectTemplates[subject.template.name]) {
918                                        flow.subjectTemplates[subject.template.name] = [
919                                                name: subject.template.name,
920                                                template: subject.template,
921                                                subjects: [:]
922                                        ]
923                                }
924
925                                // reference subject in template
926                                flow.subjectTemplates[subject.template.name].subjects[flow.subjectTemplates[subject.template.name].subjects.size()] = subjectIncrement
927                        }
928
929                        // recreate events
930                        flow.events = []
931                        flow.eventGroups = []
932                        flow.eventTemplates = [:]
933                        flow.study.events.each() { event ->
934                                def eventIncrement = flow.events.size()
935                                flow.events[eventIncrement] = event
936
937                                // add event template?
938                                if (!flow.eventTemplates[event.template.name]) {
939                                        flow.eventTemplates[event.template.name] = [
940                                                name: event.template.name,
941                                                template: event.template,
942                                                events: new ArrayList()
943                                        ]
944                                }
945
946                                // reference event in template
947                                flow.eventTemplates[event.template.name].events[flow.eventTemplates[event.template.name].events.size()] = eventIncrement
948
949                                // set dummy event
950                                flow.event = event
951                        }
952
953                        // recreate sample events
954                        flow.study.samplingEvents.each() { event ->
955                                def eventIncrement = flow.events.size()
956                                flow.events[eventIncrement] = event
957
958                                // add event template?
959                                if (!flow.eventTemplates[event.template.name]) {
960                                        flow.eventTemplates[event.template.name] = [
961                                                name: event.template.name,
962                                                template: event.template,
963                                                events: new ArrayList()
964                                        ]
965                                }
966
967                                // reference event in template
968                                flow.eventTemplates[event.template.name].events[flow.eventTemplates[event.template.name].events.size()] = eventIncrement
969
970                                // set dummy event
971                                flow.event = event
972                        }
973
974                        // recreate eventGroups
975                        flow.study.eventGroups.each() { eventGroup ->
976                                flow.eventGroups[flow.eventGroups.size()] = eventGroup
977                        }
978
979                        // set 'quicksave' variable
980                        flow.quickSave = true
981
982                        return true
983                } catch (Exception e) {
984                        // rollback
985                        this.appendErrorMap(['exception': e.toString() + ', see log for stacktrace'], flash.errors)
986
987                        return false
988                }
989        }
990
991        /**
992         * re-usable code for handling study form data in a web flow
993         * @param Map LocalAttributeMap (the flow scope)
994         * @param Map localAttributeMap (the flash scope)
995         * @param Map GrailsParameterMap (the flow parameters = form data)
996         * @returns boolean
997         */
998        def handleStudy(flow, flash, params) {
999                // create study instance if we have none
1000                if (!flow.study) flow.study = new Study()
1001
1002                // create date instance from date string?
1003                // @see WizardTagLibrary::dateElement{...}
1004                if (params.get('startDate')) {
1005                        params.startDate = new Date().parse("d/M/yyyy", params.get('startDate').toString())
1006                } else {
1007                        params.remove('startDate')
1008                }
1009
1010                // if a template is selected, get template instance
1011                def template = params.remove('template')
1012                if (template instanceof String && template.size() > 0) {
1013                        flow.study.template = Template.findByName(template)
1014                } else if (template instanceof Template) {
1015                        flow.study.template = template
1016                }
1017
1018                // iterate through fields
1019                if (flow.study.template) {
1020                        flow.study.giveFields().each() {
1021                                flow.study.setFieldValue(it.name, params.get(it.escapedName()))
1022                        }
1023                }
1024
1025                // handle Publications and Contacts
1026                handlePublications(flow, flash, params)
1027                handleContacts(flow, flash, params)
1028
1029                // validate study
1030                if (flow.study.validate()) {
1031                        return true
1032                } else {
1033                        // validation failed, feedback errors
1034                        flash.errors = [:]
1035                        this.appendErrors(flow.study, flash.errors)
1036                        return false
1037                }
1038        }
1039
1040        /**
1041         * re-usable code for handling publications form data in a web flow
1042         * @param Map LocalAttributeMap (the flow scope)
1043         * @param Map localAttributeMap (the flash scope)
1044         * @param Map GrailsParameterMap (the flow parameters = form data)
1045         * @returns boolean
1046         */
1047        def handlePublications(flow, flash, params) {
1048                // create study instance if we have none
1049                if (!flow.study) flow.study = new Study()
1050                if (!flow.study.publications) flow.study.publications = []
1051
1052                // Check the ids of the pubblications that should be attached
1053                // to this study. If they are already attached, keep 'm. If
1054                // studies are attached that are not in the selected (i.e. the
1055                // user deleted them), remove them
1056                def publicationIDs = params.get('publication_ids')
1057                if (publicationIDs) {
1058                        // Find the individual IDs and make integers
1059                        publicationIDs = publicationIDs.split(',').collect { Integer.parseInt(it, 10) }
1060
1061                        // First remove the publication that are not present in the array
1062                        flow.study.publications.removeAll { publication -> !publicationIDs.find { id -> id == publication.id } }
1063
1064                        // Add those publications not yet present in the database
1065                        publicationIDs.each { id ->
1066                                if (!flow.study.publications.find { publication -> id == publication.id }) {
1067                                        def publication = Publication.get(id)
1068                                        if (publication) {
1069                                                flow.study.addToPublications(publication)
1070                                        } else {
1071                                                println('.publication with ID ' + id + ' not found in database.')
1072                                        }
1073                                }
1074                        }
1075
1076                } else {
1077                        println('.no publications selected.')
1078                        flow.study.publications.clear()
1079                }
1080
1081        }
1082
1083        /**
1084         * re-usable code for handling contacts form data in a web flow
1085         * @param Map LocalAttributeMap (the flow scope)
1086         * @param Map localAttributeMap (the flash scope)
1087         * @param Map GrailsParameterMap (the flow parameters = form data)
1088         * @return boolean
1089         */
1090        def handleContacts(flow, flash, params) {
1091                // create study instance if we have none
1092                if (!flow.study) flow.study = new Study()
1093                if (!flow.study.persons) flow.study.persons = []
1094
1095                // Check the ids of the contacts that should be attached
1096                // to this study. If they are already attached, keep 'm. If
1097                // studies are attached that are not in the selected (i.e. the
1098                // user deleted them), remove them
1099
1100                // Contacts are saved as [person_id]-[role_id]
1101                def contactIDs = params.get('contacts_ids')
1102                if (contactIDs) {
1103                        // Find the individual IDs and make integers
1104                        contactIDs = contactIDs.split(',').collect {
1105                                def parts = it.split('-')
1106                                return [person: Integer.parseInt(parts[0]), role: Integer.parseInt(parts[1])]
1107                        }
1108
1109                        // First remove the contacts that are not present in the array
1110                        flow.study.persons.removeAll {
1111                                studyperson -> !contactIDs.find { ids -> (ids.person == studyperson.person.id) && (ids.role == studyperson.role.id) }
1112                        }
1113
1114                        // Add those contacts not yet present in the database
1115                        contactIDs.each { ids ->
1116                                if (!flow.study.persons.find { studyperson -> (ids.person == studyperson.person.id) && (ids.role == studyperson.role.id) }) {
1117                                        def person = Person.get(ids.person)
1118                                        def role = PersonRole.get(ids.role)
1119                                        if (person && role) {
1120                                                // Find a studyperson object with these parameters
1121                                                def studyPerson = StudyPerson.findAll().find { studyperson -> studyperson.person.id == person.id && studyperson.role.id == role.id }
1122
1123                                                // If if does not yet exist, save the example
1124                                                if (!studyPerson) {
1125                                                        studyPerson = new StudyPerson(
1126                                                                person: person,
1127                                                                role: role
1128                                                        )
1129                                                        studyPerson.save(flush: true)
1130                                                }
1131
1132                                                flow.study.addToPersons(studyPerson)
1133                                        } else {
1134                                                println('.person ' + ids.person + ' or Role ' + ids.role + ' not found in database.')
1135                                        }
1136                                }
1137                        }
1138                } else {
1139                        println('.no persons selected.')
1140                        flow.study.persons.clear()
1141                }
1142
1143        }
1144
1145        /**
1146         * re-usable code for handling subject form data in a web flow
1147         * @param Map LocalAttributeMap (the flow scope)
1148         * @param Map localAttributeMap (the flash scope)
1149         * @param Map GrailsParameterMap (the flow parameters = form data)
1150         * @return boolean
1151         */
1152        def handleSubjects(flow, flash, params) {
1153                def names = [:]
1154                def errors = false
1155                def id = 0
1156
1157                // iterate through subject templates
1158                flow.subjectTemplates.each() { subjectTemplate ->
1159                        // iterate through subjects
1160                        subjectTemplate.value.subjects.each() { subjectIncrement, subjectId ->
1161                                // iterate through fields (= template fields and domain properties)
1162                                flow.subjects[ subjectId ].giveFields().each() { subjectField ->
1163                                        // set the field
1164                                        flow.subjects[ subjectId ].setFieldValue(
1165                                                subjectField.name,
1166                                                params.get( 'subject_' + subjectId + '_' + subjectField.escapedName() )
1167                                        )
1168                                }
1169
1170                                // validate subject
1171                                if (!flow.subjects[ subjectId ].validate()) {
1172                                        errors = true
1173                                        this.appendErrors(flow.subjects[ subjectId ], flash.errors, 'subject_' + subjectId + '_')
1174                                }
1175                        }
1176                }
1177
1178                return !errors
1179        }
1180
1181        /**
1182         * re-usable code for handling event form data in a web flow
1183         * @param Map LocalAttributeMap (the flow scope)
1184         * @param Map localAttributeMap (the flash scope)
1185         * @param Map GrailsParameterMap (the flow parameters = form data)
1186         * @return boolean
1187         */
1188        def handleEvents(flow, flash, params) {
1189                def errors = false
1190                def template = null
1191
1192                // handle the type of event
1193                if (params.eventType == 'event') {
1194                        flow.event = new Event()
1195                        template = params.remove('eventTemplate')
1196                } else if (params.eventType == 'sample') {
1197                        flow.event = new SamplingEvent()
1198                        template = params.remove('sampleTemplate')
1199                }
1200
1201                // if a template is selected, get template instance
1202                if (template instanceof String && template.size() > 0) {
1203                        params.template = Template.findByName(template)
1204                } else if (template instanceof Template) {
1205                        params.template = template
1206                } else {
1207                        params.template = null
1208                }
1209
1210                // set template
1211                if (params.template) flow.event.template = params.template
1212
1213                // update event instance with parameters
1214                flow.event.giveFields().each() { eventField ->
1215                        flow.event.setFieldValue(eventField.name, params[ eventField.escapedName() ])   
1216                }
1217
1218                // handle event objects
1219                flow.eventTemplates.each() { eventTemplate ->
1220                        // iterate through events
1221                        eventTemplate.getValue().events.each() { eventId ->
1222                                // iterate through template fields
1223                                flow.events[ eventId ].giveFields().each() { eventField ->
1224                                        if ( params.containsKey( 'event_' + eventId + '_' + eventField.escapedName() ) ) {
1225                                                flow.events[ eventId ].setFieldValue(eventField.name, params.get( 'event_' + eventId + '_' + eventField.escapedName() ) )
1226                                        }
1227                                }
1228
1229                                // validate event
1230                                if (!flow.events[ eventId ].validate()) {
1231                                        errors = true
1232                                        this.appendErrors(flow.events[ eventId ], flash.errors, 'event_' + eventId + '_')
1233                                }
1234                        }
1235                }
1236
1237                // handle event grouping
1238                handleEventGrouping(flow, flash, params)
1239
1240                return !errors
1241        }
1242
1243        /**
1244         * re-usable code for handling event grouping in a web flow
1245         * @param Map LocalAttributeMap (the flow scope)
1246         * @param Map localAttributeMap (the flash scope)
1247         * @param Map GrailsParameterMap (the flow parameters = form data)
1248         * @return boolean
1249         */
1250        def handleEventGrouping(flow, flash, params) {
1251                // walk through eventGroups
1252                def g = 0
1253                flow.eventGroups.each() { eventGroup ->
1254                        def e = 0
1255
1256                        // reset events
1257                        eventGroup.events = new HashSet()
1258
1259                        // iterate through events
1260                        flow.events.each() {
1261                                if (params.get('event_' + e + '_group_' + g) == 'on') {
1262                                        eventGroup.addToEvents(it.value)
1263                                }
1264                                e++
1265                        }
1266                        g++
1267                }
1268        }
1269
1270        /**
1271         * re-usable code for handling subject grouping in a web flow
1272         * @param Map LocalAttributeMap (the flow scope)
1273         * @param Map localAttributeMap (the flash scope)
1274         * @param Map GrailsParameterMap (the flow parameters = form data)
1275         * @return boolean
1276         */
1277        def handleSubjectGrouping(flow, flash, params) {
1278                // iterate through event groups
1279                def g = 0
1280                flow.eventGroups.each() { eventGroup ->
1281                        // reset subjects
1282                        eventGroup.subjects = new HashSet()
1283
1284                        // iterate through subjects
1285                        flow.subjects.each() { subjectId, subject ->
1286                                // is this combination set?
1287                                if (params.get('subject_' + subjectId + '_group_' + g) != null) {
1288                                        eventGroup.addToSubjects(subject)
1289                                }
1290                        }
1291
1292                        g++
1293                }
1294        }
1295
1296
1297        /**
1298         * re-usable code for handling samples
1299         * @param Map LocalAttributeMap (the flow scope)
1300         * @param Map localAttributeMap (the flash scope)
1301         * @param Map GrailsParameterMap (the flow parameters = form data)
1302         * @return boolean
1303         */
1304        def handleSamples(flow, flash, params) {
1305                flash.errors = [:]
1306                def errors = false             
1307                def id = 0
1308
1309                // iterate through samples
1310                flow.samples.each() { sampleData ->
1311                        def sample = sampleData.sample
1312                        def sampleTemplateName = params.get('template_'+id)
1313                        def oldSampleTemplateName = sampleData.sample.template.toString()
1314
1315                        // has the sample template for this sample changed
1316                        if (sampleTemplateName && sampleTemplateName.size() > 0 && oldSampleTemplateName != sampleTemplateName) {
1317                                // yes, has the template changed?
1318                                println ".changing template for sample ${id} to ${sampleTemplateName}"
1319
1320                                // decrease previous template count
1321                                if (oldSampleTemplateName && flow.sampleTemplates[ oldSampleTemplateName ]) {
1322                                        flow.sampleTemplates[ oldSampleTemplateName ].count--
1323
1324                                        if (flow.sampleTemplates[ oldSampleTemplateName ].count < 1) {
1325                                                // no samples left, remove template altogether
1326                                                flow.sampleTemplates.remove( oldSampleTemplateName )
1327                                        }
1328                                } else {
1329                                        // increate main template counter?
1330                                        flow.samplesWithTemplate++
1331                                }
1332
1333                                // increase current template count
1334                                if (!flow.sampleTemplates[ sampleTemplateName ]) {
1335                                        flow.sampleTemplates[ sampleTemplateName ] = [
1336                                                name            : sampleTemplateName,
1337                                                template        : Template.findByName( sampleTemplateName ),
1338                                                count           : 1
1339                                        ]
1340                                } else {
1341                                        // increase count
1342                                        flow.sampleTemplates[ sampleTemplateName ].count++
1343                                }
1344
1345                                // change template
1346                                sampleData.sample.template = flow.sampleTemplates[ sampleTemplateName ].template
1347                        }
1348
1349                        // handle values
1350                        sampleData.sample.giveFields().each() { sampleField ->
1351                                if ( params.containsKey( 'sample_'+id+'_'+sampleField.escapedName() ) ) {
1352                                        sampleData.sample.setFieldValue( sampleField.name, params.get( 'sample_'+id+'_'+sampleField.escapedName() ) )
1353                                }
1354                        }
1355
1356                        // validate sample
1357                        if (!sampleData.sample.validate()) {
1358                                errors = true
1359                                this.appendErrors(sampleData.sample, flash.errors, 'sample_' + id + '_' )
1360                        }
1361
1362                        // increase counter
1363                        id++
1364                }
1365
1366                return !errors
1367        }
1368
1369        /**
1370         * groovy / java equivalent of php's ucwords function
1371         *
1372         * Capitalize all first letters of seperate words
1373         *
1374         * @param String
1375         * @return String
1376         */
1377        def ucwords(String text) {
1378                def newText = ''
1379
1380                // change case to lowercase
1381                text = text.toLowerCase()
1382
1383                // iterate through words
1384                text.split(" ").each() {
1385                        newText += it[0].toUpperCase() + it.substring(1) + " "
1386                }
1387
1388                return newText.substring(0, newText.size()-1)
1389        }
1390
1391        /**
1392         * return the object from a map of objects by searching for a name
1393         * @param String name
1394         * @param Map map of objects
1395         * @return Object
1396         */
1397        def getObjectByName(name, map) {
1398                def result = null
1399                map.each() {
1400                        if (it.name == name) {
1401                                result = it
1402                        }
1403                }
1404
1405                return result
1406        }
1407
1408        /**
1409         * transform domain class validation errors into a human readable
1410         * linked hash map
1411         * @param object validated domain class
1412         * @return object  linkedHashMap
1413         */
1414        def getHumanReadableErrors(object) {
1415                def errors = [:]
1416                object.errors.getAllErrors().each() {
1417                        def message = it.toString()
1418
1419                        //errors[it.getArguments()[0]] = it.getDefaultMessage()
1420                        errors[it.getArguments()[0]] = message.substring(0, message.indexOf(';'))
1421                }
1422
1423                return errors
1424        }
1425
1426        /**
1427         * append errors of a particular object to a map
1428         * @param object
1429         * @param map linkedHashMap
1430         * @void
1431         */
1432        def appendErrors(object, map) {
1433                this.appendErrorMap(this.getHumanReadableErrors(object), map)
1434        }
1435
1436        def appendErrors(object, map, prepend) {
1437                this.appendErrorMap(this.getHumanReadableErrors(object), map, prepend)
1438        }
1439
1440        /**
1441         * append errors of one map to another map
1442         * @param map linkedHashMap
1443         * @param map linkedHashMap
1444         * @void
1445         */
1446        def appendErrorMap(map, mapToExtend) {
1447                map.each() {key, value ->
1448                        mapToExtend[key] = ['key': key, 'value': value, 'dynamic': false]
1449                }
1450        }
1451
1452        def appendErrorMap(map, mapToExtend, prepend) {
1453                map.each() {key, value ->
1454                        mapToExtend[prepend + key] = ['key': key, 'value': value, 'dynamic': true]
1455                }
1456        }
1457
1458        /**
1459         * Parses a RelTime string and returns a nice human readable string
1460         *
1461         * @return Human Readable string or a HTTP response code 400 on error
1462         */
1463        def ajaxParseRelTime = {
1464                if (params.reltime == null) {
1465                        response.status = 400
1466                        render('reltime parameter is expected')
1467                }
1468
1469                try {
1470                        def reltime = RelTime.parseRelTime(params.reltime)
1471                        render reltime.toPrettyString()
1472                } catch (IllegalArgumentException e) {
1473                        response.status = 400
1474                        render(e.getMessage())
1475                }
1476        }
1477
1478        /**
1479         * Proxy for searching PubMed articles (or other articles from the Entrez DB).
1480         *
1481         * This proxy is needed because it is not allowed to fetch XML directly from a different
1482         * domain using javascript. So we have the javascript call a function on our own domain
1483         * and the proxy will fetch the data from Entrez
1484         *
1485         * @since       20100609
1486         * @param       _utility        The name of the utility, without the complete path. Example: 'esearch.fcgi'
1487         * @return      XML
1488         */
1489        def entrezProxy = {
1490                // Remove unnecessary parameters
1491                params.remove( "action" )
1492                params.remove( "controller" )
1493
1494                def url = "http://eutils.ncbi.nlm.nih.gov/entrez/eutils";
1495                def util = params.remove( "_utility" )
1496                def paramString = params.collect { k, v -> k + '=' + v.encodeAsURL() }.join( '&' );
1497
1498                def fullUrl = url + '/' + util + '?' + paramString;
1499
1500                // Return the output of the request
1501                // render fullUrl;
1502                render(
1503                    text:           new URL( fullUrl ).getText(),
1504                    contentType:    "text/xml",
1505                    encoding:       "UTF-8"
1506                );
1507        }
1508}
Note: See TracBrowser for help on using the browser.