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

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