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

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