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

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