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

Last change on this file since 1097 was 1097, checked in by s.h.sikkema@…, 10 years ago

A -> a

  • Property svn:keywords set to Author Date Rev
File size: 42.3 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: 1097 $
26 * $Author: s.h.sikkema@gmail.com $
27 * $Date: 2010-11-08 11:08:22 +0000 (ma, 08 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])) {
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)) {
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.errors = [:]
208                                success()
209                        }.to "study"
210            on("switchTemplate") {
211                                // handle form data
212                                studyPage(flow, flash, params)
213
214                                // reset errors
215                                flash.errors = [:]
216                                success()
217                        }.to "study"
218                        on("previous") {
219                                // handle form data
220                                studyPage(flow, flash, params)
221
222                                // reset errors
223                                flash.errors = [:]
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.errors = [:]
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.errors = [:]
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.errors = [:]
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.errors = [:]
354                                success()
355                        }.to "events"
356                        on("add") {
357                                // handle form data
358                                eventPage(flow, flash, params)
359
360                                // reset errors
361                                flash.errors = [:]
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.errors)
387                                        error()
388                                }
389                        }.to "events"
390                        on("deleteEvent") {
391                                // handle form data
392                                eventPage(flow, flash, params)
393
394                                // reset errors
395                                flash.errors = [:]
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.errors = [:]
432                                success()
433                        }.to "events"
434                        on("deleteEventGroup") {
435                                // handle form data
436                                eventPage(flow, flash, params)
437
438                                // reset errors
439                                flash.errors = [:]
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.errors = [:]
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.errors = [:]
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.errors = [:]
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.errors = [:]
586
587                                success()
588                        }.to "samples"
589                        on("previous") {
590                                // handle samples
591                                samplePage(flow, flash, params)
592
593                                // ignore errors
594                                flash.errors = [:]
595
596                                success()
597                        }.to "samplePrevious"
598                        on("next") {
599                                // handle form data
600                                samplePage(flow, flash, params) ? success() : error()
601                        }.to "assays"
602                        on("quickSave") {
603                                // handle form data
604                                samplePage(flow, flash, params) ? success() : error()
605                        }.to "waitForSave"
606                }
607
608                // assays 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.errors = [:]
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.errors = [:]
644                                success()
645                        }.to "assays"
646                        on("add") {
647                                // handle form data
648                                assayPage(flow, flash, params)
649
650                                // reset errors
651                                flash.errors = [:]
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.errors)
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.errors = [:]
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.errors = [:]
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.errors = [:]
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.errors = [:]
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.errors)
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.errors)
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) {
857//              def authenticationService
858               
859                flash.errors = 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                                return false
871                        }
872
873                        flow.study = study
874
875                        // set 'quicksave' variable
876                        flow.quickSave = true
877
878                        return true
879                } catch (Exception e) {
880                        // rollback
881                        this.appendErrorMap(['exception': e.getMessage() + ', see log for stacktrace'], flash.errors)
882
883                        return false
884                }
885        }
886
887        /**
888         * Handle the wizard study page
889         *
890         * @param Map LocalAttributeMap (the flow scope)
891         * @param Map localAttributeMap (the flash scope)
892         * @param Map GrailsParameterMap (the flow parameters = form data)
893         * @returns boolean
894         */
895        def studyPage(flow, flash, params) {
896                // remember the params in the flash scope
897                flash.values = params
898
899                // instantiate study of it is not yet present
900                if (!flow.study) flow.study = new Study()
901
902                // did the study template change?
903                if (params.get('template').size() && flow.study.template?.name != params.get('template')) {
904                        println ".change study template!"
905
906                        // yes, was the template already set?
907                        if (flow.study.template instanceof Template) {
908                                // yes, first make sure all values are unset?
909                                println "!!! check the database fields if data of a previous template remains in the database or is deleted by GORM!"
910                        }
911
912                        // set the template
913                        flow.study.template = Template.findByName(params.remove('template'))
914                }
915
916                // does the study have a template set?
917                if (flow.study.template && flow.study.template instanceof Template) {
918                        // yes, iterate through template fields
919                        flow.study.giveFields().each() {
920                                // and set their values
921                                flow.study.setFieldValue(it.name, params.get(it.escapedName()))
922                        }
923                }
924
925                // handle publications
926                handlePublications(flow, flash, params)
927
928                // handle contacts
929                handleContacts(flow, flash, params)
930
931                // handle users (readers, writers)
932                handleUsers(flow, flash, params, 'readers')
933                handleUsers(flow, flash, params, 'writers')
934
935                // handle public checkbox
936                if (params.get("publicstudy")) {
937                        flow.study.publicstudy = params.get("publicstudy")
938                }
939
940                // validate the study
941                if (flow.study.validate()) {
942                        // instance is okay
943                        return true
944                } else {
945                        // validation failed
946                        flash.errors = [:]
947                        this.appendErrors(flow.study, flash.errors)
948                        return false
949                }
950        }
951
952        /**
953         * re-usable code for handling publications form data in a web flow
954         * @param Map LocalAttributeMap (the flow scope)
955         * @param Map localAttributeMap (the flash scope)
956         * @param Map GrailsParameterMap (the flow parameters = form data)
957         * @returns boolean
958         */
959        def handlePublications(flow, flash, params) {
960                if (!flow.study.publications) flow.study.publications = []
961
962                // Check the ids of the pubblications that should be attached
963                // to this study. If they are already attached, keep 'm. If
964                // studies are attached that are not in the selected (i.e. the
965                // user deleted them), remove them
966                def publicationIDs = params.get('publication_ids')
967                if (publicationIDs) {
968                        // Find the individual IDs and make integers
969                        publicationIDs = publicationIDs.split(',').collect { Integer.parseInt(it, 10) }
970
971                        // First remove the publication that are not present in the array
972                        flow.study.publications.removeAll { publication -> !publicationIDs.find { id -> id == publication.id } }
973
974                        // Add those publications not yet present in the database
975                        publicationIDs.each { id ->
976                                if (!flow.study.publications.find { publication -> id == publication.id }) {
977                                        def publication = Publication.get(id)
978                                        if (publication) {
979                                                flow.study.addToPublications(publication)
980                                        } else {
981                                                println('.publication with ID ' + id + ' not found in database.')
982                                        }
983                                }
984                        }
985
986                } else {
987                        println('.no publications selected.')
988                        flow.study.publications.clear()
989                }
990
991        }
992
993        /**
994         * re-usable code for handling contacts form data in a web flow
995         * @param Map LocalAttributeMap (the flow scope)
996         * @param Map localAttributeMap (the flash scope)
997         * @param Map GrailsParameterMap (the flow parameters = form data)
998         * @return boolean
999         */
1000        def handleContacts(flow, flash, params) {
1001                if (!flow.study.persons) flow.study.persons = []
1002
1003                // Check the ids of the contacts that should be attached
1004                // to this study. If they are already attached, keep 'm. If
1005                // studies are attached that are not in the selected (i.e. the
1006                // user deleted them), remove them
1007
1008                // Contacts are saved as [person_id]-[role_id]
1009                def contactIDs = params.get('contacts_ids')
1010                if (contactIDs) {
1011                        // Find the individual IDs and make integers
1012                        contactIDs = contactIDs.split(',').collect {
1013                                def parts = it.split('-')
1014                                return [person: Integer.parseInt(parts[0]), role: Integer.parseInt(parts[1])]
1015                        }
1016
1017                        // First remove the contacts that are not present in the array
1018                        flow.study.persons.removeAll {
1019                                studyperson -> !contactIDs.find { ids -> (ids.person == studyperson.person.id) && (ids.role == studyperson.role.id) }
1020                        }
1021
1022                        // Add those contacts not yet present in the database
1023                        contactIDs.each { ids ->
1024                                if (!flow.study.persons.find { studyperson -> (ids.person == studyperson.person.id) && (ids.role == studyperson.role.id) }) {
1025                                        def person = Person.get(ids.person)
1026                                        def role = PersonRole.get(ids.role)
1027                                        if (person && role) {
1028                                                // Find a studyperson object with these parameters
1029                                                def studyPerson = StudyPerson.findAll().find { studyperson -> studyperson.person.id == person.id && studyperson.role.id == role.id }
1030
1031                                                // If if does not yet exist, save the example
1032                                                if (!studyPerson) {
1033                                                        studyPerson = new StudyPerson(
1034                                                                person: person,
1035                                                                role: role
1036                                                        )
1037                                                        studyPerson.save(flush: true)
1038                                                }
1039
1040                                                flow.study.addToPersons(studyPerson)
1041                                        } else {
1042                                                println('.person ' + ids.person + ' or Role ' + ids.role + ' not found in database.')
1043                                        }
1044                                }
1045                        }
1046                } else {
1047                        println('.no persons selected.')
1048                        flow.study.persons.clear()
1049                }
1050
1051        }
1052
1053        /**
1054         * re-usable code for handling contacts form data in a web flow
1055         * @param Map LocalAttributeMap (the flow scope)
1056         * @param Map localAttributeMap (the flash scope)
1057         * @param Map GrailsParameterMap (the flow parameters = form data)
1058         * @param String    'readers' or 'writers'
1059         * @return boolean
1060         */
1061        def handleUsers(flow, flash, params, type) {
1062                def users = []
1063
1064                if (type == "readers") {
1065                        users = flow.study.readers ?: []
1066                } else if (type == "writers") {
1067                        users = flow.study.writers ?: []
1068                }
1069
1070                // Check the ids of the contacts that should be attached
1071                // to this study. If they are already attached, keep 'm. If
1072                // studies are attached that are not in the selected (i.e. the
1073                // user deleted them), remove them
1074
1075                // Users are saved as user_id
1076                def userIDs = params.get( type + '_ids')
1077                if (userIDs) {
1078                        // Find the individual IDs and make integers
1079                        userIDs = userIDs.split(',').collect { Integer.parseInt(it, 10) }
1080
1081                        // First remove the publication that are not present in the array
1082                        users.removeAll { user -> !userIDs.find { id -> id == user.id } }
1083
1084                        // Add those publications not yet present in the database
1085                        userIDs.each { id ->
1086                                if (!users.find { user -> id == user.id }) {
1087                                        def user = SecUser.get(id)
1088                                        if (user) {
1089                                            users.add(user)
1090                                        } else {
1091                                                println('.user with ID ' + id + ' not found in database.')
1092                                        }
1093                                }
1094                        }
1095
1096                } else {
1097                        println('.no users selected.')
1098                        users.clear()
1099                }
1100
1101                if (type == "readers") {
1102                        if (flow.study.readers)
1103                        flow.study.readers.clear()
1104                        users.each { flow.study.addToReaders(it) }
1105                } else if (type == "writers") {
1106                        if (flow.study.writers)
1107                        flow.study.writers.clear()
1108
1109                        users.each { flow.study.addToWriters(it) }
1110                }
1111        }
1112
1113        /**
1114         * Handle the wizard subject page
1115         *
1116         * @param Map LocalAttributeMap (the flow scope)
1117         * @param Map localAttributeMap (the flash scope)
1118         * @param Map GrailsParameterMap (the flow parameters = form data)
1119         * @returns boolean
1120         */
1121        def subjectPage(flow, flash, params) {
1122                def errors = false
1123                flash.errors = [:]
1124
1125                // remember the params in the flash scope
1126                flash.values = params
1127
1128                // iterate through subjects
1129                flow.study.subjects.each() { subject ->
1130                        // iterate through (template and domain) fields
1131                        subject.giveFields().each() { field ->
1132                                // set field
1133                                subject.setFieldValue(
1134                                        field.name,
1135                                        params.get('subject_' + subject.getIdentifier() + '_' + field.escapedName())
1136                                )
1137                        }
1138
1139                        // validate subject
1140                        if (!subject.validate()) {
1141                                errors = true
1142                                this.appendErrors(subject, flash.errors, 'subject_' + subject.getIdentifier() + '_')
1143                        }
1144                }
1145
1146                return !errors
1147        }
1148
1149        /**
1150         * Add a number of subjects to a study
1151         *
1152         * required params entities:
1153         * -addNumber (int)
1154         * -species   (string)
1155         * -template  (string)
1156         *
1157         * @param Map LocalAttributeMap (the flow scope)
1158         * @param Map localAttributeMap (the flash scope)
1159         * @param Map GrailsParameterMap (the flow parameters = form data)
1160         * @returns boolean
1161         */
1162        def addSubjects(flow, flash, params) {
1163                // remember the params in the flash scope
1164                flash.values = params
1165
1166                // handle the subject page
1167                subjectPage(flow, flash, params)
1168
1169                // (re)set error message
1170                flash.errors = [:]
1171
1172                // set work variables
1173                def errors              = false
1174                def number              = params.get('addNumber') as int
1175                def species             = Term.findByName(params.get('species'))
1176                def template    = Template.findByName(params.get('template'))
1177
1178                // can we add subjects?
1179                if (number > 0 && species && template) {
1180                        // add subjects to study
1181                        number.times {
1182                                // work variables
1183                                def subjectName = 'Subject '
1184                                def subjectIterator = 1
1185                                def tempSubjectName = subjectName + subjectIterator
1186
1187                                // make sure subject name is unique
1188                                if (flow.study.subjects) {
1189                                        while (flow.study.subjects.find { it.name == tempSubjectName }) {
1190                                                subjectIterator++
1191                                                tempSubjectName = subjectName + subjectIterator
1192                                        }
1193                                }
1194                                subjectName = tempSubjectName
1195
1196                                // create a subject instance
1197                                def subject = new Subject(
1198                                        name            : subjectName,
1199                                        species         : species,
1200                                        template        : template
1201                                )
1202
1203                                // add it to the study
1204                                flow.study.addToSubjects( subject )
1205
1206                                // validate subject
1207                                if (subject.validate()) {
1208                                        println ".added subject "+subject
1209                                } else {
1210                                        // whoops?
1211                                        flow.study.removeFromSubjects( subject )
1212
1213                                        // append errors
1214                                        this.appendErrors(subject, flash.errors)
1215                                        errors = true
1216                                }
1217                        }
1218                } else {
1219                        // add feedback
1220                        errors = true
1221                        if (number < 1) this.appendErrorMap(['addNumber': 'Enter a positive number of subjects to add'], flash.errors)
1222                        if (!species)   this.appendErrorMap(['species': 'You need to select a species, or add one if it is not yet present'], flash.errors)
1223                        if (!template)  this.appendErrorMap(['template': 'You need to select a template, or add one if it is not yet present'], flash.errors)
1224                }
1225
1226                return !errors
1227        }
1228
1229        /**
1230         * Handle the wizard event page
1231         *
1232         * @param Map LocalAttributeMap (the flow scope)
1233         * @param Map localAttributeMap (the flash scope)
1234         * @param Map GrailsParameterMap (the flow parameters = form data)
1235         * @returns boolean
1236         */
1237        def eventPage(flow, flash, params) {
1238                def errors = false
1239                flash.errors = [:]
1240
1241                // remember the params in the flash scope
1242                flash.values = params
1243
1244                // handle the 'add event' form
1245                if (flow.event) {
1246                        flow.event.giveFields().each() { field ->
1247                                // set field
1248                                flow.event.setFieldValue(
1249                                        field.name,
1250                                        params.get(field.escapedName())
1251                                )
1252                        }
1253                }
1254
1255                // handle the eventGroup names and grouping
1256                def name        = ""
1257                def tempName= ""
1258                flow.study.eventGroups.each() { eventGroup ->
1259                        // iterate through templates
1260                        flow.study.giveAllEventTemplates().each() { template ->
1261                                tempName = params.get( 'eventGroup_' + eventGroup.getIdentifier() + '_' + template.getIdentifier() )
1262
1263                                // is the name different?
1264                                if (tempName != eventGroup.name) {
1265                                        name = tempName
1266                                }
1267                        }
1268
1269                        // should the name change?
1270                        if (name) {
1271                                // yes, change it
1272                                eventGroup.name = name
1273                                name = ""
1274                        }
1275
1276                        // handle eventGrouping
1277                        ( ((flow.study.events) ? flow.study.events : []) + ((flow.study.samplingEvents) ? flow.study.samplingEvents : []) ) .each() { event ->
1278                                if (params.get( 'event_' + event.getIdentifier() + '_group_' + eventGroup.getIdentifier() )) {
1279                                        // add to eventGroup
1280                                        if (event instanceof SamplingEvent) {
1281                                                // check if we are already in this eventGroup
1282                                                if (!eventGroup.samplingEvents.find { it.equals(event) }) {
1283                                                        // no, add it
1284                                                        eventGroup.addToSamplingEvents(event)
1285
1286                                                        // iterate through subjects for this eventGroup
1287                                                        eventGroup.subjects.each() { subject ->
1288                                                                // instantiate a sample for this subject / event
1289                                                                def samplingEventName = this.ucwords(event.template.name)
1290                                                                def sampleName = (this.ucwords(subject.name) + '_' + samplingEventName + '_' + new RelTime(event.startTime).toString()).replaceAll("([ ]{1,})", "")
1291                                                                def tempSampleIterator = 0
1292                                                                def tempSampleName = sampleName
1293
1294                                                                // make sure sampleName is unique
1295                                                                if (flow.study.samples) {
1296                                                                        while (flow.study.samples.find { it.name == tempSampleName }) {
1297                                                                                tempSampleIterator++
1298                                                                                tempSampleName = sampleName + "_" + tempSampleIterator
1299                                                                        }
1300                                                                        sampleName = tempSampleName
1301                                                                }
1302
1303                                                                // instantiate a sample
1304                                                                flow.study.addToSamples(
1305                                                                        new Sample(
1306                                                                                parentSubject   : subject,
1307                                                                                parentEvent             : event,
1308                                                                                parentEventGroup: eventGroup,
1309                                                                                name                    : sampleName,
1310                                                                                template                : (event.sampleTemplate) ? event.sampleTemplate : ''
1311                                                                        )
1312                                                                )
1313                                                        }
1314                                                }
1315                                        } else {
1316                                                eventGroup.addToEvents(event)
1317                                        }
1318                                } else {
1319                                        // remove from eventGroup
1320                                        if (event instanceof SamplingEvent) {
1321                                                // iterate through subjects (if we have them)
1322                                                eventGroup.subjects.each() { subject ->
1323                                                        // find all samples for this subject / event
1324                                                        flow.study.samples.findAll { (it.parentEvent.equals(event) && it.parentSubject.equals(subject) ) }.each() {
1325                                                                // delete this sample
1326                                                                flow.study.removeFromSamples( it )
1327                                                                it.delete()
1328                                                        }
1329                                                }
1330
1331                                                eventGroup.removeFromSamplingEvents(event)
1332                                        } else {
1333                                                eventGroup.removeFromEvents(event)
1334                                        }
1335                                }
1336                        }
1337                }
1338
1339                // handle the (sampling) events
1340                ( ((flow.study.events) ? flow.study.events : []) + ((flow.study.samplingEvents) ? flow.study.samplingEvents : []) ) .each() { event ->
1341                        event.giveFields().each() { field ->
1342                                event.setFieldValue(
1343                                        field.name,
1344                                        params.get( 'event_' + event.getIdentifier() + '_' + field.escapedName() )
1345                                )
1346                        }
1347
1348                        // validate event
1349                        if (!event.validate()) {
1350                                errors = true
1351                                this.appendErrors(event, flash.errors)
1352                        }
1353                }
1354
1355                return !errors
1356        }
1357
1358        /**
1359         * Handle the wizard group page
1360         *
1361         * @param Map LocalAttributeMap (the flow scope)
1362         * @param Map localAttributeMap (the flash scope)
1363         * @param Map GrailsParameterMap (the flow parameters = form data)
1364         * @returns boolean
1365         */
1366        def groupPage(flow, flash, params) {
1367                def errors = false
1368                flash.errors = [:]
1369
1370                // remember the params in the flash scope
1371                flash.values = params
1372
1373                // iterate through groups
1374                flow.study.eventGroups.each() { eventGroup ->
1375                        // iterate through subjects
1376                        flow.study.subjects.each() { subject ->
1377                                if (params.get('subject_' + subject.getIdentifier() + '_group_' + eventGroup.getIdentifier() )) {
1378                                        // check if this subject is already part of this eventGroup
1379                                        if ( !eventGroup.subjects.find { it.equals(subject) } ) {
1380                                                // add to eventGroup
1381                                                eventGroup.addToSubjects(subject)
1382
1383                                                // iterate through samplingEvents
1384                                                eventGroup.samplingEvents.each() { samplingEvent ->
1385                                                        def samplingEventName = this.ucwords(samplingEvent.template.name)
1386                                                        def sampleName = (this.ucwords(subject.name) + '_' + samplingEventName + '_' + new RelTime(samplingEvent.startTime).toString()).replaceAll("([ ]{1,})", "")
1387                                                        def tempSampleIterator = 0
1388                                                        def tempSampleName = sampleName
1389
1390                                                        // make sure sampleName is unique
1391                                                        if (flow.study.samples) {
1392                                                                while (flow.study.samples.find { it.name == tempSampleName }) {
1393                                                                        tempSampleIterator++
1394                                                                        tempSampleName = sampleName + "_" + tempSampleIterator
1395                                                                }
1396                                                                sampleName = tempSampleName
1397                                                        }
1398
1399                                                        // instantiate a sample
1400                                                        flow.study.addToSamples(
1401                                                                new Sample(
1402                                                                        parentSubject   : subject,
1403                                                                        parentEvent             : samplingEvent,
1404                                                                        parentEventGroup: eventGroup,
1405                                                                        name                    : sampleName,
1406                                                                        template                : (samplingEvent.sampleTemplate) ? samplingEvent.sampleTemplate : ''
1407                                                                )
1408                                                        )
1409                                                }
1410                                        }
1411                                } else {
1412                                        // remove from eventGroup
1413                                        eventGroup.removeFromSubjects(subject)
1414
1415                                        // iterate through samplingEvents
1416                                        eventGroup.samplingEvents.each() { samplingEvent ->
1417                                                flow.study.samples.findAll { ( it.parentEvent.equals(samplingEvent) && it.parentSubject.equals(subject) ) }.each() {
1418                                                        // delete this sample
1419                                                        flow.study.removeFromSamples( it )
1420                                                        it.delete()
1421                                                }
1422                                        }
1423                                }
1424                        }
1425                }
1426        }
1427
1428        /**
1429         * Handle the wizard samples page
1430         *
1431         * @param Map LocalAttributeMap (the flow scope)
1432         * @param Map localAttributeMap (the flash scope)
1433         * @param Map GrailsParameterMap (the flow parameters = form data)
1434         * @returns boolean
1435         */
1436        def samplePage(flow, flash, params) {
1437                def errors = false
1438                flash.errors = [:]
1439
1440                // remember the params in the flash scope
1441                flash.values = params
1442
1443                // iterate through samples
1444                flow.study.samples.each() { sample ->
1445                        // iterate through sample fields
1446                        sample.giveFields().each() { field ->
1447                                def value = params.get('sample_'+sample.getIdentifier()+'_'+field.escapedName())
1448
1449                                // set field value
1450                                if (!(field.name == 'name' && !value)) {
1451                                        println "setting "+field.name+" to "+value
1452                                        sample.setFieldValue(field.name, value)
1453                                }
1454                        }
1455
1456                        // has the template changed?
1457                        def templateName = params.get('template_' + sample.getIdentifier())
1458                        if (templateName && sample.template?.name != templateName) {
1459                                sample.template = Template.findByName(templateName)
1460                        }
1461
1462                        // validate sample
1463                        if (!sample.validate()) {
1464                                errors = true
1465                                this.appendErrors(sample, flash.errors, 'sample_' + sample.getIdentifier() + '_' )
1466                                println 'error-> sample_'+sample.getIdentifier()
1467                        }
1468                }
1469
1470                return !errors
1471        }
1472
1473        /**
1474         * Handle the wizard assays page
1475         *
1476         * @param Map LocalAttributeMap (the flow scope)
1477         * @param Map localAttributeMap (the flash scope)
1478         * @param Map GrailsParameterMap (the flow parameters = form data)
1479         * @returns boolean
1480         */
1481        def assayPage(flow, flash, params) {
1482                def errors = false
1483                flash.errors = [:]
1484
1485                // remember the params in the flash scope
1486                flash.values = params
1487
1488                // handle the 'add assay' form
1489                if (flow.assay) {
1490                        flow.assay.giveFields().each() { field ->
1491                                // set field
1492                                flow.assay.setFieldValue(
1493                                        field.name,
1494                                        params.get(field.escapedName())
1495                                )
1496                        }
1497                }
1498
1499                // handle the assay data
1500                flow.study.assays.each() { assay ->
1501                        // set data
1502                        assay.giveFields().each() { field ->
1503                                assay.setFieldValue(
1504                                        field.name,
1505                                        params.get( 'assay_' + assay.getIdentifier() + '_' + field.escapedName() )
1506                                )
1507                        }
1508
1509                        // validate assay
1510                        if (!assay.validate()) {
1511                                errors = true
1512                                this.appendErrors(assay, flash.errors, 'assay_' + assay.getIdentifier() + '_')
1513                        }
1514                }
1515
1516                return !errors
1517        }
1518
1519        /**
1520         * Handle the wizard assayGroups page
1521         *
1522         * @param Map LocalAttributeMap (the flow scope)
1523         * @param Map localAttributeMap (the flash scope)
1524         * @param Map GrailsParameterMap (the flow parameters = form data)
1525         * @returns boolean
1526         */
1527        def assayGroupPage(flow, flash, params) {
1528                def errors = false
1529                flash.errors = [:]
1530
1531                // remember the params in the flash scope
1532                flash.values = params
1533
1534                // iterate through samples
1535                flow.study.samples.each() { sample ->
1536                        // iterate through assays
1537                        flow.study.assays.each() { assay ->
1538                                if (params.get( 'sample_' + sample.getIdentifier() + '_assay_' + assay.getIdentifier() )) {
1539                                        println "add sample "+sample.getIdentifier()+" to assay "+assay.getIdentifier()
1540                                        // add sample to assay
1541                                        assay.addToSamples( sample )
1542                                } else {
1543                                        // remove sample from assay
1544                                        assay.removeFromSamples( sample )
1545                                }
1546                                println assay.samples
1547                        }
1548                }
1549
1550                return !errors
1551        }
1552
1553        /**
1554         * groovy / java equivalent of php's ucwords function
1555         *
1556         * Capitalize all first letters of separate words
1557         *
1558         * @param String
1559         * @return String
1560         */
1561        def ucwords(String text) {
1562                def newText = ''
1563
1564                // change case to lowercase
1565                text = text.toLowerCase()
1566
1567                // iterate through words
1568                text.split(" ").each() {
1569                        newText += it[0].toUpperCase() + it.substring(1) + " "
1570                }
1571
1572                return newText.substring(0, newText.size()-1)
1573        }
1574
1575        /**
1576         * return the object from a map of objects by searching for a name
1577         * @param String name
1578         * @param Map map of objects
1579         * @return Object
1580         */
1581        def getObjectByName(name, map) {
1582                def result = null
1583                map.each() {
1584                        if (it.name == name) {
1585                                result = it
1586                        }
1587                }
1588
1589                return result
1590        }
1591
1592        /**
1593         * transform domain class validation errors into a human readable
1594         * linked hash map
1595         * @param object validated domain class
1596         * @return object  linkedHashMap
1597         */
1598        def getHumanReadableErrors(object) {
1599                def errors = [:]
1600                object.errors.getAllErrors().each() {
1601                        def message = it.toString()
1602
1603                        //errors[it.getArguments()[0]] = it.getDefaultMessage()
1604                        errors[it.getArguments()[0]] = message.substring(0, message.indexOf(';'))
1605                }
1606
1607                return errors
1608        }
1609
1610        /**
1611         * append errors of a particular object to a map
1612         * @param object
1613         * @param map linkedHashMap
1614         * @void
1615         */
1616        def appendErrors(object, map) {
1617                this.appendErrorMap(this.getHumanReadableErrors(object), map)
1618        }
1619
1620        def appendErrors(object, map, prepend) {
1621                this.appendErrorMap(this.getHumanReadableErrors(object), map, prepend)
1622        }
1623
1624        /**
1625         * append errors of one map to another map
1626         * @param map linkedHashMap
1627         * @param map linkedHashMap
1628         * @void
1629         */
1630        def appendErrorMap(map, mapToExtend) {
1631                map.each() {key, value ->
1632                        mapToExtend[key] = ['key': key, 'value': value, 'dynamic': false]
1633                }
1634        }
1635
1636        def appendErrorMap(map, mapToExtend, prepend) {
1637                map.each() {key, value ->
1638                        mapToExtend[prepend + key] = ['key': key, 'value': value, 'dynamic': true]
1639                }
1640        }
1641
1642        /**
1643         * Parses a RelTime string and returns a nice human readable string
1644         *
1645         * @return Human Readable string or a HTTP response code 400 on error
1646         */
1647        def ajaxParseRelTime = {
1648                if (params.reltime == null) {
1649                        response.status = 400
1650                        render('reltime parameter is expected')
1651                }
1652
1653                try {
1654                        def reltime = RelTime.parseRelTime(params.reltime)
1655                        render reltime.toPrettyString()
1656                } catch (IllegalArgumentException e) {
1657                        response.status = 400
1658                        render(e.getMessage())
1659                }
1660        }
1661
1662        /**
1663         * Proxy for searching PubMed articles (or other articles from the Entrez DB).
1664         *
1665         * This proxy is needed because it is not allowed to fetch XML directly from a different
1666         * domain using javascript. So we have the javascript call a function on our own domain
1667         * and the proxy will fetch the data from Entrez
1668         *
1669         * @since       20100609
1670         * @param       _utility        The name of the utility, without the complete path. Example: 'esearch.fcgi'
1671         * @return      XML
1672         */
1673        def entrezProxy = {
1674                // Remove unnecessary parameters
1675                params.remove( "action" )
1676                params.remove( "controller" )
1677
1678                def url = "http://eutils.ncbi.nlm.nih.gov/entrez/eutils";
1679                def util = params.remove( "_utility" )
1680                def paramString = params.collect { k, v -> k + '=' + v.encodeAsURL() }.join( '&' );
1681
1682                def fullUrl = url + '/' + util + '?' + paramString;
1683
1684                // Return the output of the request
1685                // render fullUrl;
1686                render(
1687                    text:           new URL( fullUrl ).getText(),
1688                    contentType:    "text/xml",
1689                    encoding:       "UTF-8"
1690                );
1691        }
1692}
Note: See TracBrowser for help on using the repository browser.