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

Last change on this file since 1540 was 1540, checked in by robert@…, 10 years ago

Refactored studyWizardController so that methods can also work without a flow scope.

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