source: trunk/grails-app/controllers/dbnp/studycapturing/StudyWizardController.groovy @ 1348

Last change on this file since 1348 was 1348, checked in by work@…, 10 years ago
  • fix for Javassist issue #254, needs some thorough testing...
File size: 52.9 KB
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                        // store study in the flowscope
1149                        flow.study = study
1150
1151                        // set 'quicksave' variable
1152                        flow.quickSave = true
1153
1154                        return true
1155                } catch (Exception e) {
1156                        // rollback
1157                        this.appendErrorMap(['exception': e.getMessage() + ', see log for stacktrace'], flash.wizardErrors)
1158
1159                        return false
1160                }
1161        }
1162
1163        /**
1164         * Handle the wizard study page
1165         *
1166         * @param Map LocalAttributeMap (the flow scope)
1167         * @param Map localAttributeMap (the flash scope)
1168         * @param Map GrailsParameterMap (the flow parameters = form data)
1169         * @returns boolean
1170         */
1171        def studyPage(flow, flash, params) {
1172                flash.values            = params
1173                flash.wizardErrors      = [:]
1174
1175                // instantiate study of it is not yet present
1176                if (!flow.study) flow.study = new Study()
1177
1178                // did the study template change?
1179                if (params.get('template').size() && flow.study.template?.name != params.get('template')) {
1180                        // set the template
1181                        flow.study.template = Template.findByName(params.remove('template'))
1182                }
1183
1184                // does the study have a template set?
1185                if (flow.study.template && flow.study.template instanceof Template) {
1186                        // yes, iterate through template fields
1187                        flow.study.giveFields().each() {
1188                                // and set their values
1189                                flow.study.setFieldValue(it.name, params.get(it.escapedName()))
1190                        }
1191                }
1192
1193                // handle publications
1194                handlePublications(flow, flash, params)
1195
1196                // handle contacts
1197                handleContacts(flow, flash, params)
1198
1199                // handle users (readers, writers)
1200                handleUsers(flow, flash, params, 'readers')
1201                handleUsers(flow, flash, params, 'writers')
1202
1203                // handle public checkbox
1204                if (params.get("publicstudy")) {
1205                        flow.study.publicstudy = params.get("publicstudy")
1206                }
1207
1208                // have we got a template?
1209                if (flow.study.template && flow.study.template instanceof Template) {
1210                        // validate the study
1211                        if (flow.study.validate()) {
1212                                // instance is okay
1213                                return true
1214                        } else {
1215                                // validation failed
1216                                this.appendErrors(flow.study, flash.wizardErrors)
1217                                return false
1218                        }
1219                } else {
1220                        // no, return an error that the template is not set
1221                        this.appendErrorMap(['template': g.message(code: 'select.not.selected.or.add', args: ['template'])], flash.wizardErrors)
1222                        return false
1223                }
1224        }
1225
1226        /**
1227         * re-usable code for handling publications form data in a web flow
1228         * @param Map LocalAttributeMap (the flow scope)
1229         * @param Map localAttributeMap (the flash scope)
1230         * @param Map GrailsParameterMap (the flow parameters = form data)
1231         * @returns boolean
1232         */
1233        def handlePublications(flow, flash, params) {
1234                flash.wizardErrors      = [:]
1235
1236                if (!flow.study.publications) flow.study.publications = []
1237
1238                // Check the ids of the pubblications that should be attached
1239                // to this study. If they are already attached, keep 'm. If
1240                // studies are attached that are not in the selected (i.e. the
1241                // user deleted them), remove them
1242                def publicationIDs = params.get('publication_ids')
1243                if (publicationIDs) {
1244                        // Find the individual IDs and make integers
1245                        publicationIDs = publicationIDs.split(',').collect { Integer.parseInt(it, 10) }
1246
1247                        // First remove the publication that are not present in the array
1248                        flow.study.publications.removeAll { publication -> !publicationIDs.find { id -> id == publication.id } }
1249
1250                        // Add those publications not yet present in the database
1251                        publicationIDs.each { id ->
1252                                if (!flow.study.publications.find { publication -> id == publication.id }) {
1253                                        def publication = Publication.get(id)
1254                                        if (publication) {
1255                                                flow.study.addToPublications(publication)
1256                                        } else {
1257                                                log.info('.publication with ID ' + id + ' not found in database.')
1258                                        }
1259                                }
1260                        }
1261
1262                } else {
1263                        log.info('.no publications selected.')
1264                        flow.study.publications.clear()
1265                }
1266
1267        }
1268
1269        /**
1270         * re-usable code for handling contacts form data in a web flow
1271         * @param Map LocalAttributeMap (the flow scope)
1272         * @param Map localAttributeMap (the flash scope)
1273         * @param Map GrailsParameterMap (the flow parameters = form data)
1274         * @return boolean
1275         */
1276        def handleContacts(flow, flash, params) {
1277                flash.wizardErrors      = [:]
1278
1279                if (!flow.study.persons) flow.study.persons = []
1280
1281                // Check the ids of the contacts that should be attached
1282                // to this study. If they are already attached, keep 'm. If
1283                // studies are attached that are not in the selected (i.e. the
1284                // user deleted them), remove them
1285
1286                // Contacts are saved as [person_id]-[role_id]
1287                def contactIDs = params.get('contacts_ids')
1288                if (contactIDs) {
1289                        // Find the individual IDs and make integers
1290                        contactIDs = contactIDs.split(',').collect {
1291                                def parts = it.split('-')
1292                                return [person: Integer.parseInt(parts[0]), role: Integer.parseInt(parts[1])]
1293                        }
1294
1295                        // First remove the contacts that are not present in the array
1296                        flow.study.persons.removeAll {
1297                                studyperson -> !contactIDs.find { ids -> (ids.person == studyperson.person.id) && (ids.role == studyperson.role.id) }
1298                        }
1299
1300                        // Add those contacts not yet present in the database
1301                        contactIDs.each { ids ->
1302                                if (!flow.study.persons.find { studyperson -> (ids.person == studyperson.person.id) && (ids.role == studyperson.role.id) }) {
1303                                        def person = Person.get(ids.person)
1304                                        def role = PersonRole.get(ids.role)
1305                                        if (person && role) {
1306                                                // Find a studyperson object with these parameters
1307                                                def studyPerson = StudyPerson.findAll().find { studyperson -> studyperson.person.id == person.id && studyperson.role.id == role.id }
1308
1309                                                // If if does not yet exist, save the example
1310                                                if (!studyPerson) {
1311                                                        studyPerson = new StudyPerson(
1312                                                                person: person,
1313                                                                role: role
1314                                                        )
1315                                                        studyPerson.save(flush: true)
1316                                                }
1317
1318                                                flow.study.addToPersons(studyPerson)
1319                                        } else {
1320                                                log.info('.person ' + ids.person + ' or Role ' + ids.role + ' not found in database.')
1321                                        }
1322                                }
1323                        }
1324                } else {
1325                        log.info('.no persons selected.')
1326                        flow.study.persons.clear()
1327                }
1328
1329        }
1330
1331        /**
1332         * re-usable code for handling contacts form data in a web flow
1333         * @param Map LocalAttributeMap (the flow scope)
1334         * @param Map localAttributeMap (the flash scope)
1335         * @param Map GrailsParameterMap (the flow parameters = form data)
1336         * @param String    'readers' or 'writers'
1337         * @return boolean
1338         */
1339        def handleUsers(flow, flash, params, type) {
1340                flash.wizardErrors = [:]
1341
1342                def users = []
1343
1344                if (type == "readers") {
1345                        users = flow.study.readers ?: []
1346                } else if (type == "writers") {
1347                        users = flow.study.writers ?: []
1348                }
1349
1350                // Check the ids of the contacts that should be attached
1351                // to this study. If they are already attached, keep 'm. If
1352                // studies are attached that are not in the selected (i.e. the
1353                // user deleted them), remove them
1354
1355                // Users are saved as user_id
1356                def userIDs = params.get(type + '_ids')
1357                if (userIDs) {
1358                        // Find the individual IDs and make integers
1359                        userIDs = userIDs.split(',').collect { Integer.parseInt(it, 10) }
1360
1361                        // First remove the publication that are not present in the array
1362                        users.removeAll { user -> !userIDs.find { id -> id == user.id } }
1363
1364                        // Add those publications not yet present in the database
1365                        userIDs.each { id ->
1366                                if (!users.find { user -> id == user.id }) {
1367                                        def user = SecUser.get(id)
1368                                        if (user) {
1369                                                users.add(user)
1370                                        } else {
1371                                                log.info('.user with ID ' + id + ' not found in database.')
1372                                        }
1373                                }
1374                        }
1375
1376                } else {
1377                        log.info('.no users selected.')
1378                        users.clear()
1379                }
1380
1381                if (type == "readers") {
1382                        if (flow.study.readers)
1383                                flow.study.readers.clear()
1384                        users.each { flow.study.addToReaders(it) }
1385                } else if (type == "writers") {
1386                        if (flow.study.writers)
1387                                flow.study.writers.clear()
1388
1389                        users.each { flow.study.addToWriters(it) }
1390                }
1391        }
1392
1393        /**
1394         * Handle the wizard subject page
1395         *
1396         * @param Map LocalAttributeMap (the flow scope)
1397         * @param Map localAttributeMap (the flash scope)
1398         * @param Map GrailsParameterMap (the flow parameters = form data)
1399         * @returns boolean
1400         */
1401        def subjectPage(flow, flash, params) {
1402                def errors = false
1403                flash.wizardErrors = [:]
1404
1405                // remember the params in the flash scope
1406                flash.values = params
1407
1408                // iterate through subjects
1409                flow.study.subjects.each() { subject ->
1410                        // iterate through (template and domain) fields
1411                        subject.giveFields().each() { field ->
1412                                // set field
1413                                subject.setFieldValue(
1414                                        field.name,
1415                                        params.get('subject_' + subject.getIdentifier() + '_' + field.escapedName())
1416                                )
1417                        }
1418
1419                        // validate subject
1420                        if (!subject.validate()) {
1421                                errors = true
1422                                this.appendErrors(subject, flash.wizardErrors, 'subject_' + subject.getIdentifier() + '_')
1423                        }
1424                }
1425
1426                return !errors
1427        }
1428
1429        /**
1430         * Add a number of subjects to a study
1431         *
1432         * required params entities:
1433         * -addNumber (int)
1434         * -species   (string)
1435         * -template  (string)
1436         *
1437         * @param Map LocalAttributeMap (the flow scope)
1438         * @param Map localAttributeMap (the flash scope)
1439         * @param Map GrailsParameterMap (the flow parameters = form data)
1440         * @returns boolean
1441         */
1442        def addSubjects(flow, flash, params) {
1443                // remember the params in the flash scope
1444                flash.values = params
1445
1446                // handle the subject page
1447                subjectPage(flow, flash, params)
1448
1449                // (re)set error message
1450                flash.wizardErrors = [:]
1451
1452                // set work variables
1453                def errors              = false
1454                def number              = params.get('addNumber') as int
1455                def species             = Term.findByName(params.get('species'))
1456                def template    = Template.findByName(params.get('template'))
1457
1458                // can we add subjects?
1459                if (number > 0 && species && template) {
1460                        // add subjects to study
1461                        number.times {
1462                                // work variables
1463                                def subjectName = 'Subject '
1464                                def subjectIterator = 1
1465                                def tempSubjectName = subjectName + subjectIterator
1466
1467                                // make sure subject name is unique
1468                                if (flow.study.subjects) {
1469                                        while (flow.study.subjects.find { it.name == tempSubjectName }) {
1470                                                subjectIterator++
1471                                                tempSubjectName = subjectName + subjectIterator
1472                                        }
1473                                }
1474                                subjectName = tempSubjectName
1475
1476                                // create a subject instance
1477                                def subject = new Subject(
1478                                        name            : subjectName,
1479                                        species         : species,
1480                                        template        : template
1481                                )
1482
1483                                // add it to the study
1484                                flow.study.addToSubjects( subject )
1485                        }
1486                } else {
1487                        // add feedback
1488                        errors = true
1489                        if (number < 1) this.appendErrorMap(['addNumber': 'Enter a positive number of subjects to add'], flash.wizardErrors)
1490                        if (!species)   this.appendErrorMap(['species': g.message(code: 'select.not.selected.or.add', args: ['species'])], flash.wizardErrors)
1491                        if (!template)  this.appendErrorMap(['template': g.message(code: 'select.not.selected.or.add', args: ['template'])], flash.wizardErrors)
1492                }
1493
1494                return !errors
1495        }
1496
1497        /**
1498         * Handle the wizard event page
1499         *
1500         * @param Map LocalAttributeMap (the flow scope)
1501         * @param Map localAttributeMap (the flash scope)
1502         * @param Map GrailsParameterMap (the flow parameters = form data)
1503         * @returns boolean
1504         */
1505        def eventPage(flow, flash, params) {
1506                def errors = false
1507                flash.wizardErrors = [:]
1508
1509                // remember the params in the flash scope
1510                flash.values = params
1511
1512                // handle the 'add event' form
1513                if (flow.event) {
1514                        flow.event.giveFields().each() { field ->
1515                                // set field
1516                                flow.event.setFieldValue(
1517                                        field.name,
1518                                        params.get(field.escapedName())
1519                                )
1520                        }
1521                }
1522
1523                // handle the eventGroup names and grouping
1524                def name        = ""
1525                def tempName= ""
1526                flow.study.eventGroups.each() { eventGroup ->
1527                        // iterate through templates
1528                        flow.study.giveAllEventTemplates().each() { template ->
1529                                tempName = params.get( 'eventGroup_' + eventGroup.getIdentifier() + '_' + template.getIdentifier() )
1530
1531                                // is the name different?
1532                                if (tempName != eventGroup.name) {
1533                                        name = tempName
1534                                }
1535                        }
1536
1537                        // should the name change?
1538                        if (name) {
1539                                // yes, change it
1540                                eventGroup.name = name
1541                                name = ""
1542                        }
1543
1544                        // handle eventGrouping
1545                        ( ((flow.study.events) ? flow.study.events : []) + ((flow.study.samplingEvents) ? flow.study.samplingEvents : []) ) .each() { event ->
1546                                if (params.get( 'event_' + event.getIdentifier() + '_group_' + eventGroup.getIdentifier() )) {
1547                                        // add to eventGroup
1548                                        if (event instanceof SamplingEvent) {
1549                                                // check if we are already in this eventGroup
1550                                                if (!eventGroup.samplingEvents.find { it.equals(event) }) {
1551                                                        // no, add it
1552                                                        eventGroup.addToSamplingEvents(event)
1553
1554                                                        // iterate through subjects for this eventGroup
1555                                                        eventGroup.subjects.each() { subject ->
1556                                                                // instantiate a sample for this subject / event
1557                                                                def samplingEventName = ucwords(event.template.name)
1558                                                                def eventGroupName = ucwords(eventGroup.name).replaceAll("([ ]{1,})", "")
1559                                                                def sampleName = (ucwords(subject.name) + '_' + samplingEventName + '_' + eventGroupName + '_' + new RelTime(event.startTime).toString()).replaceAll("([ ]{1,})", "")
1560                                                                def tempSampleIterator = 0
1561                                                                def tempSampleName = sampleName
1562
1563                                                                // make sure sampleName is unique
1564                                                                if (flow.study.samples) {
1565                                                                        while (flow.study.samples.find { it.name == tempSampleName }) {
1566                                                                                tempSampleIterator++
1567                                                                                tempSampleName = sampleName + "_" + tempSampleIterator
1568                                                                        }
1569                                                                        sampleName = tempSampleName
1570                                                                }
1571
1572                                                                // instantiate a sample
1573                                                                flow.study.addToSamples(
1574                                                                        new Sample(
1575                                                                                parentSubject   : subject,
1576                                                                                parentEvent             : event,
1577                                                                                parentEventGroup: eventGroup,
1578                                                                                name                    : sampleName,
1579                                                                                template                : (event.sampleTemplate) ? event.sampleTemplate : ''
1580                                                                        )
1581                                                                )
1582                                                        }
1583                                                }
1584                                        } else {
1585                                                eventGroup.addToEvents(event)
1586                                        }
1587                                } else {
1588                                        // remove from eventGroup
1589                                        if (event instanceof SamplingEvent) {
1590                                                // iterate through subjects (if we have them)
1591                                                eventGroup.subjects.each() { subject ->
1592                                                        // find all samples for this subject / event
1593                                                        flow.study.samples.findAll { (it.parentEvent.equals(event) && it.parentSubject.equals(subject) ) }.each() {
1594                                                                // delete this sample
1595                                                                flow.study.removeFromSamples( it )
1596                                                                it.delete()
1597                                                        }
1598                                                }
1599
1600                                                eventGroup.removeFromSamplingEvents(event)
1601                                        } else {
1602                                                eventGroup.removeFromEvents(event)
1603                                        }
1604                                }
1605                        }
1606                }
1607
1608                // handle the (sampling) events
1609                ( ((flow.study.events) ? flow.study.events : []) + ((flow.study.samplingEvents) ? flow.study.samplingEvents : []) ) .each() { event ->
1610                        event.giveFields().each() { field ->
1611                                event.setFieldValue(
1612                                        field.name,
1613                                        params.get( 'event_' + event.getIdentifier() + '_' + field.escapedName() )
1614                                )
1615                        }
1616
1617                        // validate event
1618                        if (!event.validate()) {
1619                                errors = true
1620                                this.appendErrors(event, flash.wizardErrors)
1621                        }
1622                }
1623
1624                return !errors
1625        }
1626
1627        /**
1628         * Handle the wizard group page
1629         *
1630         * @param Map LocalAttributeMap (the flow scope)
1631         * @param Map localAttributeMap (the flash scope)
1632         * @param Map GrailsParameterMap (the flow parameters = form data)
1633         * @returns boolean
1634         */
1635        def groupPage(flow, flash, params) {
1636                def errors = false
1637                flash.wizardErrors = [:]
1638
1639                // remember the params in the flash scope
1640                flash.values = params
1641
1642                // iterate through groups
1643                flow.study.eventGroups.each() { eventGroup ->
1644                        // iterate through subjects
1645                        flow.study.subjects.each() { subject ->
1646                                if (params.get('subject_' + subject.getIdentifier() + '_group_' + eventGroup.getIdentifier() )) {
1647                                        // check if this subject is already part of this eventGroup
1648                                        if ( !eventGroup.subjects.find { it.equals(subject) } ) {
1649                                                // add to eventGroup
1650                                                eventGroup.addToSubjects(subject)
1651
1652                                                // iterate through samplingEvents
1653                                                eventGroup.samplingEvents.each() { samplingEvent ->
1654                                                        def samplingEventName = ucwords(samplingEvent.template.name)
1655                                                        def eventGroupName = ucwords(eventGroup.name)
1656                                                        def sampleName = (ucwords(subject.name) + '_' + samplingEventName + '_' + eventGroupName + '_' + new RelTime(samplingEvent.startTime).toString()).replaceAll("([ ]{1,})", "")
1657                                                        def tempSampleIterator = 0
1658                                                        def tempSampleName = sampleName
1659
1660                                                        // make sure sampleName is unique
1661                                                        if (flow.study.samples) {
1662                                                                while (flow.study.samples.find { it.name == tempSampleName }) {
1663                                                                        tempSampleIterator++
1664                                                                        tempSampleName = sampleName + "_" + tempSampleIterator
1665                                                                }
1666                                                                sampleName = tempSampleName
1667                                                        }
1668
1669                                                        // instantiate a sample
1670                                                        flow.study.addToSamples(
1671                                                                new Sample(
1672                                                                        parentSubject   : subject,
1673                                                                        parentEvent             : samplingEvent,
1674                                                                        parentEventGroup: eventGroup,
1675                                                                        name                    : sampleName,
1676                                                                        template                : (samplingEvent.sampleTemplate) ? samplingEvent.sampleTemplate : ''
1677                                                                )
1678                                                        )
1679                                                }
1680                                        } else {
1681                                        }
1682                                } else {
1683                                        // check if this subject is a member of this eventGroup
1684                                        if (eventGroup.subjects.find { it.equals(subject) }) {
1685                                                // remove from eventGroup
1686                                                eventGroup.removeFromSubjects(subject)
1687
1688                                                // iterate through samplingEvents
1689                                                eventGroup.samplingEvents.each() { samplingEvent ->
1690                                                        flow.study.samples.findAll { (it.parentEvent.equals(samplingEvent) && it.parentSubject.equals(subject) && it.parentEventGroup.equals(eventGroup)) }.each() {
1691                                                                // delete this sample
1692                                                                flow.study.removeFromSamples(it)
1693                                                                it.delete()
1694                                                        }
1695                                                }
1696                                        }
1697                                }
1698                        }
1699                }
1700        }
1701
1702        /**
1703         * Handle the wizard samples page
1704         *
1705         * @param Map LocalAttributeMap (the flow scope)
1706         * @param Map localAttributeMap (the flash scope)
1707         * @param Map GrailsParameterMap (the flow parameters = form data)
1708         * @returns boolean
1709         */
1710        def samplePage(flow, flash, params) {
1711                def errors = false
1712                flash.wizardErrors = [:]
1713
1714                // remember the params in the flash scope
1715                flash.values = params
1716
1717                // iterate through samples
1718                flow.study.samples.each() { sample ->
1719                        // iterate through sample fields
1720                        sample.giveFields().each() { field ->
1721                                def value = params.get('sample_'+sample.getIdentifier()+'_'+field.escapedName())
1722
1723                                // set field value
1724                                if (!(field.name == 'name' && !value)) {
1725                                        log.info "setting "+field.name+" to "+value
1726                                        sample.setFieldValue(field.name, value)
1727                                }
1728                        }
1729
1730                        // has the template changed?
1731                        def templateName = params.get('template_' + sample.getIdentifier())
1732                        if (templateName && sample.template?.name != templateName) {
1733                                sample.template = Template.findByName(templateName)
1734                        }
1735
1736                        // validate sample
1737                        if (!sample.validate()) {
1738                                errors = true
1739                                this.appendErrors(sample, flash.wizardErrors, 'sample_' + sample.getIdentifier() + '_' )
1740                                log.info 'error-> sample_'+sample.getIdentifier()
1741                        }
1742                }
1743
1744                return !errors
1745        }
1746
1747        /**
1748         * Handle the wizard assays page
1749         *
1750         * @param Map LocalAttributeMap (the flow scope)
1751         * @param Map localAttributeMap (the flash scope)
1752         * @param Map GrailsParameterMap (the flow parameters = form data)
1753         * @returns boolean
1754         */
1755        def assayPage(flow, flash, params) {
1756                def errors = false
1757                flash.wizardErrors = [:]
1758
1759                // remember the params in the flash scope
1760                flash.values = params
1761
1762                // handle the 'add assay' form
1763                if (flow.assay) {
1764                        flow.assay.giveFields().each() { field ->
1765                                // set field
1766                                flow.assay.setFieldValue(
1767                                        field.name,
1768                                        params.get(field.escapedName())
1769                                )
1770                        }
1771                }
1772
1773                // handle the assay data
1774                flow.study.assays.each() { assay ->
1775                        // set data
1776                        assay.giveFields().each() { field ->
1777                                assay.setFieldValue(
1778                                        field.name,
1779                                        params.get( 'assay_' + assay.getIdentifier() + '_' + field.escapedName() )
1780                                )
1781                        }
1782
1783                        // validate assay
1784                        if (!assay.validate()) {
1785                                errors = true
1786                                this.appendErrors(assay, flash.wizardErrors, 'assay_' + assay.getIdentifier() + '_')
1787                        }
1788                }
1789
1790                return !errors
1791        }
1792
1793        /**
1794         * Handle the wizard assayGroups page
1795         *
1796         * @param Map LocalAttributeMap (the flow scope)
1797         * @param Map localAttributeMap (the flash scope)
1798         * @param Map GrailsParameterMap (the flow parameters = form data)
1799         * @returns boolean
1800         */
1801        def assayGroupPage(flow, flash, params) {
1802                def errors = false
1803                flash.wizardErrors = [:]
1804
1805                // remember the params in the flash scope
1806                flash.values = params
1807
1808                // iterate through samples
1809                flow.study.samples.each() { sample ->
1810                        // iterate through assays
1811                        flow.study.assays.each() { assay ->
1812                                if (params.get( 'sample_' + sample.getIdentifier() + '_assay_' + assay.getIdentifier() )) {
1813                                        // add sample to assay
1814                                        assay.addToSamples( sample )
1815                                } else {
1816                                        // remove sample from assay
1817                                        assay.removeFromSamples( sample )
1818                                }
1819                        }
1820                }
1821
1822                return !errors
1823        }
1824
1825        /**
1826         * groovy / java equivalent of php's ucwords function
1827         *
1828         * Capitalize all first letters of separate words
1829         *
1830         * @param String
1831         * @return String
1832         */
1833        public static ucwords(String text) {
1834                def newText = ''
1835
1836                // change case to lowercase
1837                text = text.toLowerCase()
1838
1839                // iterate through words
1840                text.split(" ").each() {
1841                        newText += it[0].toUpperCase() + it.substring(1) + " "
1842                }
1843
1844                return newText.substring(0, newText.size()-1)
1845        }
1846
1847        /**
1848         * return the object from a map of objects by searching for a name
1849         * @param String name
1850         * @param Map map of objects
1851         * @return Object
1852         */
1853        def getObjectByName(name, map) {
1854                def result = null
1855                map.each() {
1856                        if (it.name == name) {
1857                                result = it
1858                        }
1859                }
1860
1861                return result
1862        }
1863
1864        /**
1865         * transform domain class validation errors into a human readable
1866         * linked hash map
1867         * @param object validated domain class
1868         * @return object  linkedHashMap
1869         */
1870        def getHumanReadableErrors(object) {
1871                def errors = [:]
1872                object.errors.getAllErrors().each() { error ->
1873                        // error.codes.each() { code -> println code }
1874
1875                        // generally speaking g.message(...) should work,
1876                        // however it fails in some steps of the wizard
1877                        // (add event, add assay, etc) so g is not always
1878                        // availably. Using our own instance of the
1879                        // validationTagLib instead so it is always
1880                        // available to us
1881                        errors[ error.getArguments()[0] ] = validationTagLib.message(error: error)
1882                }
1883
1884                return errors
1885        }
1886
1887        /**
1888         * append errors of a particular object to a map
1889         * @param object
1890         * @param map linkedHashMap
1891         * @void
1892         */
1893        def appendErrors(object, map) {
1894                this.appendErrorMap(getHumanReadableErrors(object), map)
1895        }
1896
1897        def appendErrors(object, map, prepend) {
1898                this.appendErrorMap(getHumanReadableErrors(object), map, prepend)
1899        }
1900
1901        /**
1902         * append errors of one map to another map
1903         * @param map linkedHashMap
1904         * @param map linkedHashMap
1905         * @void
1906         */
1907        def appendErrorMap(map, mapToExtend) {
1908                map.each() {key, value ->
1909                        mapToExtend[key] = ['key': key, 'value': value, 'dynamic': false]
1910                }
1911        }
1912
1913        def appendErrorMap(map, mapToExtend, prepend) {
1914                map.each() {key, value ->
1915                        mapToExtend[prepend + key] = ['key': key, 'value': value, 'dynamic': true]
1916                }
1917        }
1918
1919        /**
1920         * Parses a RelTime string and returns a nice human readable string
1921         *
1922         * @return Human Readable string or a HTTP response code 400 on error
1923         */
1924        def ajaxParseRelTime = {
1925                if (params.reltime == null) {
1926                        response.status = 400
1927                        render('reltime parameter is expected')
1928                }
1929
1930                try {
1931                        def reltime = RelTime.parseRelTime(params.reltime)
1932                        render reltime.toPrettyString()
1933                } catch (IllegalArgumentException e) {
1934                        response.status = 400
1935                        render(e.getMessage())
1936                }
1937        }
1938
1939        /**
1940         * Proxy for searching PubMed articles (or other articles from the Entrez DB).
1941         *
1942         * This proxy is needed because it is not allowed to fetch XML directly from a different
1943         * domain using javascript. So we have the javascript call a function on our own domain
1944         * and the proxy will fetch the data from Entrez
1945         *
1946         * @since       20100609
1947         * @param       _utility        The name of the utility, without the complete path. Example: 'esearch.fcgi'
1948         * @return      XML
1949         */
1950        def entrezProxy = {
1951                // Remove unnecessary parameters
1952                params.remove( "action" )
1953                params.remove( "controller" )
1954
1955                def url = "http://eutils.ncbi.nlm.nih.gov/entrez/eutils";
1956                def util = params.remove( "_utility" )
1957                def paramString = params.collect { k, v -> k + '=' + v.encodeAsURL() }.join( '&' );
1958
1959                def fullUrl = url + '/' + util + '?' + paramString;
1960
1961                // Return the output of the request
1962                // render fullUrl;
1963                render(
1964                    text:           new URL( fullUrl ).getText(),
1965                    contentType:    "text/xml",
1966                    encoding:       "UTF-8"
1967                );
1968        }
1969}
Note: See TracBrowser for help on using the repository browser.