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

Revision 675, 38.2 KB (checked in by duh, 4 years ago)

- temporarily disabled samples logic in controler for non-development environments (bugfix)

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