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

Last change on this file since 1977 was 1977, checked in by robert@…, 11 years ago
  • Implemented silent remote login method
  • Added a clean error message if an optimistic locking exception occurs in study wizard
  • Made the study wizard send notifications if only the assay-sample associations has been changed
  • Property svn:keywords set to Rev Author Date
File size: 56.9 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: 1977 $
16 * $Author: robert@isdat.nl $
17 * $Date: 2011-08-18 12:13:36 +0000 (do, 18 aug 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 (org.springframework.dao.OptimisticLockingFailureException e) {
1151                                        // rollback
1152                                        this.appendErrorMap(['exception': 'This study has been changed by another user while you were editing. Unfortunately, we can\'t save your changes. Please restart the wizard.' ], flash.wizardErrors)
1153
1154                                        // stacktrace in flash scope
1155                                        flash.debug = e.getStackTrace()
1156
1157                                        error()
1158                                } catch (Exception e) {
1159                                        // rollback
1160                                        this.appendErrorMap(['exception': e.toString() + ', see log for stacktrace' ], flash.wizardErrors)
1161
1162                                        // stacktrace in flash scope
1163                                        flash.debug = e.getStackTrace()
1164
1165                                        error()
1166                                }
1167                        }
1168                        on("error").to "error"
1169                        on(Exception).to "error"
1170                        on("success").to "done"
1171                }
1172
1173                // error storing data
1174                error {
1175                        render(view: "_error")
1176                        onRender {
1177                                // Grom a development message
1178                                if (pluginManager.getGrailsPlugin('grom')) "rendering the partial: pages/_error.gsp".grom()
1179
1180                                flow.page = 6
1181                        }
1182                        on("next").to "save"
1183                        on("previous").to "samples"
1184                        on("toPageOne").to "study"
1185                        on("toPageTwo").to "subjects"
1186                        on("toPageThree").to "events"
1187                        on("toPageFour").to "samples"
1188                        on("toPageFive").to "assays"
1189                        on("toPageSix").to "confirm"
1190                        on("toPageSeven").to "save"
1191                }
1192
1193                // render finish page
1194                done {
1195                        render(view: "_done")
1196                        onRender {
1197                                // Grom a development message
1198                                if (pluginManager.getGrailsPlugin('grom')) "rendering the partial: pages/_done.gsp".grom()
1199
1200                                flow.page = 7
1201                        }
1202                        onEnd {
1203                                // clean flow scope
1204                                flow.clear()
1205                        }
1206                }
1207        }
1208
1209        /**
1210         * load a study
1211         * @param Map LocalAttributeMap (the flow scope)
1212         * @param Map localAttributeMap (the flash scope)
1213         * @param Map GrailsParameterMap (the flow parameters = form data)
1214         * @returns boolean
1215         */
1216        def loadStudy(flow, flash, params, user) {
1217                flash.wizardErrors      = [:]
1218
1219                // load study
1220                try {
1221                        // load study
1222                        def study = (params.studyid) ? Study.findById( params.studyid ) : Study.findByTitle( params.study )
1223
1224                        // Check whether the user is allowed to edit this study. If it is not allowed
1225                        // the used should had never seen a link to this page, so he should never get
1226                        // here. That's why we just return false
1227            if (!study.canWrite(user)){
1228                                return false
1229                        }
1230
1231                        // store study in the flowscope
1232                        flow.study = study
1233
1234                        // set 'quicksave' variable
1235                        flow.quickSave = true
1236
1237                        // somehow the identifiers not always seem to work as expected
1238                        // when data is lazily fetched from the database. Iterating over
1239                        // the hasMany relationships and pre-fetching the identfiers after
1240                        // loading the study seems to solve the issues.
1241                        // See #367, #397 and #398
1242                        flow.study.hasMany.each { name, instance ->
1243                                flow.study."${name}"?.each {
1244                                        if (it.hasProperty('template')) it.getIdentifier()
1245                                }
1246                        }
1247
1248                        return true
1249                } catch (Exception e) {
1250                        // rollback
1251                        this.appendErrorMap(['exception': e.getMessage() + ', see log for stacktrace'], flash.wizardErrors)
1252
1253                        return false
1254                }
1255        }
1256
1257        /**
1258         * Handle the wizard study page
1259         *
1260         * @param Map LocalAttributeMap (the flow scope)
1261         * @param Map localAttributeMap (the flash scope)
1262         * @param Map GrailsParameterMap (the flow parameters = form data)
1263         * @returns boolean
1264         */
1265        def studyPage(flow, flash, params) {
1266                flash.values            = params
1267                flash.wizardErrors      = [:]
1268
1269                // instantiate study of it is not yet present
1270                if (!flow.study) flow.study = new Study()
1271
1272                // did the study template change?
1273                if (params.get('template').size() && flow.study.template?.name != params.get('template')) {
1274                        // set the template
1275                        flow.study.template = Template.findByName(params.remove('template'))
1276                }
1277
1278                // does the study have a template set?
1279                if (flow.study.template && flow.study.template instanceof Template) {
1280                        // yes, iterate through template fields
1281                        flow.study.giveFields().each() {
1282                                // and set their values
1283                                flow.study.setFieldValue(it.name, params.get(it.escapedName()))
1284                        }
1285                }
1286
1287                // handle publications
1288                handlePublications(flow, flash, params)
1289
1290                // handle contacts
1291                handleContacts(flow, flash, params)
1292
1293                // handle users (readers, writers)
1294                handleUsers(flow, flash, params, 'readers')
1295                handleUsers(flow, flash, params, 'writers')
1296
1297                // handle public checkbox
1298                flow.study.publicstudy = (params.get("publicstudy") && params.get("publicstudy") == "on") ? true : false
1299
1300                // have we got a template?
1301                if (flow.study.template && flow.study.template instanceof Template) {
1302                        // validate the study
1303                        if (flow.study.validate()) {
1304                                // instance is okay
1305                                return true
1306                        } else {
1307                                // validation failed
1308                                this.appendErrors(flow.study, flash.wizardErrors)
1309                                return false
1310                        }
1311                } else {
1312                        // no, return an error that the template is not set
1313                        this.appendErrorMap(['template': g.message(code: 'select.not.selected.or.add', args: ['template'])], flash.wizardErrors)
1314                        return false
1315                }
1316        }
1317
1318        /**
1319         * re-usable code for handling publications form data in a web flow
1320         * @param Map LocalAttributeMap (the flow scope)
1321         * @param Map localAttributeMap (the flash scope)
1322         * @param Map GrailsParameterMap (the flow parameters = form data)
1323         * @returns boolean
1324         */
1325        def handlePublications(flow, flash, params) {
1326                flash.wizardErrors      = [:]
1327                handleStudyPublications( flow.study, params )
1328        }
1329
1330        /**
1331         * re-usable code for handling contacts form data in a web flow
1332         * @param Map LocalAttributeMap (the flow scope)
1333         * @param Map localAttributeMap (the flash scope)
1334         * @param Map GrailsParameterMap (the flow parameters = form data)
1335         * @return boolean
1336         */
1337        def handleContacts(flow, flash, params) {
1338                flash.wizardErrors      = [:]
1339
1340                handleStudyContacts( flow.study, params )
1341        }
1342
1343        /**
1344         * re-usable code for handling contacts form data in a web flow
1345         * @param Map LocalAttributeMap (the flow scope)
1346         * @param Map localAttributeMap (the flash scope)
1347         * @param Map GrailsParameterMap (the flow parameters = form data)
1348     * @param String    'readers' or 'writers'
1349         * @return boolean
1350         */
1351        def handleUsers(flow, flash, params, type) {
1352                flash.wizardErrors = [:]
1353                handleStudyUsers( flow.study, params, type )
1354        }
1355       
1356    /**
1357        * re-usable code for handling publications form data
1358        * @param study  Study object to update
1359        * @param params GrailsParameterMap (the flow parameters = form data)
1360        * @returns boolean
1361        */
1362   def handleStudyPublications(Study study,  params) {
1363           if (study.publications) study.publications = []
1364
1365           // Check the ids of the pubblications that should be attached
1366           // to this study. If they are already attached, keep 'm. If
1367           // studies are attached that are not in the selected (i.e. the
1368           // user deleted them), remove them
1369           def publicationIDs = params.get('publication_ids')
1370           if (publicationIDs) {
1371                   // Find the individual IDs and make integers
1372                   publicationIDs = publicationIDs.split(',').collect { Integer.parseInt(it, 10) }
1373
1374                   // First remove the publication that are not present in the array
1375                   if( study.publications ) {
1376                           study.publications.findAll { publication -> !publicationIDs.find { id -> id == publication.id } }.each {
1377                                   study.removeFromPublications(it)
1378                           }
1379                   }
1380
1381                   // Add those publications not yet present in the database
1382                   publicationIDs.each { id ->
1383                           if (!study.publications.find { publication -> id == publication.id }) {
1384                                   def publication = Publication.get(id)
1385                                   if (publication) {
1386                                           study.addToPublications(publication)
1387                                   } else {
1388                                           log.info('.publication with ID ' + id + ' not found in database.')
1389                                   }
1390                           }
1391                   }
1392
1393           } else {
1394                   log.info('.no publications selected.')
1395                   if( study.publications ) {
1396                           study.publications.each {
1397                                   study.removeFromPublications(it)
1398                           }
1399                   }
1400           }
1401   }
1402
1403   /**
1404        * re-usable code for handling contacts form data
1405        * @param study  Study object to update
1406        * @param Map GrailsParameterMap (the flow parameters = form data)
1407        * @return boolean
1408        */
1409   def handleStudyContacts(Study study, params) {
1410           if (!study.persons) study.persons = []
1411
1412           // Check the ids of the contacts that should be attached
1413           // to this study. If they are already attached, keep 'm. If
1414           // studies are attached that are not in the selected (i.e. the
1415           // user deleted them), remove them
1416
1417           // Contacts are saved as [person_id]-[role_id]
1418           def contactIDs = params.get('contacts_ids')
1419           if (contactIDs) {
1420                   // Find the individual IDs and make integers
1421                   contactIDs = contactIDs.split(',').collect {
1422                           def parts = it.split('-')
1423                           return [person: Integer.parseInt(parts[0]), role: Integer.parseInt(parts[1])]
1424                   }
1425
1426                   // First remove the contacts that are not present in the array
1427                   if( study.persons ) {
1428                           study.persons.findAll {
1429                                   studyperson -> !contactIDs.find { ids -> (ids.person == studyperson.person.id) && (ids.role == studyperson.role.id) }
1430                           }.each {
1431                                   study.removeFromPersons(it)
1432                                   it.delete()
1433                           }
1434                   }
1435
1436                   // Add those contacts not yet present in the database
1437                   contactIDs.each { ids ->
1438                           if (!study.persons.find { studyperson -> (ids.person == studyperson.person.id) && (ids.role == studyperson.role.id) }) {
1439                                   def person = Person.get(ids.person)
1440                                   def role = PersonRole.get(ids.role)
1441                                   if (person && role) {
1442                                           // Find a studyperson object with these parameters
1443                                           def studyPerson = StudyPerson.findAll().find { studyperson -> studyperson.person.id == person.id && studyperson.role.id == role.id }
1444
1445                                           // If if does not yet exist, save the example
1446                                           if (!studyPerson) {
1447                                                   studyPerson = new StudyPerson(
1448                                                                   person: person,
1449                                                                   role: role
1450                                                                   )
1451                                                   studyPerson.save(flush: true)
1452                                           }
1453
1454                                           study.addToPersons(studyPerson)
1455                                   } else {
1456                                           log.info('.person ' + ids.person + ' or Role ' + ids.role + ' not found in database.')
1457                                   }
1458                           }
1459                   }
1460           } else {
1461                   log.info('.no persons selected.')
1462                   if( study.persons ) {
1463                           // removing persons from study
1464                           // Create a clone of persons list in order to avoid
1465                           // concurrentModification exceptions. See http://blog.springsource.com/2010/07/02/gorm-gotchas-part-2/
1466                           def persons = [] + study.persons;
1467                           ersons.each {
1468                                   study.removeFromPersons(it)
1469                                   it.delete()
1470                           }
1471                   }
1472           }
1473   }
1474
1475   /**
1476        * re-usable code for handling contacts form data
1477        * @param study  Study object to update
1478        * @param Map GrailsParameterMap (the flow parameters = form data)
1479        * @param String    'readers' or 'writers'
1480        * @return boolean
1481        */
1482   def handleStudyUsers(Study study, params, type) {
1483           def users = []
1484
1485           if (type == "readers" && study.readers ) {
1486                   users += study.readers
1487           } else if (type == "writers" && study.writers ) {
1488                   users += study.writers
1489           }
1490
1491           // Check the ids of the contacts that should be attached
1492           // to this study. If they are already attached, keep 'm. If
1493           // studies are attached that are not in the selected (i.e. the
1494           // user deleted them), remove them
1495
1496           // Users are saved as user_id
1497           def userIDs = params.get(type + '_ids')
1498           
1499           if (userIDs) {
1500                   // Find the individual IDs and make integers
1501                   userIDs = userIDs.split(',').collect { Long.valueOf(it, 10) }
1502                   
1503                   // First remove the publication that are not present in the array
1504                   users.removeAll { user -> !userIDs.find { id -> id == user.id } }
1505
1506                   // Add those publications not yet present in the database
1507                   userIDs.each { id ->
1508                           if (!users.find { user -> id == user.id }) {
1509                                   def user = SecUser.get(id)
1510                                   if (user) {
1511                                           users.add(user)
1512                                   } else {
1513                                           log.info('.user with ID ' + id + ' not found in database.')
1514                                   }
1515                           }
1516                   }
1517                   
1518           } else {
1519                   log.info('.no users selected.')
1520                   users.clear()
1521           }
1522           
1523           if (type == "readers") {
1524                   if (study.readers) {
1525                           study.readers.clear();
1526                   }
1527                           
1528                   users.each { study.addToReaders(it) }
1529           } else if (type == "writers") {
1530                           
1531                   if (study.writers) {
1532                           study.writers.clear();
1533                   }
1534
1535                   users.each { study.addToWriters(it) }
1536                   
1537           }
1538   }
1539       
1540       
1541               
1542
1543        /**
1544         * Handle the wizard subject page
1545         *
1546         * @param Map LocalAttributeMap (the flow scope)
1547         * @param Map localAttributeMap (the flash scope)
1548         * @param Map GrailsParameterMap (the flow parameters = form data)
1549         * @returns boolean
1550         */
1551        def subjectPage(flow, flash, params) {
1552                def errors = false
1553                flash.wizardErrors = [:]
1554
1555                // remember the params in the flash scope
1556                flash.values = params
1557
1558                // iterate through subjects
1559                flow.study.subjects.each() { subject ->
1560                        // iterate through (template and domain) fields
1561                        subject.giveFields().each() { field ->
1562                                // set field
1563                                subject.setFieldValue(
1564                                        field.name,
1565                                        params.get('subject_' + subject.getIdentifier() + '_' + field.escapedName())
1566                                )
1567                        }
1568
1569                        // validate subject
1570                        if (!subject.validate()) {
1571                                errors = true
1572                                this.appendErrors(subject, flash.wizardErrors, 'subject_' + subject.getIdentifier() + '_')
1573                        }
1574                }
1575
1576                return !errors
1577        }
1578
1579        /**
1580         * Add a number of subjects to a study
1581         *
1582         * required params entities:
1583         * -addNumber (int)
1584         * -species   (string)
1585         * -template  (string)
1586         *
1587         * @param Map LocalAttributeMap (the flow scope)
1588         * @param Map localAttributeMap (the flash scope)
1589         * @param Map GrailsParameterMap (the flow parameters = form data)
1590         * @returns boolean
1591         */
1592        def addSubjects(flow, flash, params) {
1593                // remember the params in the flash scope
1594                flash.values = params
1595
1596                // handle the subject page
1597                subjectPage(flow, flash, params)
1598
1599                // (re)set error message
1600                flash.wizardErrors = [:]
1601
1602                // set work variables
1603                def errors              = false
1604                def number              = params.get('addNumber') as int
1605                def species             = Term.findByName(params.get('species'))
1606                def template    = Template.findByName(params.get('template'))
1607
1608                // can we add subjects?
1609                if (number > 0 && species && template) {
1610                        // add subjects to study
1611                        number.times {
1612                                // work variables
1613                                def subjectName = 'Subject '
1614                                def subjectIterator = 1
1615                                def tempSubjectName = subjectName + subjectIterator
1616
1617                                // make sure subject name is unique
1618                                if (flow.study.subjects) {
1619                                        while (flow.study.subjects.find { it.name == tempSubjectName }) {
1620                                                subjectIterator++
1621                                                tempSubjectName = subjectName + subjectIterator
1622                                        }
1623                                }
1624                                subjectName = tempSubjectName
1625
1626                                // create a subject instance
1627                                def subject = new Subject(
1628                                        name            : subjectName,
1629                                        species         : species,
1630                                        template        : template
1631                                )
1632
1633                                // add it to the study
1634                                flow.study.addToSubjects( subject )
1635                        }
1636                } else {
1637                        // add feedback
1638                        errors = true
1639                        if (number < 1) this.appendErrorMap(['addNumber': 'Enter a positive number of subjects to add'], flash.wizardErrors)
1640                        if (!species)   this.appendErrorMap(['species': g.message(code: 'select.not.selected.or.add', args: ['species'])], flash.wizardErrors)
1641                        if (!template)  this.appendErrorMap(['template': g.message(code: 'select.not.selected.or.add', args: ['template'])], flash.wizardErrors)
1642                }
1643
1644                return !errors
1645        }
1646
1647        /**
1648         * Handle the wizard event page
1649         *
1650         * @param Map LocalAttributeMap (the flow scope)
1651         * @param Map localAttributeMap (the flash scope)
1652         * @param Map GrailsParameterMap (the flow parameters = form data)
1653         * @returns boolean
1654         */
1655        def eventPage(flow, flash, params) {
1656                def errors = false
1657                flash.wizardErrors = [:]
1658
1659                // remember the params in the flash scope
1660                flash.values = params
1661
1662                // handle the 'add event' form
1663                if (flow.event) {
1664                        flow.event.giveFields().each() { field ->
1665                                // set field
1666                                flow.event.setFieldValue(
1667                                        field.name,
1668                                        params.get(field.escapedName())
1669                                )
1670                        }
1671                }
1672
1673                // handle the eventGroup names and grouping
1674                def name        = ""
1675                def tempName= ""
1676                flow.study.eventGroups.each() { eventGroup ->
1677                        // iterate through templates
1678                        flow.study.giveAllEventTemplates().each() { template ->
1679                                tempName = params.get( 'eventGroup_' + eventGroup.getIdentifier() + '_' + template.getIdentifier() )
1680
1681                                // is the name different?
1682                                if (tempName != eventGroup.name) {
1683                                        name = tempName
1684                                }
1685                        }
1686
1687                        // should the name change?
1688                        if (name) {
1689                                // yes, change it
1690                                eventGroup.name = name
1691                                name = ""
1692                        }
1693
1694                        // handle eventGrouping
1695                        ( ((flow.study.events) ? flow.study.events : []) + ((flow.study.samplingEvents) ? flow.study.samplingEvents : []) ) .each { event ->
1696                                if (params.get( 'event_' + event.getIdentifier() + '_group_' + eventGroup.getIdentifier() )) {
1697                                        // add to eventGroup
1698                                        if (event instanceof SamplingEvent) {
1699                                                // check if we are already in this eventGroup
1700                                                if (!eventGroup.samplingEvents.find { it.equals(event) }) {
1701                                                        // no, add it
1702                                                        eventGroup.addToSamplingEvents(event)
1703
1704                                                        // iterate through subjects for this eventGroup
1705                                                        eventGroup.subjects.each() { subject ->
1706                                                                // instantiate a sample for this subject / event
1707                                                                def samplingEventName = ucwords(event.template.name)
1708                                                                def eventGroupName = ucwords(eventGroup.name).replaceAll("([ ]{1,})", "")
1709                                                                def sampleName = (ucwords(subject.name) + '_' + samplingEventName + '_' + eventGroupName + '_' + new RelTime(event.startTime).toString()).replaceAll("([ ]{1,})", "")
1710                                                                def tempSampleIterator = 0
1711                                                                def tempSampleName = sampleName
1712
1713                                                                // make sure sampleName is unique
1714                                                                if (flow.study.samples) {
1715                                                                        while (flow.study.samples.find { it.name == tempSampleName }) {
1716                                                                                tempSampleIterator++
1717                                                                                tempSampleName = sampleName + "_" + tempSampleIterator
1718                                                                        }
1719                                                                        sampleName = tempSampleName
1720                                                                }
1721
1722                                                                // instantiate a sample
1723                                                                def newSample = new Sample(
1724                                                                        parentSubject   : subject,
1725                                                                        parentEvent             : event,
1726                                                                        parentEventGroup: eventGroup,
1727                                                                        name                    : sampleName,
1728                                                                        template                : (event.sampleTemplate) ? event.sampleTemplate : ''
1729                                                                )
1730
1731                                                                // and add it
1732                                                                flow.study.addToSamples(newSample)
1733                                                        }
1734                                                }
1735                                        } else {
1736                                                eventGroup.addToEvents(event)
1737                                        }
1738                                } else {
1739                                        // remove from eventGroup
1740                                        if (event instanceof SamplingEvent) {
1741                                                // iterate through subjects (if we have them)
1742                                                eventGroup.subjects.each() { subject ->
1743                                                        // find all samples for this subject / event
1744                                                        flow.study.samples.findAll {
1745                                                                (
1746                                                                        it.parentEvent.equals(event) &&
1747                                                                        it.parentSubject.equals(subject) &&
1748                                                                        it.parentEventGroup.equals(eventGroup)
1749                                                                )
1750                                                        }.each() {
1751                                                                // delete this sample
1752                                                                flow.study.deleteSample( it )
1753                                                        }
1754                                                }
1755
1756                                                eventGroup.removeFromSamplingEvents(event)
1757                                        } else {
1758                                                eventGroup.removeFromEvents(event)
1759                                        }
1760                                }
1761                        }
1762                }
1763
1764                // handle the (sampling) events
1765                ( ((flow.study.events) ? flow.study.events : []) + ((flow.study.samplingEvents) ? flow.study.samplingEvents : []) ) .each() { event ->
1766                        event.giveFields().each() { field ->
1767                                event.setFieldValue(
1768                                        field.name,
1769                                        params.get( 'event_' + event.getIdentifier() + '_' + field.escapedName() )
1770                                )
1771                        }
1772
1773                        // validate event
1774                        if (!event.validate()) {
1775                                errors = true
1776                                this.appendErrors(event, flash.wizardErrors)
1777                        }
1778                }
1779
1780                return !errors
1781        }
1782
1783        /**
1784         * Handle the wizard group page
1785         *
1786         * @param Map LocalAttributeMap (the flow scope)
1787         * @param Map localAttributeMap (the flash scope)
1788         * @param Map GrailsParameterMap (the flow parameters = form data)
1789         * @returns boolean
1790         */
1791        def groupPage(flow, flash, params) {
1792                def errors = false
1793                flash.wizardErrors = [:]
1794
1795                // remember the params in the flash scope
1796                flash.values = params
1797
1798                // iterate through groups
1799                flow.study.eventGroups.each() { eventGroup ->
1800                        // iterate through subjects
1801                        flow.study.subjects.each() { subject ->
1802                                if (params.get('subject_' + subject.getIdentifier() + '_group_' + eventGroup.getIdentifier() )) {
1803                                        // check if this subject is already part of this eventGroup
1804                                        if ( !eventGroup.subjects.find { it.equals(subject) } ) {
1805                                                // add to eventGroup
1806                                                eventGroup.addToSubjects(subject)
1807
1808                                                // iterate through samplingEvents
1809                                                eventGroup.samplingEvents.each() { samplingEvent ->
1810                                                        def samplingEventName = ucwords(samplingEvent.template.name)
1811                                                        def eventGroupName = ucwords(eventGroup.name)
1812                                                        def sampleTemplateName = (samplingEvent.sampleTemplate) ? ucwords(samplingEvent.sampleTemplate.name) : ''
1813                                                        def sampleName = (ucwords(subject.name) + '_' + samplingEventName + '_' + eventGroupName + '_' + new RelTime(samplingEvent.startTime).toString() + sampleTemplateName).replaceAll("([ ]{1,})", "")
1814                                                        def tempSampleIterator = 0
1815                                                        def tempSampleName = sampleName
1816
1817                                                        // make sure sampleName is unique
1818                                                        if (flow.study.samples) {
1819                                                                while (flow.study.samples.find { it.name == tempSampleName }) {
1820                                                                        tempSampleIterator++
1821                                                                        tempSampleName = sampleName + "_" + tempSampleIterator
1822                                                                }
1823                                                                sampleName = tempSampleName
1824                                                        }
1825
1826                                                        // instantiate a sample
1827                                                        def newSample = new Sample(
1828                                                                        parentSubject   : subject,
1829                                                                        parentEvent             : samplingEvent,
1830                                                                        parentEventGroup: eventGroup,
1831                                                                        name                    : sampleName,
1832                                                                        template                : (samplingEvent.sampleTemplate) ? samplingEvent.sampleTemplate : ''
1833                                                                )
1834
1835                                                        flow.study.addToSamples(newSample)
1836                                                }
1837                                        } else {
1838                                                // do nothing
1839                                        }
1840                                } else {
1841                                        // check if this subject is a member of this eventGroup
1842                                        if (eventGroup.subjects.find { it.equals(subject) }) {
1843                                                // remove from eventGroup
1844                                                eventGroup.removeFromSubjects(subject)
1845
1846                                                // iterate through samplingEvents
1847                                                eventGroup.samplingEvents.each() { samplingEvent ->
1848                                                        flow.study.samples.findAll { (it.parentEvent.equals(samplingEvent) && it.parentSubject.equals(subject) && it.parentEventGroup.equals(eventGroup)) }.each() {
1849                                                                // delete this sample
1850                                                                flow.study.deleteSample( it )
1851                                                        }
1852                                                }
1853                                        }
1854                                }
1855                        }
1856                }
1857        }
1858
1859        /**
1860         * Handle the wizard samples page
1861         *
1862         * @param Map LocalAttributeMap (the flow scope)
1863         * @param Map localAttributeMap (the flash scope)
1864         * @param Map GrailsParameterMap (the flow parameters = form data)
1865         * @returns boolean
1866         */
1867        def samplePage(flow, flash, params) {
1868                def errors = false
1869                flash.wizardErrors = [:]
1870
1871                // remember the params in the flash scope
1872                flash.values = params
1873
1874                // iterate through samples
1875                flow.study.samples.each() { sample ->
1876                        // iterate through sample fields
1877                        sample.giveFields().each() { field ->
1878                                def value = params.get('sample_'+sample.getIdentifier()+'_'+field.escapedName())
1879
1880                                // set field value
1881                                if (!(field.name == 'name' && !value)) {
1882                                        log.info "setting "+field.name+" to "+value
1883                                        sample.setFieldValue(field.name, value)
1884                                }
1885                        }
1886
1887                        // has the template changed?
1888                        def templateName = params.get('template_' + sample.getIdentifier())
1889                        if (templateName && sample.template?.name != templateName) {
1890                                sample.template = Template.findByName(templateName)
1891                        }
1892
1893                        // validate sample
1894                        if (!sample.validate()) {
1895                                errors = true
1896                                this.appendErrors(sample, flash.wizardErrors, 'sample_' + sample.getIdentifier() + '_' )
1897                                log.info 'error-> sample_'+sample.getIdentifier()
1898                        }
1899                }
1900
1901                return !errors
1902        }
1903
1904        /**
1905         * Handle the wizard assays page
1906         *
1907         * @param Map LocalAttributeMap (the flow scope)
1908         * @param Map localAttributeMap (the flash scope)
1909         * @param Map GrailsParameterMap (the flow parameters = form data)
1910         * @returns boolean
1911         */
1912        def assayPage(flow, flash, params) {
1913                def errors = false
1914                flash.wizardErrors = [:]
1915
1916                // remember the params in the flash scope
1917                flash.values = params
1918
1919                // handle the 'add assay' form
1920                if (flow.assay) {
1921                        flow.assay.giveFields().each() { field ->
1922                                // set field
1923                                flow.assay.setFieldValue(
1924                                        field.name,
1925                                        params.get(field.escapedName())
1926                                )
1927                        }
1928                }
1929
1930                // handle the assay data
1931                flow.study.assays.each() { assay ->
1932                        // set data
1933                        assay.giveFields().each() { field ->
1934                                assay.setFieldValue(
1935                                        field.name,
1936                                        params.get( 'assay_' + assay.getIdentifier() + '_' + field.escapedName() )
1937                                )
1938                        }
1939
1940                        // validate assay
1941                        if (!assay.validate()) {
1942                                errors = true
1943                                this.appendErrors(assay, flash.wizardErrors, 'assay_' + assay.getIdentifier() + '_')
1944                        }
1945                }
1946
1947                return !errors
1948        }
1949
1950        /**
1951         * Handle the wizard assayGroups page
1952         *
1953         * @param Map LocalAttributeMap (the flow scope)
1954         * @param Map localAttributeMap (the flash scope)
1955         * @param Map GrailsParameterMap (the flow parameters = form data)
1956         * @returns boolean
1957         */
1958        def assayGroupPage(flow, flash, params) {
1959                def errors = false
1960                flash.wizardErrors = [:]
1961
1962                // remember the params in the flash scope
1963                flash.values = params
1964
1965                // iterate through samples
1966                flow.study.samples.each() { sample ->
1967                        // iterate through assays
1968                        flow.study.assays.each() { assay ->
1969                                if (params.get( 'sample_' + sample.getIdentifier() + '_assay_' + assay.getIdentifier() )) {
1970                                        // add sample to assay
1971                                        assay.addToSamples( sample )
1972                                } else {
1973                                        // remove sample from assay
1974                                        assay.removeFromSamples( sample )
1975                                }
1976                        }
1977                }
1978               
1979                // Mark study as dirty
1980                flow.study.version++
1981
1982                return !errors
1983        }
1984
1985        /**
1986         * groovy / java equivalent of php's ucwords function
1987         *
1988         * Capitalize all first letters of separate words
1989         *
1990         * @param String
1991         * @return String
1992         */
1993        public static ucwords(String text) {
1994                def newText = ''
1995
1996                // change case to lowercase
1997                text = text.toLowerCase()
1998
1999                // iterate through words
2000                text.split(" ").each() {
2001                        newText += it[0].toUpperCase() + it.substring(1) + " "
2002                }
2003
2004                return newText.substring(0, newText.size()-1)
2005        }
2006
2007        /**
2008         * return the object from a map of objects by searching for a name
2009         * @param String name
2010         * @param Map map of objects
2011         * @return Object
2012         */
2013        def getObjectByName(name, map) {
2014                def result = null
2015                map.each() {
2016                        if (it.name == name) {
2017                                result = it
2018                        }
2019                }
2020
2021                return result
2022        }
2023
2024        /**
2025         * transform domain class validation errors into a human readable
2026         * linked hash map
2027         * @param object validated domain class
2028         * @return object  linkedHashMap
2029         */
2030        def getHumanReadableErrors(object) {
2031                def errors = [:]
2032                object.errors.getAllErrors().each() { error ->
2033                        // error.codes.each() { code -> println code }
2034
2035                        // generally speaking g.message(...) should work,
2036                        // however it fails in some steps of the wizard
2037                        // (add event, add assay, etc) so g is not always
2038                        // availably. Using our own instance of the
2039                        // validationTagLib instead so it is always
2040                        // available to us
2041                        errors[ error.getArguments()[0] ] = validationTagLib.message(error: error)
2042                }
2043
2044                return errors
2045        }
2046
2047        /**
2048         * append errors of a particular object to a map
2049         * @param object
2050         * @param map linkedHashMap
2051         * @void
2052         */
2053        def appendErrors(object, map) {
2054                this.appendErrorMap(getHumanReadableErrors(object), map)
2055        }
2056
2057        def appendErrors(object, map, prepend) {
2058                this.appendErrorMap(getHumanReadableErrors(object), map, prepend)
2059        }
2060
2061        /**
2062         * append errors of one map to another map
2063         * @param map linkedHashMap
2064         * @param map linkedHashMap
2065         * @void
2066         */
2067        def appendErrorMap(map, mapToExtend) {
2068                map.each() {key, value ->
2069                        mapToExtend[key] = ['key': key, 'value': value, 'dynamic': false]
2070                }
2071        }
2072
2073        def appendErrorMap(map, mapToExtend, prepend) {
2074                map.each() {key, value ->
2075                        mapToExtend[prepend + key] = ['key': key, 'value': value, 'dynamic': true]
2076                }
2077        }
2078
2079        /**
2080         * Parses a RelTime string and returns a nice human readable string
2081         *
2082         * @return Human Readable string or a HTTP response code 400 on error
2083         */
2084        def ajaxParseRelTime = {
2085                if (params.reltime == null) {
2086                        response.status = 400
2087                        render('reltime parameter is expected')
2088                }
2089
2090                try {
2091                        def reltime = RelTime.parseRelTime(params.reltime)
2092                        response.setContentType("text/plain; charset=UTF-8")
2093                        render reltime.toPrettyString()
2094                } catch (IllegalArgumentException e) {
2095                        response.status = 400
2096                        render(e.getMessage())
2097                }
2098        }
2099
2100        /**
2101         * Proxy for searching PubMed articles (or other articles from the Entrez DB).
2102         *
2103         * This proxy is needed because it is not allowed to fetch XML directly from a different
2104         * domain using javascript. So we have the javascript call a function on our own domain
2105         * and the proxy will fetch the data from Entrez
2106         *
2107         * @since       20100609
2108         * @param       _utility        The name of the utility, without the complete path. Example: 'esearch.fcgi'
2109         * @return      XML
2110         */
2111        def entrezProxy = {
2112                // Remove unnecessary parameters
2113                params.remove( "action" )
2114                params.remove( "controller" )
2115
2116                def url = "http://eutils.ncbi.nlm.nih.gov/entrez/eutils";
2117                def util = params.remove( "_utility" )
2118               
2119                if( !util ) { 
2120                        response.setStatus( 404, "File not found" );
2121                        return;
2122                }
2123       
2124                def paramString = params.collect { k, v -> k + '=' + v.encodeAsURL() }.join( '&' );
2125
2126                def fullUrl = url + '/' + util + '?' + paramString;
2127
2128                // Return the output of the request
2129                response.setContentType("text/xml; charset=UTF-8")
2130
2131                response <<  new URL( fullUrl ).getText('UTF-8')
2132
2133                render ""
2134        }
2135}
Note: See TracBrowser for help on using the repository browser.