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

Last change on this file since 2122 was 2122, checked in by work@…, 11 years ago

fixed issue #518 where some templates did not show all template fields when multiple templates exist for different entities but with the same name

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