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

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