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

Revision 959, 39.5 KB (checked in by j.a.m.wesbeek@…, 4 years ago)

- set keyword expansion

  • Property svn:keywords set to Author Date Rev
Line 
1package dbnp.studycapturing
2
3import dbnp.data.*
4
5// Grails convertors is imported in order to create JSON objects
6import grails.converters.*
7
8
9/**
10 * Wizard Controler
11 *
12 * The wizard controller handles the handeling of pages and data flow
13 * through the study capturing wizard.
14 *
15 * @author Jeroen Wesbeek
16 * @since 20100107
17 * @package studycapturing
18 *
19 * Revision information:
20 * $Rev$
21 * $Author$
22 * $Date$
23 */
24class WizardController {
25        /**
26         * index method, redirect to the webflow
27         * @void
28         */
29        def index = {
30                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: 'Event Groups'],              // groups
83                                [title: 'Samples'],                             // samples
84                                [title: 'Assays'],                              // assays
85                                //[title: 'Assay Groups'],              // assays
86                                [title: 'Confirmation'],                // confirmation page
87                                [title: 'Done']                                 // finish page
88                        ]
89                        flow.jump = session.jump
90                        success()
91                }
92
93                // render the main wizard page which immediately
94                // triggers the 'next' action (hence, the main
95                // page dynamically renders the study template
96                // and makes the flow jump to the study logic)
97                mainPage {
98                        render(view: "/wizard/index")
99                        onRender {
100                                flow.page = 1
101                                success()
102                        }
103                        on("next").to "handleJump"
104                }
105
106                // handle the jump parameter
107                //
108                // I came to get down [2x]
109                // So get out your seats and jump around
110                // Jump around [3x]
111                // Jump up Jump up and get down
112                // Jump [18x]
113                handleJump {
114                        action {
115                                if (flow.jump && flow.jump.action == 'edit' && flow.jump.id) {
116                                        // load study
117                                        if (this.loadStudy(flow, flash, [studyid:flow.jump.id])) {
118                                                toStudyPage()
119                                        } else {
120                                                toStartPage()
121                                        }
122                                } else if (flow.jump && flow.jump.action == 'create') {
123                                        toStudyPage()
124                                } else {
125                                        toStartPage()
126                                }
127                        }
128                        on("toStartPage").to "start"
129                        on("toStudyPage").to "study"
130                }
131
132                // create or modify a study
133                start {
134                        render(view: "_start")
135                        onRender {
136                                flow.page = 1
137                                success()
138                        }
139                        on("next") {
140                                // clean the flow scope
141                                flow.remove('study')
142
143                                // set 'quicksave' variable to false
144                                flow.quickSave = false
145                        }.to "study"
146                        on("modify").to "modify"
147                        on("import").to "redirectToImport"
148                }
149
150                // redirect to the import wizard
151                redirectToImport {
152                        render(view: "_redirect")
153                        onRender {
154                                flash.uri = "/importer/index"
155                        }
156                        on("next").to "start"
157                }
158
159                // load a study to modify
160                modify {
161                        render(view: "_modify")
162                        onRender {
163                                flow.page = 1
164                                flash.cancel = true
165                                success()
166                        }
167                        on("cancel") {
168                                flow.remove('study')
169
170                                success()
171                        }.to "start"
172                        on("next") {
173                                // load study
174                                if (this.loadStudy(flow, flash, params)) {
175                                        success()
176                                } else {
177                                        error()
178                                }
179                        }.to "study"
180                }
181
182                // render and handle the study page
183                study {
184                        render(view: "_study")
185                        onRender {
186                                flow.page = 2
187                                success()
188                        }
189                        on("refresh") {
190                                // handle form data
191                                studyPage(flow, flash, params)
192
193                                // force refresh of the template
194                                if (flow.study.template && flow.study.template instanceof Template) {
195                                        flow.study.template.refresh()
196                                }
197
198                                // reset errors
199                                flash.errors = [:]
200                                success()
201                        }.to "study"
202            on("switchTemplate") {
203                                // handle form data
204                                studyPage(flow, flash, params)
205
206                                // reset errors
207                                flash.errors = [:]
208                                success()
209                        }.to "study"
210                        on("previous") {
211                                // handle form data
212                                studyPage(flow, flash, params)
213
214                                // reset errors
215                                flash.errors = [:]
216                                success()
217                        }.to "start"
218                        on("next") {
219                                studyPage(flow, flash, params) ? success() : error()
220                        }.to "subjects"
221                        on("quickSave") {
222                                studyPage(flow, flash, params) ? success() : error()
223                        }.to "waitForSave"
224                }
225
226                // render and handle subjects page
227                subjects {
228                        render(view: "_subjects")
229                        onRender {
230                                flow.page = 3
231
232                                if (!flash.values || !flash.values.addNumber) flash.values = [addNumber:1]
233
234                                success()
235                        }
236                        on("refresh") {
237                                // remember the params in the flash scope
238                                flash.values = params
239
240                                // refresh templates
241                                if (flow.study.subjects) {
242                                        flow.study.giveSubjectTemplates().each() {
243                                                it.refresh()
244                                        }
245                                }
246
247                                success()
248                        }.to "subjects"
249                        on("add") {
250                                // handle form data
251                                addSubjects(flow, flash, params) ? success() : error()
252                        }.to "subjects"
253                        on("delete") {
254                                // handle form data
255                                subjectPage(flow, flash, params)
256
257                                // reset errors
258                                flash.errors = [:]
259
260                                // remove subject
261                                def subjectToRemove = flow.study.subjects.find { it.identifier == (params.get('do') as int) }
262                                if (subjectToRemove) {
263                                        flow.study.deleteSubject( subjectToRemove )
264                                }
265                        }.to "subjects"
266                        on("previous") {
267                                // handle form data
268                                subjectPage(flow, flash, params)
269
270                                // reset errors
271                                flash.errors = [:]
272                                success()
273                        }.to "study"
274                        on("next") {
275                                // handle form data
276                                subjectPage(flow, flash, params) ? success() : error()
277                        }.to "events"
278                        on("quickSave") {                               
279                                // handle form data
280                                subjectPage(flow, flash, params) ? success() : error()
281                        }.to "waitForSave"
282                }
283
284                // render events page
285                events {
286                        render(view: "_events")
287                        onRender {
288                                flow.page = 4
289
290                                // add initial eventGroup to study
291                                if (!flow.study.eventGroups?.size()) {
292                                        flow.study.addToEventGroups(
293                                                new EventGroup(name: 'Group 1')
294                                        )
295                                }
296
297                                success()
298                        }
299                        on("clear") {
300                                // remove all events
301                                (flow.study.events + flow.study.samplingEvents).each() { event ->
302                                        if (event instanceof SamplingEvent) {
303                                                flow.study.deleteSamplingEvent( event )
304                                        } else {
305                                                flow.study.deleteEvent( event )
306                                        }
307                                }
308
309                                success()
310                        }.to "events"
311                        on("switchTemplate") {
312                                // handle form data
313                                eventPage(flow, flash, params)
314
315                                // get template
316                                def type        = params.get('eventType')
317                                def template= Template.findByName( params.get( type + 'Template' ) )
318
319                                // change template and/or instance?
320                                if (!flow.event || (flow.event instanceof Event && type == "sample") || (flow.event instanceof SamplingEvent && type == "event")) {
321                                        // create new instance
322                                        flow.event = (type == "event") ? new Event(template: template) : new SamplingEvent(template: template)
323                                } else {
324                                        flow.event.template = template
325                                }
326
327                                // reset errors
328                                flash.errors = [:]
329                                success()
330
331                        }.to "events"
332                        on("refresh") {
333                                // handle form data
334                                eventPage(flow, flash, params)
335
336                                // refresh templates
337                                flow.study.giveEventTemplates().each() {
338                                        it.refresh()
339                                }
340
341                                // refresh event template
342                                if (flow.event?.template) flow.event.template.refresh()
343
344                                // reset errors
345                                flash.errors = [:]
346                                success()
347                        }.to "events"
348                        on("add") {
349                                // handle form data
350                                eventPage(flow, flash, params)
351
352                                // reset errors
353                                flash.errors = [:]
354
355                                // add event to study
356                                if (flow.event instanceof SamplingEvent) {
357                                        flow.study.addToSamplingEvents( flow.event )
358                                } else {
359                                        flow.study.addToEvents( flow.event )
360                                }
361
362                                // validate event
363                                if (flow.event.validate()) {
364                                        // remove event from the flowscope
365                                        flow.remove('event')
366                                       
367                                        success()
368                                } else {
369                                        // event does not validate
370                                        // remove from study
371                                        if (flow.event instanceof SamplingEvent) {
372                                                flow.study.removeFromSamplingEvents( flow.event )
373                                        } else {
374                                                flow.study.removeFromEvents( flow.event )
375                                        }
376
377                                        // append errors
378                                        this.appendErrors(flow.event, flash.errors)
379                                        error()
380                                }
381                        }.to "events"
382                        on("deleteEvent") {
383                                // handle form data
384                                eventPage(flow, flash, params)
385
386                                // reset errors
387                                flash.errors = [:]
388
389                                // find matching (sampling) event
390                                def event                       = flow.study.events.find { it.getIdentifier() == (params.get('do') as int) }
391                                def samplingEvent       = flow.study.samplingEvents.find { it.getIdentifier() == (params.get('do') as int) }
392
393                                // perform delete
394                                if (event) flow.study.deleteEvent( event )
395                                if (samplingEvent) flow.study.deleteSamplingEvent( samplingEvent )
396                        }.to "events"
397                        on("addEventGroup") {
398                                // handle form data
399                                eventPage(flow, flash, params)
400
401                                // set work variables
402                                def groupName = 'Group '
403                                def tempGroupIterator = 1
404                                def tempGroupName = groupName + tempGroupIterator
405
406                                // make sure group name is unique
407                                if (flow.study.eventGroups) {
408                                        while (flow.study.eventGroups.find { it.name == tempGroupName }) {
409                                                tempGroupIterator++
410                                                tempGroupName = groupName + tempGroupIterator
411                                        }
412                                }
413                                groupName = tempGroupName
414
415                                // add a new eventGroup
416                                flow.study.addToEventGroups(
417                                        new EventGroup(
418                                                name    : groupName
419                                        )
420                                )
421
422                                // reset errors
423                                flash.errors = [:]
424                                success()
425                        }.to "events"
426                        on("deleteEventGroup") {
427                                // handle form data
428                                eventPage(flow, flash, params)
429
430                                // reset errors
431                                flash.errors = [:]
432
433                                // remove eventGroup
434                                def eventGroupToRemove = flow.study.eventGroups.find { it.getIdentifier() == (params.get('do') as int) }
435                                if (eventGroupToRemove) {
436                                        println flow.study.deleteEventGroup( eventGroupToRemove )
437                                }
438                        }.to "events"
439                        on("previous") {
440                                // handle form data
441                                eventPage(flow, flash, params)
442
443                                // reset errors
444                                flash.errors = [:]
445                                success()
446                        }.to "subjects"
447                        on("next") {
448                                // handle form data
449                                eventPage(flow, flash, params) ? success() : error()
450                        }.to "eventsNext"
451                        on("quickSave") {
452                                // handle form data
453                                eventPage(flow, flash, params) ? success() : error()
454                        }.to "waitForSave"
455                }
456
457                // decide to show a warning page or not
458                eventsNext {
459                        action {
460                                def assigned = false
461
462                                // check if all sampling events are in an eventGroup
463                                flow.study.samplingEvents.each() { samplingEvent ->
464                                        // iterate through eventGroups
465                                        flow.study.eventGroups.each() { eventGroup ->
466                                                if ( eventGroup.samplingEvents.find { it.equals(samplingEvent) } ) {
467                                                        assigned = true
468                                                }
469                                        }
470                                }
471
472                                if (assigned) {
473                                        toGroupsPage()
474                                } else {
475                                        toWarningPage()
476                                }
477                        }
478                        on("toWarningPage").to "unassignedSamplingEventWarning"
479                        on("toGroupsPage").to "groups"
480                }
481
482                // warning page for unassigned samplingEvent
483                unassignedSamplingEventWarning {
484                        render(view: "_unassigned_samplingEvent_warning")
485                        onRender {
486                                flow.page = 4
487                                success()
488                        }
489                        on("next").to "groups"
490                        on("previous").to "events"
491                }
492
493                // groups page
494                groups {
495                        render(view: "_groups")
496                        onRender {
497                                flow.page = 4
498                                success()
499                        }
500                        on("previous") {
501                                // handle form data
502                                groupPage(flow, flash, params) ? success() : error()
503                        }.to "events"
504                        on("next") {
505                                // handle form data
506                                groupPage(flow, flash, params) ? success() : error()
507                        }.to "samples"
508                        on("quickSave") {
509                                // handle form data
510                                groupPage(flow, flash, params) ? success() : error()
511                        }.to "waitForSave"
512                }
513
514                // sample 'previous' page with warning
515                samplePrevious {
516                        render(view: "_samples_previous_warning")
517                        onRender {
518                                flow.page = 5
519
520                                // TEMPORARY FIX TO REMOVE ALL SAMPLES AND REGENERATE THEM
521                                // THEN USER BROWSED BACK
522                                println ".removing samples from study"
523
524                                // remove samples from study
525                                flow.samples.each() {
526                                        flow.study.removeFromSamples(it.sample)
527                                }
528
529                                // remove samples from flow
530                                flow.remove('samples')
531                                // END FIX
532                        }
533                        on("next").to "samples"
534                        on("previous").to "groups"
535                }
536
537                // samples page
538                samples {
539                        render(view: "_samples")
540                        onRender {
541                                flow.page = 5
542                                success()
543                        }
544                        on("switchTemplate") {
545                                // handle form data
546                                samplePage(flow, flash, params)
547
548                                // ignore errors
549                                flash.errors = [:]
550                               
551                                succes()
552                        }.to "samples"
553                        on("refresh") {
554                                // handle samples
555                                samplePage(flow, flash, params)
556
557                                // refresh all sample templates
558                                flow.study.giveSampleTemplates().each() {
559                                        it.refresh()
560                                }
561
562                                // ignore errors
563                                flash.errors = [:]
564
565                                success()
566                        }.to "samples"
567                        on("regenerate") {
568                                // handle samples
569                                samplePage(flow, flash, params)
570
571                                // remove all samples from the study
572                                flow.study.samples.findAll{true}.each() { sample ->
573                                        flow.study.removeFromSamples(sample)
574                                }
575
576                                // ignore errors
577                                flash.errors = [:]
578
579                                success()
580                        }.to "samples"
581                        on("previous") {
582                                // handle samples
583                                samplePage(flow, flash, params)
584
585                                // ignore errors
586                                flash.errors = [:]
587
588                                success()
589                        }.to "samplePrevious"
590                        on("next") {
591                                // handle form data
592                                samplePage(flow, flash, params) ? success() : error()
593                        }.to "assays"
594                        on("quickSave") {
595                                // handle form data
596                                samplePage(flow, flash, params) ? success() : error()
597                        }.to "waitForSave"
598                }
599
600                // assays page
601                assays {
602                        render(view: "_assays")
603                        onRender {
604                                flow.page = 6
605                        }
606                        on("refresh") {
607                                // handle form data
608                                assayPage(flow, flash, params)
609
610                                // force refresh of the template
611                                if (flow.assay && flow.assay.template && flow.assay.template instanceof Template) {
612                                        flow.assay.template.refresh()
613                                }
614
615                                // reset errors
616                                flash.errors = [:]
617                                success()
618                        }.to "assays"
619            on("switchTemplate") {
620                                // handle form data
621                                assayPage(flow, flash, params)
622
623                                // find assay template
624                                def template = Template.findByName( params.get('template') )
625
626                                if (flow.assay) {
627                                        // set template
628                                        flow.assay.template = template
629                                } else {
630                                        // create a new assay instance
631                                        flow.assay = new Assay(template: template)
632                                }
633
634                                // reset errors
635                                flash.errors = [:]
636                                success()
637                        }.to "assays"
638                        on("add") {
639                                // handle form data
640                                assayPage(flow, flash, params)
641
642                                // reset errors
643                                flash.errors = [:]
644
645                                // add assay to study
646                                flow.study.addToAssays( flow.assay )
647
648                                // validate assay
649                                if (flow.assay.validate()) {
650                                        // remove assay from the flowscope
651                                        flow.remove('assay')
652                                        success()
653                                } else {
654                                        // assay does not validate
655                                        // remove from study
656                                        flow.study.removeFromAssays( flow.assay )
657
658                                        // append errors
659                                        this.appendErrors(flow.assay, flash.errors)
660                                        error()
661                                }
662                        }.to "assays"
663                        on("deleteAssay") {
664                                println params
665                               
666                                // handle form data
667                                assayPage(flow, flash, params)
668
669                                // reset errors
670                                flash.errors = [:]
671
672                                // find this assay
673                                def assay = flow.study.assays.find { it.getIdentifier() == (params.get('do') as int) }
674
675                                // perform delete
676                                if (assay) flow.study.deleteAssay( assay )
677                        }.to "assays"
678                        on("previous") {
679                                // handle form data
680                                assayPage(flow, flash, params)
681
682                                // ignore errors
683                                flash.errors = [:]
684
685                                success()
686                        }.to "samples"
687                        on("next") {
688                                // handle form data
689                                assayPage(flow, flash, params) ? success() : error()
690                        }.to "assayNext"
691                        on("quickSave") {
692                                // handle form data
693                                assayPage(flow, flash, params) ? success() : error()
694                        }.to "waitForSave"
695                }
696
697                // assayNext
698                assayNext {
699                        action {
700                                // have we got samples and assays?
701                                if (flow.study.assays && flow.study.samples) {
702                                        // yes, go to the group page
703                                        toAssayGroups()
704                                } else {
705                                        // no need to show the group page as
706                                        // there's nothing to group
707                                        toConfirm()
708                                }
709                        }
710                        on("toAssayGroups").to "assayGroups"
711                        on("toConfirm").to "confirm"
712                }
713
714                // assay grouping page
715                assayGroups {
716                        render(view: "_assay_groups")
717                        onRender {
718                                flow.page = 6
719                        }
720                        on("previous") {
721                                // handle form data
722                                assayGroupPage(flow, flash, params)
723
724                                // ignore errors
725                                flash.errors = [:]
726
727                                success()
728                        }.to "assays"
729                        on("next") {
730                                // handle form data
731                                assayGroupPage(flow, flash, params) ? success() : error()
732                        }.to "confirm"
733                        on("quickSave") {
734                                // handle form data
735                                assayGroupPage(flow, flash, params) ? success() : error()
736                        }.to "waitForSave"
737                }
738
739                // confirm Previous
740                confirmPrevious {
741                        action {
742                                // have we got samples and assays?
743                                if (flow.study.assays && flow.study.samples) {
744                                        // yes, go to the group page
745                                        toAssayGroups()
746                                } else {
747                                        // no need to show the group page as
748                                        // there's nothing to group
749                                        toAssays()
750                                }
751                        }
752                        on("toAssayGroups").to "assayGroups"
753                        on("toAssays").to "assays"
754                }
755
756                // confirmation
757                confirm {
758                        render(view: "_confirmation")
759                        onRender {
760                                flow.page = 7
761                        }
762                        on("toStudy").to "study"
763                        on("toSubjects").to "subjects"
764                        on("toEvents").to "events"
765                        on("toGroups").to "groups"
766                        on("toSamples").to "samples"
767                        on("toAssays").to "assays"
768                        on("toAssayGroups").to "assayGroups"
769                        on("previous").to "confirmPrevious"
770                        on("next").to "waitForSave"
771                        on("quickSave").to "waitForSave"
772                }
773
774                waitForSave {
775                        render(view: "_wait")
776                        onRender {
777                                flow.page = 8
778                        }
779                        on("next").to "save"
780                }
781
782                // store all study data
783                save {
784                        action {
785                                println "saving..."
786                                flash.errors = [:]
787
788                                // persist data to the database
789                                try {
790                                        // save study
791                                        println ".saving study"
792                                        if (!flow.study.save(flush:true)) {
793                                                this.appendErrors(flow.study, flash.errors)
794                                                throw new Exception('error saving study')
795                                        }
796                                        println ".saved study "+flow.study+" (id: "+flow.study.id+")"
797
798                                        success()
799                                } catch (Exception e) {
800                                        // rollback
801                                        this.appendErrorMap(['exception': e.toString() + ', see log for stacktrace' ], flash.errors)
802
803                                        // stacktrace in flash scope
804                                        flash.debug = e.getStackTrace()
805
806                                        error()
807                                }
808                        }
809                        on("error").to "error"
810                        on(Exception).to "error"
811                        on("success").to "done"
812                }
813
814                // error storing data
815                error {
816                        render(view: "_error")
817                        onRender {
818                                flow.page = 7
819                        }
820                        on("next").to "waitForSave"
821                        on("previous").to "samples"
822                }
823
824                // render finish page
825                done {
826                        render(view: "_done")
827                        onRender {
828                                flow.page = 8
829                        }
830                        onEnd {
831                                // clean flow scope
832                                flow.clear()
833                        }
834                }
835        }
836
837        /**
838         * load a study
839         * @param Map LocalAttributeMap (the flow scope)
840         * @param Map localAttributeMap (the flash scope)
841         * @param Map GrailsParameterMap (the flow parameters = form data)
842         * @returns boolean
843         */
844        def loadStudy(flow, flash, params) {
845                // load study
846                try {
847                        // load study
848                        flow.study = (params.studyid) ? Study.findById( params.studyid ) : Study.findByTitle( params.study )
849
850                        // set 'quicksave' variable
851                        flow.quickSave = true
852
853                        return true
854                } catch (Exception e) {
855                        // rollback
856                        this.appendErrorMap(['exception': e.toString() + ', see log for stacktrace'], flash.errors)
857
858                        return false
859                }
860        }
861
862        /**
863         * Handle the wizard study page
864         *
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 studyPage(flow, flash, params) {
871                // remember the params in the flash scope
872                flash.values = params
873               
874                // instantiate study of it is not yet present
875                if (!flow.study) flow.study = new Study()
876
877                // did the study template change?
878                if (params.get('template').size() && flow.study.template?.name != params.get('template')) {
879                        println ".change study template!"
880
881                        // yes, was the template already set?
882                        if (flow.study.template instanceof Template) {
883                                // yes, first make sure all values are unset?
884                                println "!!! check the database fields if data of a previous template remains in the database or is deleted by GORM!"
885                        }
886
887                        // set the template
888                        flow.study.template = Template.findByName(params.remove('template'))
889                }
890
891                // does the study have a template set?
892                if (flow.study.template && flow.study.template instanceof Template) {
893                        // yes, iterate through template fields
894                        flow.study.giveFields().each() {
895                                // and set their values
896                                flow.study.setFieldValue(it.name, params.get(it.escapedName()))
897                        }
898                }
899
900                // handle publications
901                handlePublications(flow, flash, params)
902
903                // handle contacts
904                handleContacts(flow, flash, params)
905
906                // validate the study
907                if (flow.study.validate()) {
908                        // instance is okay
909                        return true
910                } else {
911                        // validation failed
912                        flash.errors = [:]
913                        this.appendErrors(flow.study, flash.errors)
914                        return false
915                }
916        }
917
918        /**
919         * re-usable code for handling publications form data in a web flow
920         * @param Map LocalAttributeMap (the flow scope)
921         * @param Map localAttributeMap (the flash scope)
922         * @param Map GrailsParameterMap (the flow parameters = form data)
923         * @returns boolean
924         */
925        def handlePublications(flow, flash, params) {
926                if (!flow.study.publications) flow.study.publications = []
927
928                // Check the ids of the pubblications that should be attached
929                // to this study. If they are already attached, keep 'm. If
930                // studies are attached that are not in the selected (i.e. the
931                // user deleted them), remove them
932                def publicationIDs = params.get('publication_ids')
933                if (publicationIDs) {
934                        // Find the individual IDs and make integers
935                        publicationIDs = publicationIDs.split(',').collect { Integer.parseInt(it, 10) }
936
937                        // First remove the publication that are not present in the array
938                        flow.study.publications.removeAll { publication -> !publicationIDs.find { id -> id == publication.id } }
939
940                        // Add those publications not yet present in the database
941                        publicationIDs.each { id ->
942                                if (!flow.study.publications.find { publication -> id == publication.id }) {
943                                        def publication = Publication.get(id)
944                                        if (publication) {
945                                                flow.study.addToPublications(publication)
946                                        } else {
947                                                println('.publication with ID ' + id + ' not found in database.')
948                                        }
949                                }
950                        }
951
952                } else {
953                        println('.no publications selected.')
954                        flow.study.publications.clear()
955                }
956
957        }
958
959        /**
960         * re-usable code for handling contacts form data in a web flow
961         * @param Map LocalAttributeMap (the flow scope)
962         * @param Map localAttributeMap (the flash scope)
963         * @param Map GrailsParameterMap (the flow parameters = form data)
964         * @return boolean
965         */
966        def handleContacts(flow, flash, params) {
967                if (!flow.study.persons) flow.study.persons = []
968
969                // Check the ids of the contacts that should be attached
970                // to this study. If they are already attached, keep 'm. If
971                // studies are attached that are not in the selected (i.e. the
972                // user deleted them), remove them
973
974                // Contacts are saved as [person_id]-[role_id]
975                def contactIDs = params.get('contacts_ids')
976                if (contactIDs) {
977                        // Find the individual IDs and make integers
978                        contactIDs = contactIDs.split(',').collect {
979                                def parts = it.split('-')
980                                return [person: Integer.parseInt(parts[0]), role: Integer.parseInt(parts[1])]
981                        }
982
983                        // First remove the contacts that are not present in the array
984                        flow.study.persons.removeAll {
985                                studyperson -> !contactIDs.find { ids -> (ids.person == studyperson.person.id) && (ids.role == studyperson.role.id) }
986                        }
987
988                        // Add those contacts not yet present in the database
989                        contactIDs.each { ids ->
990                                if (!flow.study.persons.find { studyperson -> (ids.person == studyperson.person.id) && (ids.role == studyperson.role.id) }) {
991                                        def person = Person.get(ids.person)
992                                        def role = PersonRole.get(ids.role)
993                                        if (person && role) {
994                                                // Find a studyperson object with these parameters
995                                                def studyPerson = StudyPerson.findAll().find { studyperson -> studyperson.person.id == person.id && studyperson.role.id == role.id }
996
997                                                // If if does not yet exist, save the example
998                                                if (!studyPerson) {
999                                                        studyPerson = new StudyPerson(
1000                                                                person: person,
1001                                                                role: role
1002                                                        )
1003                                                        studyPerson.save(flush: true)
1004                                                }
1005
1006                                                flow.study.addToPersons(studyPerson)
1007                                        } else {
1008                                                println('.person ' + ids.person + ' or Role ' + ids.role + ' not found in database.')
1009                                        }
1010                                }
1011                        }
1012                } else {
1013                        println('.no persons selected.')
1014                        flow.study.persons.clear()
1015                }
1016
1017        }
1018                                                 
1019        /**
1020         * Handle the wizard subject page
1021         *
1022         * @param Map LocalAttributeMap (the flow scope)
1023         * @param Map localAttributeMap (the flash scope)
1024         * @param Map GrailsParameterMap (the flow parameters = form data)
1025         * @returns boolean
1026         */
1027        def subjectPage(flow, flash, params) {
1028                def errors = false
1029                flash.errors = [:]
1030
1031                // remember the params in the flash scope
1032                flash.values = params
1033
1034                // iterate through subjects
1035                flow.study.subjects.each() { subject ->
1036                        // iterate through (template and domain) fields
1037                        subject.giveFields().each() { field ->
1038                                // set field
1039                                subject.setFieldValue(
1040                                        field.name,
1041                                        params.get('subject_' + subject.getIdentifier() + '_' + field.escapedName())
1042                                )
1043                        }
1044
1045                        // validate subject
1046                        if (!subject.validate()) {
1047                                errors = true
1048                                this.appendErrors(subject, flash.errors, 'subject_' + subject.getIdentifier() + '_')
1049                        }
1050                }
1051
1052                return !errors
1053        }
1054
1055        /**
1056         * Add a number of subjects to a study
1057         *
1058         * required params entities:
1059         * -addNumber (int)
1060         * -species   (string)
1061         * -template  (string)
1062         *
1063         * @param Map LocalAttributeMap (the flow scope)
1064         * @param Map localAttributeMap (the flash scope)
1065         * @param Map GrailsParameterMap (the flow parameters = form data)
1066         * @returns boolean
1067         */
1068        def addSubjects(flow, flash, params) {
1069                // remember the params in the flash scope
1070                flash.values = params
1071
1072                // handle the subject page
1073                subjectPage(flow, flash, params)
1074
1075                // (re)set error message
1076                flash.errors = [:]
1077
1078                // set work variables
1079                def errors              = false
1080                def number              = params.get('addNumber') as int
1081                def species             = Term.findByName(params.get('species'))
1082                def template    = Template.findByName(params.get('template'))
1083
1084                // can we add subjects?
1085                if (number > 0 && species && template) {
1086                        // add subjects to study
1087                        number.times {
1088                                // work variables
1089                                def subjectName = 'Subject '
1090                                def subjectIterator = 1
1091                                def tempSubjectName = subjectName + subjectIterator
1092
1093                                // make sure subject name is unique
1094                                if (flow.study.subjects) {
1095                                        while (flow.study.subjects.find { it.name == tempSubjectName }) {
1096                                                subjectIterator++
1097                                                tempSubjectName = subjectName + subjectIterator
1098                                        }
1099                                }
1100                                subjectName = tempSubjectName
1101                               
1102                                // create a subject instance
1103                                def subject = new Subject(
1104                                        name            : subjectName,
1105                                        species         : species,
1106                                        template        : template
1107                                )
1108
1109                                // add it to the study
1110                                flow.study.addToSubjects( subject )
1111
1112                                // validate subject
1113                                if (subject.validate()) {
1114                                        println ".added subject "+subject
1115                                } else {
1116                                        // whoops?
1117                                        flow.study.removeFromSubjects( subject )
1118
1119                                        // append errors
1120                                        this.appendErrors(subject, flash.errors)
1121                                        errors = true
1122                                }
1123                        }
1124                } else {
1125                        // add feedback
1126                        errors = true
1127                        if (number < 1) this.appendErrorMap(['addNumber': 'Enter a positive number of subjects to add'], flash.errors)
1128                        if (!species)   this.appendErrorMap(['species': 'You need to select a species, or add one if it is not yet present'], flash.errors)
1129                        if (!template)  this.appendErrorMap(['template': 'You need to select a template, or add one if it is not yet present'], flash.errors)
1130                }
1131
1132                return !errors
1133        }
1134
1135        /**
1136         * Handle the wizard event page
1137         *
1138         * @param Map LocalAttributeMap (the flow scope)
1139         * @param Map localAttributeMap (the flash scope)
1140         * @param Map GrailsParameterMap (the flow parameters = form data)
1141         * @returns boolean
1142         */
1143        def eventPage(flow, flash, params) {
1144                def errors = false
1145                flash.errors = [:]
1146
1147                // remember the params in the flash scope
1148                flash.values = params
1149
1150                // handle the 'add event' form
1151                if (flow.event) {
1152                        flow.event.giveFields().each() { field ->
1153                                // set field
1154                                flow.event.setFieldValue(
1155                                        field.name,
1156                                        params.get(field.escapedName())
1157                                )
1158                        }
1159                }
1160
1161                // handle the eventGroup names and grouping
1162                def name        = ""
1163                def tempName= ""
1164                flow.study.eventGroups.each() { eventGroup ->
1165                        // iterate through templates
1166                        flow.study.giveAllEventTemplates().each() { template ->
1167                                tempName = params.get( 'eventGroup_' + eventGroup.getIdentifier() + '_' + template.getIdentifier() )
1168
1169                                // is the name different?
1170                                if (tempName != eventGroup.name) {
1171                                        name = tempName
1172                                }
1173                        }
1174
1175                        // should the name change?
1176                        if (name) {
1177                                // yes, change it
1178                                eventGroup.name = name
1179                                name = ""
1180                        }
1181
1182                        // handle eventGrouping
1183                        ( ((flow.study.events) ? flow.study.events : []) + ((flow.study.samplingEvents) ? flow.study.samplingEvents : []) ) .each() { event ->
1184                                if (params.get( 'event_' + event.getIdentifier() + '_group_' + eventGroup.getIdentifier() )) {
1185                                        // add to eventGroup
1186                                        if (event instanceof SamplingEvent) {
1187                                                // check if we are already in this eventGroup
1188                                                if (!eventGroup.samplingEvents.find { it.equals(event) }) {
1189                                                        // no, add it
1190                                                        eventGroup.addToSamplingEvents(event)
1191
1192                                                        // iterate through subjects for this eventGroup
1193                                                        eventGroup.subjects.each() { subject ->
1194                                                                // instantiate a sample for this subject / event
1195                                                                def samplingEventName = this.ucwords(event.template.name)
1196                                                                def sampleName = (this.ucwords(subject.name) + '_' + samplingEventName + '_' + new RelTime(event.startTime).toString()).replaceAll("([ ]{1,})", "")
1197                                                                def tempSampleIterator = 0
1198                                                                def tempSampleName = sampleName
1199
1200                                                                // make sure sampleName is unique
1201                                                                if (flow.study.samples) {
1202                                                                        while (flow.study.samples.find { it.name == tempSampleName }) {
1203                                                                                tempSampleIterator++
1204                                                                                tempSampleName = sampleName + "_" + tempSampleIterator
1205                                                                        }
1206                                                                        sampleName = tempSampleName
1207                                                                }
1208
1209                                                                // instantiate a sample
1210                                                                flow.study.addToSamples(
1211                                                                        new Sample(
1212                                                                                parentSubject: subject,
1213                                                                                parentEvent: event,
1214                                                                                name: sampleName,
1215                                                                                template: (event.sampleTemplate) ? event.sampleTemplate : ''
1216                                                                        )
1217                                                                )
1218                                                        }
1219                                                }
1220                                        } else {
1221                                                eventGroup.addToEvents(event)
1222                                        }
1223                                } else {
1224                                        // remove from eventGroup
1225                                        if (event instanceof SamplingEvent) {
1226                                                // iterate through subjects (if we have them)
1227                                                eventGroup.subjects.each() { subject ->
1228                                                        // find all samples for this subject / event
1229                                                        flow.study.samples.findAll { (it.parentEvent.equals(event) && it.parentSubject.equals(subject) ) }.each() {
1230                                                                // delete this sample
1231                                                                flow.study.removeFromSamples( it )
1232                                                                it.delete()
1233                                                        }
1234                                                }
1235                                               
1236                                                eventGroup.removeFromSamplingEvents(event)
1237                                        } else {
1238                                                eventGroup.removeFromEvents(event)
1239                                        }
1240                                }
1241                        }
1242                }
1243
1244                // handle the (sampling) events
1245                ( ((flow.study.events) ? flow.study.events : []) + ((flow.study.samplingEvents) ? flow.study.samplingEvents : []) ) .each() { event ->
1246                        event.giveFields().each() { field ->
1247                                event.setFieldValue(
1248                                        field.name,
1249                                        params.get( 'event_' + event.getIdentifier() + '_' + field.escapedName() )
1250                                )
1251                        }
1252
1253                        // validate event
1254                        if (!event.validate()) {
1255                                errors = true
1256                                this.appendErrors(event, flash.errors)
1257                        }
1258                }
1259
1260                return !errors
1261        }
1262
1263        /**
1264         * Handle the wizard group page
1265         *
1266         * @param Map LocalAttributeMap (the flow scope)
1267         * @param Map localAttributeMap (the flash scope)
1268         * @param Map GrailsParameterMap (the flow parameters = form data)
1269         * @returns boolean
1270         */
1271        def groupPage(flow, flash, params) {
1272                def errors = false
1273                flash.errors = [:]
1274
1275                // remember the params in the flash scope
1276                flash.values = params
1277
1278                // iterate through groups
1279                flow.study.eventGroups.each() { eventGroup ->
1280                        // iterate through subjects
1281                        flow.study.subjects.each() { subject ->
1282                                if (params.get('subject_' + subject.getIdentifier() + '_group_' + eventGroup.getIdentifier() )) {
1283                                        // check if this subject is already part of this eventGroup
1284                                        if ( !eventGroup.subjects.find { it.equals(subject) } ) {
1285                                                // add to eventGroup
1286                                                eventGroup.addToSubjects(subject)
1287
1288                                                // iterate through samplingEvents
1289                                                eventGroup.samplingEvents.each() { samplingEvent ->
1290                                                        def samplingEventName = this.ucwords(samplingEvent.template.name)
1291                                                        def sampleName = (this.ucwords(subject.name) + '_' + samplingEventName + '_' + new RelTime(samplingEvent.startTime).toString()).replaceAll("([ ]{1,})", "")
1292                                                        def tempSampleIterator = 0
1293                                                        def tempSampleName = sampleName
1294
1295                                                        // make sure sampleName is unique
1296                                                        if (flow.study.samples) {
1297                                                                while (flow.study.samples.find { it.name == tempSampleName }) {
1298                                                                        tempSampleIterator++
1299                                                                        tempSampleName = sampleName + "_" + tempSampleIterator
1300                                                                }
1301                                                                sampleName = tempSampleName
1302                                                        }
1303
1304                                                        // instantiate a sample
1305                                                        flow.study.addToSamples(
1306                                                                new Sample(
1307                                                                        parentSubject: subject,
1308                                                                        parentEvent: samplingEvent,
1309                                                                        name: sampleName,
1310                                                                        template: (samplingEvent.sampleTemplate) ? samplingEvent.sampleTemplate : ''
1311                                                                )
1312                                                        )
1313                                                }
1314                                        }
1315                                } else {
1316                                        // remove from eventGroup
1317                                        eventGroup.removeFromSubjects(subject)
1318
1319                                        // iterate through samplingEvents
1320                                        eventGroup.samplingEvents.each() { samplingEvent ->
1321                                                flow.study.samples.findAll { ( it.parentEvent.equals(samplingEvent) && it.parentSubject.equals(subject) ) }.each() {
1322                                                        // delete this sample
1323                                                        flow.study.removeFromSamples( it )
1324                                                        it.delete()
1325                                                }
1326                                        }
1327                                }
1328                        }
1329                }
1330        }
1331
1332        /**
1333         * Handle the wizard samples page
1334         *
1335         * @param Map LocalAttributeMap (the flow scope)
1336         * @param Map localAttributeMap (the flash scope)
1337         * @param Map GrailsParameterMap (the flow parameters = form data)
1338         * @returns boolean
1339         */
1340        def samplePage(flow, flash, params) {
1341                def errors = false
1342                flash.errors = [:]
1343
1344                // remember the params in the flash scope
1345                flash.values = params
1346
1347                // iterate through samples
1348                flow.study.samples.each() { sample ->
1349                        // iterate through sample fields
1350                        sample.giveFields().each() { field ->
1351                                def value = params.get('sample_'+sample.getIdentifier()+'_'+field.escapedName())
1352
1353                                // set field value
1354                                if (!(field.name == 'name' && !value)) {
1355                                        println "setting "+field.name+" to "+value
1356                                        sample.setFieldValue(field.name, value)
1357                                }
1358                        }
1359
1360                        // has the template changed?
1361                        def templateName = params.get('template_' + sample.getIdentifier())
1362                        if (templateName && sample.template?.name != templateName) {
1363                                sample.template = Template.findByName(templateName)
1364                        }
1365
1366                        // validate sample
1367                        if (!sample.validate()) {
1368                                errors = true
1369                                this.appendErrors(sample, flash.errors, 'sample_' + sample.getIdentifier() + '_' )
1370                                println 'error-> sample_'+sample.getIdentifier()
1371                        }
1372                }
1373
1374                return !errors
1375        }
1376
1377        /**
1378         * Handle the wizard assays page
1379         *
1380         * @param Map LocalAttributeMap (the flow scope)
1381         * @param Map localAttributeMap (the flash scope)
1382         * @param Map GrailsParameterMap (the flow parameters = form data)
1383         * @returns boolean
1384         */
1385        def assayPage(flow, flash, params) {
1386                def errors = false
1387                flash.errors = [:]
1388
1389                // remember the params in the flash scope
1390                flash.values = params
1391
1392                // handle the 'add assay' form
1393                if (flow.assay) {
1394                        flow.assay.giveFields().each() { field ->
1395                                // set field
1396                                flow.assay.setFieldValue(
1397                                        field.name,
1398                                        params.get(field.escapedName())
1399                                )
1400                        }
1401                }
1402
1403                // handle the assay data
1404                flow.study.assays.each() { assay ->
1405                        // set data
1406                        assay.giveFields().each() { field ->
1407                                assay.setFieldValue(
1408                                        field.name,
1409                                        params.get( 'assay_' + assay.getIdentifier() + '_' + field.escapedName() )
1410                                )
1411                        }
1412
1413                        // validate assay
1414                        if (!assay.validate()) {
1415                                errors = true
1416                                this.appendErrors(assay, flash.errors, 'assay_' + assay.getIdentifier() + '_')
1417                        }
1418                }
1419
1420                return !errors
1421        }
1422
1423        /**
1424         * Handle the wizard assayGroups page
1425         *
1426         * @param Map LocalAttributeMap (the flow scope)
1427         * @param Map localAttributeMap (the flash scope)
1428         * @param Map GrailsParameterMap (the flow parameters = form data)
1429         * @returns boolean
1430         */
1431        def assayGroupPage(flow, flash, params) {
1432                def errors = false
1433                flash.errors = [:]
1434
1435                // remember the params in the flash scope
1436                flash.values = params
1437
1438                // iterate through samples
1439                flow.study.samples.each() { sample ->
1440                        // iterate through assays
1441                        flow.study.assays.each() { assay ->
1442                                if (params.get( 'sample_' + sample.getIdentifier() + '_assay_' + assay.getIdentifier() )) {
1443                                        println "add sample "+sample.getIdentifier()+" to assay "+assay.getIdentifier()
1444                                        // add sample to assay
1445                                        assay.addToSamples( sample )
1446                                } else {
1447                                        // remove sample from assay
1448                                        assay.removeFromSamples( sample )
1449                                }
1450                                println assay.samples
1451                        }
1452                }
1453
1454                return !errors
1455        }
1456
1457        /**
1458         * groovy / java equivalent of php's ucwords function
1459         *
1460         * Capitalize all first letters of separate words
1461         *
1462         * @param String
1463         * @return String
1464         */
1465        def ucwords(String text) {
1466                def newText = ''
1467
1468                // change case to lowercase
1469                text = text.toLowerCase()
1470
1471                // iterate through words
1472                text.split(" ").each() {
1473                        newText += it[0].toUpperCase() + it.substring(1) + " "
1474                }
1475
1476                return newText.substring(0, newText.size()-1)
1477        }
1478
1479        /**
1480         * return the object from a map of objects by searching for a name
1481         * @param String name
1482         * @param Map map of objects
1483         * @return Object
1484         */
1485        def getObjectByName(name, map) {
1486                def result = null
1487                map.each() {
1488                        if (it.name == name) {
1489                                result = it
1490                        }
1491                }
1492
1493                return result
1494        }
1495
1496        /**
1497         * transform domain class validation errors into a human readable
1498         * linked hash map
1499         * @param object validated domain class
1500         * @return object  linkedHashMap
1501         */
1502        def getHumanReadableErrors(object) {
1503                def errors = [:]
1504                object.errors.getAllErrors().each() {
1505                        def message = it.toString()
1506
1507                        //errors[it.getArguments()[0]] = it.getDefaultMessage()
1508                        errors[it.getArguments()[0]] = message.substring(0, message.indexOf(';'))
1509                }
1510
1511                return errors
1512        }
1513
1514        /**
1515         * append errors of a particular object to a map
1516         * @param object
1517         * @param map linkedHashMap
1518         * @void
1519         */
1520        def appendErrors(object, map) {
1521                this.appendErrorMap(this.getHumanReadableErrors(object), map)
1522        }
1523
1524        def appendErrors(object, map, prepend) {
1525                this.appendErrorMap(this.getHumanReadableErrors(object), map, prepend)
1526        }
1527
1528        /**
1529         * append errors of one map to another map
1530         * @param map linkedHashMap
1531         * @param map linkedHashMap
1532         * @void
1533         */
1534        def appendErrorMap(map, mapToExtend) {
1535                map.each() {key, value ->
1536                        mapToExtend[key] = ['key': key, 'value': value, 'dynamic': false]
1537                }
1538        }
1539
1540        def appendErrorMap(map, mapToExtend, prepend) {
1541                map.each() {key, value ->
1542                        mapToExtend[prepend + key] = ['key': key, 'value': value, 'dynamic': true]
1543                }
1544        }
1545
1546        /**
1547         * Parses a RelTime string and returns a nice human readable string
1548         *
1549         * @return Human Readable string or a HTTP response code 400 on error
1550         */
1551        def ajaxParseRelTime = {
1552                if (params.reltime == null) {
1553                        response.status = 400
1554                        render('reltime parameter is expected')
1555                }
1556
1557                try {
1558                        def reltime = RelTime.parseRelTime(params.reltime)
1559                        render reltime.toPrettyString()
1560                } catch (IllegalArgumentException e) {
1561                        response.status = 400
1562                        render(e.getMessage())
1563                }
1564        }
1565
1566        /**
1567         * Proxy for searching PubMed articles (or other articles from the Entrez DB).
1568         *
1569         * This proxy is needed because it is not allowed to fetch XML directly from a different
1570         * domain using javascript. So we have the javascript call a function on our own domain
1571         * and the proxy will fetch the data from Entrez
1572         *
1573         * @since       20100609
1574         * @param       _utility        The name of the utility, without the complete path. Example: 'esearch.fcgi'
1575         * @return      XML
1576         */
1577        def entrezProxy = {
1578                // Remove unnecessary parameters
1579                params.remove( "action" )
1580                params.remove( "controller" )
1581
1582                def url = "http://eutils.ncbi.nlm.nih.gov/entrez/eutils";
1583                def util = params.remove( "_utility" )
1584                def paramString = params.collect { k, v -> k + '=' + v.encodeAsURL() }.join( '&' );
1585
1586                def fullUrl = url + '/' + util + '?' + paramString;
1587
1588                // Return the output of the request
1589                // render fullUrl;
1590                render(
1591                    text:           new URL( fullUrl ).getText(),
1592                    contentType:    "text/xml",
1593                    encoding:       "UTF-8"
1594                );
1595        }
1596}
Note: See TracBrowser for help on using the browser.