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

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