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

Last change on this file since 1155 was 1155, checked in by work@…, 11 years ago
  • resolving issue #195, sample generation madness. This was partly caused by the intoduction of ParentEvent? and only happened in strange situations which normally should not happen in a serious study. But of course, it should never happen.
  • also extended the sample name with eventGroupName
  • Property svn:keywords set to Author Date Rev
File size: 43.7 KB
Line 
1package dbnp.studycapturing
2
3import dbnp.data.*
4
5// Grails convertors is imported in order to create JSON objects
6import grails.converters.*
7import grails.plugins.springsecurity.Secured
8import dbnp.authentication.AuthenticationService
9import dbnp.authentication.SecUser
10
11
12/**
13 * Wizard Controler
14 *
15 * The wizard controller handles the handeling of pages and data flow
16 * through the study capturing wizard.
17 *
18 * This controller is only accessible by logged in users.
19 *
20 * @author Jeroen Wesbeek
21 * @since 20100107
22 * @package studycapturing
23 *
24 * Revision information:
25 * $Rev: 1155 $
26 * $Author: work@osx.eu $
27 * $Date: 2010-11-17 13:12:48 +0000 (wo, 17 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                        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
654                                if (flow.assay) {
655                                        // set template
656                                        flow.assay.template = template
657                                } else {
658                                        // create a new assay instance
659                                        flow.assay = new Assay(template: template)
660                                }
661
662                                // reset errors
663                                flash.wizardErrors = [:]
664                                success()
665                        }.to "assays"
666                        on("add") {
667                                // handle form data
668                                assayPage(flow, flash, params)
669
670                                // reset errors
671                                flash.wizardErrors = [:]
672
673                                // add assay to study
674                                flow.study.addToAssays( flow.assay )
675
676                                // validate assay
677                                if (flow.assay.validate()) {
678                                        // remove assay from the flowscope
679                                        flow.remove('assay')
680                                        success()
681                                } else {
682                                        // assay does not validate
683                                        // remove from study
684                                        flow.study.removeFromAssays( flow.assay )
685
686                                        // append errors
687                                        this.appendErrors(flow.assay, flash.wizardErrors)
688                                        error()
689                                }
690                        }.to "assays"
691                        on("deleteAssay") {
692                                println params
693
694                                // handle form data
695                                assayPage(flow, flash, params)
696
697                                // reset errors
698                                flash.wizardErrors = [:]
699
700                                // find this assay
701                                def assay = flow.study.assays.find { it.getIdentifier() == (params.get('do') as int) }
702
703                                // perform delete
704                                if (assay) flow.study.deleteAssay( assay )
705                        }.to "assays"
706                        on("previous") {
707                                // handle form data
708                                assayPage(flow, flash, params)
709
710                                // ignore errors
711                                flash.wizardErrors = [:]
712
713                                success()
714                        }.to "samples"
715                        on("next") {
716                                // handle form data
717                                assayPage(flow, flash, params) ? success() : error()
718                        }.to "assayNext"
719                        on("quickSave") {
720                                // handle form data
721                                assayPage(flow, flash, params) ? success() : error()
722                        }.to "waitForSave"
723                }
724
725                // assayNext
726                assayNext {
727                        action {
728                                log.info ".entering assayNext"
729
730                                // have we got samples and assays?
731                                if (flow.study.assays && flow.study.samples) {
732                                        // yes, go to the group page
733                                        toAssayGroups()
734                                } else {
735                                        // no need to show the group page as
736                                        // there's nothing to group
737                                        toConfirm()
738                                }
739                        }
740                        on("toAssayGroups").to "assayGroups"
741                        on("toConfirm").to "confirm"
742                }
743
744                // assay grouping page
745                assayGroups {
746                        render(view: "_assay_groups")
747                        onRender {
748                                log.info ".entering assayGroups"
749
750                                flow.page = 5
751                        }
752                        on("previous") {
753                                // handle form data
754                                assayGroupPage(flow, flash, params)
755
756                                // ignore errors
757                                flash.wizardErrors = [:]
758
759                                success()
760                        }.to "assays"
761                        on("next") {
762                                // handle form data
763                                assayGroupPage(flow, flash, params) ? success() : error()
764                        }.to "confirm"
765                        on("quickSave") {
766                                // handle form data
767                                assayGroupPage(flow, flash, params) ? success() : error()
768                        }.to "waitForSave"
769                }
770
771                // confirm Previous
772                confirmPrevious {
773                        action {
774                                log.info ".entering confirmPrevious"
775
776                                // have we got samples and assays?
777                                if (flow.study.assays && flow.study.samples) {
778                                        // yes, go to the group page
779                                        toAssayGroups()
780                                } else {
781                                        // no need to show the group page as
782                                        // there's nothing to group
783                                        toAssays()
784                                }
785                        }
786                        on("toAssayGroups").to "assayGroups"
787                        on("toAssays").to "assays"
788                }
789
790                // confirmation
791                confirm {
792                        render(view: "_confirmation")
793                        onRender {
794                                log.info ".entering confirm"
795
796                                flow.page = 6
797                        }
798                        on("toStudy").to "study"
799                        on("toSubjects").to "subjects"
800                        on("toEvents").to "events"
801                        on("toGroups").to "groups"
802                        on("toSamples").to "samples"
803                        on("toAssays").to "assays"
804                        on("toAssayGroups").to "assayGroups"
805                        on("previous").to "confirmPrevious"
806                        on("next").to "waitForSave"
807                        on("quickSave").to "waitForSave"
808                }
809
810                waitForSave {
811                        render(view: "_wait")
812                        onRender {
813                                log.info ".entering waitForSave"
814
815                                flow.page = 7
816                        }
817                        on("next").to "save"
818                }
819
820                // store all study data
821                save {
822                        action {
823                                log.info ".entering save"
824
825                                flash.wizardErrors = [:]
826
827                                // persist data to the database
828                                try {
829                                        // save study
830                                        log.info ".saving study"
831
832                                        // Make sure the owner of the study is set right
833                                        flow.study.owner = authenticationService.getLoggedInUser()
834
835                                        if (!flow.study.save(flush:true)) {
836                                                this.appendErrors(flow.study, flash.wizardErrors)
837                                                throw new Exception('error saving study')
838                                        }
839                                        log.info ".saved study "+flow.study+" (id: "+flow.study.id+")"
840
841                                        success()
842                                } catch (Exception e) {
843                                        // rollback
844                                        this.appendErrorMap(['exception': e.toString() + ', see log for stacktrace' ], flash.wizardErrors)
845
846                                        // stacktrace in flash scope
847                                        flash.debug = e.getStackTrace()
848
849                                        error()
850                                }
851                        }
852                        on("error").to "error"
853                        on(Exception).to "error"
854                        on("success").to "done"
855                }
856
857                // error storing data
858                error {
859                        render(view: "_error")
860                        onRender {
861                                log.info ".entering error"
862
863                                flow.page = 6
864                        }
865                        on("next").to "waitForSave"
866                        on("previous").to "samples"
867                }
868
869                // render finish page
870                done {
871                        render(view: "_done")
872                        onRender {
873                                log.info ".entering done"
874
875                                flow.page = 7
876                        }
877                        onEnd {
878                                // clean flow scope
879                                flow.clear()
880                        }
881                }
882        }
883
884        /**
885         * load a study
886         * @param Map LocalAttributeMap (the flow scope)
887         * @param Map localAttributeMap (the flash scope)
888         * @param Map GrailsParameterMap (the flow parameters = form data)
889         * @returns boolean
890         */
891        def loadStudy(flow, flash, params, user) {
892//              def authenticationService
893
894                flash.wizardErrors = new LinkedHashMap()
895               
896                // load study
897                try {
898                        // load study
899                        def study = (params.studyid) ? Study.findById( params.studyid ) : Study.findByTitle( params.study )
900
901                        // Check whether the user is allowed to edit this study. If it is not allowed
902                        // the used should had never seen a link to this page, so he should never get
903                        // here. That's why we just return false
904//                      if (!study.canWrite(authenticationService.getLoggedInUser())) {
905            if (!study.canWrite(user)){
906                                return false
907                        }
908
909                        flow.study = study
910
911                        // set 'quicksave' variable
912                        flow.quickSave = true
913
914                        return true
915                } catch (Exception e) {
916                        // rollback
917                        this.appendErrorMap(['exception': e.getMessage() + ', see log for stacktrace'], flash.wizardErrors)
918
919                        return false
920                }
921        }
922
923        /**
924         * Handle the wizard study page
925         *
926         * @param Map LocalAttributeMap (the flow scope)
927         * @param Map localAttributeMap (the flash scope)
928         * @param Map GrailsParameterMap (the flow parameters = form data)
929         * @returns boolean
930         */
931        def studyPage(flow, flash, params) {
932                // remember the params in the flash scope
933                flash.values = params
934
935                // instantiate study of it is not yet present
936                if (!flow.study) flow.study = new Study()
937
938                // did the study template change?
939                if (params.get('template').size() && flow.study.template?.name != params.get('template')) {
940                        println ".change study template!"
941
942                        // yes, was the template already set?
943                        if (flow.study.template instanceof Template) {
944                                // yes, first make sure all values are unset?
945                                println "!!! check the database fields if data of a previous template remains in the database or is deleted by GORM!"
946                        }
947
948                        // set the template
949                        flow.study.template = Template.findByName(params.remove('template'))
950                }
951
952                // does the study have a template set?
953                if (flow.study.template && flow.study.template instanceof Template) {
954                        // yes, iterate through template fields
955                        flow.study.giveFields().each() {
956                                // and set their values
957                                flow.study.setFieldValue(it.name, params.get(it.escapedName()))
958                        }
959                }
960
961                // handle publications
962                handlePublications(flow, flash, params)
963
964                // handle contacts
965                handleContacts(flow, flash, params)
966
967                // handle users (readers, writers)
968                handleUsers(flow, flash, params, 'readers')
969                handleUsers(flow, flash, params, 'writers')
970
971                // handle public checkbox
972                if (params.get("publicstudy")) {
973                        flow.study.publicstudy = params.get("publicstudy")
974                }
975
976                // validate the study
977                if (flow.study.validate()) {
978                        // instance is okay
979                        return true
980                } else {
981                        // validation failed
982                        flash.wizardErrors = [:]
983                        this.appendErrors(flow.study, flash.wizardErrors)
984                        return false
985                }
986        }
987
988        /**
989         * re-usable code for handling publications form data in a web flow
990         * @param Map LocalAttributeMap (the flow scope)
991         * @param Map localAttributeMap (the flash scope)
992         * @param Map GrailsParameterMap (the flow parameters = form data)
993         * @returns boolean
994         */
995        def handlePublications(flow, flash, params) {
996                if (!flow.study.publications) flow.study.publications = []
997
998                // Check the ids of the pubblications that should be attached
999                // to this study. If they are already attached, keep 'm. If
1000                // studies are attached that are not in the selected (i.e. the
1001                // user deleted them), remove them
1002                def publicationIDs = params.get('publication_ids')
1003                if (publicationIDs) {
1004                        // Find the individual IDs and make integers
1005                        publicationIDs = publicationIDs.split(',').collect { Integer.parseInt(it, 10) }
1006
1007                        // First remove the publication that are not present in the array
1008                        flow.study.publications.removeAll { publication -> !publicationIDs.find { id -> id == publication.id } }
1009
1010                        // Add those publications not yet present in the database
1011                        publicationIDs.each { id ->
1012                                if (!flow.study.publications.find { publication -> id == publication.id }) {
1013                                        def publication = Publication.get(id)
1014                                        if (publication) {
1015                                                flow.study.addToPublications(publication)
1016                                        } else {
1017                                                log.info('.publication with ID ' + id + ' not found in database.')
1018                                        }
1019                                }
1020                        }
1021
1022                } else {
1023                        log.info('.no publications selected.')
1024                        flow.study.publications.clear()
1025                }
1026
1027        }
1028
1029        /**
1030         * re-usable code for handling contacts form data in a web flow
1031         * @param Map LocalAttributeMap (the flow scope)
1032         * @param Map localAttributeMap (the flash scope)
1033         * @param Map GrailsParameterMap (the flow parameters = form data)
1034         * @return boolean
1035         */
1036        def handleContacts(flow, flash, params) {
1037                if (!flow.study.persons) flow.study.persons = []
1038
1039                // Check the ids of the contacts that should be attached
1040                // to this study. If they are already attached, keep 'm. If
1041                // studies are attached that are not in the selected (i.e. the
1042                // user deleted them), remove them
1043
1044                // Contacts are saved as [person_id]-[role_id]
1045                def contactIDs = params.get('contacts_ids')
1046                if (contactIDs) {
1047                        // Find the individual IDs and make integers
1048                        contactIDs = contactIDs.split(',').collect {
1049                                def parts = it.split('-')
1050                                return [person: Integer.parseInt(parts[0]), role: Integer.parseInt(parts[1])]
1051                        }
1052
1053                        // First remove the contacts that are not present in the array
1054                        flow.study.persons.removeAll {
1055                                studyperson -> !contactIDs.find { ids -> (ids.person == studyperson.person.id) && (ids.role == studyperson.role.id) }
1056                        }
1057
1058                        // Add those contacts not yet present in the database
1059                        contactIDs.each { ids ->
1060                                if (!flow.study.persons.find { studyperson -> (ids.person == studyperson.person.id) && (ids.role == studyperson.role.id) }) {
1061                                        def person = Person.get(ids.person)
1062                                        def role = PersonRole.get(ids.role)
1063                                        if (person && role) {
1064                                                // Find a studyperson object with these parameters
1065                                                def studyPerson = StudyPerson.findAll().find { studyperson -> studyperson.person.id == person.id && studyperson.role.id == role.id }
1066
1067                                                // If if does not yet exist, save the example
1068                                                if (!studyPerson) {
1069                                                        studyPerson = new StudyPerson(
1070                                                                person: person,
1071                                                                role: role
1072                                                        )
1073                                                        studyPerson.save(flush: true)
1074                                                }
1075
1076                                                flow.study.addToPersons(studyPerson)
1077                                        } else {
1078                                                log.info('.person ' + ids.person + ' or Role ' + ids.role + ' not found in database.')
1079                                        }
1080                                }
1081                        }
1082                } else {
1083                        log.info('.no persons selected.')
1084                        flow.study.persons.clear()
1085                }
1086
1087        }
1088
1089        /**
1090         * re-usable code for handling contacts form data in a web flow
1091         * @param Map LocalAttributeMap (the flow scope)
1092         * @param Map localAttributeMap (the flash scope)
1093         * @param Map GrailsParameterMap (the flow parameters = form data)
1094         * @param String    'readers' or 'writers'
1095         * @return boolean
1096         */
1097        def handleUsers(flow, flash, params, type) {
1098                def users = []
1099
1100                if (type == "readers") {
1101                        users = flow.study.readers ?: []
1102                } else if (type == "writers") {
1103                        users = flow.study.writers ?: []
1104                }
1105
1106                // Check the ids of the contacts that should be attached
1107                // to this study. If they are already attached, keep 'm. If
1108                // studies are attached that are not in the selected (i.e. the
1109                // user deleted them), remove them
1110
1111                // Users are saved as user_id
1112                def userIDs = params.get( type + '_ids')
1113                if (userIDs) {
1114                        // Find the individual IDs and make integers
1115                        userIDs = userIDs.split(',').collect { Integer.parseInt(it, 10) }
1116
1117                        // First remove the publication that are not present in the array
1118                        users.removeAll { user -> !userIDs.find { id -> id == user.id } }
1119
1120                        // Add those publications not yet present in the database
1121                        userIDs.each { id ->
1122                                if (!users.find { user -> id == user.id }) {
1123                                        def user = SecUser.get(id)
1124                                        if (user) {
1125                                            users.add(user)
1126                                        } else {
1127                                                log.info('.user with ID ' + id + ' not found in database.')
1128                                        }
1129                                }
1130                        }
1131
1132                } else {
1133                        log.info('.no users selected.')
1134                        users.clear()
1135                }
1136
1137                if (type == "readers") {
1138                        if (flow.study.readers)
1139                        flow.study.readers.clear()
1140                        users.each { flow.study.addToReaders(it) }
1141                } else if (type == "writers") {
1142                        if (flow.study.writers)
1143                        flow.study.writers.clear()
1144
1145                        users.each { flow.study.addToWriters(it) }
1146                }
1147        }
1148
1149        /**
1150         * Handle the wizard subject page
1151         *
1152         * @param Map LocalAttributeMap (the flow scope)
1153         * @param Map localAttributeMap (the flash scope)
1154         * @param Map GrailsParameterMap (the flow parameters = form data)
1155         * @returns boolean
1156         */
1157        def subjectPage(flow, flash, params) {
1158                def errors = false
1159                flash.wizardErrors = [:]
1160
1161                // remember the params in the flash scope
1162                flash.values = params
1163
1164                // iterate through subjects
1165                flow.study.subjects.each() { subject ->
1166                        // iterate through (template and domain) fields
1167                        subject.giveFields().each() { field ->
1168                                // set field
1169                                subject.setFieldValue(
1170                                        field.name,
1171                                        params.get('subject_' + subject.getIdentifier() + '_' + field.escapedName())
1172                                )
1173                        }
1174
1175                        // validate subject
1176                        if (!subject.validate()) {
1177                                errors = true
1178                                this.appendErrors(subject, flash.wizardErrors, 'subject_' + subject.getIdentifier() + '_')
1179                        }
1180                }
1181
1182                return !errors
1183        }
1184
1185        /**
1186         * Add a number of subjects to a study
1187         *
1188         * required params entities:
1189         * -addNumber (int)
1190         * -species   (string)
1191         * -template  (string)
1192         *
1193         * @param Map LocalAttributeMap (the flow scope)
1194         * @param Map localAttributeMap (the flash scope)
1195         * @param Map GrailsParameterMap (the flow parameters = form data)
1196         * @returns boolean
1197         */
1198        def addSubjects(flow, flash, params) {
1199                // remember the params in the flash scope
1200                flash.values = params
1201
1202                // handle the subject page
1203                subjectPage(flow, flash, params)
1204
1205                // (re)set error message
1206                flash.wizardErrors = [:]
1207
1208                // set work variables
1209                def errors              = false
1210                def number              = params.get('addNumber') as int
1211                def species             = Term.findByName(params.get('species'))
1212                def template    = Template.findByName(params.get('template'))
1213
1214                // can we add subjects?
1215                if (number > 0 && species && template) {
1216                        // add subjects to study
1217                        number.times {
1218                                // work variables
1219                                def subjectName = 'Subject '
1220                                def subjectIterator = 1
1221                                def tempSubjectName = subjectName + subjectIterator
1222
1223                                // make sure subject name is unique
1224                                if (flow.study.subjects) {
1225                                        while (flow.study.subjects.find { it.name == tempSubjectName }) {
1226                                                subjectIterator++
1227                                                tempSubjectName = subjectName + subjectIterator
1228                                        }
1229                                }
1230                                subjectName = tempSubjectName
1231
1232                                // create a subject instance
1233                                def subject = new Subject(
1234                                        name            : subjectName,
1235                                        species         : species,
1236                                        template        : template
1237                                )
1238
1239                                // add it to the study
1240                                flow.study.addToSubjects( subject )
1241
1242                                // validate subject
1243                                if (subject.validate()) {
1244                                        log.info ".added subject "+subject
1245                                } else {
1246                                        // whoops?
1247                                        flow.study.removeFromSubjects( subject )
1248
1249                                        // append errors
1250                                        this.appendErrors(subject, flash.wizardErrors)
1251                                        errors = true
1252                                }
1253                        }
1254                } else {
1255                        // add feedback
1256                        errors = true
1257                        if (number < 1) this.appendErrorMap(['addNumber': 'Enter a positive number of subjects to add'], flash.wizardErrors)
1258                        if (!species)   this.appendErrorMap(['species': 'You need to select a species, or add one if it is not yet present'], flash.wizardErrors)
1259                        if (!template)  this.appendErrorMap(['template': 'You need to select a template, or add one if it is not yet present'], flash.wizardErrors)
1260                }
1261
1262                return !errors
1263        }
1264
1265        /**
1266         * Handle the wizard event page
1267         *
1268         * @param Map LocalAttributeMap (the flow scope)
1269         * @param Map localAttributeMap (the flash scope)
1270         * @param Map GrailsParameterMap (the flow parameters = form data)
1271         * @returns boolean
1272         */
1273        def eventPage(flow, flash, params) {
1274                def errors = false
1275                flash.wizardErrors = [:]
1276
1277                // remember the params in the flash scope
1278                flash.values = params
1279
1280                // handle the 'add event' form
1281                if (flow.event) {
1282                        flow.event.giveFields().each() { field ->
1283                                // set field
1284                                flow.event.setFieldValue(
1285                                        field.name,
1286                                        params.get(field.escapedName())
1287                                )
1288                        }
1289                }
1290
1291                // handle the eventGroup names and grouping
1292                def name        = ""
1293                def tempName= ""
1294                flow.study.eventGroups.each() { eventGroup ->
1295                        // iterate through templates
1296                        flow.study.giveAllEventTemplates().each() { template ->
1297                                tempName = params.get( 'eventGroup_' + eventGroup.getIdentifier() + '_' + template.getIdentifier() )
1298
1299                                // is the name different?
1300                                if (tempName != eventGroup.name) {
1301                                        name = tempName
1302                                }
1303                        }
1304
1305                        // should the name change?
1306                        if (name) {
1307                                // yes, change it
1308                                eventGroup.name = name
1309                                name = ""
1310                        }
1311
1312                        // handle eventGrouping
1313                        ( ((flow.study.events) ? flow.study.events : []) + ((flow.study.samplingEvents) ? flow.study.samplingEvents : []) ) .each() { event ->
1314                                if (params.get( 'event_' + event.getIdentifier() + '_group_' + eventGroup.getIdentifier() )) {
1315                                        // add to eventGroup
1316                                        if (event instanceof SamplingEvent) {
1317                                                // check if we are already in this eventGroup
1318                                                if (!eventGroup.samplingEvents.find { it.equals(event) }) {
1319                                                        // no, add it
1320                                                        eventGroup.addToSamplingEvents(event)
1321
1322                                                        // iterate through subjects for this eventGroup
1323                                                        eventGroup.subjects.each() { subject ->
1324                                                                // instantiate a sample for this subject / event
1325                                                                def samplingEventName = this.ucwords(event.template.name)
1326                                                                def eventGroupName = eventGroup.name.replaceAll(/\w+/, { it[0].toUpperCase() + ((it.size() > 1) ? it[1..-1] : '') }).replaceAll("([ ]{1,})", "")
1327                                                                def sampleName = (this.ucwords(subject.name) + '_' + samplingEventName + '_' + eventGroupName + '_' + new RelTime(event.startTime).toString()).replaceAll("([ ]{1,})", "")
1328                                                                def tempSampleIterator = 0
1329                                                                def tempSampleName = sampleName
1330
1331                                                                // make sure sampleName is unique
1332                                                                if (flow.study.samples) {
1333                                                                        while (flow.study.samples.find { it.name == tempSampleName }) {
1334                                                                                tempSampleIterator++
1335                                                                                tempSampleName = sampleName + "_" + tempSampleIterator
1336                                                                        }
1337                                                                        sampleName = tempSampleName
1338                                                                }
1339
1340                                                                // instantiate a sample
1341                                                                flow.study.addToSamples(
1342                                                                        new Sample(
1343                                                                                parentSubject   : subject,
1344                                                                                parentEvent             : event,
1345                                                                                parentEventGroup: eventGroup,
1346                                                                                name                    : sampleName,
1347                                                                                template                : (event.sampleTemplate) ? event.sampleTemplate : ''
1348                                                                        )
1349                                                                )
1350                                                        }
1351                                                }
1352                                        } else {
1353                                                eventGroup.addToEvents(event)
1354                                        }
1355                                } else {
1356                                        // remove from eventGroup
1357                                        if (event instanceof SamplingEvent) {
1358                                                // iterate through subjects (if we have them)
1359                                                eventGroup.subjects.each() { subject ->
1360                                                        // find all samples for this subject / event
1361                                                        flow.study.samples.findAll { (it.parentEvent.equals(event) && it.parentSubject.equals(subject) ) }.each() {
1362                                                                // delete this sample
1363                                                                flow.study.removeFromSamples( it )
1364                                                                it.delete()
1365                                                        }
1366                                                }
1367
1368                                                eventGroup.removeFromSamplingEvents(event)
1369                                        } else {
1370                                                eventGroup.removeFromEvents(event)
1371                                        }
1372                                }
1373                        }
1374                }
1375
1376                // handle the (sampling) events
1377                ( ((flow.study.events) ? flow.study.events : []) + ((flow.study.samplingEvents) ? flow.study.samplingEvents : []) ) .each() { event ->
1378                        event.giveFields().each() { field ->
1379                                event.setFieldValue(
1380                                        field.name,
1381                                        params.get( 'event_' + event.getIdentifier() + '_' + field.escapedName() )
1382                                )
1383                        }
1384
1385                        // validate event
1386                        if (!event.validate()) {
1387                                errors = true
1388                                this.appendErrors(event, flash.wizardErrors)
1389                        }
1390                }
1391
1392                return !errors
1393        }
1394
1395        /**
1396         * Handle the wizard group page
1397         *
1398         * @param Map LocalAttributeMap (the flow scope)
1399         * @param Map localAttributeMap (the flash scope)
1400         * @param Map GrailsParameterMap (the flow parameters = form data)
1401         * @returns boolean
1402         */
1403        def groupPage(flow, flash, params) {
1404                def errors = false
1405                flash.wizardErrors = [:]
1406
1407                // remember the params in the flash scope
1408                flash.values = params
1409
1410                // iterate through groups
1411                flow.study.eventGroups.each() { eventGroup ->
1412                        // iterate through subjects
1413                        flow.study.subjects.each() { subject ->
1414                                if (params.get('subject_' + subject.getIdentifier() + '_group_' + eventGroup.getIdentifier() )) {
1415                                        // check if this subject is already part of this eventGroup
1416                                        if ( !eventGroup.subjects.find { it.equals(subject) } ) {
1417                                                // add to eventGroup
1418                                                eventGroup.addToSubjects(subject)
1419
1420                                                // iterate through samplingEvents
1421                                                eventGroup.samplingEvents.each() { samplingEvent ->
1422                                                        def samplingEventName = this.ucwords(samplingEvent.template.name)
1423                                                        def eventGroupName = eventGroup.name.replaceAll(/\w+/, { it[0].toUpperCase() + ((it.size() > 1) ? it[1..-1] : '') }).replaceAll("([ ]{1,})", "")
1424                                                        def sampleName = (this.ucwords(subject.name) + '_' + samplingEventName + '_' + eventGroupName + '_' + new RelTime(samplingEvent.startTime).toString()).replaceAll("([ ]{1,})", "")
1425                                                        def tempSampleIterator = 0
1426                                                        def tempSampleName = sampleName
1427
1428                                                        // make sure sampleName is unique
1429                                                        if (flow.study.samples) {
1430                                                                while (flow.study.samples.find { it.name == tempSampleName }) {
1431                                                                        tempSampleIterator++
1432                                                                        tempSampleName = sampleName + "_" + tempSampleIterator
1433                                                                }
1434                                                                sampleName = tempSampleName
1435                                                        }
1436
1437                                                        // instantiate a sample
1438                                                        flow.study.addToSamples(
1439                                                                new Sample(
1440                                                                        parentSubject   : subject,
1441                                                                        parentEvent             : samplingEvent,
1442                                                                        parentEventGroup: eventGroup,
1443                                                                        name                    : sampleName,
1444                                                                        template                : (samplingEvent.sampleTemplate) ? samplingEvent.sampleTemplate : ''
1445                                                                )
1446                                                        )
1447                                                }
1448                                        } else {
1449                                        }
1450                                } else {
1451                                        // check if this subject is a member of this eventGroup
1452                                        if (eventGroup.subjects.find { it.equals(subject) }) {
1453                                                // remove from eventGroup
1454                                                eventGroup.removeFromSubjects(subject)
1455
1456                                                // iterate through samplingEvents
1457                                                eventGroup.samplingEvents.each() { samplingEvent ->
1458                                                        flow.study.samples.findAll { (it.parentEvent.equals(samplingEvent) && it.parentSubject.equals(subject) && it.parentEventGroup.equals(eventGroup)) }.each() {
1459                                                                // delete this sample
1460                                                                flow.study.removeFromSamples(it)
1461                                                                it.delete()
1462                                                        }
1463                                                }
1464                                        }
1465                                }
1466                        }
1467                }
1468        }
1469
1470        /**
1471         * Handle the wizard samples page
1472         *
1473         * @param Map LocalAttributeMap (the flow scope)
1474         * @param Map localAttributeMap (the flash scope)
1475         * @param Map GrailsParameterMap (the flow parameters = form data)
1476         * @returns boolean
1477         */
1478        def samplePage(flow, flash, params) {
1479                def errors = false
1480                flash.wizardErrors = [:]
1481
1482                // remember the params in the flash scope
1483                flash.values = params
1484
1485                // iterate through samples
1486                flow.study.samples.each() { sample ->
1487                        // iterate through sample fields
1488                        sample.giveFields().each() { field ->
1489                                def value = params.get('sample_'+sample.getIdentifier()+'_'+field.escapedName())
1490
1491                                // set field value
1492                                if (!(field.name == 'name' && !value)) {
1493                                        println "setting "+field.name+" to "+value
1494                                        sample.setFieldValue(field.name, value)
1495                                }
1496                        }
1497
1498                        // has the template changed?
1499                        def templateName = params.get('template_' + sample.getIdentifier())
1500                        if (templateName && sample.template?.name != templateName) {
1501                                sample.template = Template.findByName(templateName)
1502                        }
1503
1504                        // validate sample
1505                        if (!sample.validate()) {
1506                                errors = true
1507                                this.appendErrors(sample, flash.wizardErrors, 'sample_' + sample.getIdentifier() + '_' )
1508                                println 'error-> sample_'+sample.getIdentifier()
1509                        }
1510                }
1511
1512                return !errors
1513        }
1514
1515        /**
1516         * Handle the wizard assays page
1517         *
1518         * @param Map LocalAttributeMap (the flow scope)
1519         * @param Map localAttributeMap (the flash scope)
1520         * @param Map GrailsParameterMap (the flow parameters = form data)
1521         * @returns boolean
1522         */
1523        def assayPage(flow, flash, params) {
1524                def errors = false
1525                flash.wizardErrors = [:]
1526
1527                // remember the params in the flash scope
1528                flash.values = params
1529
1530                // handle the 'add assay' form
1531                if (flow.assay) {
1532                        flow.assay.giveFields().each() { field ->
1533                                // set field
1534                                flow.assay.setFieldValue(
1535                                        field.name,
1536                                        params.get(field.escapedName())
1537                                )
1538                        }
1539                }
1540
1541                // handle the assay data
1542                flow.study.assays.each() { assay ->
1543                        // set data
1544                        assay.giveFields().each() { field ->
1545                                assay.setFieldValue(
1546                                        field.name,
1547                                        params.get( 'assay_' + assay.getIdentifier() + '_' + field.escapedName() )
1548                                )
1549                        }
1550
1551                        // validate assay
1552                        if (!assay.validate()) {
1553                                errors = true
1554                                this.appendErrors(assay, flash.wizardErrors, 'assay_' + assay.getIdentifier() + '_')
1555                        }
1556                }
1557
1558                return !errors
1559        }
1560
1561        /**
1562         * Handle the wizard assayGroups page
1563         *
1564         * @param Map LocalAttributeMap (the flow scope)
1565         * @param Map localAttributeMap (the flash scope)
1566         * @param Map GrailsParameterMap (the flow parameters = form data)
1567         * @returns boolean
1568         */
1569        def assayGroupPage(flow, flash, params) {
1570                def errors = false
1571                flash.wizardErrors = [:]
1572
1573                // remember the params in the flash scope
1574                flash.values = params
1575
1576                // iterate through samples
1577                flow.study.samples.each() { sample ->
1578                        // iterate through assays
1579                        flow.study.assays.each() { assay ->
1580                                if (params.get( 'sample_' + sample.getIdentifier() + '_assay_' + assay.getIdentifier() )) {
1581                                        println "add sample "+sample.getIdentifier()+" to assay "+assay.getIdentifier()
1582                                        // add sample to assay
1583                                        assay.addToSamples( sample )
1584                                } else {
1585                                        // remove sample from assay
1586                                        assay.removeFromSamples( sample )
1587                                }
1588                                println assay.samples
1589                        }
1590                }
1591
1592                return !errors
1593        }
1594
1595        /**
1596         * groovy / java equivalent of php's ucwords function
1597         *
1598         * Capitalize all first letters of separate words
1599         *
1600         * @param String
1601         * @return String
1602         */
1603        def ucwords(String text) {
1604                def newText = ''
1605
1606                // change case to lowercase
1607                text = text.toLowerCase()
1608
1609                // iterate through words
1610                text.split(" ").each() {
1611                        newText += it[0].toUpperCase() + it.substring(1) + " "
1612                }
1613
1614                return newText.substring(0, newText.size()-1)
1615        }
1616
1617        /**
1618         * return the object from a map of objects by searching for a name
1619         * @param String name
1620         * @param Map map of objects
1621         * @return Object
1622         */
1623        def getObjectByName(name, map) {
1624                def result = null
1625                map.each() {
1626                        if (it.name == name) {
1627                                result = it
1628                        }
1629                }
1630
1631                return result
1632        }
1633
1634        /**
1635         * transform domain class validation errors into a human readable
1636         * linked hash map
1637         * @param object validated domain class
1638         * @return object  linkedHashMap
1639         */
1640        def getHumanReadableErrors(object) {
1641                def errors = [:]
1642                object.errors.getAllErrors().each() {
1643                        def message = it.toString()
1644
1645                        //errors[it.getArguments()[0]] = it.getDefaultMessage()
1646                        errors[it.getArguments()[0]] = message.substring(0, message.indexOf(';'))
1647                }
1648
1649                return errors
1650        }
1651
1652        /**
1653         * append errors of a particular object to a map
1654         * @param object
1655         * @param map linkedHashMap
1656         * @void
1657         */
1658        def appendErrors(object, map) {
1659                this.appendErrorMap(this.getHumanReadableErrors(object), map)
1660        }
1661
1662        def appendErrors(object, map, prepend) {
1663                this.appendErrorMap(this.getHumanReadableErrors(object), map, prepend)
1664        }
1665
1666        /**
1667         * append errors of one map to another map
1668         * @param map linkedHashMap
1669         * @param map linkedHashMap
1670         * @void
1671         */
1672        def appendErrorMap(map, mapToExtend) {
1673                map.each() {key, value ->
1674                        mapToExtend[key] = ['key': key, 'value': value, 'dynamic': false]
1675                }
1676        }
1677
1678        def appendErrorMap(map, mapToExtend, prepend) {
1679                map.each() {key, value ->
1680                        mapToExtend[prepend + key] = ['key': key, 'value': value, 'dynamic': true]
1681                }
1682        }
1683
1684        /**
1685         * Parses a RelTime string and returns a nice human readable string
1686         *
1687         * @return Human Readable string or a HTTP response code 400 on error
1688         */
1689        def ajaxParseRelTime = {
1690                if (params.reltime == null) {
1691                        response.status = 400
1692                        render('reltime parameter is expected')
1693                }
1694
1695                try {
1696                        def reltime = RelTime.parseRelTime(params.reltime)
1697                        render reltime.toPrettyString()
1698                } catch (IllegalArgumentException e) {
1699                        response.status = 400
1700                        render(e.getMessage())
1701                }
1702        }
1703
1704        /**
1705         * Proxy for searching PubMed articles (or other articles from the Entrez DB).
1706         *
1707         * This proxy is needed because it is not allowed to fetch XML directly from a different
1708         * domain using javascript. So we have the javascript call a function on our own domain
1709         * and the proxy will fetch the data from Entrez
1710         *
1711         * @since       20100609
1712         * @param       _utility        The name of the utility, without the complete path. Example: 'esearch.fcgi'
1713         * @return      XML
1714         */
1715        def entrezProxy = {
1716                // Remove unnecessary parameters
1717                params.remove( "action" )
1718                params.remove( "controller" )
1719
1720                def url = "http://eutils.ncbi.nlm.nih.gov/entrez/eutils";
1721                def util = params.remove( "_utility" )
1722                def paramString = params.collect { k, v -> k + '=' + v.encodeAsURL() }.join( '&' );
1723
1724                def fullUrl = url + '/' + util + '?' + paramString;
1725
1726                // Return the output of the request
1727                // render fullUrl;
1728                render(
1729                    text:           new URL( fullUrl ).getText(),
1730                    contentType:    "text/xml",
1731                    encoding:       "UTF-8"
1732                );
1733        }
1734}
Note: See TracBrowser for help on using the repository browser.