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

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