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

Last change on this file since 544 was 544, checked in by roberth, 11 years ago

Template editor is now able to add, edit, move and delete fields. Also fixed bug #90 (searching publications in IE didn't work because fetching XML from another domain was not allowed)

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