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

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