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

Last change on this file since 1282 was 1282, checked in by work@…, 10 years ago

resolves #210, added possibility to duplicate events

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