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

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