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

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