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

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

studyWizardController.Event - sampleName + templateName fix

  • Property svn:keywords set to Rev Author Date
File size: 56.2 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: 2153 $
16 * $Author: ferryjagers@gmail.com $
17 * $Date: 2012-01-26 16:12:54 +0000 (do, 26 jan 2012) $
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 the (sampling) events
1705            ( ((flow.study.events) ? flow.study.events : []) + ((flow.study.samplingEvents) ? flow.study.samplingEvents : []) ) .each() { event ->
1706                event.giveFields().each() { field ->
1707                    event.setFieldValue(
1708                        field.name,
1709                        params.get( 'event_' + event.getIdentifier() + '_' + field.escapedName() )
1710                    )
1711                }
1712   
1713                // validate event
1714                if (!event.validate()) {
1715                    errors = true
1716                    this.appendErrors(event, flash.wizardErrors)
1717                }
1718            }
1719
1720                        // handle eventGrouping
1721                        ( ((flow.study.events) ? flow.study.events : []) + ((flow.study.samplingEvents) ? flow.study.samplingEvents : []) ) .each { event ->
1722                                if (params.get( 'event_' + event.getIdentifier() + '_group_' + eventGroup.getIdentifier() )) {
1723                                        // add to eventGroup
1724                                        if (event instanceof SamplingEvent) {
1725                                                // check if we are already in this eventGroup
1726                                                if (!eventGroup.samplingEvents.find { it.equals(event) }) {
1727                                                        // no, add it
1728                                                        eventGroup.addToSamplingEvents(event)
1729
1730                                                        // iterate through subjects for this eventGroup
1731                                                        eventGroup.subjects.each() { subject ->
1732                                                                // instantiate a sample for this subject / event
1733
1734                                def sampleName = Sample.generateSampleName(flow, subject, eventGroup, event)
1735
1736                                                                // instantiate a sample
1737                                                                def newSample = new Sample(
1738                                                                        parentSubject   : subject,
1739                                                                        parentEvent             : event,
1740                                                                        parentEventGroup: eventGroup,
1741                                                                        name                    : sampleName,
1742                                                                        template                : (event.sampleTemplate) ? event.sampleTemplate : ''
1743                                                                )
1744
1745                                                                // and add it
1746                                                                flow.study.addToSamples(newSample)
1747                                                        }
1748                                                }
1749                                        } else {
1750                                                eventGroup.addToEvents(event)
1751                                        }
1752                                } else {
1753                                        // remove from eventGroup
1754                                        if (event instanceof SamplingEvent) {
1755                                                // iterate through subjects (if we have them)
1756                                                eventGroup.subjects.each() { subject ->
1757                                                        // find all samples for this subject / event
1758                                                        flow.study.samples.findAll {
1759                                                                (
1760                                                                        it.parentEvent.equals(event) &&
1761                                                                        it.parentSubject.equals(subject) &&
1762                                                                        it.parentEventGroup.equals(eventGroup)
1763                                                                )
1764                                                        }.each() {
1765                                                                // delete this sample
1766                                                                flow.study.deleteSample( it )
1767                                                        }
1768                                                }
1769
1770                                                eventGroup.removeFromSamplingEvents(event)
1771                                        } else {
1772                                                eventGroup.removeFromEvents(event)
1773                                        }
1774                                }
1775                        }
1776                }
1777
1778                return !errors
1779        }
1780
1781        /**
1782         * Handle the wizard group page
1783         *
1784         * @param Map LocalAttributeMap (the flow scope)
1785         * @param Map localAttributeMap (the flash scope)
1786         * @param Map GrailsParameterMap (the flow parameters = form data)
1787         * @returns boolean
1788         */
1789        def groupPage(flow, flash, params) {
1790                def errors = false
1791                flash.wizardErrors = [:]
1792
1793                // remember the params in the flash scope
1794                flash.values = params
1795
1796                // iterate through groups
1797                flow.study.eventGroups.each() { eventGroup ->
1798                        // iterate through subjects
1799                        flow.study.subjects.each() { subject ->
1800                                if (params.get('subject_' + subject.getIdentifier() + '_group_' + eventGroup.getIdentifier() )) {
1801                                        // check if this subject is already part of this eventGroup
1802                                        if ( !eventGroup.subjects.find { it.equals(subject) } ) {
1803                                                // add to eventGroup
1804                                                eventGroup.addToSubjects(subject)
1805
1806                                                // iterate through samplingEvents
1807                                                eventGroup.samplingEvents.each() { samplingEvent ->
1808
1809                            def sampleName = Sample.generateSampleName(flow, subject, eventGroup, samplingEvent)
1810
1811                                                        // instantiate a sample
1812                                                        def newSample = new Sample(
1813                                                                        parentSubject   : subject,
1814                                                                        parentEvent             : samplingEvent,
1815                                                                        parentEventGroup: eventGroup,
1816                                                                        name                    : sampleName,
1817                                                                        template                : (samplingEvent.sampleTemplate) ? samplingEvent.sampleTemplate : ''
1818                                                                )
1819
1820                                                        flow.study.addToSamples(newSample)
1821                                                }
1822                                        } else {
1823                                                // do nothing
1824                                        }
1825                                } else {
1826                                        // check if this subject is a member of this eventGroup
1827                                        if (eventGroup.subjects.find { it.equals(subject) }) {
1828                                                // remove from eventGroup
1829                                                eventGroup.removeFromSubjects(subject)
1830
1831                                                // iterate through samplingEvents
1832                                                eventGroup.samplingEvents.each() { samplingEvent ->
1833                                                        flow.study.samples.findAll { (it.parentEvent.equals(samplingEvent) && it.parentSubject.equals(subject) && it.parentEventGroup.equals(eventGroup)) }.each() {
1834                                                                // delete this sample
1835                                                                flow.study.deleteSample( it )
1836                                                        }
1837                                                }
1838                                        }
1839                                }
1840                        }
1841                }
1842        }
1843
1844        /**
1845         * Handle the wizard samples page
1846         *
1847         * @param Map LocalAttributeMap (the flow scope)
1848         * @param Map localAttributeMap (the flash scope)
1849         * @param Map GrailsParameterMap (the flow parameters = form data)
1850         * @returns boolean
1851         */
1852        def samplePage(flow, flash, params) {
1853                def errors = false
1854                flash.wizardErrors = [:]
1855
1856                // remember the params in the flash scope
1857                flash.values = params
1858
1859                // iterate through samples
1860                flow.study.samples.each() { sample ->
1861                        // iterate through sample fields
1862                        sample.giveFields().each() { field ->
1863                                def value = params.get('sample_'+sample.getIdentifier()+'_'+field.escapedName())
1864
1865                                // set field value
1866                                if (!(field.name == 'name' && !value)) {
1867                                        log.info "setting "+field.name+" to "+value
1868                                        sample.setFieldValue(field.name, value)
1869                                }
1870                        }
1871
1872                        // has the template changed?
1873                        def templateName = params.get('template_' + sample.getIdentifier())
1874                        if (templateName && sample.template?.name != templateName) {
1875                                sample.template = Template.findAllByName(params.get(templateName)).find { it.entity == dbnp.studycapturing.Sample }
1876                        }
1877
1878                        // validate sample
1879                        if (!sample.validate()) {
1880                                errors = true
1881                                this.appendErrors(sample, flash.wizardErrors, 'sample_' + sample.getIdentifier() + '_' )
1882                                log.info 'error-> sample_'+sample.getIdentifier()
1883                        }
1884                }
1885
1886                return !errors
1887        }
1888
1889        /**
1890         * Handle the wizard assays page
1891         *
1892         * @param Map LocalAttributeMap (the flow scope)
1893         * @param Map localAttributeMap (the flash scope)
1894         * @param Map GrailsParameterMap (the flow parameters = form data)
1895         * @returns boolean
1896         */
1897        def assayPage(flow, flash, params) {
1898                def errors = false
1899                flash.wizardErrors = [:]
1900
1901                // remember the params in the flash scope
1902                flash.values = params
1903
1904                // handle the 'add assay' form
1905                if (flow.assay) {
1906                        flow.assay.giveFields().each() { field ->
1907                                // set field
1908                                flow.assay.setFieldValue(
1909                                        field.name,
1910                                        params.get(field.escapedName())
1911                                )
1912                        }
1913                }
1914
1915                // handle the assay data
1916                flow.study.assays.each() { assay ->
1917                        // set data
1918                        assay.giveFields().each() { field ->
1919                                assay.setFieldValue(
1920                                        field.name,
1921                                        params.get( 'assay_' + assay.getIdentifier() + '_' + field.escapedName() )
1922                                )
1923                        }
1924
1925                        // validate assay
1926                        if (!assay.validate()) {
1927                                errors = true
1928                                this.appendErrors(assay, flash.wizardErrors, 'assay_' + assay.getIdentifier() + '_')
1929                        }
1930                }
1931
1932                return !errors
1933        }
1934
1935        /**
1936         * Handle the wizard assayGroups page
1937         *
1938         * @param Map LocalAttributeMap (the flow scope)
1939         * @param Map localAttributeMap (the flash scope)
1940         * @param Map GrailsParameterMap (the flow parameters = form data)
1941         * @returns boolean
1942         */
1943        def assayGroupPage(flow, flash, params) {
1944                def errors = false
1945                flash.wizardErrors = [:]
1946
1947                // remember the params in the flash scope
1948                flash.values = params
1949
1950                // iterate through samples
1951                flow.study.samples.each() { sample ->
1952                        // iterate through assays
1953                        flow.study.assays.each() { assay ->
1954                                if (params.get( 'sample_' + sample.getIdentifier() + '_assay_' + assay.getIdentifier() )) {
1955                                        // add sample to assay
1956                                        assay.addToSamples( sample )
1957                                } else {
1958                                        // remove sample from assay
1959                                        assay.removeFromSamples( sample )
1960                                }
1961                        }
1962                }
1963               
1964                // Mark study as dirty
1965                if( flow.study.version )
1966                        flow.study.version++
1967                else   
1968                        flow.study.version = 1
1969
1970                return !errors
1971        }
1972
1973        /**
1974         * return the object from a map of objects by searching for a name
1975         * @param String name
1976         * @param Map map of objects
1977         * @return Object
1978         */
1979        def getObjectByName(name, map) {
1980                def result = null
1981                map.each() {
1982                        if (it.name == name) {
1983                                result = it
1984                        }
1985                }
1986
1987                return result
1988        }
1989
1990        /**
1991         * transform domain class validation errors into a human readable
1992         * linked hash map
1993         * @param object validated domain class
1994         * @return object  linkedHashMap
1995         */
1996        def getHumanReadableErrors(object) {
1997                def errors = [:]
1998                object.errors.getAllErrors().each() { error ->
1999                        // error.codes.each() { code -> println code }
2000
2001                        // generally speaking g.message(...) should work,
2002                        // however it fails in some steps of the wizard
2003                        // (add event, add assay, etc) so g is not always
2004                        // availably. Using our own instance of the
2005                        // validationTagLib instead so it is always
2006                        // available to us
2007                        errors[ error.getArguments()[0] ] = validationTagLib.message(error: error)
2008                }
2009
2010                return errors
2011        }
2012
2013        /**
2014         * append errors of a particular object to a map
2015         * @param object
2016         * @param map linkedHashMap
2017         * @void
2018         */
2019        def appendErrors(object, map) {
2020                this.appendErrorMap(getHumanReadableErrors(object), map)
2021        }
2022
2023        def appendErrors(object, map, prepend) {
2024                this.appendErrorMap(getHumanReadableErrors(object), map, prepend)
2025        }
2026
2027        /**
2028         * append errors of one map to another map
2029         * @param map linkedHashMap
2030         * @param map linkedHashMap
2031         * @void
2032         */
2033        def appendErrorMap(map, mapToExtend) {
2034                map.each() {key, value ->
2035                        mapToExtend[key] = ['key': key, 'value': value, 'dynamic': false]
2036                }
2037        }
2038
2039        def appendErrorMap(map, mapToExtend, prepend) {
2040                map.each() {key, value ->
2041                        mapToExtend[prepend + key] = ['key': key, 'value': value, 'dynamic': true]
2042                }
2043        }
2044
2045        /**
2046         * Parses a RelTime string and returns a nice human readable string
2047         *
2048         * @return Human Readable string or a HTTP response code 400 on error
2049         */
2050        def ajaxParseRelTime = {
2051                if (params.reltime == null) {
2052                        response.status = 400
2053                        render('reltime parameter is expected')
2054                }
2055
2056                try {
2057                        def reltime = RelTime.parseRelTime(params.reltime)
2058                        response.setContentType("text/plain; charset=UTF-8")
2059                        render reltime.toPrettyString()
2060                } catch (IllegalArgumentException e) {
2061                        response.status = 400
2062                        render(e.getMessage())
2063                }
2064        }
2065
2066        /**
2067         * Proxy for searching PubMed articles (or other articles from the Entrez DB).
2068         *
2069         * This proxy is needed because it is not allowed to fetch XML directly from a different
2070         * domain using javascript. So we have the javascript call a function on our own domain
2071         * and the proxy will fetch the data from Entrez
2072         *
2073         * @since       20100609
2074         * @param       _utility        The name of the utility, without the complete path. Example: 'esearch.fcgi'
2075         * @return      XML
2076         */
2077        def entrezProxy = {
2078                // Remove unnecessary parameters
2079                params.remove( "action" )
2080                params.remove( "controller" )
2081
2082                def url = "http://eutils.ncbi.nlm.nih.gov/entrez/eutils";
2083                def util = params.remove( "_utility" )
2084               
2085                if( !util ) { 
2086                        response.setStatus( 404, "File not found" );
2087                        return;
2088                }
2089       
2090                def paramString = params.collect { k, v -> k + '=' + v.encodeAsURL() }.join( '&' );
2091
2092                def fullUrl = url + '/' + util + '?' + paramString;
2093
2094                // Return the output of the request
2095                response.setContentType("text/xml; charset=UTF-8")
2096
2097                response <<  new URL( fullUrl ).getText('UTF-8')
2098
2099                render ""
2100        }
2101}
Note: See TracBrowser for help on using the repository browser.