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

Revision 1180, 44.4 KB (checked in by work@…, 3 years ago)

- enabling quicksave through the wizard

// since this controller was refactored it's technically
// safe to enable quicksave throughout the application.
// However, we must keep an eye on the quality of the
// studies as this change makes it easier to create dummy
// studies which will create garbage in out database.

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