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

Last change on this file since 1732 was 1732, checked in by work@…, 10 years ago
  • resolved an issue with identity (ticket #367)
  • Property svn:keywords set to Rev Author Date
File size: 54.9 KB
Line 
1package dbnp.studycapturing
2
3import grails.plugins.springsecurity.Secured
4import dbnp.authentication.SecUser
5import org.codehaus.groovy.grails.plugins.web.taglib.ValidationTagLib
6import org.dbnp.gdt.*
7
8/**
9 * ajaxflow Controller
10 *
11 * @author      Jeroen Wesbeek
12 * @since       20101220
13 *
14 * Revision information:
15 * $Rev: 1732 $
16 * $Author: work@osx.eu $
17 * $Date: 2011-04-08 12:13:21 +0000 (vr, 08 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("toPageOne").to "study"
766                        on("toPageTwo").to "subjects"
767                        on("toPageThree").to "events"
768                        on("toPageFive").to "assays"
769                        on("toPageSix").to "confirm"
770                        on("toPageSeven").to "save"
771                }
772
773                // samples page
774                samples {
775                        render(view: "_samples")
776                        onRender {
777                                // Grom a development message
778                                if (pluginManager.getGrailsPlugin('grom')) "rendering the partial: pages/_samples.gsp".grom()
779
780                                flow.page = 4
781                                success()
782                        }
783                        on("switchTemplate") {
784                                // handle form data
785                                samplePage(flow, flash, params)
786
787                                // ignore errors
788                                flash.wizardErrors = [:]
789
790                                succes()
791                        }.to "samples"
792                        on("refresh") {
793                                // handle samples
794                                samplePage(flow, flash, params)
795
796                                // refresh all sample templates
797                                flow.study.giveSampleTemplates().each() {
798                                        it.refresh()
799                                }
800
801                                // ignore errors
802                                flash.wizardErrors = [:]
803
804                                success()
805                        }.to "samples"
806                        on("regenerate") {
807                                // handle samples
808                                samplePage(flow, flash, params)
809
810                                // remove all samples from the study
811                                flow.study.samples.findAll{true}.each() { sample ->
812                                        flow.study.deleteSample( sample )
813                                }
814
815                                // ignore errors
816                                flash.wizardErrors = [:]
817
818                                success()
819                        }.to "samples"
820                        on("previous") {
821                                // handle samples
822                                samplePage(flow, flash, params)
823
824                                // ignore errors
825                                flash.wizardErrors = [:]
826
827                                success()
828                        }.to "samplePrevious"
829                        on("next") {
830                                // handle form data
831                                samplePage(flow, flash, params) ? success() : error()
832                        }.to "assays"
833                        on("quickSave") {
834                                // handle form data
835                                samplePage(flow, flash, params) ? success() : error()
836                        }.to "save"
837                        on("toPageOne") {
838                                samplePage(flow, flash, params) ? success() : error()
839                        }.to "study"
840                        on("toPageTwo") {
841                                samplePage(flow, flash, params) ? success() : error()
842                        }.to "subjects"
843                        on("toPageThree") {
844                                samplePage(flow, flash, params) ? success() : error()
845                        }.to "events"
846                        on("toPageFive") {
847                                samplePage(flow, flash, params) ? success() : error()
848                        }.to "assays"
849                        on("toPageSix") {
850                                samplePage(flow, flash, params) ? success() : error()
851                        }.to "confirm"
852                        on("toPageSeven") {
853                                samplePage(flow, flash, params) ? success() : error()
854                        }.to "save"
855                }
856
857                // assays page
858                assays {
859                        render(view: "_assays")
860                        onRender {
861                                // Grom a development message
862                                if (pluginManager.getGrailsPlugin('grom')) "rendering the partial: pages/_assays.gsp".grom()
863
864                                flow.page = 5
865                        }
866                        on("refresh") {
867                                // handle form data
868                                assayPage(flow, flash, params)
869
870                                // force refresh of the template
871                                if (flow.assay && flow.assay.template && flow.assay.template instanceof Template) {
872                                        flow.assay.template.refresh()
873                                }
874
875                                // reset errors
876                                flash.wizardErrors = [:]
877                                success()
878                        }.to "assays"
879            on("switchTemplate") {
880                                // handle form data
881                    assayPage(flow, flash, params)
882
883                    // find assay template
884                    def template = Template.findByName(params.get('template'))
885                    if (flow.assay) {
886                            // set template
887                            flow.assay.template = template
888                    } else {
889                            // create a new assay instance
890                            flow.assay = new Assay(template: template)
891                    }
892
893                                // reset errors
894                                flash.wizardErrors = [:]
895                                success()
896                        }.to "assays"
897                        on("add") {
898                                // handle form data
899                                assayPage(flow, flash, params)
900
901                                // reset errors
902                                flash.wizardErrors = [:]
903
904                                // add assay to study
905                                flow.study.addToAssays( flow.assay )
906
907                                // validate assay
908                                if (flow.assay.validate()) {
909                                        // remove assay from the flowscope
910                                        flow.remove('assay')
911                                        success()
912                                } else {
913                                        // assay does not validate
914                                        // remove from study
915                                        flow.study.deleteAssay( flow.assay )
916
917                                        // append errors
918                                        this.appendErrors(flow.assay, flash.wizardErrors)
919                                        error()
920                                }
921                        }.to "assays"
922                        on("deleteAssay") {
923                                // handle form data
924                                assayPage(flow, flash, params)
925
926                                // reset errors
927                                flash.wizardErrors = [:]
928
929                                // find this assay
930                                def assay = flow.study.assays.find { it.getIdentifier() == (params.get('do') as int) }
931
932                                // perform delete
933                                if (assay) flow.study.deleteAssay( assay )
934                        }.to "assays"
935                        on("previous") {
936                                // handle form data
937                                assayPage(flow, flash, params)
938
939                                // ignore errors
940                                flash.wizardErrors = [:]
941
942                                success()
943                        }.to "samples"
944                        on("next") {
945                                // handle form data
946                                assayPage(flow, flash, params) ? success() : error()
947                        }.to "assayNext"
948                        on("quickSave") {
949                                // handle form data
950                                assayPage(flow, flash, params) ? success() : error()
951                        }.to "save"
952                        on("toPageOne") {
953                                assayPage(flow, flash, params) ? success() : error()
954                        }.to "study"
955                        on("toPageTwo") {
956                                assayPage(flow, flash, params) ? success() : error()
957                        }.to "subjects"
958                        on("toPageThree") {
959                                assayPage(flow, flash, params) ? success() : error()
960                        }.to "events"
961                        on("toPageFour") {
962                                assayPage(flow, flash, params) ? success() : error()
963                        }.to "samples"
964                        on("toPageSix") {
965                                assayPage(flow, flash, params) ? success() : error()
966                        }.to "confirm"
967                        on("toPageSeven") {
968                                assayPage(flow, flash, params) ? success() : error()
969                        }.to "save"
970                }
971
972                // assayNext
973                assayNext {
974                        action {
975                                // Grom a development message
976                                if (pluginManager.getGrailsPlugin('grom')) "entering assayNext".grom()
977
978                                // have we got samples and assays?
979                                if (flow.study.assays && flow.study.samples) {
980                                        // yes, go to the group page
981                                        toAssayGroups()
982                                } else {
983                                        // no need to show the group page as
984                                        // there's nothing to group
985                                        toConfirm()
986                                }
987                        }
988                        on("toAssayGroups").to "assayGroups"
989                        on("toConfirm").to "confirm"
990                }
991
992                // assay grouping page
993                assayGroups {
994                        render(view: "_assay_groups")
995                        onRender {
996                                // Grom a development message
997                                if (pluginManager.getGrailsPlugin('grom')) "rendering the partial: pages/_assay_groups.gsp".grom()
998
999                                flow.page = 5
1000                        }
1001                        on("previous") {
1002                                // handle form data
1003                                assayGroupPage(flow, flash, params)
1004
1005                                // ignore errors
1006                                flash.wizardErrors = [:]
1007
1008                                success()
1009                        }.to "assays"
1010                        on("next") {
1011                                // handle form data
1012                                assayGroupPage(flow, flash, params) ? success() : error()
1013                        }.to "confirm"
1014                        on("quickSave") {
1015                                // handle form data
1016                                assayGroupPage(flow, flash, params) ? success() : error()
1017                        }.to "save"
1018                        on("toPageOne") {
1019                                assayGroupPage(flow, flash, params) ? success() : error()
1020                        }.to "study"
1021                        on("toPageTwo") {
1022                                assayGroupPage(flow, flash, params) ? success() : error()
1023                        }.to "subjects"
1024                        on("toPageThree") {
1025                                assayGroupPage(flow, flash, params) ? success() : error()
1026                        }.to "events"
1027                        on("toPageFour") {
1028                                assayGroupPage(flow, flash, params) ? success() : error()
1029                        }.to "samples"
1030                        on("toPageSix") {
1031                                assayGroupPage(flow, flash, params) ? success() : error()
1032                        }.to "confirm"
1033                        on("toPageSeven") {
1034                                assayGroupPage(flow, flash, params) ? success() : error()
1035                        }.to "save"
1036                }
1037
1038                // confirm Previous
1039                confirmPrevious {
1040                        action {
1041                                // Grom a development message
1042                                if (pluginManager.getGrailsPlugin('grom')) "entering confirmPrevious".grom()
1043
1044                                // have we got samples and assays?
1045                                if (flow.study.assays && flow.study.samples) {
1046                                        // yes, go to the group page
1047                                        toAssayGroups()
1048                                } else {
1049                                        // no need to show the group page as
1050                                        // there's nothing to group
1051                                        toAssays()
1052                                }
1053                        }
1054                        on("toAssayGroups").to "assayGroups"
1055                        on("toAssays").to "assays"
1056                }
1057
1058                // confirmation
1059                confirm {
1060                        render(view: "_confirmation")
1061                        onRender {
1062                                // Grom a development message
1063                                if (pluginManager.getGrailsPlugin('grom')) "rendering the partial: pages/_confirmation.gsp".grom()
1064
1065                                flow.page = 6
1066                        }
1067                        on("toStudy").to "study"
1068                        on("toSubjects").to "subjects"
1069                        on("toEvents").to "events"
1070                        on("toGroups").to "groups"
1071                        on("toSamples").to "samples"
1072                        on("toAssays").to "assays"
1073                        on("toAssayGroups").to "assayGroups"
1074                        on("previous").to "confirmPrevious"
1075                        on("next").to "save"
1076                        on("quickSave").to "save"
1077                        on("toPageOne").to "study"
1078                        on("toPageTwo").to "subjects"
1079                        on("toPageThree").to "events"
1080                        on("toPageFour").to "samples"
1081                        on("toPageFive").to "assays"
1082                        on("toPageSeven").to "save"
1083                }
1084
1085                // store all study data
1086                save {
1087                        action {
1088                                // Grom a development message
1089                                if (pluginManager.getGrailsPlugin('grom')) "entering save".grom()
1090
1091                                flow.page = 7
1092                                flash.wizardErrors = [:]
1093
1094                                // persist data to the database
1095//                              try {
1096                                        // save study
1097                                        // Grom a development message
1098                                        if (pluginManager.getGrailsPlugin('grom')) "saving study".grom()
1099
1100                                        // Make sure the owner of the study is set right
1101                                        if (!flow.study.owner) {
1102                                                flow.study.owner = authenticationService.getLoggedInUser()
1103                                        }
1104
1105                                        if (!flow.study.save(flush:true)) {
1106                                                this.appendErrors(flow.study, flash.wizardErrors)
1107                                                throw new Exception('error saving study')
1108                                        }
1109                                        log.info ".saved study "+flow.study+" (id: "+flow.study.id+")"
1110
1111                                        success()
1112//                              } catch (Exception e) {
1113                                        // rollback
1114//                                      this.appendErrorMap(['exception': e.toString() + ', see log for stacktrace' ], flash.wizardErrors)
1115
1116                                        // stacktrace in flash scope
1117//                                      flash.debug = e.getStackTrace()
1118
1119//                                      error()
1120//                              }
1121                        }
1122                        on("error").to "error"
1123                        on(Exception).to "error"
1124                        on("success").to "done"
1125                }
1126
1127                // error storing data
1128                error {
1129                        render(view: "_error")
1130                        onRender {
1131                                // Grom a development message
1132                                if (pluginManager.getGrailsPlugin('grom')) "rendering the partial: pages/_error.gsp".grom()
1133
1134                                flow.page = 6
1135                        }
1136                        on("next").to "save"
1137                        on("previous").to "samples"
1138                        on("toPageOne").to "study"
1139                        on("toPageTwo").to "subjects"
1140                        on("toPageThree").to "events"
1141                        on("toPageFour").to "samples"
1142                        on("toPageFive").to "assays"
1143                        on("toPageSix").to "confirm"
1144                        on("toPageSeven").to "save"
1145                }
1146
1147                // render finish page
1148                done {
1149                        render(view: "_done")
1150                        onRender {
1151                                // Grom a development message
1152                                if (pluginManager.getGrailsPlugin('grom')) "rendering the partial: pages/_done.gsp".grom()
1153
1154                                flow.page = 7
1155                        }
1156                        onEnd {
1157                                // clean flow scope
1158                                flow.clear()
1159                        }
1160                }
1161        }
1162
1163        /**
1164         * load a study
1165         * @param Map LocalAttributeMap (the flow scope)
1166         * @param Map localAttributeMap (the flash scope)
1167         * @param Map GrailsParameterMap (the flow parameters = form data)
1168         * @returns boolean
1169         */
1170        def loadStudy(flow, flash, params, user) {
1171                flash.wizardErrors      = [:]
1172
1173                // load study
1174                try {
1175                        // load study
1176                        def study = (params.studyid) ? Study.findById( params.studyid ) : Study.findByTitle( params.study )
1177
1178                        // Check whether the user is allowed to edit this study. If it is not allowed
1179                        // the used should had never seen a link to this page, so he should never get
1180                        // here. That's why we just return false
1181            if (!study.canWrite(user)){
1182                                return false
1183                        }
1184
1185                        // store study in the flowscope
1186                        flow.study = study
1187
1188                        // set 'quicksave' variable
1189                        flow.quickSave = true
1190
1191                        return true
1192                } catch (Exception e) {
1193                        // rollback
1194                        this.appendErrorMap(['exception': e.getMessage() + ', see log for stacktrace'], flash.wizardErrors)
1195
1196                        return false
1197                }
1198        }
1199
1200        /**
1201         * Handle the wizard study page
1202         *
1203         * @param Map LocalAttributeMap (the flow scope)
1204         * @param Map localAttributeMap (the flash scope)
1205         * @param Map GrailsParameterMap (the flow parameters = form data)
1206         * @returns boolean
1207         */
1208        def studyPage(flow, flash, params) {
1209                flash.values            = params
1210                flash.wizardErrors      = [:]
1211
1212                // instantiate study of it is not yet present
1213                if (!flow.study) flow.study = new Study()
1214
1215                // did the study template change?
1216                if (params.get('template').size() && flow.study.template?.name != params.get('template')) {
1217                        // set the template
1218                        flow.study.template = Template.findByName(params.remove('template'))
1219                }
1220
1221                // does the study have a template set?
1222                if (flow.study.template && flow.study.template instanceof Template) {
1223                        // yes, iterate through template fields
1224                        flow.study.giveFields().each() {
1225                                // and set their values
1226                                flow.study.setFieldValue(it.name, params.get(it.escapedName()))
1227                        }
1228                }
1229
1230                // handle publications
1231                handlePublications(flow, flash, params)
1232
1233                // handle contacts
1234                handleContacts(flow, flash, params)
1235
1236                // handle users (readers, writers)
1237                handleUsers(flow, flash, params, 'readers')
1238                handleUsers(flow, flash, params, 'writers')
1239
1240                // handle public checkbox
1241                flow.study.publicstudy = (params.get("publicstudy") && params.get("publicstudy") == "on") ? true : false
1242
1243                // have we got a template?
1244                if (flow.study.template && flow.study.template instanceof Template) {
1245                        // validate the study
1246                        if (flow.study.validate()) {
1247                                // instance is okay
1248                                return true
1249                        } else {
1250                                // validation failed
1251                                this.appendErrors(flow.study, flash.wizardErrors)
1252                                return false
1253                        }
1254                } else {
1255                        // no, return an error that the template is not set
1256                        this.appendErrorMap(['template': g.message(code: 'select.not.selected.or.add', args: ['template'])], flash.wizardErrors)
1257                        return false
1258                }
1259        }
1260
1261        /**
1262         * re-usable code for handling publications form data in a web flow
1263         * @param Map LocalAttributeMap (the flow scope)
1264         * @param Map localAttributeMap (the flash scope)
1265         * @param Map GrailsParameterMap (the flow parameters = form data)
1266         * @returns boolean
1267         */
1268        def handlePublications(flow, flash, params) {
1269                flash.wizardErrors      = [:]
1270                handleStudyPublications( flow.study, params );
1271        }
1272
1273        /**
1274         * re-usable code for handling contacts form data in a web flow
1275         * @param Map LocalAttributeMap (the flow scope)
1276         * @param Map localAttributeMap (the flash scope)
1277         * @param Map GrailsParameterMap (the flow parameters = form data)
1278         * @return boolean
1279         */
1280        def handleContacts(flow, flash, params) {
1281                flash.wizardErrors      = [:]
1282
1283                handleStudyContacts( flow.study, params );
1284        }
1285
1286        /**
1287         * re-usable code for handling contacts form data in a web flow
1288         * @param Map LocalAttributeMap (the flow scope)
1289         * @param Map localAttributeMap (the flash scope)
1290         * @param Map GrailsParameterMap (the flow parameters = form data)
1291     * @param String    'readers' or 'writers'
1292         * @return boolean
1293         */
1294        def handleUsers(flow, flash, params, type) {
1295                flash.wizardErrors = [:]
1296                handleStudyUsers( flow.study, params, type );
1297        }
1298       
1299    /**
1300        * re-usable code for handling publications form data
1301        * @param study  Study object to update
1302        * @param params GrailsParameterMap (the flow parameters = form data)
1303        * @returns boolean
1304        */
1305   def handleStudyPublications(Study study,  params) {
1306           if (study.publications) study.publications = []
1307
1308           // Check the ids of the pubblications that should be attached
1309           // to this study. If they are already attached, keep 'm. If
1310           // studies are attached that are not in the selected (i.e. the
1311           // user deleted them), remove them
1312           def publicationIDs = params.get('publication_ids')
1313           if (publicationIDs) {
1314                   // Find the individual IDs and make integers
1315                   publicationIDs = publicationIDs.split(',').collect { Integer.parseInt(it, 10) }
1316
1317                   // First remove the publication that are not present in the array
1318                   if( study.publications )
1319                           study.publications.removeAll { publication -> !publicationIDs.find { id -> id == publication.id } }
1320
1321                   // Add those publications not yet present in the database
1322                   publicationIDs.each { id ->
1323                           if (!study.publications.find { publication -> id == publication.id }) {
1324                                   def publication = Publication.get(id)
1325                                   if (publication) {
1326                                           study.addToPublications(publication)
1327                                   } else {
1328                                           log.info('.publication with ID ' + id + ' not found in database.')
1329                                   }
1330                           }
1331                   }
1332
1333           } else {
1334                   log.info('.no publications selected.')
1335                   if( study.publications )
1336                           study.publications.clear()
1337           }
1338   }
1339
1340   /**
1341        * re-usable code for handling contacts form data
1342        * @param study  Study object to update
1343        * @param Map GrailsParameterMap (the flow parameters = form data)
1344        * @return boolean
1345        */
1346   def handleStudyContacts(Study study, params) {
1347           if (!study.persons) study.persons = []
1348
1349           // Check the ids of the contacts that should be attached
1350           // to this study. If they are already attached, keep 'm. If
1351           // studies are attached that are not in the selected (i.e. the
1352           // user deleted them), remove them
1353
1354           // Contacts are saved as [person_id]-[role_id]
1355           def contactIDs = params.get('contacts_ids')
1356           if (contactIDs) {
1357                   // Find the individual IDs and make integers
1358                   contactIDs = contactIDs.split(',').collect {
1359                           def parts = it.split('-')
1360                           return [person: Integer.parseInt(parts[0]), role: Integer.parseInt(parts[1])]
1361                   }
1362
1363                   // First remove the contacts that are not present in the array
1364                   if( study.persons ) {
1365                           study.persons.removeAll {
1366                                   studyperson -> !contactIDs.find { ids -> (ids.person == studyperson.person.id) && (ids.role == studyperson.role.id) }
1367                           }
1368                   }
1369
1370                   // Add those contacts not yet present in the database
1371                   contactIDs.each { ids ->
1372                           if (!study.persons.find { studyperson -> (ids.person == studyperson.person.id) && (ids.role == studyperson.role.id) }) {
1373                                   def person = Person.get(ids.person)
1374                                   def role = PersonRole.get(ids.role)
1375                                   if (person && role) {
1376                                           // Find a studyperson object with these parameters
1377                                           def studyPerson = StudyPerson.findAll().find { studyperson -> studyperson.person.id == person.id && studyperson.role.id == role.id }
1378
1379                                           // If if does not yet exist, save the example
1380                                           if (!studyPerson) {
1381                                                   studyPerson = new StudyPerson(
1382                                                                   person: person,
1383                                                                   role: role
1384                                                                   )
1385                                                   studyPerson.save(flush: true)
1386                                           }
1387
1388                                           study.addToPersons(studyPerson)
1389                                   } else {
1390                                           log.info('.person ' + ids.person + ' or Role ' + ids.role + ' not found in database.')
1391                                   }
1392                           }
1393                   }
1394           } else {
1395                   log.info('.no persons selected.')
1396                   if( study.persons )
1397                           study.persons.clear()
1398           }
1399   }
1400
1401   /**
1402        * re-usable code for handling contacts form data
1403        * @param study  Study object to update
1404        * @param Map GrailsParameterMap (the flow parameters = form data)
1405        * @param String    'readers' or 'writers'
1406        * @return boolean
1407        */
1408   def handleStudyUsers(Study study, params, type) {
1409           def users = []
1410
1411           if (type == "readers" && study.readers ) {
1412                   users += study.readers
1413           } else if (type == "writers" && study.writers ) {
1414                   users += study.writers
1415           }
1416
1417           // Check the ids of the contacts that should be attached
1418           // to this study. If they are already attached, keep 'm. If
1419           // studies are attached that are not in the selected (i.e. the
1420           // user deleted them), remove them
1421
1422           // Users are saved as user_id
1423           def userIDs = params.get(type + '_ids')
1424           
1425           if (userIDs) {
1426                   // Find the individual IDs and make integers
1427                   userIDs = userIDs.split(',').collect { Long.valueOf(it, 10) }
1428                   
1429                   // First remove the publication that are not present in the array
1430                   users.removeAll { user -> !userIDs.find { id -> id == user.id } }
1431
1432                   // Add those publications not yet present in the database
1433                   userIDs.each { id ->
1434                           if (!users.find { user -> id == user.id }) {
1435                                   def user = SecUser.get(id)
1436                                   if (user) {
1437                                           users.add(user)
1438                                   } else {
1439                                           log.info('.user with ID ' + id + ' not found in database.')
1440                                   }
1441                           }
1442                   }
1443                   
1444           } else {
1445                   log.info('.no users selected.')
1446                   users.clear()
1447           }
1448           
1449           if (type == "readers") {
1450                   if (study.readers)
1451                           study.readers.clear()
1452                           
1453                   users.each { study.addToReaders(it) }
1454           } else if (type == "writers") {
1455                   if (study.writers)
1456                           study.writers.clear()
1457
1458                   users.each { study.addToWriters(it) }
1459                   
1460           }
1461   }
1462       
1463       
1464               
1465
1466        /**
1467         * Handle the wizard subject page
1468         *
1469         * @param Map LocalAttributeMap (the flow scope)
1470         * @param Map localAttributeMap (the flash scope)
1471         * @param Map GrailsParameterMap (the flow parameters = form data)
1472         * @returns boolean
1473         */
1474        def subjectPage(flow, flash, params) {
1475                def errors = false
1476                flash.wizardErrors = [:]
1477
1478                // remember the params in the flash scope
1479                flash.values = params
1480
1481                // iterate through subjects
1482                flow.study.subjects.each() { subject ->
1483                        // iterate through (template and domain) fields
1484                        subject.giveFields().each() { field ->
1485                                // set field
1486                                subject.setFieldValue(
1487                                        field.name,
1488                                        params.get('subject_' + subject.getIdentifier() + '_' + field.escapedName())
1489                                )
1490                        }
1491
1492                        // validate subject
1493                        if (!subject.validate()) {
1494                                errors = true
1495                                this.appendErrors(subject, flash.wizardErrors, 'subject_' + subject.getIdentifier() + '_')
1496                        }
1497                }
1498
1499                return !errors
1500        }
1501
1502        /**
1503         * Add a number of subjects to a study
1504         *
1505         * required params entities:
1506         * -addNumber (int)
1507         * -species   (string)
1508         * -template  (string)
1509         *
1510         * @param Map LocalAttributeMap (the flow scope)
1511         * @param Map localAttributeMap (the flash scope)
1512         * @param Map GrailsParameterMap (the flow parameters = form data)
1513         * @returns boolean
1514         */
1515        def addSubjects(flow, flash, params) {
1516                // remember the params in the flash scope
1517                flash.values = params
1518
1519                // handle the subject page
1520                subjectPage(flow, flash, params)
1521
1522                // (re)set error message
1523                flash.wizardErrors = [:]
1524
1525                // set work variables
1526                def errors              = false
1527                def number              = params.get('addNumber') as int
1528                def species             = Term.findByName(params.get('species'))
1529                def template    = Template.findByName(params.get('template'))
1530
1531                // can we add subjects?
1532                if (number > 0 && species && template) {
1533                        // add subjects to study
1534                        number.times {
1535                                // work variables
1536                                def subjectName = 'Subject '
1537                                def subjectIterator = 1
1538                                def tempSubjectName = subjectName + subjectIterator
1539
1540                                // make sure subject name is unique
1541                                if (flow.study.subjects) {
1542                                        while (flow.study.subjects.find { it.name == tempSubjectName }) {
1543                                                subjectIterator++
1544                                                tempSubjectName = subjectName + subjectIterator
1545                                        }
1546                                }
1547                                subjectName = tempSubjectName
1548
1549                                // create a subject instance
1550                                def subject = new Subject(
1551                                        name            : subjectName,
1552                                        species         : species,
1553                                        template        : template
1554                                )
1555
1556                                // add it to the study
1557                                flow.study.addToSubjects( subject )
1558                        }
1559                } else {
1560                        // add feedback
1561                        errors = true
1562                        if (number < 1) this.appendErrorMap(['addNumber': 'Enter a positive number of subjects to add'], flash.wizardErrors)
1563                        if (!species)   this.appendErrorMap(['species': g.message(code: 'select.not.selected.or.add', args: ['species'])], flash.wizardErrors)
1564                        if (!template)  this.appendErrorMap(['template': g.message(code: 'select.not.selected.or.add', args: ['template'])], flash.wizardErrors)
1565                }
1566
1567                return !errors
1568        }
1569
1570        /**
1571         * Handle the wizard event page
1572         *
1573         * @param Map LocalAttributeMap (the flow scope)
1574         * @param Map localAttributeMap (the flash scope)
1575         * @param Map GrailsParameterMap (the flow parameters = form data)
1576         * @returns boolean
1577         */
1578        def eventPage(flow, flash, params) {
1579                def errors = false
1580                flash.wizardErrors = [:]
1581
1582                // remember the params in the flash scope
1583                flash.values = params
1584
1585                // handle the 'add event' form
1586                if (flow.event) {
1587                        flow.event.giveFields().each() { field ->
1588                                // set field
1589                                flow.event.setFieldValue(
1590                                        field.name,
1591                                        params.get(field.escapedName())
1592                                )
1593                        }
1594                }
1595
1596                // handle the eventGroup names and grouping
1597                def name        = ""
1598                def tempName= ""
1599                flow.study.eventGroups.each() { eventGroup ->
1600                        // iterate through templates
1601                        flow.study.giveAllEventTemplates().each() { template ->
1602                                tempName = params.get( 'eventGroup_' + eventGroup.getIdentifier() + '_' + template.getIdentifier() )
1603
1604                                // is the name different?
1605                                if (tempName != eventGroup.name) {
1606                                        name = tempName
1607                                }
1608                        }
1609
1610                        // should the name change?
1611                        if (name) {
1612                                // yes, change it
1613                                eventGroup.name = name
1614                                name = ""
1615                        }
1616
1617                        // handle eventGrouping
1618                        ( ((flow.study.events) ? flow.study.events : []) + ((flow.study.samplingEvents) ? flow.study.samplingEvents : []) ) .each() { event ->
1619                                if (params.get( 'event_' + event.getIdentifier() + '_group_' + eventGroup.getIdentifier() )) {
1620                                        // add to eventGroup
1621                                        if (event instanceof SamplingEvent) {
1622                                                // check if we are already in this eventGroup
1623                                                if (!eventGroup.samplingEvents.find { it.equals(event) }) {
1624                                                        // no, add it
1625                                                        eventGroup.addToSamplingEvents(event)
1626
1627                                                        // iterate through subjects for this eventGroup
1628                                                        eventGroup.subjects.each() { subject ->
1629                                                                // instantiate a sample for this subject / event
1630                                                                def samplingEventName = ucwords(event.template.name)
1631                                                                def eventGroupName = ucwords(eventGroup.name).replaceAll("([ ]{1,})", "")
1632                                                                def sampleName = (ucwords(subject.name) + '_' + samplingEventName + '_' + eventGroupName + '_' + new RelTime(event.startTime).toString()).replaceAll("([ ]{1,})", "")
1633                                                                def tempSampleIterator = 0
1634                                                                def tempSampleName = sampleName
1635
1636                                                                // make sure sampleName is unique
1637                                                                if (flow.study.samples) {
1638                                                                        while (flow.study.samples.find { it.name == tempSampleName }) {
1639                                                                                tempSampleIterator++
1640                                                                                tempSampleName = sampleName + "_" + tempSampleIterator
1641                                                                        }
1642                                                                        sampleName = tempSampleName
1643                                                                }
1644
1645                                                                // instantiate a sample
1646                                                                flow.study.addToSamples(
1647                                                                        new Sample(
1648                                                                                parentSubject   : subject,
1649                                                                                parentEvent             : event,
1650                                                                                parentEventGroup: eventGroup,
1651                                                                                name                    : sampleName,
1652                                                                                template                : (event.sampleTemplate) ? event.sampleTemplate : ''
1653                                                                        )
1654                                                                )
1655                                                        }
1656                                                }
1657                                        } else {
1658                                                eventGroup.addToEvents(event)
1659                                        }
1660                                } else {
1661                                        // remove from eventGroup
1662                                        if (event instanceof SamplingEvent) {
1663                                                // iterate through subjects (if we have them)
1664                                                eventGroup.subjects.each() { subject ->
1665                                                        // find all samples for this subject / event
1666                                                        flow.study.samples.findAll { (it.parentEvent.equals(event) && it.parentSubject.equals(subject) ) }.each() {
1667                                                                // delete this sample
1668                                                                flow.study.deleteSample( it )
1669                                                        }
1670                                                }
1671
1672                                                eventGroup.removeFromSamplingEvents(event)
1673                                        } else {
1674                                                eventGroup.removeFromEvents(event)
1675                                        }
1676                                }
1677                        }
1678                }
1679
1680                // handle the (sampling) events
1681                ( ((flow.study.events) ? flow.study.events : []) + ((flow.study.samplingEvents) ? flow.study.samplingEvents : []) ) .each() { event ->
1682                        event.giveFields().each() { field ->
1683                                event.setFieldValue(
1684                                        field.name,
1685                                        params.get( 'event_' + event.getIdentifier() + '_' + field.escapedName() )
1686                                )
1687                        }
1688
1689                        // validate event
1690                        if (!event.validate()) {
1691                                errors = true
1692                                this.appendErrors(event, flash.wizardErrors)
1693                        }
1694                }
1695
1696                return !errors
1697        }
1698
1699        /**
1700         * Handle the wizard group page
1701         *
1702         * @param Map LocalAttributeMap (the flow scope)
1703         * @param Map localAttributeMap (the flash scope)
1704         * @param Map GrailsParameterMap (the flow parameters = form data)
1705         * @returns boolean
1706         */
1707        def groupPage(flow, flash, params) {
1708                def errors = false
1709                flash.wizardErrors = [:]
1710
1711                // remember the params in the flash scope
1712                flash.values = params
1713
1714                // iterate through groups
1715                flow.study.eventGroups.each() { eventGroup ->
1716                        // iterate through subjects
1717                        flow.study.subjects.each() { subject ->
1718                                if (params.get('subject_' + subject.getIdentifier() + '_group_' + eventGroup.getIdentifier() )) {
1719                                        // check if this subject is already part of this eventGroup
1720                                        if ( !eventGroup.subjects.find { it.equals(subject) } ) {
1721                                                // add to eventGroup
1722                                                eventGroup.addToSubjects(subject)
1723
1724                                                // iterate through samplingEvents
1725                                                eventGroup.samplingEvents.each() { samplingEvent ->
1726                                                        def samplingEventName = ucwords(samplingEvent.template.name)
1727                                                        def eventGroupName = ucwords(eventGroup.name)
1728                                                        def sampleName = (ucwords(subject.name) + '_' + samplingEventName + '_' + eventGroupName + '_' + new RelTime(samplingEvent.startTime).toString()).replaceAll("([ ]{1,})", "")
1729                                                        def tempSampleIterator = 0
1730                                                        def tempSampleName = sampleName
1731
1732                                                        // make sure sampleName is unique
1733                                                        if (flow.study.samples) {
1734                                                                while (flow.study.samples.find { it.name == tempSampleName }) {
1735                                                                        tempSampleIterator++
1736                                                                        tempSampleName = sampleName + "_" + tempSampleIterator
1737                                                                }
1738                                                                sampleName = tempSampleName
1739                                                        }
1740
1741                                                        // instantiate a sample
1742                                                        flow.study.addToSamples(
1743                                                                new Sample(
1744                                                                        parentSubject   : subject,
1745                                                                        parentEvent             : samplingEvent,
1746                                                                        parentEventGroup: eventGroup,
1747                                                                        name                    : sampleName,
1748                                                                        template                : (samplingEvent.sampleTemplate) ? samplingEvent.sampleTemplate : ''
1749                                                                )
1750                                                        )
1751                                                }
1752                                        } else {
1753                                        }
1754                                } else {
1755                                        // check if this subject is a member of this eventGroup
1756                                        if (eventGroup.subjects.find { it.equals(subject) }) {
1757                                                // remove from eventGroup
1758                                                eventGroup.removeFromSubjects(subject)
1759
1760                                                // iterate through samplingEvents
1761                                                eventGroup.samplingEvents.each() { samplingEvent ->
1762                                                        flow.study.samples.findAll { (it.parentEvent.equals(samplingEvent) && it.parentSubject.equals(subject) && it.parentEventGroup.equals(eventGroup)) }.each() {
1763                                                                // delete this sample
1764                                                                flow.study.deleteSample( it )
1765                                                        }
1766                                                }
1767                                        }
1768                                }
1769                        }
1770                }
1771        }
1772
1773        /**
1774         * Handle the wizard samples page
1775         *
1776         * @param Map LocalAttributeMap (the flow scope)
1777         * @param Map localAttributeMap (the flash scope)
1778         * @param Map GrailsParameterMap (the flow parameters = form data)
1779         * @returns boolean
1780         */
1781        def samplePage(flow, flash, params) {
1782                def errors = false
1783                flash.wizardErrors = [:]
1784
1785                // remember the params in the flash scope
1786                flash.values = params
1787
1788                // iterate through samples
1789                flow.study.samples.each() { sample ->
1790                        // iterate through sample fields
1791                        sample.giveFields().each() { field ->
1792                                def value = params.get('sample_'+sample.getIdentifier()+'_'+field.escapedName())
1793
1794                                // set field value
1795                                if (!(field.name == 'name' && !value)) {
1796                                        log.info "setting "+field.name+" to "+value
1797                                        sample.setFieldValue(field.name, value)
1798                                }
1799                        }
1800
1801                        // has the template changed?
1802                        def templateName = params.get('template_' + sample.getIdentifier())
1803                        if (templateName && sample.template?.name != templateName) {
1804                                sample.template = Template.findByName(templateName)
1805                        }
1806
1807                        // validate sample
1808                        if (!sample.validate()) {
1809                                errors = true
1810                                this.appendErrors(sample, flash.wizardErrors, 'sample_' + sample.getIdentifier() + '_' )
1811                                log.info 'error-> sample_'+sample.getIdentifier()
1812                        }
1813                }
1814
1815                return !errors
1816        }
1817
1818        /**
1819         * Handle the wizard assays page
1820         *
1821         * @param Map LocalAttributeMap (the flow scope)
1822         * @param Map localAttributeMap (the flash scope)
1823         * @param Map GrailsParameterMap (the flow parameters = form data)
1824         * @returns boolean
1825         */
1826        def assayPage(flow, flash, params) {
1827                def errors = false
1828                flash.wizardErrors = [:]
1829
1830                // remember the params in the flash scope
1831                flash.values = params
1832
1833                // handle the 'add assay' form
1834                if (flow.assay) {
1835                        flow.assay.giveFields().each() { field ->
1836                                // set field
1837                                flow.assay.setFieldValue(
1838                                        field.name,
1839                                        params.get(field.escapedName())
1840                                )
1841                        }
1842                }
1843
1844                // handle the assay data
1845                flow.study.assays.each() { assay ->
1846                        // set data
1847                        assay.giveFields().each() { field ->
1848                                assay.setFieldValue(
1849                                        field.name,
1850                                        params.get( 'assay_' + assay.getIdentifier() + '_' + field.escapedName() )
1851                                )
1852                        }
1853
1854                        // validate assay
1855                        if (!assay.validate()) {
1856                                errors = true
1857                                this.appendErrors(assay, flash.wizardErrors, 'assay_' + assay.getIdentifier() + '_')
1858                        }
1859                }
1860
1861                return !errors
1862        }
1863
1864        /**
1865         * Handle the wizard assayGroups page
1866         *
1867         * @param Map LocalAttributeMap (the flow scope)
1868         * @param Map localAttributeMap (the flash scope)
1869         * @param Map GrailsParameterMap (the flow parameters = form data)
1870         * @returns boolean
1871         */
1872        def assayGroupPage(flow, flash, params) {
1873                def errors = false
1874                flash.wizardErrors = [:]
1875
1876                // remember the params in the flash scope
1877                flash.values = params
1878
1879                // iterate through samples
1880                flow.study.samples.each() { sample ->
1881                        // iterate through assays
1882                        flow.study.assays.each() { assay ->
1883                                if (params.get( 'sample_' + sample.getIdentifier() + '_assay_' + assay.getIdentifier() )) {
1884                                        // add sample to assay
1885                                        assay.addToSamples( sample )
1886                                } else {
1887                                        // remove sample from assay
1888                                        assay.removeFromSamples( sample )
1889                                }
1890                        }
1891                }
1892
1893                return !errors
1894        }
1895
1896        /**
1897         * groovy / java equivalent of php's ucwords function
1898         *
1899         * Capitalize all first letters of separate words
1900         *
1901         * @param String
1902         * @return String
1903         */
1904        public static ucwords(String text) {
1905                def newText = ''
1906
1907                // change case to lowercase
1908                text = text.toLowerCase()
1909
1910                // iterate through words
1911                text.split(" ").each() {
1912                        newText += it[0].toUpperCase() + it.substring(1) + " "
1913                }
1914
1915                return newText.substring(0, newText.size()-1)
1916        }
1917
1918        /**
1919         * return the object from a map of objects by searching for a name
1920         * @param String name
1921         * @param Map map of objects
1922         * @return Object
1923         */
1924        def getObjectByName(name, map) {
1925                def result = null
1926                map.each() {
1927                        if (it.name == name) {
1928                                result = it
1929                        }
1930                }
1931
1932                return result
1933        }
1934
1935        /**
1936         * transform domain class validation errors into a human readable
1937         * linked hash map
1938         * @param object validated domain class
1939         * @return object  linkedHashMap
1940         */
1941        def getHumanReadableErrors(object) {
1942                def errors = [:]
1943                object.errors.getAllErrors().each() { error ->
1944                        // error.codes.each() { code -> println code }
1945
1946                        // generally speaking g.message(...) should work,
1947                        // however it fails in some steps of the wizard
1948                        // (add event, add assay, etc) so g is not always
1949                        // availably. Using our own instance of the
1950                        // validationTagLib instead so it is always
1951                        // available to us
1952                        errors[ error.getArguments()[0] ] = validationTagLib.message(error: error)
1953                }
1954
1955                return errors
1956        }
1957
1958        /**
1959         * append errors of a particular object to a map
1960         * @param object
1961         * @param map linkedHashMap
1962         * @void
1963         */
1964        def appendErrors(object, map) {
1965                this.appendErrorMap(getHumanReadableErrors(object), map)
1966        }
1967
1968        def appendErrors(object, map, prepend) {
1969                this.appendErrorMap(getHumanReadableErrors(object), map, prepend)
1970        }
1971
1972        /**
1973         * append errors of one map to another map
1974         * @param map linkedHashMap
1975         * @param map linkedHashMap
1976         * @void
1977         */
1978        def appendErrorMap(map, mapToExtend) {
1979                map.each() {key, value ->
1980                        mapToExtend[key] = ['key': key, 'value': value, 'dynamic': false]
1981                }
1982        }
1983
1984        def appendErrorMap(map, mapToExtend, prepend) {
1985                map.each() {key, value ->
1986                        mapToExtend[prepend + key] = ['key': key, 'value': value, 'dynamic': true]
1987                }
1988        }
1989
1990        /**
1991         * Parses a RelTime string and returns a nice human readable string
1992         *
1993         * @return Human Readable string or a HTTP response code 400 on error
1994         */
1995        def ajaxParseRelTime = {
1996                if (params.reltime == null) {
1997                        response.status = 400
1998                        render('reltime parameter is expected')
1999                }
2000
2001                try {
2002                        def reltime = RelTime.parseRelTime(params.reltime)
2003                        render reltime.toPrettyString()
2004                } catch (IllegalArgumentException e) {
2005                        response.status = 400
2006                        render(e.getMessage())
2007                }
2008        }
2009
2010        /**
2011         * Proxy for searching PubMed articles (or other articles from the Entrez DB).
2012         *
2013         * This proxy is needed because it is not allowed to fetch XML directly from a different
2014         * domain using javascript. So we have the javascript call a function on our own domain
2015         * and the proxy will fetch the data from Entrez
2016         *
2017         * @since       20100609
2018         * @param       _utility        The name of the utility, without the complete path. Example: 'esearch.fcgi'
2019         * @return      XML
2020         */
2021        def entrezProxy = {
2022                // Remove unnecessary parameters
2023                params.remove( "action" )
2024                params.remove( "controller" )
2025
2026                def url = "http://eutils.ncbi.nlm.nih.gov/entrez/eutils";
2027                def util = params.remove( "_utility" )
2028               
2029                if( !util ) { 
2030                        response.setStatus( 404, "File not found" );
2031                        return;
2032                }
2033       
2034                def paramString = params.collect { k, v -> k + '=' + v.encodeAsURL() }.join( '&' );
2035
2036                def fullUrl = url + '/' + util + '?' + paramString;
2037
2038                // Return the output of the request
2039                response.setContentType("text/xml; charset=UTF-8")
2040
2041                response <<  new URL( fullUrl ).getText('UTF-8')
2042
2043                render ""
2044        }
2045}
Note: See TracBrowser for help on using the repository browser.