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

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