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

Last change on this file since 1149 was 1149, checked in by work@…, 10 years ago
  • resolving issue #197 where the study wizard breaks is flash.errors does not contain a map which it expects. Probably because elsewhere in the application the flash.errors is also being set
  • Property svn:keywords set to Author Date Rev
File size: 42.7 KB
Line 
1package dbnp.studycapturing
2
3import dbnp.data.*
4
5// Grails convertors is imported in order to create JSON objects
6import grails.converters.*
7import grails.plugins.springsecurity.Secured
8import dbnp.authentication.AuthenticationService
9import dbnp.authentication.SecUser
10
11
12/**
13 * Wizard Controler
14 *
15 * The wizard controller handles the handeling of pages and data flow
16 * through the study capturing wizard.
17 *
18 * This controller is only accessible by logged in users.
19 *
20 * @author Jeroen Wesbeek
21 * @since 20100107
22 * @package studycapturing
23 *
24 * Revision information:
25 * $Rev: 1149 $
26 * $Author: work@osx.eu $
27 * $Date: 2010-11-16 17:30:11 +0000 (di, 16 nov 2010) $
28 */
29@Secured(['IS_AUTHENTICATED_REMEMBERED'])
30class WizardController {
31        def authenticationService
32
33        /**
34         * index method, redirect to the webflow
35         * @void
36         */
37        def index = {
38                def jump = [:]
39
40                // allow quickjumps to:
41                //      edit a study    : /wizard?jump=edit&id=1
42                //      create a study  : /wizard?jump=create
43                if (params.get('jump')) {
44                        switch (params.get('jump')) {
45                                case 'create':
46                                        jump = [
47                                            action: 'create'
48                                        ]
49                                        break
50                                case 'edit':
51                                        jump = [
52                                            action      : 'edit',
53                                                id              : params.get('id')
54                                        ]
55                                        break
56                                default:
57                                        break
58                        }
59                }
60
61                // store in session
62                session.jump = jump
63
64                /**
65                 * Do you believe it in your head?
66                 * I can go with the flow
67                 * Don't say it doesn't matter (with the flow) matter anymore
68                 * I can go with the flow (I can go)
69                 * Do you believe it in your head?
70                 */
71                redirect(action: 'pages')
72        }
73
74        /**
75         * WebFlow definition
76         * @see http://grails.org/WebFlow
77         * @void
78         */
79        def pagesFlow = {
80                // start the flow
81                onStart {
82                        // define flow variables
83                        flow.page = 0
84                        flow.pages = [
85                                //[title: 'Templates'],                 // templates
86                                [title: 'Start'],                               // load or create a study
87                                [title: 'Study'],                               // study
88                                [title: 'Subjects'],                    // subjects
89                                [title: 'Events'],                              // events and event grouping
90                                //[title: 'Event Groups'],              // groups
91                                [title: 'Samples'],                             // samples
92                                [title: 'Assays'],                              // assays
93                                //[title: 'Assay Groups'],              // assays
94                                [title: 'Confirmation'],                // confirmation page
95                                [title: 'Done']                                 // finish page
96                        ]
97                        flow.jump = session.jump
98                        success()
99                }
100
101                // render the main wizard page which immediately
102                // triggers the 'next' action (hence, the main
103                // page dynamically renders the study template
104                // and makes the flow jump to the study logic)
105                mainPage {
106                        render(view: "/wizard/index")
107                        onRender {
108                                flow.page = 1
109                                success()
110                        }
111                        on("next").to "handleJump"
112                }
113
114                // handle the jump parameter
115                //
116                // I came to get down [2x]
117                // So get out your seats and jump around
118                // Jump around [3x]
119                // Jump up Jump up and get down
120                // Jump [18x]
121                handleJump {
122                        action {
123                                if (flow.jump && flow.jump.action == 'edit' && flow.jump.id) {
124                                        // load study
125                                        if (this.loadStudy(flow, flash, [studyid:flow.jump.id],authenticationService.getLoggedInUser())) {
126                                                toStudyPage()
127                                        } else {
128                                                toStartPage()
129                                        }
130                                } else if (flow.jump && flow.jump.action == 'create') {
131                                        toStudyPage()
132                                } else {
133                                        toStartPage()
134                                }
135                        }
136                        on("toStartPage").to "start"
137                        on("toStudyPage").to "study"
138                }
139
140                // create or modify a study
141                start {
142                        render(view: "_start")
143                        onRender {
144                                flow.page = 1
145                                success()
146                        }
147                        on("next") {
148                                // clean the flow scope
149                                flow.remove('study')
150
151                                // set 'quicksave' variable to false
152                                flow.quickSave = false
153                        }.to "study"
154                        on("modify").to "modify"
155                        on("import").to "redirectToImport"
156                }
157
158                // redirect to the import wizard
159                redirectToImport {
160                        render(view: "_redirect")
161                        onRender {
162                                flash.uri = "/importer/index"
163                        }
164                        on("next").to "start"
165                }
166
167                // load a study to modify
168                modify {
169                        render(view: "_modify")
170                        onRender {
171                                flow.page = 1
172                                flash.cancel = true
173                                success()
174                        }
175                        on("cancel") {
176                                flow.remove('study')
177
178                                success()
179                        }.to "start"
180                        on("next") {
181                                // load study
182                                if (this.loadStudy(flow, flash, params, authenticationService.getLoggedInUser())) {
183                                        success()
184                                } else {
185                                        error()
186                                }
187                        }.to "study"
188                }
189
190                // render and handle the study page
191                study {
192                        render(view: "_study")
193                        onRender {
194                                flow.page = 2
195                                success()
196                        }
197                        on("refresh") {
198                                // handle form data
199                                studyPage(flow, flash, params)
200
201                                // force refresh of the template
202                                if (flow.study.template && flow.study.template instanceof Template) {
203                                        flow.study.template.refresh()
204                                }
205
206                                // reset errors
207                                flash.wizardErrors = [:]
208                                success()
209                        }.to "study"
210            on("switchTemplate") {
211                                // handle form data
212                                studyPage(flow, flash, params)
213
214                                // reset errors
215                                flash.wizardErrors = [:]
216                                success()
217                        }.to "study"
218                        on("previous") {
219                                // handle form data
220                                studyPage(flow, flash, params)
221
222                                // reset errors
223                                flash.wizardErrors = [:]
224                                success()
225                        }.to "start"
226                        on("next") {
227                                studyPage(flow, flash, params) ? success() : error()
228                        }.to "subjects"
229                        on("quickSave") {
230                                studyPage(flow, flash, params) ? success() : error()
231                        }.to "waitForSave"
232                }
233
234                // render and handle subjects page
235                subjects {
236                        render(view: "_subjects")
237                        onRender {
238                                flow.page = 3
239
240                                if (!flash.values || !flash.values.addNumber) flash.values = [addNumber:1]
241
242                                success()
243                        }
244                        on("refresh") {
245                                // remember the params in the flash scope
246                                flash.values = params
247
248                                // refresh templates
249                                if (flow.study.subjects) {
250                                        flow.study.giveSubjectTemplates().each() {
251                                                it.refresh()
252                                        }
253                                }
254
255                                success()
256                        }.to "subjects"
257                        on("add") {
258                                // handle form data
259                                addSubjects(flow, flash, params) ? success() : error()
260                        }.to "subjects"
261                        on("delete") {
262                                // handle form data
263                                subjectPage(flow, flash, params)
264
265                                // reset errors
266                                flash.wizardErrors = [:]
267
268                                // remove subject
269                                def subjectToRemove = flow.study.subjects.find { it.identifier == (params.get('do') as int) }
270                                if (subjectToRemove) {
271                                        flow.study.deleteSubject( subjectToRemove )
272                                }
273                        }.to "subjects"
274                        on("previous") {
275                                // handle form data
276                                subjectPage(flow, flash, params)
277
278                                // reset errors
279                                flash.wizardErrors = [:]
280                                success()
281                        }.to "study"
282                        on("next") {
283                                // handle form data
284                                subjectPage(flow, flash, params) ? success() : error()
285                        }.to "events"
286                        on("quickSave") {
287                                // handle form data
288                                subjectPage(flow, flash, params) ? success() : error()
289                        }.to "waitForSave"
290                }
291
292                // render events page
293                events {
294                        render(view: "_events")
295                        onRender {
296                                flow.page = 4
297
298                                // add initial eventGroup to study
299                                if (!flow.study.eventGroups?.size()) {
300                                        flow.study.addToEventGroups(
301                                                new EventGroup(name: 'Group 1')
302                                        )
303                                }
304
305                                success()
306                        }
307                        on("clear") {
308                                // remove all events
309                                (flow.study.events + flow.study.samplingEvents).each() { event ->
310                                        if (event instanceof SamplingEvent) {
311                                                flow.study.deleteSamplingEvent( event )
312                                        } else {
313                                                flow.study.deleteEvent( event )
314                                        }
315                                }
316
317                                success()
318                        }.to "events"
319                        on("switchTemplate") {
320                                // handle form data
321                                eventPage(flow, flash, params)
322
323                                // get template
324                                def type        = params.get('eventType')
325                                def template= Template.findByName( params.get( type + 'Template' ) )
326
327                                // change template and/or instance?
328                                if (!flow.event || (flow.event instanceof Event && type == "sample") || (flow.event instanceof SamplingEvent && type == "event")) {
329                                        // create new instance
330                                        flow.event = (type == "event") ? new Event(template: template) : new SamplingEvent(template: template)
331                                } else {
332                                        flow.event.template = template
333                                }
334
335                                // reset errors
336                                flash.wizardErrors = [:]
337                                success()
338
339                        }.to "events"
340                        on("refresh") {
341                                // handle form data
342                                eventPage(flow, flash, params)
343
344                                // refresh templates
345                                flow.study.giveEventTemplates().each() {
346                                        it.refresh()
347                                }
348
349                                // refresh event template
350                                if (flow.event?.template) flow.event.template.refresh()
351
352                                // reset errors
353                                flash.wizardErrors = [:]
354                                success()
355                        }.to "events"
356                        on("add") {
357                                // handle form data
358                                eventPage(flow, flash, params)
359
360                                // reset errors
361                                flash.wizardErrors = [:]
362
363                                // add event to study
364                                if (flow.event instanceof SamplingEvent) {
365                                        flow.study.addToSamplingEvents( flow.event )
366                                } else {
367                                        flow.study.addToEvents( flow.event )
368                                }
369
370                                // validate event
371                                if (flow.event.validate()) {
372                                        // remove event from the flowscope
373                                        flow.remove('event')
374
375                                        success()
376                                } else {
377                                        // event does not validate
378                                        // remove from study
379                                        if (flow.event instanceof SamplingEvent) {
380                                                flow.study.removeFromSamplingEvents( flow.event )
381                                        } else {
382                                                flow.study.removeFromEvents( flow.event )
383                                        }
384
385                                        // append errors
386                                        this.appendErrors(flow.event, flash.wizardErrors)
387                                        error()
388                                }
389                        }.to "events"
390                        on("deleteEvent") {
391                                // handle form data
392                                eventPage(flow, flash, params)
393
394                                // reset errors
395                                flash.wizardErrors = [:]
396
397                                // find matching (sampling) event
398                                def event                       = flow.study.events.find { it.getIdentifier() == (params.get('do') as int) }
399                                def samplingEvent       = flow.study.samplingEvents.find { it.getIdentifier() == (params.get('do') as int) }
400
401                                // perform delete
402                                if (event) flow.study.deleteEvent( event )
403                                if (samplingEvent) flow.study.deleteSamplingEvent( samplingEvent )
404                        }.to "events"
405                        on("addEventGroup") {
406                                // handle form data
407                                eventPage(flow, flash, params)
408
409                                // set work variables
410                                def groupName = 'Group '
411                                def tempGroupIterator = 1
412                                def tempGroupName = groupName + tempGroupIterator
413
414                                // make sure group name is unique
415                                if (flow.study.eventGroups) {
416                                        while (flow.study.eventGroups.find { it.name == tempGroupName }) {
417                                                tempGroupIterator++
418                                                tempGroupName = groupName + tempGroupIterator
419                                        }
420                                }
421                                groupName = tempGroupName
422
423                                // add a new eventGroup
424                                flow.study.addToEventGroups(
425                                        new EventGroup(
426                                                name    : groupName
427                                        )
428                                )
429
430                                // reset errors
431                                flash.wizardErrors = [:]
432                                success()
433                        }.to "events"
434                        on("deleteEventGroup") {
435                                // handle form data
436                                eventPage(flow, flash, params)
437
438                                // reset errors
439                                flash.wizardErrors = [:]
440
441                                // remove eventGroup
442                                def eventGroupToRemove = flow.study.eventGroups.find { it.getIdentifier() == (params.get('do') as int) }
443                                if (eventGroupToRemove) {
444                                        println flow.study.deleteEventGroup( eventGroupToRemove )
445                                }
446                        }.to "events"
447                        on("previous") {
448                                // handle form data
449                                eventPage(flow, flash, params)
450
451                                // reset errors
452                                flash.wizardErrors = [:]
453                                success()
454                        }.to "subjects"
455                        on("next") {
456                                // handle form data
457                                eventPage(flow, flash, params) ? success() : error()
458                        }.to "eventsNext"
459                        on("quickSave") {
460                                // handle form data
461                                eventPage(flow, flash, params) ? success() : error()
462                        }.to "waitForSave"
463                }
464
465                // decide to show a warning page or not
466                eventsNext {
467                        action {
468                                def assigned = false
469
470                                // check if all sampling events are in an eventGroup
471                                flow.study.samplingEvents.each() { samplingEvent ->
472                                        // iterate through eventGroups
473                                        flow.study.eventGroups.each() { eventGroup ->
474                                                if ( eventGroup.samplingEvents.find { it.equals(samplingEvent) } ) {
475                                                        assigned = true
476                                                }
477                                        }
478                                }
479
480                                if (assigned) {
481                                        toGroupsPage()
482                                } else {
483                                        toWarningPage()
484                                }
485                        }
486                        on("toWarningPage").to "unassignedSamplingEventWarning"
487                        on("toGroupsPage").to "groups"
488                }
489
490                // warning page for unassigned samplingEvent
491                unassignedSamplingEventWarning {
492                        render(view: "_unassigned_samplingEvent_warning")
493                        onRender {
494                                flow.page = 4
495                                success()
496                        }
497                        on("next").to "groups"
498                        on("previous").to "events"
499                }
500
501                // groups page
502                groups {
503                        render(view: "_groups")
504                        onRender {
505                                flow.page = 4
506                                success()
507                        }
508                        on("previous") {
509                                // handle form data
510                                groupPage(flow, flash, params) ? success() : error()
511                        }.to "events"
512                        on("next") {
513                                // handle form data
514                                groupPage(flow, flash, params) ? success() : error()
515                        }.to "samples"
516                        on("quickSave") {
517                                // handle form data
518                                groupPage(flow, flash, params) ? success() : error()
519                        }.to "waitForSave"
520                }
521
522                // sample 'previous' page with warning
523                samplePrevious {
524                        render(view: "_samples_previous_warning")
525                        onRender {
526                                flow.page = 5
527
528                                // TEMPORARY FIX TO REMOVE ALL SAMPLES AND REGENERATE THEM
529                                // THEN USER BROWSED BACK
530                                println ".removing samples from study"
531
532                                // remove samples from study
533                                flow.samples.each() {
534                                        flow.study.removeFromSamples(it.sample)
535                                }
536
537                                // remove samples from flow
538                                flow.remove('samples')
539                                // END FIX
540                        }
541                        on("next").to "samples"
542                        on("previous").to "groups"
543                }
544
545                // samples page
546                samples {
547                        render(view: "_samples")
548                        onRender {
549                                flow.page = 5
550                                success()
551                        }
552                        on("switchTemplate") {
553                                // handle form data
554                                samplePage(flow, flash, params)
555
556                                // ignore errors
557                                flash.wizardErrors = [:]
558
559                                succes()
560                        }.to "samples"
561                        on("refresh") {
562                                // handle samples
563                                samplePage(flow, flash, params)
564
565                                // refresh all sample templates
566                                flow.study.giveSampleTemplates().each() {
567                                        it.refresh()
568                                }
569
570                                // ignore errors
571                                flash.wizardErrors = [:]
572
573                                success()
574                        }.to "samples"
575                        on("regenerate") {
576                                // handle samples
577                                samplePage(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.wizardErrors = [:]
586
587                                success()
588                        }.to "samples"
589                        on("previous") {
590                                // handle samples
591                                samplePage(flow, flash, params)
592
593                                // ignore errors
594                                flash.wizardErrors = [:]
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 page
609                assays {
610                        render(view: "_assays")
611                        onRender {
612                                flow.page = 6
613                        }
614                        on("refresh") {
615                                // handle form data
616                                assayPage(flow, flash, params)
617
618                                // force refresh of the template
619                                if (flow.assay && flow.assay.template && flow.assay.template instanceof Template) {
620                                        flow.assay.template.refresh()
621                                }
622
623                                // reset errors
624                                flash.wizardErrors = [:]
625                                success()
626                        }.to "assays"
627            on("switchTemplate") {
628                                // handle form data
629                                assayPage(flow, flash, params)
630
631                                // find assay template
632                                def template = Template.findByName( params.get('template') )
633
634                                if (flow.assay) {
635                                        // set template
636                                        flow.assay.template = template
637                                } else {
638                                        // create a new assay instance
639                                        flow.assay = new Assay(template: template)
640                                }
641
642                                // reset errors
643                                flash.wizardErrors = [:]
644                                success()
645                        }.to "assays"
646                        on("add") {
647                                // handle form data
648                                assayPage(flow, flash, params)
649
650                                // reset errors
651                                flash.wizardErrors = [:]
652
653                                // add assay to study
654                                flow.study.addToAssays( flow.assay )
655
656                                // validate assay
657                                if (flow.assay.validate()) {
658                                        // remove assay from the flowscope
659                                        flow.remove('assay')
660                                        success()
661                                } else {
662                                        // assay does not validate
663                                        // remove from study
664                                        flow.study.removeFromAssays( flow.assay )
665
666                                        // append errors
667                                        this.appendErrors(flow.assay, flash.wizardErrors)
668                                        error()
669                                }
670                        }.to "assays"
671                        on("deleteAssay") {
672                                println params
673
674                                // handle form data
675                                assayPage(flow, flash, params)
676
677                                // reset errors
678                                flash.wizardErrors = [:]
679
680                                // find this assay
681                                def assay = flow.study.assays.find { it.getIdentifier() == (params.get('do') as int) }
682
683                                // perform delete
684                                if (assay) flow.study.deleteAssay( assay )
685                        }.to "assays"
686                        on("previous") {
687                                // handle form data
688                                assayPage(flow, flash, params)
689
690                                // ignore errors
691                                flash.wizardErrors = [:]
692
693                                success()
694                        }.to "samples"
695                        on("next") {
696                                // handle form data
697                                assayPage(flow, flash, params) ? success() : error()
698                        }.to "assayNext"
699                        on("quickSave") {
700                                // handle form data
701                                assayPage(flow, flash, params) ? success() : error()
702                        }.to "waitForSave"
703                }
704
705                // assayNext
706                assayNext {
707                        action {
708                                // have we got samples and assays?
709                                if (flow.study.assays && flow.study.samples) {
710                                        // yes, go to the group page
711                                        toAssayGroups()
712                                } else {
713                                        // no need to show the group page as
714                                        // there's nothing to group
715                                        toConfirm()
716                                }
717                        }
718                        on("toAssayGroups").to "assayGroups"
719                        on("toConfirm").to "confirm"
720                }
721
722                // assay grouping page
723                assayGroups {
724                        render(view: "_assay_groups")
725                        onRender {
726                                flow.page = 6
727                        }
728                        on("previous") {
729                                // handle form data
730                                assayGroupPage(flow, flash, params)
731
732                                // ignore errors
733                                flash.wizardErrors = [:]
734
735                                success()
736                        }.to "assays"
737                        on("next") {
738                                // handle form data
739                                assayGroupPage(flow, flash, params) ? success() : error()
740                        }.to "confirm"
741                        on("quickSave") {
742                                // handle form data
743                                assayGroupPage(flow, flash, params) ? success() : error()
744                        }.to "waitForSave"
745                }
746
747                // confirm Previous
748                confirmPrevious {
749                        action {
750                                // have we got samples and assays?
751                                if (flow.study.assays && flow.study.samples) {
752                                        // yes, go to the group page
753                                        toAssayGroups()
754                                } else {
755                                        // no need to show the group page as
756                                        // there's nothing to group
757                                        toAssays()
758                                }
759                        }
760                        on("toAssayGroups").to "assayGroups"
761                        on("toAssays").to "assays"
762                }
763
764                // confirmation
765                confirm {
766                        render(view: "_confirmation")
767                        onRender {
768                                flow.page = 7
769                        }
770                        on("toStudy").to "study"
771                        on("toSubjects").to "subjects"
772                        on("toEvents").to "events"
773                        on("toGroups").to "groups"
774                        on("toSamples").to "samples"
775                        on("toAssays").to "assays"
776                        on("toAssayGroups").to "assayGroups"
777                        on("previous").to "confirmPrevious"
778                        on("next").to "waitForSave"
779                        on("quickSave").to "waitForSave"
780                }
781
782                waitForSave {
783                        render(view: "_wait")
784                        onRender {
785                                flow.page = 8
786                        }
787                        on("next").to "save"
788                }
789
790                // store all study data
791                save {
792                        action {
793                                println "saving..."
794                                flash.wizardErrors = [:]
795
796                                // persist data to the database
797                                try {
798                                        // save study
799                                        println ".saving study"
800
801                                        // Make sure the owner of the study is set right
802                                        flow.study.owner = authenticationService.getLoggedInUser()
803
804                                        if (!flow.study.save(flush:true)) {
805                                                this.appendErrors(flow.study, flash.wizardErrors)
806                                                throw new Exception('error saving study')
807                                        }
808                                        println ".saved study "+flow.study+" (id: "+flow.study.id+")"
809
810                                        success()
811                                } catch (Exception e) {
812                                        // rollback
813                                        this.appendErrorMap(['exception': e.toString() + ', see log for stacktrace' ], flash.wizardErrors)
814
815                                        // stacktrace in flash scope
816                                        flash.debug = e.getStackTrace()
817
818                                        error()
819                                }
820                        }
821                        on("error").to "error"
822                        on(Exception).to "error"
823                        on("success").to "done"
824                }
825
826                // error storing data
827                error {
828                        render(view: "_error")
829                        onRender {
830                                flow.page = 7
831                        }
832                        on("next").to "waitForSave"
833                        on("previous").to "samples"
834                }
835
836                // render finish page
837                done {
838                        render(view: "_done")
839                        onRender {
840                                flow.page = 8
841                        }
842                        onEnd {
843                                // clean flow scope
844                                flow.clear()
845                        }
846                }
847        }
848
849        /**
850         * load a study
851         * @param Map LocalAttributeMap (the flow scope)
852         * @param Map localAttributeMap (the flash scope)
853         * @param Map GrailsParameterMap (the flow parameters = form data)
854         * @returns boolean
855         */
856        def loadStudy(flow, flash, params, user) {
857//              def authenticationService
858
859                flash.wizardErrors = new LinkedHashMap()
860               
861                // load study
862                try {
863                        // load study
864                        def study = (params.studyid) ? Study.findById( params.studyid ) : Study.findByTitle( params.study )
865
866                        // Check whether the user is allowed to edit this study. If it is not allowed
867                        // the used should had never seen a link to this page, so he should never get
868                        // here. That's why we just return false
869//                      if (!study.canWrite(authenticationService.getLoggedInUser())) {
870            if (!study.canWrite(user)){
871                                return false
872                        }
873
874                        flow.study = study
875
876                        // set 'quicksave' variable
877                        flow.quickSave = true
878
879                        return true
880                } catch (Exception e) {
881                        // rollback
882                        this.appendErrorMap(['exception': e.getMessage() + ', see log for stacktrace'], flash.wizardErrors)
883
884                        return false
885                }
886        }
887
888        /**
889         * Handle the wizard study page
890         *
891         * @param Map LocalAttributeMap (the flow scope)
892         * @param Map localAttributeMap (the flash scope)
893         * @param Map GrailsParameterMap (the flow parameters = form data)
894         * @returns boolean
895         */
896        def studyPage(flow, flash, params) {
897                // remember the params in the flash scope
898                flash.values = params
899
900                // instantiate study of it is not yet present
901                if (!flow.study) flow.study = new Study()
902
903                // did the study template change?
904                if (params.get('template').size() && flow.study.template?.name != params.get('template')) {
905                        println ".change study template!"
906
907                        // yes, was the template already set?
908                        if (flow.study.template instanceof Template) {
909                                // yes, first make sure all values are unset?
910                                println "!!! check the database fields if data of a previous template remains in the database or is deleted by GORM!"
911                        }
912
913                        // set the template
914                        flow.study.template = Template.findByName(params.remove('template'))
915                }
916
917                // does the study have a template set?
918                if (flow.study.template && flow.study.template instanceof Template) {
919                        // yes, iterate through template fields
920                        flow.study.giveFields().each() {
921                                // and set their values
922                                flow.study.setFieldValue(it.name, params.get(it.escapedName()))
923                        }
924                }
925
926                // handle publications
927                handlePublications(flow, flash, params)
928
929                // handle contacts
930                handleContacts(flow, flash, params)
931
932                // handle users (readers, writers)
933                handleUsers(flow, flash, params, 'readers')
934                handleUsers(flow, flash, params, 'writers')
935
936                // handle public checkbox
937                if (params.get("publicstudy")) {
938                        flow.study.publicstudy = params.get("publicstudy")
939                }
940
941                // validate the study
942                if (flow.study.validate()) {
943                        // instance is okay
944                        return true
945                } else {
946                        // validation failed
947                        flash.wizardErrors = [:]
948                        this.appendErrors(flow.study, flash.wizardErrors)
949                        return false
950                }
951        }
952
953        /**
954         * re-usable code for handling publications form data in a web flow
955         * @param Map LocalAttributeMap (the flow scope)
956         * @param Map localAttributeMap (the flash scope)
957         * @param Map GrailsParameterMap (the flow parameters = form data)
958         * @returns boolean
959         */
960        def handlePublications(flow, flash, params) {
961                if (!flow.study.publications) flow.study.publications = []
962
963                // Check the ids of the pubblications that should be attached
964                // to this study. If they are already attached, keep 'm. If
965                // studies are attached that are not in the selected (i.e. the
966                // user deleted them), remove them
967                def publicationIDs = params.get('publication_ids')
968                if (publicationIDs) {
969                        // Find the individual IDs and make integers
970                        publicationIDs = publicationIDs.split(',').collect { Integer.parseInt(it, 10) }
971
972                        // First remove the publication that are not present in the array
973                        flow.study.publications.removeAll { publication -> !publicationIDs.find { id -> id == publication.id } }
974
975                        // Add those publications not yet present in the database
976                        publicationIDs.each { id ->
977                                if (!flow.study.publications.find { publication -> id == publication.id }) {
978                                        def publication = Publication.get(id)
979                                        if (publication) {
980                                                flow.study.addToPublications(publication)
981                                        } else {
982                                                println('.publication with ID ' + id + ' not found in database.')
983                                        }
984                                }
985                        }
986
987                } else {
988                        println('.no publications selected.')
989                        flow.study.publications.clear()
990                }
991
992        }
993
994        /**
995         * re-usable code for handling contacts form data in a web flow
996         * @param Map LocalAttributeMap (the flow scope)
997         * @param Map localAttributeMap (the flash scope)
998         * @param Map GrailsParameterMap (the flow parameters = form data)
999         * @return boolean
1000         */
1001        def handleContacts(flow, flash, params) {
1002                if (!flow.study.persons) flow.study.persons = []
1003
1004                // Check the ids of the contacts that should be attached
1005                // to this study. If they are already attached, keep 'm. If
1006                // studies are attached that are not in the selected (i.e. the
1007                // user deleted them), remove them
1008
1009                // Contacts are saved as [person_id]-[role_id]
1010                def contactIDs = params.get('contacts_ids')
1011                if (contactIDs) {
1012                        // Find the individual IDs and make integers
1013                        contactIDs = contactIDs.split(',').collect {
1014                                def parts = it.split('-')
1015                                return [person: Integer.parseInt(parts[0]), role: Integer.parseInt(parts[1])]
1016                        }
1017
1018                        // First remove the contacts that are not present in the array
1019                        flow.study.persons.removeAll {
1020                                studyperson -> !contactIDs.find { ids -> (ids.person == studyperson.person.id) && (ids.role == studyperson.role.id) }
1021                        }
1022
1023                        // Add those contacts not yet present in the database
1024                        contactIDs.each { ids ->
1025                                if (!flow.study.persons.find { studyperson -> (ids.person == studyperson.person.id) && (ids.role == studyperson.role.id) }) {
1026                                        def person = Person.get(ids.person)
1027                                        def role = PersonRole.get(ids.role)
1028                                        if (person && role) {
1029                                                // Find a studyperson object with these parameters
1030                                                def studyPerson = StudyPerson.findAll().find { studyperson -> studyperson.person.id == person.id && studyperson.role.id == role.id }
1031
1032                                                // If if does not yet exist, save the example
1033                                                if (!studyPerson) {
1034                                                        studyPerson = new StudyPerson(
1035                                                                person: person,
1036                                                                role: role
1037                                                        )
1038                                                        studyPerson.save(flush: true)
1039                                                }
1040
1041                                                flow.study.addToPersons(studyPerson)
1042                                        } else {
1043                                                println('.person ' + ids.person + ' or Role ' + ids.role + ' not found in database.')
1044                                        }
1045                                }
1046                        }
1047                } else {
1048                        println('.no persons selected.')
1049                        flow.study.persons.clear()
1050                }
1051
1052        }
1053
1054        /**
1055         * re-usable code for handling contacts form data in a web flow
1056         * @param Map LocalAttributeMap (the flow scope)
1057         * @param Map localAttributeMap (the flash scope)
1058         * @param Map GrailsParameterMap (the flow parameters = form data)
1059         * @param String    'readers' or 'writers'
1060         * @return boolean
1061         */
1062        def handleUsers(flow, flash, params, type) {
1063                def users = []
1064
1065                if (type == "readers") {
1066                        users = flow.study.readers ?: []
1067                } else if (type == "writers") {
1068                        users = flow.study.writers ?: []
1069                }
1070
1071                // Check the ids of the contacts that should be attached
1072                // to this study. If they are already attached, keep 'm. If
1073                // studies are attached that are not in the selected (i.e. the
1074                // user deleted them), remove them
1075
1076                // Users are saved as user_id
1077                def userIDs = params.get( type + '_ids')
1078                if (userIDs) {
1079                        // Find the individual IDs and make integers
1080                        userIDs = userIDs.split(',').collect { Integer.parseInt(it, 10) }
1081
1082                        // First remove the publication that are not present in the array
1083                        users.removeAll { user -> !userIDs.find { id -> id == user.id } }
1084
1085                        // Add those publications not yet present in the database
1086                        userIDs.each { id ->
1087                                if (!users.find { user -> id == user.id }) {
1088                                        def user = SecUser.get(id)
1089                                        if (user) {
1090                                            users.add(user)
1091                                        } else {
1092                                                println('.user with ID ' + id + ' not found in database.')
1093                                        }
1094                                }
1095                        }
1096
1097                } else {
1098                        println('.no users selected.')
1099                        users.clear()
1100                }
1101
1102                if (type == "readers") {
1103                        if (flow.study.readers)
1104                        flow.study.readers.clear()
1105                        users.each { flow.study.addToReaders(it) }
1106                } else if (type == "writers") {
1107                        if (flow.study.writers)
1108                        flow.study.writers.clear()
1109
1110                        users.each { flow.study.addToWriters(it) }
1111                }
1112        }
1113
1114        /**
1115         * Handle the wizard subject page
1116         *
1117         * @param Map LocalAttributeMap (the flow scope)
1118         * @param Map localAttributeMap (the flash scope)
1119         * @param Map GrailsParameterMap (the flow parameters = form data)
1120         * @returns boolean
1121         */
1122        def subjectPage(flow, flash, params) {
1123                def errors = false
1124                flash.wizardErrors = [:]
1125
1126                // remember the params in the flash scope
1127                flash.values = params
1128
1129                // iterate through subjects
1130                flow.study.subjects.each() { subject ->
1131                        // iterate through (template and domain) fields
1132                        subject.giveFields().each() { field ->
1133                                // set field
1134                                subject.setFieldValue(
1135                                        field.name,
1136                                        params.get('subject_' + subject.getIdentifier() + '_' + field.escapedName())
1137                                )
1138                        }
1139
1140                        // validate subject
1141                        if (!subject.validate()) {
1142                                errors = true
1143                                this.appendErrors(subject, flash.wizardErrors, 'subject_' + subject.getIdentifier() + '_')
1144                        }
1145                }
1146
1147                return !errors
1148        }
1149
1150        /**
1151         * Add a number of subjects to a study
1152         *
1153         * required params entities:
1154         * -addNumber (int)
1155         * -species   (string)
1156         * -template  (string)
1157         *
1158         * @param Map LocalAttributeMap (the flow scope)
1159         * @param Map localAttributeMap (the flash scope)
1160         * @param Map GrailsParameterMap (the flow parameters = form data)
1161         * @returns boolean
1162         */
1163        def addSubjects(flow, flash, params) {
1164                // remember the params in the flash scope
1165                flash.values = params
1166
1167                // handle the subject page
1168                subjectPage(flow, flash, params)
1169
1170                // (re)set error message
1171                flash.wizardErrors = [:]
1172
1173                // set work variables
1174                def errors              = false
1175                def number              = params.get('addNumber') as int
1176                def species             = Term.findByName(params.get('species'))
1177                def template    = Template.findByName(params.get('template'))
1178
1179                // can we add subjects?
1180                if (number > 0 && species && template) {
1181                        // add subjects to study
1182                        number.times {
1183                                // work variables
1184                                def subjectName = 'Subject '
1185                                def subjectIterator = 1
1186                                def tempSubjectName = subjectName + subjectIterator
1187
1188                                // make sure subject name is unique
1189                                if (flow.study.subjects) {
1190                                        while (flow.study.subjects.find { it.name == tempSubjectName }) {
1191                                                subjectIterator++
1192                                                tempSubjectName = subjectName + subjectIterator
1193                                        }
1194                                }
1195                                subjectName = tempSubjectName
1196
1197                                // create a subject instance
1198                                def subject = new Subject(
1199                                        name            : subjectName,
1200                                        species         : species,
1201                                        template        : template
1202                                )
1203
1204                                // add it to the study
1205                                flow.study.addToSubjects( subject )
1206
1207                                // validate subject
1208                                if (subject.validate()) {
1209                                        println ".added subject "+subject
1210                                } else {
1211                                        // whoops?
1212                                        flow.study.removeFromSubjects( subject )
1213
1214                                        // append errors
1215                                        this.appendErrors(subject, flash.wizardErrors)
1216                                        errors = true
1217                                }
1218                        }
1219                } else {
1220                        // add feedback
1221                        errors = true
1222                        if (number < 1) this.appendErrorMap(['addNumber': 'Enter a positive number of subjects to add'], flash.wizardErrors)
1223                        if (!species)   this.appendErrorMap(['species': 'You need to select a species, or add one if it is not yet present'], flash.wizardErrors)
1224                        if (!template)  this.appendErrorMap(['template': 'You need to select a template, or add one if it is not yet present'], flash.wizardErrors)
1225                }
1226
1227                return !errors
1228        }
1229
1230        /**
1231         * Handle the wizard event page
1232         *
1233         * @param Map LocalAttributeMap (the flow scope)
1234         * @param Map localAttributeMap (the flash scope)
1235         * @param Map GrailsParameterMap (the flow parameters = form data)
1236         * @returns boolean
1237         */
1238        def eventPage(flow, flash, params) {
1239                def errors = false
1240                flash.wizardErrors = [:]
1241
1242                // remember the params in the flash scope
1243                flash.values = params
1244
1245                // handle the 'add event' form
1246                if (flow.event) {
1247                        flow.event.giveFields().each() { field ->
1248                                // set field
1249                                flow.event.setFieldValue(
1250                                        field.name,
1251                                        params.get(field.escapedName())
1252                                )
1253                        }
1254                }
1255
1256                // handle the eventGroup names and grouping
1257                def name        = ""
1258                def tempName= ""
1259                flow.study.eventGroups.each() { eventGroup ->
1260                        // iterate through templates
1261                        flow.study.giveAllEventTemplates().each() { template ->
1262                                tempName = params.get( 'eventGroup_' + eventGroup.getIdentifier() + '_' + template.getIdentifier() )
1263
1264                                // is the name different?
1265                                if (tempName != eventGroup.name) {
1266                                        name = tempName
1267                                }
1268                        }
1269
1270                        // should the name change?
1271                        if (name) {
1272                                // yes, change it
1273                                eventGroup.name = name
1274                                name = ""
1275                        }
1276
1277                        // handle eventGrouping
1278                        ( ((flow.study.events) ? flow.study.events : []) + ((flow.study.samplingEvents) ? flow.study.samplingEvents : []) ) .each() { event ->
1279                                if (params.get( 'event_' + event.getIdentifier() + '_group_' + eventGroup.getIdentifier() )) {
1280                                        // add to eventGroup
1281                                        if (event instanceof SamplingEvent) {
1282                                                // check if we are already in this eventGroup
1283                                                if (!eventGroup.samplingEvents.find { it.equals(event) }) {
1284                                                        // no, add it
1285                                                        eventGroup.addToSamplingEvents(event)
1286
1287                                                        // iterate through subjects for this eventGroup
1288                                                        eventGroup.subjects.each() { subject ->
1289                                                                // instantiate a sample for this subject / event
1290                                                                def samplingEventName = this.ucwords(event.template.name)
1291                                                                def sampleName = (this.ucwords(subject.name) + '_' + samplingEventName + '_' + new RelTime(event.startTime).toString()).replaceAll("([ ]{1,})", "")
1292                                                                def tempSampleIterator = 0
1293                                                                def tempSampleName = sampleName
1294
1295                                                                // make sure sampleName is unique
1296                                                                if (flow.study.samples) {
1297                                                                        while (flow.study.samples.find { it.name == tempSampleName }) {
1298                                                                                tempSampleIterator++
1299                                                                                tempSampleName = sampleName + "_" + tempSampleIterator
1300                                                                        }
1301                                                                        sampleName = tempSampleName
1302                                                                }
1303
1304                                                                // instantiate a sample
1305                                                                flow.study.addToSamples(
1306                                                                        new Sample(
1307                                                                                parentSubject   : subject,
1308                                                                                parentEvent             : event,
1309                                                                                parentEventGroup: eventGroup,
1310                                                                                name                    : sampleName,
1311                                                                                template                : (event.sampleTemplate) ? event.sampleTemplate : ''
1312                                                                        )
1313                                                                )
1314                                                        }
1315                                                }
1316                                        } else {
1317                                                eventGroup.addToEvents(event)
1318                                        }
1319                                } else {
1320                                        // remove from eventGroup
1321                                        if (event instanceof SamplingEvent) {
1322                                                // iterate through subjects (if we have them)
1323                                                eventGroup.subjects.each() { subject ->
1324                                                        // find all samples for this subject / event
1325                                                        flow.study.samples.findAll { (it.parentEvent.equals(event) && it.parentSubject.equals(subject) ) }.each() {
1326                                                                // delete this sample
1327                                                                flow.study.removeFromSamples( it )
1328                                                                it.delete()
1329                                                        }
1330                                                }
1331
1332                                                eventGroup.removeFromSamplingEvents(event)
1333                                        } else {
1334                                                eventGroup.removeFromEvents(event)
1335                                        }
1336                                }
1337                        }
1338                }
1339
1340                // handle the (sampling) events
1341                ( ((flow.study.events) ? flow.study.events : []) + ((flow.study.samplingEvents) ? flow.study.samplingEvents : []) ) .each() { event ->
1342                        event.giveFields().each() { field ->
1343                                event.setFieldValue(
1344                                        field.name,
1345                                        params.get( 'event_' + event.getIdentifier() + '_' + field.escapedName() )
1346                                )
1347                        }
1348
1349                        // validate event
1350                        if (!event.validate()) {
1351                                errors = true
1352                                this.appendErrors(event, flash.wizardErrors)
1353                        }
1354                }
1355
1356                return !errors
1357        }
1358
1359        /**
1360         * Handle the wizard group page
1361         *
1362         * @param Map LocalAttributeMap (the flow scope)
1363         * @param Map localAttributeMap (the flash scope)
1364         * @param Map GrailsParameterMap (the flow parameters = form data)
1365         * @returns boolean
1366         */
1367        def groupPage(flow, flash, params) {
1368                def errors = false
1369                flash.wizardErrors = [:]
1370
1371                // remember the params in the flash scope
1372                flash.values = params
1373
1374                // iterate through groups
1375                flow.study.eventGroups.each() { eventGroup ->
1376                        // iterate through subjects
1377                        flow.study.subjects.each() { subject ->
1378                                if (params.get('subject_' + subject.getIdentifier() + '_group_' + eventGroup.getIdentifier() )) {
1379                                        // check if this subject is already part of this eventGroup
1380                                        if ( !eventGroup.subjects.find { it.equals(subject) } ) {
1381                                                // add to eventGroup
1382                                                eventGroup.addToSubjects(subject)
1383
1384                                                // iterate through samplingEvents
1385                                                eventGroup.samplingEvents.each() { samplingEvent ->
1386                                                        def samplingEventName = this.ucwords(samplingEvent.template.name)
1387                                                        def sampleName = (this.ucwords(subject.name) + '_' + samplingEventName + '_' + new RelTime(samplingEvent.startTime).toString()).replaceAll("([ ]{1,})", "")
1388                                                        def tempSampleIterator = 0
1389                                                        def tempSampleName = sampleName
1390
1391                                                        // make sure sampleName is unique
1392                                                        if (flow.study.samples) {
1393                                                                while (flow.study.samples.find { it.name == tempSampleName }) {
1394                                                                        tempSampleIterator++
1395                                                                        tempSampleName = sampleName + "_" + tempSampleIterator
1396                                                                }
1397                                                                sampleName = tempSampleName
1398                                                        }
1399
1400                                                        // instantiate a sample
1401                                                        flow.study.addToSamples(
1402                                                                new Sample(
1403                                                                        parentSubject   : subject,
1404                                                                        parentEvent             : samplingEvent,
1405                                                                        parentEventGroup: eventGroup,
1406                                                                        name                    : sampleName,
1407                                                                        template                : (samplingEvent.sampleTemplate) ? samplingEvent.sampleTemplate : ''
1408                                                                )
1409                                                        )
1410                                                }
1411                                        }
1412                                } else {
1413                                        // remove from eventGroup
1414                                        eventGroup.removeFromSubjects(subject)
1415
1416                                        // iterate through samplingEvents
1417                                        eventGroup.samplingEvents.each() { samplingEvent ->
1418                                                flow.study.samples.findAll { ( it.parentEvent.equals(samplingEvent) && it.parentSubject.equals(subject) ) }.each() {
1419                                                        // delete this sample
1420                                                        flow.study.removeFromSamples( it )
1421                                                        it.delete()
1422                                                }
1423                                        }
1424                                }
1425                        }
1426                }
1427        }
1428
1429        /**
1430         * Handle the wizard samples page
1431         *
1432         * @param Map LocalAttributeMap (the flow scope)
1433         * @param Map localAttributeMap (the flash scope)
1434         * @param Map GrailsParameterMap (the flow parameters = form data)
1435         * @returns boolean
1436         */
1437        def samplePage(flow, flash, params) {
1438                def errors = false
1439                flash.wizardErrors = [:]
1440
1441                // remember the params in the flash scope
1442                flash.values = params
1443
1444                // iterate through samples
1445                flow.study.samples.each() { sample ->
1446                        // iterate through sample fields
1447                        sample.giveFields().each() { field ->
1448                                def value = params.get('sample_'+sample.getIdentifier()+'_'+field.escapedName())
1449
1450                                // set field value
1451                                if (!(field.name == 'name' && !value)) {
1452                                        println "setting "+field.name+" to "+value
1453                                        sample.setFieldValue(field.name, value)
1454                                }
1455                        }
1456
1457                        // has the template changed?
1458                        def templateName = params.get('template_' + sample.getIdentifier())
1459                        if (templateName && sample.template?.name != templateName) {
1460                                sample.template = Template.findByName(templateName)
1461                        }
1462
1463                        // validate sample
1464                        if (!sample.validate()) {
1465                                errors = true
1466                                this.appendErrors(sample, flash.wizardErrors, 'sample_' + sample.getIdentifier() + '_' )
1467                                println 'error-> sample_'+sample.getIdentifier()
1468                        }
1469                }
1470
1471                return !errors
1472        }
1473
1474        /**
1475         * Handle the wizard assays page
1476         *
1477         * @param Map LocalAttributeMap (the flow scope)
1478         * @param Map localAttributeMap (the flash scope)
1479         * @param Map GrailsParameterMap (the flow parameters = form data)
1480         * @returns boolean
1481         */
1482        def assayPage(flow, flash, params) {
1483                def errors = false
1484                flash.wizardErrors = [:]
1485
1486                // remember the params in the flash scope
1487                flash.values = params
1488
1489                // handle the 'add assay' form
1490                if (flow.assay) {
1491                        flow.assay.giveFields().each() { field ->
1492                                // set field
1493                                flow.assay.setFieldValue(
1494                                        field.name,
1495                                        params.get(field.escapedName())
1496                                )
1497                        }
1498                }
1499
1500                // handle the assay data
1501                flow.study.assays.each() { assay ->
1502                        // set data
1503                        assay.giveFields().each() { field ->
1504                                assay.setFieldValue(
1505                                        field.name,
1506                                        params.get( 'assay_' + assay.getIdentifier() + '_' + field.escapedName() )
1507                                )
1508                        }
1509
1510                        // validate assay
1511                        if (!assay.validate()) {
1512                                errors = true
1513                                this.appendErrors(assay, flash.wizardErrors, 'assay_' + assay.getIdentifier() + '_')
1514                        }
1515                }
1516
1517                return !errors
1518        }
1519
1520        /**
1521         * Handle the wizard assayGroups page
1522         *
1523         * @param Map LocalAttributeMap (the flow scope)
1524         * @param Map localAttributeMap (the flash scope)
1525         * @param Map GrailsParameterMap (the flow parameters = form data)
1526         * @returns boolean
1527         */
1528        def assayGroupPage(flow, flash, params) {
1529                def errors = false
1530                flash.wizardErrors = [:]
1531
1532                // remember the params in the flash scope
1533                flash.values = params
1534
1535                // iterate through samples
1536                flow.study.samples.each() { sample ->
1537                        // iterate through assays
1538                        flow.study.assays.each() { assay ->
1539                                if (params.get( 'sample_' + sample.getIdentifier() + '_assay_' + assay.getIdentifier() )) {
1540                                        println "add sample "+sample.getIdentifier()+" to assay "+assay.getIdentifier()
1541                                        // add sample to assay
1542                                        assay.addToSamples( sample )
1543                                } else {
1544                                        // remove sample from assay
1545                                        assay.removeFromSamples( sample )
1546                                }
1547                                println assay.samples
1548                        }
1549                }
1550
1551                return !errors
1552        }
1553
1554        /**
1555         * groovy / java equivalent of php's ucwords function
1556         *
1557         * Capitalize all first letters of separate words
1558         *
1559         * @param String
1560         * @return String
1561         */
1562        def ucwords(String text) {
1563                def newText = ''
1564
1565                // change case to lowercase
1566                text = text.toLowerCase()
1567
1568                // iterate through words
1569                text.split(" ").each() {
1570                        newText += it[0].toUpperCase() + it.substring(1) + " "
1571                }
1572
1573                return newText.substring(0, newText.size()-1)
1574        }
1575
1576        /**
1577         * return the object from a map of objects by searching for a name
1578         * @param String name
1579         * @param Map map of objects
1580         * @return Object
1581         */
1582        def getObjectByName(name, map) {
1583                def result = null
1584                map.each() {
1585                        if (it.name == name) {
1586                                result = it
1587                        }
1588                }
1589
1590                return result
1591        }
1592
1593        /**
1594         * transform domain class validation errors into a human readable
1595         * linked hash map
1596         * @param object validated domain class
1597         * @return object  linkedHashMap
1598         */
1599        def getHumanReadableErrors(object) {
1600                def errors = [:]
1601                object.errors.getAllErrors().each() {
1602                        def message = it.toString()
1603
1604                        //errors[it.getArguments()[0]] = it.getDefaultMessage()
1605                        errors[it.getArguments()[0]] = message.substring(0, message.indexOf(';'))
1606                }
1607
1608                return errors
1609        }
1610
1611        /**
1612         * append errors of a particular object to a map
1613         * @param object
1614         * @param map linkedHashMap
1615         * @void
1616         */
1617        def appendErrors(object, map) {
1618                this.appendErrorMap(this.getHumanReadableErrors(object), map)
1619        }
1620
1621        def appendErrors(object, map, prepend) {
1622                this.appendErrorMap(this.getHumanReadableErrors(object), map, prepend)
1623        }
1624
1625        /**
1626         * append errors of one map to another map
1627         * @param map linkedHashMap
1628         * @param map linkedHashMap
1629         * @void
1630         */
1631        def appendErrorMap(map, mapToExtend) {
1632                map.each() {key, value ->
1633                        mapToExtend[key] = ['key': key, 'value': value, 'dynamic': false]
1634                }
1635        }
1636
1637        def appendErrorMap(map, mapToExtend, prepend) {
1638                map.each() {key, value ->
1639                        mapToExtend[prepend + key] = ['key': key, 'value': value, 'dynamic': true]
1640                }
1641        }
1642
1643        /**
1644         * Parses a RelTime string and returns a nice human readable string
1645         *
1646         * @return Human Readable string or a HTTP response code 400 on error
1647         */
1648        def ajaxParseRelTime = {
1649                if (params.reltime == null) {
1650                        response.status = 400
1651                        render('reltime parameter is expected')
1652                }
1653
1654                try {
1655                        def reltime = RelTime.parseRelTime(params.reltime)
1656                        render reltime.toPrettyString()
1657                } catch (IllegalArgumentException e) {
1658                        response.status = 400
1659                        render(e.getMessage())
1660                }
1661        }
1662
1663        /**
1664         * Proxy for searching PubMed articles (or other articles from the Entrez DB).
1665         *
1666         * This proxy is needed because it is not allowed to fetch XML directly from a different
1667         * domain using javascript. So we have the javascript call a function on our own domain
1668         * and the proxy will fetch the data from Entrez
1669         *
1670         * @since       20100609
1671         * @param       _utility        The name of the utility, without the complete path. Example: 'esearch.fcgi'
1672         * @return      XML
1673         */
1674        def entrezProxy = {
1675                // Remove unnecessary parameters
1676                params.remove( "action" )
1677                params.remove( "controller" )
1678
1679                def url = "http://eutils.ncbi.nlm.nih.gov/entrez/eutils";
1680                def util = params.remove( "_utility" )
1681                def paramString = params.collect { k, v -> k + '=' + v.encodeAsURL() }.join( '&' );
1682
1683                def fullUrl = url + '/' + util + '?' + paramString;
1684
1685                // Return the output of the request
1686                // render fullUrl;
1687                render(
1688                    text:           new URL( fullUrl ).getText(),
1689                    contentType:    "text/xml",
1690                    encoding:       "UTF-8"
1691                );
1692        }
1693}
Note: See TracBrowser for help on using the repository browser.