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

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