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

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