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

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