root/trunk/grails-app/controllers/dbnp/studycapturing/StudyWizardController.groovy @ 1304

Revision 1304, 48.6 KB (checked in by work@…, 3 years ago)

- and redirecting into studyWizard instead of studywizard (views)

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