source: trunk/grails-app/controllers/dbnp/studycapturing/WizardController.groovy @ 1227

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