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

Revision 607, 35.9 KB (checked in by keesvb, 4 years ago)

fixed problem with sample templates not refreshing; added and deleted some debug comments

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