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

Revision 1775, 54.7 KB (checked in by work@…, 3 years ago)

- resolves #400, study should contain at least one contact

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