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

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