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

Last change on this file since 1978 was 1978, checked in by s.h.sikkema@…, 9 years ago

added missing option to jump from viewing samples to editing samples page. Possible fix for #496

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