root/trunk/grails-app/controllers/dbnp/studycapturing/WizardController.groovy @ 1162

Revision 1162, 44.1 KB (checked in by work@…, 3 years ago)

- resolves improvement #1, generating an assay id suggestion in the assay page

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