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

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