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

Last change on this file since 1286 was 1286, checked in by work@…, 10 years ago
  • BIG refactoring / rewrite of the wizard to use the ajaxflow plugin, part one of ticket #183
File size: 48.6 KB
Line 
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 {
111                        render(view: "/studywizard/index")
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()
272                        }.to "waitForSave"
273                }
274
275                // render and handle subjects page
276                subjects {
277                        render(view: "_subjects")
278                        onRender {
279                                // Grom a development message
280                                if (pluginManager.getGrailsPlugin('grom')) "rendering the partial: pages/_subjects.gsp".grom()
281
282                                flow.page = 2
283
284                                if (!flash.values || !flash.values.addNumber) flash.values = [addNumber:1]
285
286                                success()
287                        }
288                        on("refresh") {
289                                // remember the params in the flash scope
290                                flash.values = params
291
292                                // refresh templates
293                                if (flow.study.subjects) {
294                                        flow.study.giveSubjectTemplates().each() {
295                                                it.refresh()
296                                        }
297                                }
298
299                                success()
300                        }.to "subjects"
301                        on("add") {
302                                // handle form data
303                                addSubjects(flow, flash, params) ? success() : error()
304                        }.to "subjects"
305                        on("delete") {
306                                // handle form data
307                                subjectPage(flow, flash, params)
308
309                                // reset errors
310                                flash.wizardErrors = [:]
311
312                                // remove subject
313                                def subjectToRemove = flow.study.subjects.find { it.identifier == (params.get('do') as int) }
314                                if (subjectToRemove) {
315                                        flow.study.deleteSubject( subjectToRemove )
316                                }
317                        }.to "subjects"
318                        on("previous") {
319                                // handle form data
320                                subjectPage(flow, flash, params)
321
322                                // reset errors
323                                flash.wizardErrors = [:]
324                                success()
325                        }.to "study"
326                        on("next") {
327                                // handle form data
328                                subjectPage(flow, flash, params) ? success() : error()
329                        }.to "events"
330                        on("quickSave") {
331                                // handle form data
332                                subjectPage(flow, flash, params) ? success() : error()
333                        }.to "waitForSave"
334                }
335
336                // render events page
337                events {
338                        render(view: "_events")
339                        onRender {
340                                // Grom a development message
341                                if (pluginManager.getGrailsPlugin('grom')) "rendering the partial: pages/_events.gsp".grom()
342
343                                flow.page = 3
344
345                                // add initial eventGroup to study
346                                if (!flow.study.eventGroups?.size()) {
347                                        flow.study.addToEventGroups(
348                                                new EventGroup(name: 'Group 1')
349                                        )
350                                }
351
352                                success()
353                        }
354                        on("clear") {
355                                // remove all events
356                                (flow.study.events + flow.study.samplingEvents).each() { event ->
357                                        if (event instanceof SamplingEvent) {
358                                                flow.study.deleteSamplingEvent( event )
359                                        } else {
360                                                flow.study.deleteEvent( event )
361                                        }
362                                }
363
364                                success()
365                        }.to "events"
366                        on("switchTemplate") {
367                                // handle form data
368                                eventPage(flow, flash, params)
369
370                                // get template
371                                def type        = params.get('eventType')
372                                def template= Template.findByName( params.get( type + 'Template' ) )
373
374                                // change template and/or instance?
375                                if (!flow.event || (flow.event instanceof Event && type == "sample") || (flow.event instanceof SamplingEvent && type == "event")) {
376                                        // create new instance
377                                        flow.event = (type == "event") ? new Event(template: template) : new SamplingEvent(template: template)
378                                } else {
379                                        flow.event.template = template
380                                }
381
382                                // reset errors
383                                flash.wizardErrors = [:]
384                                success()
385
386                        }.to "events"
387                        on("refresh") {
388                                // handle form data
389                                eventPage(flow, flash, params)
390
391                                // refresh templates
392                                flow.study.giveEventTemplates().each() {
393                                        it.refresh()
394                                }
395
396                                // refresh event template
397                                if (flow.event?.template) flow.event.template.refresh()
398
399                                // reset errors
400                                flash.wizardErrors = [:]
401                                success()
402                        }.to "events"
403                        on("add") {
404                                // handle form data
405                                eventPage(flow, flash, params)
406
407                                // reset errors
408                                flash.wizardErrors = [:]
409
410                                // add event to study
411                                if (flow.event instanceof SamplingEvent) {
412                                        flow.study.addToSamplingEvents( flow.event )
413                                } else {
414                                        flow.study.addToEvents( flow.event )
415                                }
416
417                                // validate event
418                                if (flow.event.validate()) {
419                                        // remove event from the flowscope
420                                        flow.remove('event')
421
422                                        success()
423                                } else {
424                                        // event does not validate
425                                        // remove from study
426                                        if (flow.event instanceof SamplingEvent) {
427                                                flow.study.removeFromSamplingEvents( flow.event )
428                                        } else {
429                                                flow.study.removeFromEvents( flow.event )
430                                        }
431
432                                        // append errors
433                                        this.appendErrors(flow.event, flash.wizardErrors)
434                                        error()
435                                }
436                        }.to "events"
437                        on("deleteEvent") {
438                                // handle form data
439                                eventPage(flow, flash, params)
440
441                                // reset errors
442                                flash.wizardErrors = [:]
443
444                                // find matching (sampling) event
445                                def event                       = flow.study.events.find { it.getIdentifier() == (params.get('do') as int) }
446                                def samplingEvent       = flow.study.samplingEvents.find { it.getIdentifier() == (params.get('do') as int) }
447
448                                // perform delete
449                                if (event) flow.study.deleteEvent( event )
450                                if (samplingEvent) flow.study.deleteSamplingEvent( samplingEvent )
451                        }.to "events"
452                        on("addEventGroup") {
453                                // handle form data
454                                eventPage(flow, flash, params)
455
456                                // set work variables
457                                def groupName = 'Group '
458                                def tempGroupIterator = 1
459                                def tempGroupName = groupName + tempGroupIterator
460
461                                // make sure group name is unique
462                                if (flow.study.eventGroups) {
463                                        while (flow.study.eventGroups.find { it.name == tempGroupName }) {
464                                                tempGroupIterator++
465                                                tempGroupName = groupName + tempGroupIterator
466                                        }
467                                }
468                                groupName = tempGroupName
469
470                                // add a new eventGroup
471                                flow.study.addToEventGroups(
472                                        new EventGroup(
473                                                name    : groupName
474                                        )
475                                )
476
477                                // reset errors
478                                flash.wizardErrors = [:]
479                                success()
480                        }.to "events"
481                        on("deleteEventGroup") {
482                                // handle form data
483                                eventPage(flow, flash, params)
484
485                                // reset errors
486                                flash.wizardErrors = [:]
487
488                                // remove eventGroup
489                                def eventGroupToRemove = flow.study.eventGroups.find { it.getIdentifier() == (params.get('do') as int) }
490                        }.to "events"
491                        on("duplicate") {
492                                println "duplicate"
493                                println params
494                                // handle form data
495                                eventPage(flow, flash, params)
496
497                                // reset errors
498                                flash.wizardErrors = [:]
499
500                                // clone event
501                                def event = null
502                                (((flow.study.events) ? flow.study.events : []) + ((flow.study.samplingEvents) ? flow.study.samplingEvents : [])).find { it.getIdentifier() == (params.get('do') as int) }.each {
503                                        event = (it instanceof SamplingEvent) ? new SamplingEvent() : new Event()
504
505                                        // set template
506                                        event.template = it.template
507
508                                        // copy data
509                                        it.giveFields().each() { field ->
510                                                event.setFieldValue(
511                                                        field.name,
512                                                        it.getFieldValue(field.name)
513                                                )
514                                        }
515
516                                        // assign duplicate event to study
517                                        if (event instanceof SamplingEvent) {
518                                                flow.study.addToSamplingEvents(event)
519                                        } else {
520                                                flow.study.addToEvents(event)
521                                        }
522                                }
523
524                                success()
525                        }.to "events"
526                        on("previous") {
527                                // handle form data
528                                eventPage(flow, flash, params)
529
530                                // reset errors
531                                flash.wizardErrors = [:]
532                                success()
533                        }.to "subjects"
534                        on("next") {
535                                // handle form data
536                                eventPage(flow, flash, params) ? success() : error()
537                        }.to "eventsNext"
538                        on("quickSave") {
539                                // handle form data
540                                eventPage(flow, flash, params) ? success() : error()
541                        }.to "waitForSave"
542                }
543
544                // decide to show a warning page or not
545                eventsNext {
546                        action {
547                                // Grom a development message
548                                if (pluginManager.getGrailsPlugin('grom')) ".entering eventsNext".grom()
549
550                                def assigned = false
551
552                                // check if all sampling events are in an eventGroup
553                                flow.study.samplingEvents.each() { samplingEvent ->
554                                        // iterate through eventGroups
555                                        flow.study.eventGroups.each() { eventGroup ->
556                                                if ( eventGroup.samplingEvents.find { it.equals(samplingEvent) } ) {
557                                                        assigned = true
558                                                }
559                                        }
560                                }
561
562                                if (assigned) {
563                                        toGroupsPage()
564                                } else {
565                                        toWarningPage()
566                                }
567                        }
568                        on("toWarningPage").to "unassignedSamplingEventWarning"
569                        on("toGroupsPage").to "groups"
570                }
571
572                // warning page for unassigned samplingEvent
573                unassignedSamplingEventWarning {
574                        render(view: "_unassigned_samplingEvent_warning")
575                        onRender {
576                                // Grom a development message
577                                if (pluginManager.getGrailsPlugin('grom')) "rendering the partial: pages/_unnassigned_samplingEvent_warning.gsp".grom()
578
579                                flow.page = 3
580                                success()
581                        }
582                        on("next").to "groups"
583                        on("previous").to "events"
584                }
585
586                // groups page
587                groups {
588                        render(view: "_groups")
589                        onRender {
590                                // Grom a development message
591                                if (pluginManager.getGrailsPlugin('grom')) "rendering the partial: pages/_groups.gsp".grom()
592
593                                flow.page = 3
594                                success()
595                        }
596                        on("previous") {
597                                // handle form data
598                                groupPage(flow, flash, params) ? success() : error()
599                        }.to "events"
600                        on("next") {
601                                // handle form data
602                                groupPage(flow, flash, params) ? success() : error()
603                        }.to "samples"
604                        on("quickSave") {
605                                // handle form data
606                                groupPage(flow, flash, params) ? success() : error()
607                        }.to "waitForSave"
608                }
609
610                // sample 'previous' page with warning
611                samplePrevious {
612                        render(view: "_samples_previous_warning")
613                        onRender {
614                                // Grom a development message
615                                if (pluginManager.getGrailsPlugin('grom')) "rendering the partial: pages/_samples_previous_warning.gsp".grom()
616
617                                flow.page = 4
618                        }
619                        on("next").to "samples"
620                        on("previous").to "groups"
621                }
622
623                // samples page
624                samples {
625                        render(view: "_samples")
626                        onRender {
627                                // Grom a development message
628                                if (pluginManager.getGrailsPlugin('grom')) "rendering the partial: pages/_samples.gsp".grom()
629
630                                flow.page = 4
631                                success()
632                        }
633                        on("switchTemplate") {
634                                // handle form data
635                                samplePage(flow, flash, params)
636
637                                // ignore errors
638                                flash.wizardErrors = [:]
639
640                                succes()
641                        }.to "samples"
642                        on("refresh") {
643                                // handle samples
644                                samplePage(flow, flash, params)
645
646                                // refresh all sample templates
647                                flow.study.giveSampleTemplates().each() {
648                                        it.refresh()
649                                }
650
651                                // ignore errors
652                                flash.wizardErrors = [:]
653
654                                success()
655                        }.to "samples"
656                        on("regenerate") {
657                                // handle samples
658                                samplePage(flow, flash, params)
659
660                                // remove all samples from the study
661                                flow.study.samples.findAll{true}.each() { sample ->
662                                        flow.study.removeFromSamples(sample)
663                                }
664
665                                // ignore errors
666                                flash.wizardErrors = [:]
667
668                                success()
669                        }.to "samples"
670                        on("previous") {
671                                // handle samples
672                                samplePage(flow, flash, params)
673
674                                // ignore errors
675                                flash.wizardErrors = [:]
676
677                                success()
678                        }.to "samplePrevious"
679                        on("next") {
680                                // handle form data
681                                samplePage(flow, flash, params) ? success() : error()
682                        }.to "assays"
683                        on("quickSave") {
684                                // handle form data
685                                samplePage(flow, flash, params) ? success() : error()
686                        }.to "waitForSave"
687                }
688
689                // assays page
690                assays {
691                        render(view: "_assays")
692                        onRender {
693                                // Grom a development message
694                                if (pluginManager.getGrailsPlugin('grom')) "rendering the partial: pages/_assays.gsp".grom()
695
696                                flow.page = 5
697                        }
698                        on("refresh") {
699                                // handle form data
700                                assayPage(flow, flash, params)
701
702                                // force refresh of the template
703                                if (flow.assay && flow.assay.template && flow.assay.template instanceof Template) {
704                                        flow.assay.template.refresh()
705                                }
706
707                                // reset errors
708                                flash.wizardErrors = [:]
709                                success()
710                        }.to "assays"
711            on("switchTemplate") {
712                                // handle form data
713                    assayPage(flow, flash, params)
714
715                    // find assay template
716                    def template = Template.findByName(params.get('template'))
717                    if (flow.assay) {
718                            // set template
719                            flow.assay.template = template
720                            if (template) {
721                                    flow.assay.setFieldValue(
722                                            'externalAssayID',
723                                            ucwords(flow.study.code).replaceAll("([ ]{1,})", "") + '_' + ucwords(template.name).replaceAll("([ ]{1,})", "")
724                                    )
725                            }
726                    } else {
727                            // create a new assay instance
728                            flow.assay = new Assay(template: template)
729                            if (template) {
730                                    flow.assay.setFieldValue(
731                                            'externalAssayID',
732                                            ucwords(flow.study.code).replaceAll("([ ]{1,})", "") + '_' + ucwords(template.name).replaceAll("([ ]{1,})", "")
733                                    )
734                            }
735                    }
736
737                                // reset errors
738                                flash.wizardErrors = [:]
739                                success()
740                        }.to "assays"
741                        on("add") {
742                                // handle form data
743                                assayPage(flow, flash, params)
744
745                                // reset errors
746                                flash.wizardErrors = [:]
747
748                                // add assay to study
749                                flow.study.addToAssays( flow.assay )
750
751                                // validate assay
752                                if (flow.assay.validate()) {
753                                        // remove assay from the flowscope
754                                        flow.remove('assay')
755                                        success()
756                                } else {
757                                        // assay does not validate
758                                        // remove from study
759                                        flow.study.removeFromAssays( flow.assay )
760
761                                        // append errors
762                                        this.appendErrors(flow.assay, flash.wizardErrors)
763                                        error()
764                                }
765                        }.to "assays"
766                        on("deleteAssay") {
767                                // handle form data
768                                assayPage(flow, flash, params)
769
770                                // reset errors
771                                flash.wizardErrors = [:]
772
773                                // find this assay
774                                def assay = flow.study.assays.find { it.getIdentifier() == (params.get('do') as int) }
775
776                                // perform delete
777                                if (assay) flow.study.deleteAssay( assay )
778                        }.to "assays"
779                        on("previous") {
780                                // handle form data
781                                assayPage(flow, flash, params)
782
783                                // ignore errors
784                                flash.wizardErrors = [:]
785
786                                success()
787                        }.to "samples"
788                        on("next") {
789                                // handle form data
790                                assayPage(flow, flash, params) ? success() : error()
791                        }.to "assayNext"
792                        on("quickSave") {
793                                // handle form data
794                                assayPage(flow, flash, params) ? success() : error()
795                        }.to "waitForSave"
796                }
797
798                // assayNext
799                assayNext {
800                        action {
801                                // Grom a development message
802                                if (pluginManager.getGrailsPlugin('grom')) "entering assayNext".grom()
803
804                                // have we got samples and assays?
805                                if (flow.study.assays && flow.study.samples) {
806                                        // yes, go to the group page
807                                        toAssayGroups()
808                                } else {
809                                        // no need to show the group page as
810                                        // there's nothing to group
811                                        toConfirm()
812                                }
813                        }
814                        on("toAssayGroups").to "assayGroups"
815                        on("toConfirm").to "confirm"
816                }
817
818                // assay grouping page
819                assayGroups {
820                        render(view: "_assay_groups")
821                        onRender {
822                                // Grom a development message
823                                if (pluginManager.getGrailsPlugin('grom')) "rendering the partial: pages/_assay_groups.gsp".grom()
824
825                                flow.page = 5
826                        }
827                        on("previous") {
828                                // handle form data
829                                assayGroupPage(flow, flash, params)
830
831                                // ignore errors
832                                flash.wizardErrors = [:]
833
834                                success()
835                        }.to "assays"
836                        on("next") {
837                                // handle form data
838                                assayGroupPage(flow, flash, params) ? success() : error()
839                        }.to "confirm"
840                        on("quickSave") {
841                                // handle form data
842                                assayGroupPage(flow, flash, params) ? success() : error()
843                        }.to "waitForSave"
844                }
845
846                // confirm Previous
847                confirmPrevious {
848                        action {
849                                // Grom a development message
850                                if (pluginManager.getGrailsPlugin('grom')) "entering confirmPrevious".grom()
851
852                                // have we got samples and assays?
853                                if (flow.study.assays && flow.study.samples) {
854                                        // yes, go to the group page
855                                        toAssayGroups()
856                                } else {
857                                        // no need to show the group page as
858                                        // there's nothing to group
859                                        toAssays()
860                                }
861                        }
862                        on("toAssayGroups").to "assayGroups"
863                        on("toAssays").to "assays"
864                }
865
866                // confirmation
867                confirm {
868                        render(view: "_confirmation")
869                        onRender {
870                                // Grom a development message
871                                if (pluginManager.getGrailsPlugin('grom')) "rendering the partial: pages/_confirmation.gsp".grom()
872
873                                flow.page = 6
874                        }
875                        on("toStudy").to "study"
876                        on("toSubjects").to "subjects"
877                        on("toEvents").to "events"
878                        on("toGroups").to "groups"
879                        on("toSamples").to "samples"
880                        on("toAssays").to "assays"
881                        on("toAssayGroups").to "assayGroups"
882                        on("previous").to "confirmPrevious"
883                        on("next").to "waitForSave"
884                        on("quickSave").to "waitForSave"
885                }
886
887                waitForSave {
888                        render(view: "_wait")
889                        onRender {
890                                // Grom a development message
891                                if (pluginManager.getGrailsPlugin('grom')) "rendering the partial: pages/_wait.gsp".grom()
892
893                                flow.page = 7
894                        }
895                        on("next").to "save"
896                }
897
898                // store all study data
899                save {
900                        action {
901                                // Grom a development message
902                                if (pluginManager.getGrailsPlugin('grom')) "entering save".grom()
903
904                                flash.wizardErrors = [:]
905
906                                // persist data to the database
907                                try {
908                                        // save study
909                                        // Grom a development message
910                                        if (pluginManager.getGrailsPlugin('grom')) "saving study".grom()
911
912                                        // Make sure the owner of the study is set right
913                                        flow.study.owner = authenticationService.getLoggedInUser()
914
915                                        if (!flow.study.save(flush:true)) {
916                                                this.appendErrors(flow.study, flash.wizardErrors)
917                                                throw new Exception('error saving study')
918                                        }
919                                        log.info ".saved study "+flow.study+" (id: "+flow.study.id+")"
920
921                                        success()
922                                } catch (Exception e) {
923                                        // rollback
924                                        this.appendErrorMap(['exception': e.toString() + ', see log for stacktrace' ], flash.wizardErrors)
925
926                                        // stacktrace in flash scope
927                                        flash.debug = e.getStackTrace()
928
929                                        error()
930                                }
931                        }
932                        on("error").to "error"
933                        on(Exception).to "error"
934                        on("success").to "done"
935                }
936
937                // error storing data
938                error {
939                        render(view: "_error")
940                        onRender {
941                                // Grom a development message
942                                if (pluginManager.getGrailsPlugin('grom')) "rendering the partial: pages/_error.gsp".grom()
943
944                                flow.page = 6
945                        }
946                        on("next").to "waitForSave"
947                        on("previous").to "samples"
948                }
949
950                // render finish page
951                done {
952                        render(view: "_done")
953                        onRender {
954                                // Grom a development message
955                                if (pluginManager.getGrailsPlugin('grom')) "rendering the partial: pages/_done.gsp".grom()
956
957                                flow.page = 7
958                        }
959                        onEnd {
960                                // clean flow scope
961                                flow.clear()
962                        }
963                }
964        }
965
966        /**
967         * load a study
968         * @param Map LocalAttributeMap (the flow scope)
969         * @param Map localAttributeMap (the flash scope)
970         * @param Map GrailsParameterMap (the flow parameters = form data)
971         * @returns boolean
972         */
973        def loadStudy(flow, flash, params, user) {
974                flash.wizardErrors      = [:]
975
976                // load study
977                try {
978                        // load study
979                        println "een"
980                        def study = (params.studyid) ? Study.findById( params.studyid ) : Study.findByTitle( params.study )
981                        println "twee"
982
983                        // Check whether the user is allowed to edit this study. If it is not allowed
984                        // the used should had never seen a link to this page, so he should never get
985                        // here. That's why we just return false
986            if (!study.canWrite(user)){
987                                return false
988                        }
989
990                        // Grails tends to lazily initialize objects. While in theory this
991                        // is a nice feature, it does not work well with our complex setup
992                        // using the Identity class to match POST variables with object
993                        // instances. This lazy initialization caused two issues:
994                        // #147 and #223, and both are now resolved by forcing objects to
995                        // be instantiated / initialized when a study is loaded from the
996                        // database
997                        study.template
998                        study.hasMany.each { name, type ->
999                                // dynamically instantiate all identity classes
1000                                if (type.toString() =~ "dbnp.studycapturing") {
1001                                        study.getProperty( name ).each { }
1002                                }
1003                        }
1004
1005                        // store study in the flowscope
1006                        flow.study = study
1007
1008                        // set 'quicksave' variable
1009                        flow.quickSave = true
1010
1011                        return true
1012                } catch (Exception e) {
1013                        // rollback
1014                        this.appendErrorMap(['exception': e.getMessage() + ', see log for stacktrace'], flash.wizardErrors)
1015
1016                        return false
1017                }
1018        }
1019
1020        /**
1021         * Handle the wizard study page
1022         *
1023         * @param Map LocalAttributeMap (the flow scope)
1024         * @param Map localAttributeMap (the flash scope)
1025         * @param Map GrailsParameterMap (the flow parameters = form data)
1026         * @returns boolean
1027         */
1028        def studyPage(flow, flash, params) {
1029                flash.values            = params
1030                flash.wizardErrors      = [:]
1031
1032                // instantiate study of it is not yet present
1033                if (!flow.study) flow.study = new Study()
1034
1035                // did the study template change?
1036                if (params.get('template').size() && flow.study.template?.name != params.get('template')) {
1037                        // set the template
1038                        flow.study.template = Template.findByName(params.remove('template'))
1039                }
1040
1041                // does the study have a template set?
1042                if (flow.study.template && flow.study.template instanceof Template) {
1043                        // yes, iterate through template fields
1044                        flow.study.giveFields().each() {
1045                                // and set their values
1046                                flow.study.setFieldValue(it.name, params.get(it.escapedName()))
1047                        }
1048                }
1049
1050                // handle publications
1051                handlePublications(flow, flash, params)
1052
1053                // handle contacts
1054                handleContacts(flow, flash, params)
1055
1056                // handle users (readers, writers)
1057                handleUsers(flow, flash, params, 'readers')
1058                handleUsers(flow, flash, params, 'writers')
1059
1060                // handle public checkbox
1061                if (params.get("publicstudy")) {
1062                        flow.study.publicstudy = params.get("publicstudy")
1063                }
1064
1065                // have we got a template?
1066                if (flow.study.template && flow.study.template instanceof Template) {
1067                        // validate the study
1068                        if (flow.study.validate()) {
1069                                // instance is okay
1070                                return true
1071                        } else {
1072                                // validation failed
1073                                this.appendErrors(flow.study, flash.wizardErrors)
1074                                return false
1075                        }
1076                } else {
1077                        // no, return an error that the template is not set
1078                        this.appendErrorMap(['template': g.message(code: 'select.not.selected.or.add', args: ['template'])], flash.wizardErrors)
1079                        return false
1080                }
1081        }
1082
1083        /**
1084         * re-usable code for handling publications form data in a web flow
1085         * @param Map LocalAttributeMap (the flow scope)
1086         * @param Map localAttributeMap (the flash scope)
1087         * @param Map GrailsParameterMap (the flow parameters = form data)
1088         * @returns boolean
1089         */
1090        def handlePublications(flow, flash, params) {
1091                flash.wizardErrors      = [:]
1092
1093                if (!flow.study.publications) flow.study.publications = []
1094
1095                // Check the ids of the pubblications that should be attached
1096                // to this study. If they are already attached, keep 'm. If
1097                // studies are attached that are not in the selected (i.e. the
1098                // user deleted them), remove them
1099                def publicationIDs = params.get('publication_ids')
1100                if (publicationIDs) {
1101                        // Find the individual IDs and make integers
1102                        publicationIDs = publicationIDs.split(',').collect { Integer.parseInt(it, 10) }
1103
1104                        // First remove the publication that are not present in the array
1105                        flow.study.publications.removeAll { publication -> !publicationIDs.find { id -> id == publication.id } }
1106
1107                        // Add those publications not yet present in the database
1108                        publicationIDs.each { id ->
1109                                if (!flow.study.publications.find { publication -> id == publication.id }) {
1110                                        def publication = Publication.get(id)
1111                                        if (publication) {
1112                                                flow.study.addToPublications(publication)
1113                                        } else {
1114                                                log.info('.publication with ID ' + id + ' not found in database.')
1115                                        }
1116                                }
1117                        }
1118
1119                } else {
1120                        log.info('.no publications selected.')
1121                        flow.study.publications.clear()
1122                }
1123
1124        }
1125
1126        /**
1127         * re-usable code for handling contacts form data in a web flow
1128         * @param Map LocalAttributeMap (the flow scope)
1129         * @param Map localAttributeMap (the flash scope)
1130         * @param Map GrailsParameterMap (the flow parameters = form data)
1131         * @return boolean
1132         */
1133        def handleContacts(flow, flash, params) {
1134                flash.wizardErrors      = [:]
1135
1136                if (!flow.study.persons) flow.study.persons = []
1137
1138                // Check the ids of the contacts that should be attached
1139                // to this study. If they are already attached, keep 'm. If
1140                // studies are attached that are not in the selected (i.e. the
1141                // user deleted them), remove them
1142
1143                // Contacts are saved as [person_id]-[role_id]
1144                def contactIDs = params.get('contacts_ids')
1145                if (contactIDs) {
1146                        // Find the individual IDs and make integers
1147                        contactIDs = contactIDs.split(',').collect {
1148                                def parts = it.split('-')
1149                                return [person: Integer.parseInt(parts[0]), role: Integer.parseInt(parts[1])]
1150                        }
1151
1152                        // First remove the contacts that are not present in the array
1153                        flow.study.persons.removeAll {
1154                                studyperson -> !contactIDs.find { ids -> (ids.person == studyperson.person.id) && (ids.role == studyperson.role.id) }
1155                        }
1156
1157                        // Add those contacts not yet present in the database
1158                        contactIDs.each { ids ->
1159                                if (!flow.study.persons.find { studyperson -> (ids.person == studyperson.person.id) && (ids.role == studyperson.role.id) }) {
1160                                        def person = Person.get(ids.person)
1161                                        def role = PersonRole.get(ids.role)
1162                                        if (person && role) {
1163                                                // Find a studyperson object with these parameters
1164                                                def studyPerson = StudyPerson.findAll().find { studyperson -> studyperson.person.id == person.id && studyperson.role.id == role.id }
1165
1166                                                // If if does not yet exist, save the example
1167                                                if (!studyPerson) {
1168                                                        studyPerson = new StudyPerson(
1169                                                                person: person,
1170                                                                role: role
1171                                                        )
1172                                                        studyPerson.save(flush: true)
1173                                                }
1174
1175                                                flow.study.addToPersons(studyPerson)
1176                                        } else {
1177                                                log.info('.person ' + ids.person + ' or Role ' + ids.role + ' not found in database.')
1178                                        }
1179                                }
1180                        }
1181                } else {
1182                        log.info('.no persons selected.')
1183                        flow.study.persons.clear()
1184                }
1185
1186        }
1187
1188        /**
1189         * re-usable code for handling contacts form data in a web flow
1190         * @param Map LocalAttributeMap (the flow scope)
1191         * @param Map localAttributeMap (the flash scope)
1192         * @param Map GrailsParameterMap (the flow parameters = form data)
1193         * @param String    'readers' or 'writers'
1194         * @return boolean
1195         */
1196        def handleUsers(flow, flash, params, type) {
1197                flash.wizardErrors = [:]
1198
1199                def users = []
1200
1201                if (type == "readers") {
1202                        users = flow.study.readers ?: []
1203                } else if (type == "writers") {
1204                        users = flow.study.writers ?: []
1205                }
1206
1207                // Check the ids of the contacts that should be attached
1208                // to this study. If they are already attached, keep 'm. If
1209                // studies are attached that are not in the selected (i.e. the
1210                // user deleted them), remove them
1211
1212                // Users are saved as user_id
1213                def userIDs = params.get(type + '_ids')
1214                if (userIDs) {
1215                        // Find the individual IDs and make integers
1216                        userIDs = userIDs.split(',').collect { Integer.parseInt(it, 10) }
1217
1218                        // First remove the publication that are not present in the array
1219                        users.removeAll { user -> !userIDs.find { id -> id == user.id } }
1220
1221                        // Add those publications not yet present in the database
1222                        userIDs.each { id ->
1223                                if (!users.find { user -> id == user.id }) {
1224                                        def user = SecUser.get(id)
1225                                        if (user) {
1226                                                users.add(user)
1227                                        } else {
1228                                                log.info('.user with ID ' + id + ' not found in database.')
1229                                        }
1230                                }
1231                        }
1232
1233                } else {
1234                        log.info('.no users selected.')
1235                        users.clear()
1236                }
1237
1238                if (type == "readers") {
1239                        if (flow.study.readers)
1240                                flow.study.readers.clear()
1241                        users.each { flow.study.addToReaders(it) }
1242                } else if (type == "writers") {
1243                        if (flow.study.writers)
1244                                flow.study.writers.clear()
1245
1246                        users.each { flow.study.addToWriters(it) }
1247                }
1248        }
1249
1250        /**
1251         * Handle the wizard subject page
1252         *
1253         * @param Map LocalAttributeMap (the flow scope)
1254         * @param Map localAttributeMap (the flash scope)
1255         * @param Map GrailsParameterMap (the flow parameters = form data)
1256         * @returns boolean
1257         */
1258        def subjectPage(flow, flash, params) {
1259                def errors = false
1260                flash.wizardErrors = [:]
1261
1262                // remember the params in the flash scope
1263                flash.values = params
1264
1265                // iterate through subjects
1266                flow.study.subjects.each() { subject ->
1267                        // iterate through (template and domain) fields
1268                        subject.giveFields().each() { field ->
1269                                // set field
1270                                subject.setFieldValue(
1271                                        field.name,
1272                                        params.get('subject_' + subject.getIdentifier() + '_' + field.escapedName())
1273                                )
1274                        }
1275
1276                        // validate subject
1277                        if (!subject.validate()) {
1278                                errors = true
1279                                this.appendErrors(subject, flash.wizardErrors, 'subject_' + subject.getIdentifier() + '_')
1280                        }
1281                }
1282
1283                return !errors
1284        }
1285
1286        /**
1287         * Add a number of subjects to a study
1288         *
1289         * required params entities:
1290         * -addNumber (int)
1291         * -species   (string)
1292         * -template  (string)
1293         *
1294         * @param Map LocalAttributeMap (the flow scope)
1295         * @param Map localAttributeMap (the flash scope)
1296         * @param Map GrailsParameterMap (the flow parameters = form data)
1297         * @returns boolean
1298         */
1299        def addSubjects(flow, flash, params) {
1300                // remember the params in the flash scope
1301                flash.values = params
1302
1303                // handle the subject page
1304                subjectPage(flow, flash, params)
1305
1306                // (re)set error message
1307                flash.wizardErrors = [:]
1308
1309                // set work variables
1310                def errors              = false
1311                def number              = params.get('addNumber') as int
1312                def species             = Term.findByName(params.get('species'))
1313                def template    = Template.findByName(params.get('template'))
1314
1315                // can we add subjects?
1316                if (number > 0 && species && template) {
1317                        // add subjects to study
1318                        number.times {
1319                                // work variables
1320                                def subjectName = 'Subject '
1321                                def subjectIterator = 1
1322                                def tempSubjectName = subjectName + subjectIterator
1323
1324                                // make sure subject name is unique
1325                                if (flow.study.subjects) {
1326                                        while (flow.study.subjects.find { it.name == tempSubjectName }) {
1327                                                subjectIterator++
1328                                                tempSubjectName = subjectName + subjectIterator
1329                                        }
1330                                }
1331                                subjectName = tempSubjectName
1332
1333                                // create a subject instance
1334                                def subject = new Subject(
1335                                        name            : subjectName,
1336                                        species         : species,
1337                                        template        : template
1338                                )
1339
1340                                // add it to the study
1341                                flow.study.addToSubjects( subject )
1342
1343                                // validate subject
1344                                if (subject.validate()) {
1345                                        log.info ".added subject "+subject
1346                                } else {
1347                                        // whoops?
1348                                        flow.study.removeFromSubjects( subject )
1349
1350                                        // append errors
1351                                        this.appendErrors(subject, flash.wizardErrors)
1352                                        errors = true
1353                                }
1354                        }
1355                } else {
1356                        // add feedback
1357                        errors = true
1358                        if (number < 1) this.appendErrorMap(['addNumber': 'Enter a positive number of subjects to add'], flash.wizardErrors)
1359                        if (!species)   this.appendErrorMap(['species': g.message(code: 'select.not.selected.or.add', args: ['species'])], flash.wizardErrors)
1360                        if (!template)  this.appendErrorMap(['template': g.message(code: 'select.not.selected.or.add', args: ['template'])], flash.wizardErrors)
1361                }
1362
1363                return !errors
1364        }
1365
1366        /**
1367         * Handle the wizard event page
1368         *
1369         * @param Map LocalAttributeMap (the flow scope)
1370         * @param Map localAttributeMap (the flash scope)
1371         * @param Map GrailsParameterMap (the flow parameters = form data)
1372         * @returns boolean
1373         */
1374        def eventPage(flow, flash, params) {
1375                def errors = false
1376                flash.wizardErrors = [:]
1377
1378                // remember the params in the flash scope
1379                flash.values = params
1380
1381                // handle the 'add event' form
1382                if (flow.event) {
1383                        flow.event.giveFields().each() { field ->
1384                                // set field
1385                                flow.event.setFieldValue(
1386                                        field.name,
1387                                        params.get(field.escapedName())
1388                                )
1389                        }
1390                }
1391
1392                // handle the eventGroup names and grouping
1393                def name        = ""
1394                def tempName= ""
1395                flow.study.eventGroups.each() { eventGroup ->
1396                        // iterate through templates
1397                        flow.study.giveAllEventTemplates().each() { template ->
1398                                tempName = params.get( 'eventGroup_' + eventGroup.getIdentifier() + '_' + template.getIdentifier() )
1399
1400                                // is the name different?
1401                                if (tempName != eventGroup.name) {
1402                                        name = tempName
1403                                }
1404                        }
1405
1406                        // should the name change?
1407                        if (name) {
1408                                // yes, change it
1409                                eventGroup.name = name
1410                                name = ""
1411                        }
1412
1413                        // handle eventGrouping
1414                        ( ((flow.study.events) ? flow.study.events : []) + ((flow.study.samplingEvents) ? flow.study.samplingEvents : []) ) .each() { event ->
1415                                if (params.get( 'event_' + event.getIdentifier() + '_group_' + eventGroup.getIdentifier() )) {
1416                                        // add to eventGroup
1417                                        if (event instanceof SamplingEvent) {
1418                                                // check if we are already in this eventGroup
1419                                                if (!eventGroup.samplingEvents.find { it.equals(event) }) {
1420                                                        // no, add it
1421                                                        eventGroup.addToSamplingEvents(event)
1422
1423                                                        // iterate through subjects for this eventGroup
1424                                                        eventGroup.subjects.each() { subject ->
1425                                                                // instantiate a sample for this subject / event
1426                                                                def samplingEventName = ucwords(event.template.name)
1427                                                                def eventGroupName = ucwords(eventGroup.name).replaceAll("([ ]{1,})", "")
1428                                                                def sampleName = (ucwords(subject.name) + '_' + samplingEventName + '_' + eventGroupName + '_' + new RelTime(event.startTime).toString()).replaceAll("([ ]{1,})", "")
1429                                                                def tempSampleIterator = 0
1430                                                                def tempSampleName = sampleName
1431
1432                                                                // make sure sampleName is unique
1433                                                                if (flow.study.samples) {
1434                                                                        while (flow.study.samples.find { it.name == tempSampleName }) {
1435                                                                                tempSampleIterator++
1436                                                                                tempSampleName = sampleName + "_" + tempSampleIterator
1437                                                                        }
1438                                                                        sampleName = tempSampleName
1439                                                                }
1440
1441                                                                // instantiate a sample
1442                                                                flow.study.addToSamples(
1443                                                                        new Sample(
1444                                                                                parentSubject   : subject,
1445                                                                                parentEvent             : event,
1446                                                                                parentEventGroup: eventGroup,
1447                                                                                name                    : sampleName,
1448                                                                                template                : (event.sampleTemplate) ? event.sampleTemplate : ''
1449                                                                        )
1450                                                                )
1451                                                        }
1452                                                }
1453                                        } else {
1454                                                eventGroup.addToEvents(event)
1455                                        }
1456                                } else {
1457                                        // remove from eventGroup
1458                                        if (event instanceof SamplingEvent) {
1459                                                // iterate through subjects (if we have them)
1460                                                eventGroup.subjects.each() { subject ->
1461                                                        // find all samples for this subject / event
1462                                                        flow.study.samples.findAll { (it.parentEvent.equals(event) && it.parentSubject.equals(subject) ) }.each() {
1463                                                                // delete this sample
1464                                                                flow.study.removeFromSamples( it )
1465                                                                it.delete()
1466                                                        }
1467                                                }
1468
1469                                                eventGroup.removeFromSamplingEvents(event)
1470                                        } else {
1471                                                eventGroup.removeFromEvents(event)
1472                                        }
1473                                }
1474                        }
1475                }
1476
1477                // handle the (sampling) events
1478                ( ((flow.study.events) ? flow.study.events : []) + ((flow.study.samplingEvents) ? flow.study.samplingEvents : []) ) .each() { event ->
1479                        event.giveFields().each() { field ->
1480                                event.setFieldValue(
1481                                        field.name,
1482                                        params.get( 'event_' + event.getIdentifier() + '_' + field.escapedName() )
1483                                )
1484                        }
1485
1486                        // validate event
1487                        if (!event.validate()) {
1488                                errors = true
1489                                this.appendErrors(event, flash.wizardErrors)
1490                        }
1491                }
1492
1493                return !errors
1494        }
1495
1496        /**
1497         * Handle the wizard group page
1498         *
1499         * @param Map LocalAttributeMap (the flow scope)
1500         * @param Map localAttributeMap (the flash scope)
1501         * @param Map GrailsParameterMap (the flow parameters = form data)
1502         * @returns boolean
1503         */
1504        def groupPage(flow, flash, params) {
1505                def errors = false
1506                flash.wizardErrors = [:]
1507
1508                // remember the params in the flash scope
1509                flash.values = params
1510
1511                // iterate through groups
1512                flow.study.eventGroups.each() { eventGroup ->
1513                        // iterate through subjects
1514                        flow.study.subjects.each() { subject ->
1515                                if (params.get('subject_' + subject.getIdentifier() + '_group_' + eventGroup.getIdentifier() )) {
1516                                        // check if this subject is already part of this eventGroup
1517                                        if ( !eventGroup.subjects.find { it.equals(subject) } ) {
1518                                                // add to eventGroup
1519                                                eventGroup.addToSubjects(subject)
1520
1521                                                // iterate through samplingEvents
1522                                                eventGroup.samplingEvents.each() { samplingEvent ->
1523                                                        def samplingEventName = ucwords(samplingEvent.template.name)
1524                                                        def eventGroupName = ucwords(eventGroup.name)
1525                                                        def sampleName = (ucwords(subject.name) + '_' + samplingEventName + '_' + eventGroupName + '_' + new RelTime(samplingEvent.startTime).toString()).replaceAll("([ ]{1,})", "")
1526                                                        def tempSampleIterator = 0
1527                                                        def tempSampleName = sampleName
1528
1529                                                        // make sure sampleName is unique
1530                                                        if (flow.study.samples) {
1531                                                                while (flow.study.samples.find { it.name == tempSampleName }) {
1532                                                                        tempSampleIterator++
1533                                                                        tempSampleName = sampleName + "_" + tempSampleIterator
1534                                                                }
1535                                                                sampleName = tempSampleName
1536                                                        }
1537
1538                                                        // instantiate a sample
1539                                                        flow.study.addToSamples(
1540                                                                new Sample(
1541                                                                        parentSubject   : subject,
1542                                                                        parentEvent             : samplingEvent,
1543                                                                        parentEventGroup: eventGroup,
1544                                                                        name                    : sampleName,
1545                                                                        template                : (samplingEvent.sampleTemplate) ? samplingEvent.sampleTemplate : ''
1546                                                                )
1547                                                        )
1548                                                }
1549                                        } else {
1550                                        }
1551                                } else {
1552                                        // check if this subject is a member of this eventGroup
1553                                        if (eventGroup.subjects.find { it.equals(subject) }) {
1554                                                // remove from eventGroup
1555                                                eventGroup.removeFromSubjects(subject)
1556
1557                                                // iterate through samplingEvents
1558                                                eventGroup.samplingEvents.each() { samplingEvent ->
1559                                                        flow.study.samples.findAll { (it.parentEvent.equals(samplingEvent) && it.parentSubject.equals(subject) && it.parentEventGroup.equals(eventGroup)) }.each() {
1560                                                                // delete this sample
1561                                                                flow.study.removeFromSamples(it)
1562                                                                it.delete()
1563                                                        }
1564                                                }
1565                                        }
1566                                }
1567                        }
1568                }
1569        }
1570
1571        /**
1572         * Handle the wizard samples page
1573         *
1574         * @param Map LocalAttributeMap (the flow scope)
1575         * @param Map localAttributeMap (the flash scope)
1576         * @param Map GrailsParameterMap (the flow parameters = form data)
1577         * @returns boolean
1578         */
1579        def samplePage(flow, flash, params) {
1580                def errors = false
1581                flash.wizardErrors = [:]
1582
1583                // remember the params in the flash scope
1584                flash.values = params
1585
1586                // iterate through samples
1587                flow.study.samples.each() { sample ->
1588                        // iterate through sample fields
1589                        sample.giveFields().each() { field ->
1590                                def value = params.get('sample_'+sample.getIdentifier()+'_'+field.escapedName())
1591
1592                                // set field value
1593                                if (!(field.name == 'name' && !value)) {
1594                                        log.info "setting "+field.name+" to "+value
1595                                        sample.setFieldValue(field.name, value)
1596                                }
1597                        }
1598
1599                        // has the template changed?
1600                        def templateName = params.get('template_' + sample.getIdentifier())
1601                        if (templateName && sample.template?.name != templateName) {
1602                                sample.template = Template.findByName(templateName)
1603                        }
1604
1605                        // validate sample
1606                        if (!sample.validate()) {
1607                                errors = true
1608                                this.appendErrors(sample, flash.wizardErrors, 'sample_' + sample.getIdentifier() + '_' )
1609                                log.info 'error-> sample_'+sample.getIdentifier()
1610                        }
1611                }
1612
1613                return !errors
1614        }
1615
1616        /**
1617         * Handle the wizard assays page
1618         *
1619         * @param Map LocalAttributeMap (the flow scope)
1620         * @param Map localAttributeMap (the flash scope)
1621         * @param Map GrailsParameterMap (the flow parameters = form data)
1622         * @returns boolean
1623         */
1624        def assayPage(flow, flash, params) {
1625                def errors = false
1626                flash.wizardErrors = [:]
1627
1628                // remember the params in the flash scope
1629                flash.values = params
1630
1631                // handle the 'add assay' form
1632                if (flow.assay) {
1633                        flow.assay.giveFields().each() { field ->
1634                                // set field
1635                                flow.assay.setFieldValue(
1636                                        field.name,
1637                                        params.get(field.escapedName())
1638                                )
1639                        }
1640                }
1641
1642                // handle the assay data
1643                flow.study.assays.each() { assay ->
1644                        // set data
1645                        assay.giveFields().each() { field ->
1646                                assay.setFieldValue(
1647                                        field.name,
1648                                        params.get( 'assay_' + assay.getIdentifier() + '_' + field.escapedName() )
1649                                )
1650                        }
1651
1652                        // validate assay
1653                        if (!assay.validate()) {
1654                                errors = true
1655                                this.appendErrors(assay, flash.wizardErrors, 'assay_' + assay.getIdentifier() + '_')
1656                        }
1657                }
1658
1659                return !errors
1660        }
1661
1662        /**
1663         * Handle the wizard assayGroups page
1664         *
1665         * @param Map LocalAttributeMap (the flow scope)
1666         * @param Map localAttributeMap (the flash scope)
1667         * @param Map GrailsParameterMap (the flow parameters = form data)
1668         * @returns boolean
1669         */
1670        def assayGroupPage(flow, flash, params) {
1671                def errors = false
1672                flash.wizardErrors = [:]
1673
1674                // remember the params in the flash scope
1675                flash.values = params
1676
1677                // iterate through samples
1678                flow.study.samples.each() { sample ->
1679                        // iterate through assays
1680                        flow.study.assays.each() { assay ->
1681                                if (params.get( 'sample_' + sample.getIdentifier() + '_assay_' + assay.getIdentifier() )) {
1682                                        // add sample to assay
1683                                        assay.addToSamples( sample )
1684                                } else {
1685                                        // remove sample from assay
1686                                        assay.removeFromSamples( sample )
1687                                }
1688                        }
1689                }
1690
1691                return !errors
1692        }
1693
1694        /**
1695         * groovy / java equivalent of php's ucwords function
1696         *
1697         * Capitalize all first letters of separate words
1698         *
1699         * @param String
1700         * @return String
1701         */
1702        public static ucwords(String text) {
1703                def newText = ''
1704
1705                // change case to lowercase
1706                text = text.toLowerCase()
1707
1708                // iterate through words
1709                text.split(" ").each() {
1710                        newText += it[0].toUpperCase() + it.substring(1) + " "
1711                }
1712
1713                return newText.substring(0, newText.size()-1)
1714        }
1715
1716        /**
1717         * return the object from a map of objects by searching for a name
1718         * @param String name
1719         * @param Map map of objects
1720         * @return Object
1721         */
1722        def getObjectByName(name, map) {
1723                def result = null
1724                map.each() {
1725                        if (it.name == name) {
1726                                result = it
1727                        }
1728                }
1729
1730                return result
1731        }
1732
1733        /**
1734         * transform domain class validation errors into a human readable
1735         * linked hash map
1736         * @param object validated domain class
1737         * @return object  linkedHashMap
1738         */
1739        def getHumanReadableErrors(object) {
1740                def errors = [:]
1741                object.errors.getAllErrors().each() { error ->
1742                        // error.codes.each() { code -> println code }
1743
1744                        // generally speaking g.message(...) should work,
1745                        // however it fails in some steps of the wizard
1746                        // (add event, add assay, etc) so g is not always
1747                        // availably. Using our own instance of the
1748                        // validationTagLib instead so it is always
1749                        // available to us
1750                        errors[ error.getArguments()[0] ] = validationTagLib.message(error: error)
1751                }
1752
1753                return errors
1754        }
1755
1756        /**
1757         * append errors of a particular object to a map
1758         * @param object
1759         * @param map linkedHashMap
1760         * @void
1761         */
1762        def appendErrors(object, map) {
1763                this.appendErrorMap(getHumanReadableErrors(object), map)
1764        }
1765
1766        def appendErrors(object, map, prepend) {
1767                this.appendErrorMap(getHumanReadableErrors(object), map, prepend)
1768        }
1769
1770        /**
1771         * append errors of one map to another map
1772         * @param map linkedHashMap
1773         * @param map linkedHashMap
1774         * @void
1775         */
1776        def appendErrorMap(map, mapToExtend) {
1777                map.each() {key, value ->
1778                        mapToExtend[key] = ['key': key, 'value': value, 'dynamic': false]
1779                }
1780        }
1781
1782        def appendErrorMap(map, mapToExtend, prepend) {
1783                map.each() {key, value ->
1784                        mapToExtend[prepend + key] = ['key': key, 'value': value, 'dynamic': true]
1785                }
1786        }
1787
1788        /**
1789         * Parses a RelTime string and returns a nice human readable string
1790         *
1791         * @return Human Readable string or a HTTP response code 400 on error
1792         */
1793        def ajaxParseRelTime = {
1794                if (params.reltime == null) {
1795                        response.status = 400
1796                        render('reltime parameter is expected')
1797                }
1798
1799                try {
1800                        def reltime = RelTime.parseRelTime(params.reltime)
1801                        render reltime.toPrettyString()
1802                } catch (IllegalArgumentException e) {
1803                        response.status = 400
1804                        render(e.getMessage())
1805                }
1806        }
1807
1808        /**
1809         * Proxy for searching PubMed articles (or other articles from the Entrez DB).
1810         *
1811         * This proxy is needed because it is not allowed to fetch XML directly from a different
1812         * domain using javascript. So we have the javascript call a function on our own domain
1813         * and the proxy will fetch the data from Entrez
1814         *
1815         * @since       20100609
1816         * @param       _utility        The name of the utility, without the complete path. Example: 'esearch.fcgi'
1817         * @return      XML
1818         */
1819        def entrezProxy = {
1820                // Remove unnecessary parameters
1821                params.remove( "action" )
1822                params.remove( "controller" )
1823
1824                def url = "http://eutils.ncbi.nlm.nih.gov/entrez/eutils";
1825                def util = params.remove( "_utility" )
1826                def paramString = params.collect { k, v -> k + '=' + v.encodeAsURL() }.join( '&' );
1827
1828                def fullUrl = url + '/' + util + '?' + paramString;
1829
1830                // Return the output of the request
1831                // render fullUrl;
1832                render(
1833                    text:           new URL( fullUrl ).getText(),
1834                    contentType:    "text/xml",
1835                    encoding:       "UTF-8"
1836                );
1837        }
1838}
Note: See TracBrowser for help on using the repository browser.