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

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