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

Last change on this file since 1594 was 1594, checked in by robert@…, 8 years ago

Finishing touch after refactoring entity tokens (#329)

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