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

Last change on this file since 827 was 827, checked in by keesvb, 12 years ago

repaired study create wizard flow

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