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

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