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

Last change on this file since 952 was 952, checked in by j.a.m.wesbeek@…, 11 years ago
  • fixed references to handleSamples() which should have been samplePage()
  • Property svn:keywords set to Date Author Rev
File size: 38.7 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: 952 $
21 * $Author: j.a.m.wesbeek@umail.leidenuniv.nl $
22 * $Date: 2010-10-20 15:39:07 +0000 (wo, 20 okt 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: '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 "groups"
451                        on("quickSave") {
452                                // handle form data
453                                eventPage(flow, flash, params) ? success() : error()
454                        }.to "waitForSave"
455                }
456
457                // groups page
458                groups {
459                        render(view: "_groups")
460                        onRender {
461                                flow.page = 4
462                                success()
463                        }
464                        on("previous") {
465                                // handle form data
466                                groupPage(flow, flash, params) ? success() : error()
467                        }.to "events"
468                        on("next") {
469                                // handle form data
470                                groupPage(flow, flash, params) ? success() : error()
471                        }.to "samples"
472                        on("quickSave") {
473                                // handle form data
474                                groupPage(flow, flash, params) ? success() : error()
475                        }.to "waitForSave"
476                }
477
478                // decide to show a warning page or not
479
480
481                // sample 'previous' page with warning
482                samplePrevious {
483                        render(view: "_samples_previous_warning")
484                        onRender {
485                                flow.page = 5
486
487                                // TEMPORARY FIX TO REMOVE ALL SAMPLES AND REGENERATE THEM
488                                // THEN USER BROWSED BACK
489                                println ".removing samples from study"
490
491                                // remove samples from study
492                                flow.samples.each() {
493                                        flow.study.removeFromSamples(it.sample)
494                                }
495
496                                // remove samples from flow
497                                flow.remove('samples')
498                                // END FIX
499                        }
500                        on("next").to "samples"
501                        on("previous").to "groups"
502                }
503
504                // samples page
505                samples {
506                        render(view: "_samples")
507                        onRender {
508                                flow.page = 5
509                                success()
510                        }
511                        on("switchTemplate") {
512                                // handle form data
513                                samplePage(flow, flash, params)
514
515                                // ignore errors
516                                flash.errors = [:]
517                               
518                                succes()
519                        }.to "samples"
520                        on("refresh") {
521                                // handle samples
522                                samplePage(flow, flash, params)
523
524                                // refresh all sample templates
525                                flow.study.giveSampleTemplates().each() {
526                                        it.refresh()
527                                }
528
529                                // ignore errors
530                                flash.errors = [:]
531
532                                success()
533                        }.to "samples"
534                        on("regenerate") {
535                                // handle samples
536                                samplePage(flow, flash, params)
537
538                                // remove all samples from the study
539                                flow.study.samples.findAll{true}.each() { sample ->
540                                        flow.study.removeFromSamples(sample)
541                                }
542
543                                // ignore errors
544                                flash.errors = [:]
545
546                                success()
547                        }.to "samples"
548                        on("previous") {
549                                // handle samples
550                                samplePage(flow, flash, params)
551
552                                // ignore errors
553                                flash.errors = [:]
554
555                                success()
556                        }.to "samplePrevious"
557                        on("next") {
558                                // handle form data
559                                samplePage(flow, flash, params) ? success() : error()
560                        }.to "assays"
561                        on("quickSave") {
562                                // handle form data
563                                samplePage(flow, flash, params) ? success() : error()
564                        }.to "waitForSave"
565                }
566
567                // assays page
568                assays {
569                        render(view: "_assays")
570                        onRender {
571                                flow.page = 6
572                        }
573                        on("refresh") {
574                                // handle form data
575                                assayPage(flow, flash, params)
576
577                                // force refresh of the template
578                                if (flow.assay && flow.assay.template && flow.assay.template instanceof Template) {
579                                        flow.assay.template.refresh()
580                                }
581
582                                // reset errors
583                                flash.errors = [:]
584                                success()
585                        }.to "assays"
586            on("switchTemplate") {
587                                // handle form data
588                                assayPage(flow, flash, params)
589
590                                // find assay template
591                                def template = Template.findByName( params.get('template') )
592
593                                if (flow.assay) {
594                                        // set template
595                                        flow.assay.template = template
596                                } else {
597                                        // create a new assay instance
598                                        flow.assay = new Assay(template: template)
599                                }
600
601                                // reset errors
602                                flash.errors = [:]
603                                success()
604                        }.to "assays"
605                        on("add") {
606                                // handle form data
607                                assayPage(flow, flash, params)
608
609                                // reset errors
610                                flash.errors = [:]
611
612                                // add assay to study
613                                flow.study.addToAssays( flow.assay )
614
615                                // validate assay
616                                if (flow.assay.validate()) {
617                                        // remove assay from the flowscope
618                                        flow.remove('assay')
619                                        success()
620                                } else {
621                                        // assay does not validate
622                                        // remove from study
623                                        flow.study.removeFromAssays( flow.assay )
624
625                                        // append errors
626                                        this.appendErrors(flow.assay, flash.errors)
627                                        error()
628                                }
629                        }.to "assays"
630                        on("deleteAssay") {
631                                println params
632                               
633                                // handle form data
634                                assayPage(flow, flash, params)
635
636                                // reset errors
637                                flash.errors = [:]
638
639                                // find this assay
640                                def assay = flow.study.assays.find { it.getIdentifier() == (params.get('do') as int) }
641
642                                // perform delete
643                                if (assay) flow.study.deleteAssay( assay )
644                        }.to "assays"
645                        on("previous") {
646                                // handle form data
647                                assayPage(flow, flash, params)
648
649                                // ignore errors
650                                flash.errors = [:]
651
652                                success()
653                        }.to "samples"
654                        on("next") {
655                                // handle form data
656                                assayPage(flow, flash, params) ? success() : error()
657                        }.to "assayNext"
658                        on("quickSave") {
659                                // handle form data
660                                assayPage(flow, flash, params) ? success() : error()
661                        }.to "waitForSave"
662                }
663
664                // assayNext
665                assayNext {
666                        action {
667                                // have we got samples and assays?
668                                if (flow.study.assays && flow.study.samples) {
669                                        // yes, go to the group page
670                                        toAssayGroups()
671                                } else {
672                                        // no need to show the group page as
673                                        // there's nothing to group
674                                        toConfirm()
675                                }
676                        }
677                        on("toAssayGroups").to "assayGroups"
678                        on("toConfirm").to "confirm"
679                }
680
681                // assay grouping page
682                assayGroups {
683                        render(view: "_assay_groups")
684                        onRender {
685                                flow.page = 6
686                        }
687                        on("previous") {
688                                // handle form data
689                                assayGroupPage(flow, flash, params)
690
691                                // ignore errors
692                                flash.errors = [:]
693
694                                success()
695                        }.to "assays"
696                        on("next") {
697                                // handle form data
698                                assayGroupPage(flow, flash, params) ? success() : error()
699                        }.to "confirm"
700                        on("quickSave") {
701                                // handle form data
702                                assayGroupPage(flow, flash, params) ? success() : error()
703                        }.to "waitForSave"
704                }
705
706                // confirm Previous
707                confirmPrevious {
708                        action {
709                                // have we got samples and assays?
710                                if (flow.study.assays && flow.study.samples) {
711                                        // yes, go to the group page
712                                        toAssayGroups()
713                                } else {
714                                        // no need to show the group page as
715                                        // there's nothing to group
716                                        toAssays()
717                                }
718                        }
719                        on("toAssayGroups").to "assayGroups"
720                        on("toAssays").to "assays"
721                }
722
723                // confirmation
724                confirm {
725                        render(view: "_confirmation")
726                        onRender {
727                                flow.page = 7
728                        }
729                        on("toStudy").to "study"
730                        on("toSubjects").to "subjects"
731                        on("toEvents").to "events"
732                        on("toGroups").to "groups"
733                        on("toSamples").to "samples"
734                        on("toAssays").to "assays"
735                        on("toAssayGroups").to "assayGroups"
736                        on("previous").to "confirmPrevious"
737                        on("next").to "waitForSave"
738                        on("quickSave").to "waitForSave"
739                }
740
741                waitForSave {
742                        render(view: "_wait")
743                        onRender {
744                                flow.page = 8
745                        }
746                        on("next").to "save"
747                }
748
749                // store all study data
750                save {
751                        action {
752                                println "saving..."
753                                flash.errors = [:]
754
755                                // persist data to the database
756                                try {
757                                        // save study
758                                        println ".saving study"
759                                        if (!flow.study.save(flush:true)) {
760                                                this.appendErrors(flow.study, flash.errors)
761                                                throw new Exception('error saving study')
762                                        }
763                                        println ".saved study "+flow.study+" (id: "+flow.study.id+")"
764
765                                        success()
766                                } catch (Exception e) {
767                                        // rollback
768                                        this.appendErrorMap(['exception': e.toString() + ', see log for stacktrace' ], flash.errors)
769
770                                        // stacktrace in flash scope
771                                        flash.debug = e.getStackTrace()
772
773                                        error()
774                                }
775                        }
776                        on("error").to "error"
777                        on(Exception).to "error"
778                        on("success").to "done"
779                }
780
781                // error storing data
782                error {
783                        render(view: "_error")
784                        onRender {
785                                flow.page = 7
786                        }
787                        on("next").to "waitForSave"
788                        on("previous").to "samples"
789                }
790
791                // render finish page
792                done {
793                        render(view: "_done")
794                        onRender {
795                                flow.page = 8
796                        }
797                        onEnd {
798                                // clean flow scope
799                                flow.clear()
800                        }
801                }
802        }
803
804        /**
805         * load a study
806         * @param Map LocalAttributeMap (the flow scope)
807         * @param Map localAttributeMap (the flash scope)
808         * @param Map GrailsParameterMap (the flow parameters = form data)
809         * @returns boolean
810         */
811        def loadStudy(flow, flash, params) {
812                // load study
813                try {
814                        // load study
815                        flow.study = (params.studyid) ? Study.findById( params.studyid ) : Study.findByTitle( params.study )
816
817                        // set 'quicksave' variable
818                        flow.quickSave = true
819
820                        return true
821                } catch (Exception e) {
822                        // rollback
823                        this.appendErrorMap(['exception': e.toString() + ', see log for stacktrace'], flash.errors)
824
825                        return false
826                }
827        }
828
829        /**
830         * Handle the wizard study page
831         *
832         * @param Map LocalAttributeMap (the flow scope)
833         * @param Map localAttributeMap (the flash scope)
834         * @param Map GrailsParameterMap (the flow parameters = form data)
835         * @returns boolean
836         */
837        def studyPage(flow, flash, params) {
838                // remember the params in the flash scope
839                flash.values = params
840               
841                // instantiate study of it is not yet present
842                if (!flow.study) flow.study = new Study()
843
844                // did the study template change?
845                if (params.get('template').size() && flow.study.template?.name != params.get('template')) {
846                        println ".change study template!"
847
848                        // yes, was the template already set?
849                        if (flow.study.template instanceof Template) {
850                                // yes, first make sure all values are unset?
851                                println "!!! check the database fields if data of a previous template remains in the database or is deleted by GORM!"
852                        }
853
854                        // set the template
855                        flow.study.template = Template.findByName(params.remove('template'))
856                }
857
858                // does the study have a template set?
859                if (flow.study.template && flow.study.template instanceof Template) {
860                        // yes, iterate through template fields
861                        flow.study.giveFields().each() {
862                                // and set their values
863                                flow.study.setFieldValue(it.name, params.get(it.escapedName()))
864                        }
865                }
866
867                // handle publications
868                handlePublications(flow, flash, params)
869
870                // handle contacts
871                handleContacts(flow, flash, params)
872
873                // validate the study
874                if (flow.study.validate()) {
875                        // instance is okay
876                        return true
877                } else {
878                        // validation failed
879                        flash.errors = [:]
880                        this.appendErrors(flow.study, flash.errors)
881                        return false
882                }
883        }
884
885        /**
886         * re-usable code for handling publications form data in a web flow
887         * @param Map LocalAttributeMap (the flow scope)
888         * @param Map localAttributeMap (the flash scope)
889         * @param Map GrailsParameterMap (the flow parameters = form data)
890         * @returns boolean
891         */
892        def handlePublications(flow, flash, params) {
893                if (!flow.study.publications) flow.study.publications = []
894
895                // Check the ids of the pubblications that should be attached
896                // to this study. If they are already attached, keep 'm. If
897                // studies are attached that are not in the selected (i.e. the
898                // user deleted them), remove them
899                def publicationIDs = params.get('publication_ids')
900                if (publicationIDs) {
901                        // Find the individual IDs and make integers
902                        publicationIDs = publicationIDs.split(',').collect { Integer.parseInt(it, 10) }
903
904                        // First remove the publication that are not present in the array
905                        flow.study.publications.removeAll { publication -> !publicationIDs.find { id -> id == publication.id } }
906
907                        // Add those publications not yet present in the database
908                        publicationIDs.each { id ->
909                                if (!flow.study.publications.find { publication -> id == publication.id }) {
910                                        def publication = Publication.get(id)
911                                        if (publication) {
912                                                flow.study.addToPublications(publication)
913                                        } else {
914                                                println('.publication with ID ' + id + ' not found in database.')
915                                        }
916                                }
917                        }
918
919                } else {
920                        println('.no publications selected.')
921                        flow.study.publications.clear()
922                }
923
924        }
925
926        /**
927         * re-usable code for handling contacts form data in a web flow
928         * @param Map LocalAttributeMap (the flow scope)
929         * @param Map localAttributeMap (the flash scope)
930         * @param Map GrailsParameterMap (the flow parameters = form data)
931         * @return boolean
932         */
933        def handleContacts(flow, flash, params) {
934                if (!flow.study.persons) flow.study.persons = []
935
936                // Check the ids of the contacts that should be attached
937                // to this study. If they are already attached, keep 'm. If
938                // studies are attached that are not in the selected (i.e. the
939                // user deleted them), remove them
940
941                // Contacts are saved as [person_id]-[role_id]
942                def contactIDs = params.get('contacts_ids')
943                if (contactIDs) {
944                        // Find the individual IDs and make integers
945                        contactIDs = contactIDs.split(',').collect {
946                                def parts = it.split('-')
947                                return [person: Integer.parseInt(parts[0]), role: Integer.parseInt(parts[1])]
948                        }
949
950                        // First remove the contacts that are not present in the array
951                        flow.study.persons.removeAll {
952                                studyperson -> !contactIDs.find { ids -> (ids.person == studyperson.person.id) && (ids.role == studyperson.role.id) }
953                        }
954
955                        // Add those contacts not yet present in the database
956                        contactIDs.each { ids ->
957                                if (!flow.study.persons.find { studyperson -> (ids.person == studyperson.person.id) && (ids.role == studyperson.role.id) }) {
958                                        def person = Person.get(ids.person)
959                                        def role = PersonRole.get(ids.role)
960                                        if (person && role) {
961                                                // Find a studyperson object with these parameters
962                                                def studyPerson = StudyPerson.findAll().find { studyperson -> studyperson.person.id == person.id && studyperson.role.id == role.id }
963
964                                                // If if does not yet exist, save the example
965                                                if (!studyPerson) {
966                                                        studyPerson = new StudyPerson(
967                                                                person: person,
968                                                                role: role
969                                                        )
970                                                        studyPerson.save(flush: true)
971                                                }
972
973                                                flow.study.addToPersons(studyPerson)
974                                        } else {
975                                                println('.person ' + ids.person + ' or Role ' + ids.role + ' not found in database.')
976                                        }
977                                }
978                        }
979                } else {
980                        println('.no persons selected.')
981                        flow.study.persons.clear()
982                }
983
984        }
985                                                 
986        /**
987         * Handle the wizard subject page
988         *
989         * @param Map LocalAttributeMap (the flow scope)
990         * @param Map localAttributeMap (the flash scope)
991         * @param Map GrailsParameterMap (the flow parameters = form data)
992         * @returns boolean
993         */
994        def subjectPage(flow, flash, params) {
995                def errors = false
996                flash.errors = [:]
997
998                // remember the params in the flash scope
999                flash.values = params
1000
1001                // iterate through subjects
1002                flow.study.subjects.each() { subject ->
1003                        // iterate through (template and domain) fields
1004                        subject.giveFields().each() { field ->
1005                                // set field
1006                                subject.setFieldValue(
1007                                        field.name,
1008                                        params.get('subject_' + subject.getIdentifier() + '_' + field.escapedName())
1009                                )
1010                        }
1011
1012                        // validate subject
1013                        if (!subject.validate()) {
1014                                errors = true
1015                                this.appendErrors(subject, flash.errors, 'subject_' + subject.getIdentifier() + '_')
1016                        }
1017                }
1018
1019                return !errors
1020        }
1021
1022        /**
1023         * Add a number of subjects to a study
1024         *
1025         * required params entities:
1026         * -addNumber (int)
1027         * -species   (string)
1028         * -template  (string)
1029         *
1030         * @param Map LocalAttributeMap (the flow scope)
1031         * @param Map localAttributeMap (the flash scope)
1032         * @param Map GrailsParameterMap (the flow parameters = form data)
1033         * @returns boolean
1034         */
1035        def addSubjects(flow, flash, params) {
1036                // remember the params in the flash scope
1037                flash.values = params
1038
1039                // handle the subject page
1040                subjectPage(flow, flash, params)
1041
1042                // (re)set error message
1043                flash.errors = [:]
1044
1045                // set work variables
1046                def errors              = false
1047                def number              = params.get('addNumber') as int
1048                def species             = Term.findByName(params.get('species'))
1049                def template    = Template.findByName(params.get('template'))
1050
1051                // can we add subjects?
1052                if (number > 0 && species && template) {
1053                        // add subjects to study
1054                        number.times {
1055                                // work variables
1056                                def subjectName = 'Subject '
1057                                def subjectIterator = 1
1058                                def tempSubjectName = subjectName + subjectIterator
1059
1060                                // make sure subject name is unique
1061                                if (flow.study.subjects) {
1062                                        while (flow.study.subjects.find { it.name == tempSubjectName }) {
1063                                                subjectIterator++
1064                                                tempSubjectName = subjectName + subjectIterator
1065                                        }
1066                                }
1067                                subjectName = tempSubjectName
1068                               
1069                                // create a subject instance
1070                                def subject = new Subject(
1071                                        name            : subjectName,
1072                                        species         : species,
1073                                        template        : template
1074                                )
1075
1076                                // add it to the study
1077                                flow.study.addToSubjects( subject )
1078
1079                                // validate subject
1080                                if (subject.validate()) {
1081                                        println ".added subject "+subject
1082                                } else {
1083                                        // whoops?
1084                                        flow.study.removeFromSubjects( subject )
1085
1086                                        // append errors
1087                                        this.appendErrors(subject, flash.errors)
1088                                        errors = true
1089                                }
1090                        }
1091                } else {
1092                        // add feedback
1093                        errors = true
1094                        if (number < 1) this.appendErrorMap(['addNumber': 'Enter a positive number of subjects to add'], flash.errors)
1095                        if (!species)   this.appendErrorMap(['species': 'You need to select a species, or add one if it is not yet present'], flash.errors)
1096                        if (!template)  this.appendErrorMap(['template': 'You need to select a template, or add one if it is not yet present'], flash.errors)
1097                }
1098
1099                return !errors
1100        }
1101
1102        /**
1103         * Handle the wizard event page
1104         *
1105         * @param Map LocalAttributeMap (the flow scope)
1106         * @param Map localAttributeMap (the flash scope)
1107         * @param Map GrailsParameterMap (the flow parameters = form data)
1108         * @returns boolean
1109         */
1110        def eventPage(flow, flash, params) {
1111                def errors = false
1112                flash.errors = [:]
1113
1114                // remember the params in the flash scope
1115                flash.values = params
1116
1117                // handle the 'add event' form
1118                if (flow.event) {
1119                        flow.event.giveFields().each() { field ->
1120                                // set field
1121                                flow.event.setFieldValue(
1122                                        field.name,
1123                                        params.get(field.escapedName())
1124                                )
1125                        }
1126                }
1127
1128                // handle the eventGroup names and grouping
1129                def name        = ""
1130                def tempName= ""
1131                flow.study.eventGroups.each() { eventGroup ->
1132                        // iterate through templates
1133                        flow.study.giveAllEventTemplates().each() { template ->
1134                                tempName = params.get( 'eventGroup_' + eventGroup.getIdentifier() + '_' + template.getIdentifier() )
1135
1136                                // is the name different?
1137                                if (tempName != eventGroup.name) {
1138                                        name = tempName
1139                                }
1140                        }
1141
1142                        // should the name change?
1143                        if (name) {
1144                                // yes, change it
1145                                eventGroup.name = name
1146                                name = ""
1147                        }
1148
1149                        // handle eventGrouping
1150                        ( ((flow.study.events) ? flow.study.events : []) + ((flow.study.samplingEvents) ? flow.study.samplingEvents : []) ) .each() { event ->
1151                                if (params.get( 'event_' + event.getIdentifier() + '_group_' + eventGroup.getIdentifier() )) {
1152                                        // add to eventGroup
1153                                        if (event instanceof SamplingEvent) {
1154                                                // check if we are already in this eventGroup
1155                                                if (!eventGroup.samplingEvents.find { it.equals(event) }) {
1156                                                        // no, add it
1157                                                        eventGroup.addToSamplingEvents(event)
1158
1159                                                        // iterate through subjects for this eventGroup
1160                                                        eventGroup.subjects.each() { subject ->
1161                                                                // instantiate a sample for this subject / event
1162                                                                def samplingEventName = this.ucwords(event.template.name)
1163                                                                def sampleName = (this.ucwords(subject.name) + '_' + samplingEventName + '_' + new RelTime(event.startTime).toString()).replaceAll("([ ]{1,})", "")
1164                                                                def tempSampleIterator = 0
1165                                                                def tempSampleName = sampleName
1166
1167                                                                // make sure sampleName is unique
1168                                                                if (flow.study.samples) {
1169                                                                        while (flow.study.samples.find { it.name == tempSampleName }) {
1170                                                                                tempSampleIterator++
1171                                                                                tempSampleName = sampleName + "_" + tempSampleIterator
1172                                                                        }
1173                                                                        sampleName = tempSampleName
1174                                                                }
1175
1176                                                                // instantiate a sample
1177                                                                flow.study.addToSamples(
1178                                                                        new Sample(
1179                                                                                parentSubject: subject,
1180                                                                                parentEvent: event,
1181                                                                                name: sampleName,
1182                                                                                template: (event.sampleTemplate) ? event.sampleTemplate : ''
1183                                                                        )
1184                                                                )
1185                                                        }
1186                                                }
1187                                        } else {
1188                                                eventGroup.addToEvents(event)
1189                                        }
1190                                } else {
1191                                        // remove from eventGroup
1192                                        if (event instanceof SamplingEvent) {
1193                                                // iterate through subjects (if we have them)
1194                                                eventGroup.subjects.each() { subject ->
1195                                                        // find all samples for this subject / event
1196                                                        flow.study.samples.findAll { (it.parentEvent.equals(event) && it.parentSubject.equals(subject) ) }.each() {
1197                                                                // delete this sample
1198                                                                flow.study.removeFromSamples( it )
1199                                                                it.delete()
1200                                                        }
1201                                                }
1202                                               
1203                                                eventGroup.removeFromSamplingEvents(event)
1204                                        } else {
1205                                                eventGroup.removeFromEvents(event)
1206                                        }
1207                                }
1208                        }
1209                }
1210
1211                // handle the (sampling) events
1212                ( ((flow.study.events) ? flow.study.events : []) + ((flow.study.samplingEvents) ? flow.study.samplingEvents : []) ) .each() { event ->
1213                        event.giveFields().each() { field ->
1214                                event.setFieldValue(
1215                                        field.name,
1216                                        params.get( 'event_' + event.getIdentifier() + '_' + field.escapedName() )
1217                                )
1218                        }
1219
1220                        // validate event
1221                        if (!event.validate()) {
1222                                errors = true
1223                                this.appendErrors(event, flash.errors)
1224                        }
1225                }
1226
1227                return !errors
1228        }
1229
1230        /**
1231         * Handle the wizard group page
1232         *
1233         * @param Map LocalAttributeMap (the flow scope)
1234         * @param Map localAttributeMap (the flash scope)
1235         * @param Map GrailsParameterMap (the flow parameters = form data)
1236         * @returns boolean
1237         */
1238        def groupPage(flow, flash, params) {
1239                def errors = false
1240                flash.errors = [:]
1241
1242                // remember the params in the flash scope
1243                flash.values = params
1244
1245                // iterate through groups
1246                flow.study.eventGroups.each() { eventGroup ->
1247                        // iterate through subjects
1248                        flow.study.subjects.each() { subject ->
1249                                if (params.get('subject_' + subject.getIdentifier() + '_group_' + eventGroup.getIdentifier() )) {
1250                                        // check if this subject is already part of this eventGroup
1251                                        if ( !eventGroup.subjects.find { it.equals(subject) } ) {
1252                                                // add to eventGroup
1253                                                eventGroup.addToSubjects(subject)
1254
1255                                                // iterate through samplingEvents
1256                                                eventGroup.samplingEvents.each() { samplingEvent ->
1257                                                        def samplingEventName = this.ucwords(samplingEvent.template.name)
1258                                                        def sampleName = (this.ucwords(subject.name) + '_' + samplingEventName + '_' + new RelTime(samplingEvent.startTime).toString()).replaceAll("([ ]{1,})", "")
1259                                                        def tempSampleIterator = 0
1260                                                        def tempSampleName = sampleName
1261
1262                                                        // make sure sampleName is unique
1263                                                        if (flow.study.samples) {
1264                                                                while (flow.study.samples.find { it.name == tempSampleName }) {
1265                                                                        tempSampleIterator++
1266                                                                        tempSampleName = sampleName + "_" + tempSampleIterator
1267                                                                }
1268                                                                sampleName = tempSampleName
1269                                                        }
1270
1271                                                        // instantiate a sample
1272                                                        flow.study.addToSamples(
1273                                                                new Sample(
1274                                                                        parentSubject: subject,
1275                                                                        parentEvent: samplingEvent,
1276                                                                        name: sampleName,
1277                                                                        template: (samplingEvent.sampleTemplate) ? samplingEvent.sampleTemplate : ''
1278                                                                )
1279                                                        )
1280                                                }
1281                                        }
1282                                } else {
1283                                        // remove from eventGroup
1284                                        eventGroup.removeFromSubjects(subject)
1285
1286                                        // iterate through samplingEvents
1287                                        eventGroup.samplingEvents.each() { samplingEvent ->
1288                                                flow.study.samples.findAll { ( it.parentEvent.equals(samplingEvent) && it.parentSubject.equals(subject) ) }.each() {
1289                                                        // delete this sample
1290                                                        flow.study.removeFromSamples( it )
1291                                                        it.delete()
1292                                                }
1293                                        }
1294                                }
1295                        }
1296                }
1297        }
1298
1299        /**
1300         * Handle the wizard samples page
1301         *
1302         * @param Map LocalAttributeMap (the flow scope)
1303         * @param Map localAttributeMap (the flash scope)
1304         * @param Map GrailsParameterMap (the flow parameters = form data)
1305         * @returns boolean
1306         */
1307        def samplePage(flow, flash, params) {
1308                def errors = false
1309                flash.errors = [:]
1310
1311                // remember the params in the flash scope
1312                flash.values = params
1313
1314                // iterate through samples
1315                flow.study.samples.each() { sample ->
1316                        // iterate through sample fields
1317                        sample.giveFields().each() { field ->
1318                                def value = params.get('sample_'+sample.getIdentifier()+'_'+field.escapedName())
1319
1320                                // set field value
1321                                if (!(field.name == 'name' && !value)) {
1322                                        println "setting "+field.name+" to "+value
1323                                        sample.setFieldValue(field.name, value)
1324                                }
1325                        }
1326
1327                        // has the template changed?
1328                        def templateName = params.get('template_' + sample.getIdentifier())
1329                        if (templateName && sample.template?.name != templateName) {
1330                                sample.template = Template.findByName(templateName)
1331                        }
1332
1333                        // validate sample
1334                        if (!sample.validate()) {
1335                                errors = true
1336                                this.appendErrors(sample, flash.errors, 'sample_' + sample.getIdentifier() + '_' )
1337                                println 'error-> sample_'+sample.getIdentifier()
1338                        }
1339                }
1340
1341                return !errors
1342        }
1343
1344        /**
1345         * Handle the wizard assays page
1346         *
1347         * @param Map LocalAttributeMap (the flow scope)
1348         * @param Map localAttributeMap (the flash scope)
1349         * @param Map GrailsParameterMap (the flow parameters = form data)
1350         * @returns boolean
1351         */
1352        def assayPage(flow, flash, params) {
1353                def errors = false
1354                flash.errors = [:]
1355
1356                // remember the params in the flash scope
1357                flash.values = params
1358
1359                // handle the 'add assay' form
1360                if (flow.assay) {
1361                        flow.assay.giveFields().each() { field ->
1362                                // set field
1363                                flow.assay.setFieldValue(
1364                                        field.name,
1365                                        params.get(field.escapedName())
1366                                )
1367                        }
1368                }
1369
1370                // handle the assay data
1371                flow.study.assays.each() { assay ->
1372                        // set data
1373                        assay.giveFields().each() { field ->
1374                                assay.setFieldValue(
1375                                        field.name,
1376                                        params.get( 'assay_' + assay.getIdentifier() + '_' + field.escapedName() )
1377                                )
1378                        }
1379
1380                        // validate assay
1381                        if (!assay.validate()) {
1382                                errors = true
1383                                this.appendErrors(assay, flash.errors, 'assay_' + assay.getIdentifier() + '_')
1384                        }
1385                }
1386
1387                return !errors
1388        }
1389
1390        /**
1391         * Handle the wizard assayGroups page
1392         *
1393         * @param Map LocalAttributeMap (the flow scope)
1394         * @param Map localAttributeMap (the flash scope)
1395         * @param Map GrailsParameterMap (the flow parameters = form data)
1396         * @returns boolean
1397         */
1398        def assayGroupPage(flow, flash, params) {
1399                def errors = false
1400                flash.errors = [:]
1401
1402                // remember the params in the flash scope
1403                flash.values = params
1404
1405                // iterate through samples
1406                flow.study.samples.each() { sample ->
1407                        // iterate through assays
1408                        flow.study.assays.each() { assay ->
1409                                if (params.get( 'sample_' + sample.getIdentifier() + '_assay_' + assay.getIdentifier() )) {
1410                                        println "add sample "+sample.getIdentifier()+" to assay "+assay.getIdentifier()
1411                                        // add sample to assay
1412                                        assay.addToSamples( sample )
1413                                } else {
1414                                        // remove sample from assay
1415                                        assay.removeFromSamples( sample )
1416                                }
1417                                println assay.samples
1418                        }
1419                }
1420
1421                return !errors
1422        }
1423
1424        /**
1425         * groovy / java equivalent of php's ucwords function
1426         *
1427         * Capitalize all first letters of separate words
1428         *
1429         * @param String
1430         * @return String
1431         */
1432        def ucwords(String text) {
1433                def newText = ''
1434
1435                // change case to lowercase
1436                text = text.toLowerCase()
1437
1438                // iterate through words
1439                text.split(" ").each() {
1440                        newText += it[0].toUpperCase() + it.substring(1) + " "
1441                }
1442
1443                return newText.substring(0, newText.size()-1)
1444        }
1445
1446        /**
1447         * return the object from a map of objects by searching for a name
1448         * @param String name
1449         * @param Map map of objects
1450         * @return Object
1451         */
1452        def getObjectByName(name, map) {
1453                def result = null
1454                map.each() {
1455                        if (it.name == name) {
1456                                result = it
1457                        }
1458                }
1459
1460                return result
1461        }
1462
1463        /**
1464         * transform domain class validation errors into a human readable
1465         * linked hash map
1466         * @param object validated domain class
1467         * @return object  linkedHashMap
1468         */
1469        def getHumanReadableErrors(object) {
1470                def errors = [:]
1471                object.errors.getAllErrors().each() {
1472                        def message = it.toString()
1473
1474                        //errors[it.getArguments()[0]] = it.getDefaultMessage()
1475                        errors[it.getArguments()[0]] = message.substring(0, message.indexOf(';'))
1476                }
1477
1478                return errors
1479        }
1480
1481        /**
1482         * append errors of a particular object to a map
1483         * @param object
1484         * @param map linkedHashMap
1485         * @void
1486         */
1487        def appendErrors(object, map) {
1488                this.appendErrorMap(this.getHumanReadableErrors(object), map)
1489        }
1490
1491        def appendErrors(object, map, prepend) {
1492                this.appendErrorMap(this.getHumanReadableErrors(object), map, prepend)
1493        }
1494
1495        /**
1496         * append errors of one map to another map
1497         * @param map linkedHashMap
1498         * @param map linkedHashMap
1499         * @void
1500         */
1501        def appendErrorMap(map, mapToExtend) {
1502                map.each() {key, value ->
1503                        mapToExtend[key] = ['key': key, 'value': value, 'dynamic': false]
1504                }
1505        }
1506
1507        def appendErrorMap(map, mapToExtend, prepend) {
1508                map.each() {key, value ->
1509                        mapToExtend[prepend + key] = ['key': key, 'value': value, 'dynamic': true]
1510                }
1511        }
1512
1513        /**
1514         * Parses a RelTime string and returns a nice human readable string
1515         *
1516         * @return Human Readable string or a HTTP response code 400 on error
1517         */
1518        def ajaxParseRelTime = {
1519                if (params.reltime == null) {
1520                        response.status = 400
1521                        render('reltime parameter is expected')
1522                }
1523
1524                try {
1525                        def reltime = RelTime.parseRelTime(params.reltime)
1526                        render reltime.toPrettyString()
1527                } catch (IllegalArgumentException e) {
1528                        response.status = 400
1529                        render(e.getMessage())
1530                }
1531        }
1532
1533        /**
1534         * Proxy for searching PubMed articles (or other articles from the Entrez DB).
1535         *
1536         * This proxy is needed because it is not allowed to fetch XML directly from a different
1537         * domain using javascript. So we have the javascript call a function on our own domain
1538         * and the proxy will fetch the data from Entrez
1539         *
1540         * @since       20100609
1541         * @param       _utility        The name of the utility, without the complete path. Example: 'esearch.fcgi'
1542         * @return      XML
1543         */
1544        def entrezProxy = {
1545                // Remove unnecessary parameters
1546                params.remove( "action" )
1547                params.remove( "controller" )
1548
1549                def url = "http://eutils.ncbi.nlm.nih.gov/entrez/eutils";
1550                def util = params.remove( "_utility" )
1551                def paramString = params.collect { k, v -> k + '=' + v.encodeAsURL() }.join( '&' );
1552
1553                def fullUrl = url + '/' + util + '?' + paramString;
1554
1555                // Return the output of the request
1556                // render fullUrl;
1557                render(
1558                    text:           new URL( fullUrl ).getText(),
1559                    contentType:    "text/xml",
1560                    encoding:       "UTF-8"
1561                );
1562        }
1563}
Note: See TracBrowser for help on using the repository browser.