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

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