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

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