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

Revision 635, 38.0 KB (checked in by duh, 4 years ago)

- development version of samples page
-- improved handeling of samples
-- added warning page after clicking 'previous' in samples page
- fixed exceptions in deleting events
- added debug button to events page to reset revents
- changed events page to check for at least 1 sampling event instead of just 1 event (you need sampling events in order to have samples and groups later on)

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