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

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