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

Revision 544, 29.7 KB (checked in by roberth, 4 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
Line 
1package dbnp.studycapturing
2
3import dbnp.data.*
4
5// Grails convertors is imported in order to create JSON objects
6import grails.converters.*
7
8
9/**
10 * Wizard Controler
11 *
12 * The wizard controller handles the handeling of pages and data flow
13 * through the study capturing wizard.
14 *
15 * @author Jeroen Wesbeek
16 * @since 20100107
17 * @package studycapturing
18 *
19 * Revision information:
20 * $Rev$
21 * $Author$
22 * $Date$
23 */
24class WizardController {
25        /**
26         * index method, redirect to the webflow
27         * @void
28         */
29        def index = {
30                /**
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 browser.