root/trunk/grails-app/controllers/dbnp/studycapturing/StudyWizardController.groovy

Revision 2163, 57.6 KB (checked in by work@…, 2 years ago)

- resolves issue #528

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