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

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