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

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