root/trunk/grails-app/controllers/dbnp/studycapturing/StudyWizardController.groovy @ 1339

Revision 1339, 53.5 KB (checked in by work@…, 3 years ago)

- fixed spelling error
- removed validation in 'add subject' logic as this breaks adding of subjects when template fields are required

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