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

Last change on this file since 562 was 562, checked in by keesvb, 9 years ago

version 0.3.1: re-enabled saving and deleted >0 subjects requirement in create wizard, disabled test persons/publications in BootStrap?

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