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

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