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

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