root/trunk/grails-app/controllers/dbnp/studycapturing/WizardController.groovy @ 1225

Revision 1225, 45.0 KB (checked in by work@…, 3 years ago)

- resolved issue #223

  • Property svn:keywords set to Author Date Rev
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$
26 * $Author$
27 * $Date$
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 dynamically load objects. This is a nice
925                        // feature, but can cause problems (see ticket #223) as objects
926                        // get instantiated multiple times which causes the POST variable
927                        // to have a different 'identity' as the actual instances. The
928                        // workaround is to 'touch' them here so they get instantiated:
929                        study.hasMany.each { name, type ->
930                                // dynamically instantiate all identity classes
931                                if (type.toString() =~ "dbnp.studycapturing") {
932                                        study.getProperty( name ).each { }
933                                }
934                        }
935
936                        // store study in the flowscope
937                        flow.study = study
938
939                        // set 'quicksave' variable
940                        flow.quickSave = true
941
942                        return true
943                } catch (Exception e) {
944                        // rollback
945                        this.appendErrorMap(['exception': e.getMessage() + ', see log for stacktrace'], flash.wizardErrors)
946
947                        return false
948                }
949        }
950
951        /**
952         * Handle the wizard study page
953         *
954         * @param Map LocalAttributeMap (the flow scope)
955         * @param Map localAttributeMap (the flash scope)
956         * @param Map GrailsParameterMap (the flow parameters = form data)
957         * @returns boolean
958         */
959        def studyPage(flow, flash, params) {
960                // remember the params in the flash scope
961                flash.values = params
962
963                // instantiate study of it is not yet present
964                if (!flow.study) flow.study = new Study()
965
966                // did the study template change?
967                if (params.get('template').size() && flow.study.template?.name != params.get('template')) {
968                        println ".change study template!"
969
970                        // yes, was the template already set?
971                        if (flow.study.template instanceof Template) {
972                                // yes, first make sure all values are unset?
973                                println "!!! check the database fields if data of a previous template remains in the database or is deleted by GORM!"
974                        }
975
976                        // set the template
977                        flow.study.template = Template.findByName(params.remove('template'))
978                }
979
980                // does the study have a template set?
981                if (flow.study.template && flow.study.template instanceof Template) {
982                        // yes, iterate through template fields
983                        flow.study.giveFields().each() {
984                                // and set their values
985                                flow.study.setFieldValue(it.name, params.get(it.escapedName()))
986                        }
987                }
988
989                // handle publications
990                handlePublications(flow, flash, params)
991
992                // handle contacts
993                handleContacts(flow, flash, params)
994
995                // handle users (readers, writers)
996                handleUsers(flow, flash, params, 'readers')
997                handleUsers(flow, flash, params, 'writers')
998
999                // handle public checkbox
1000                if (params.get("publicstudy")) {
1001                        flow.study.publicstudy = params.get("publicstudy")
1002                }
1003
1004                // validate the study
1005                if (flow.study.validate()) {
1006                        // instance is okay
1007                        return true
1008                } else {
1009                        // validation failed
1010                        flash.wizardErrors = [:]
1011                        this.appendErrors(flow.study, flash.wizardErrors)
1012                        return false
1013                }
1014        }
1015
1016        /**
1017         * re-usable code for handling publications form data in a web flow
1018         * @param Map LocalAttributeMap (the flow scope)
1019         * @param Map localAttributeMap (the flash scope)
1020         * @param Map GrailsParameterMap (the flow parameters = form data)
1021         * @returns boolean
1022         */
1023        def handlePublications(flow, flash, params) {
1024                if (!flow.study.publications) flow.study.publications = []
1025
1026                // Check the ids of the pubblications that should be attached
1027                // to this study. If they are already attached, keep 'm. If
1028                // studies are attached that are not in the selected (i.e. the
1029                // user deleted them), remove them
1030                def publicationIDs = params.get('publication_ids')
1031                if (publicationIDs) {
1032                        // Find the individual IDs and make integers
1033                        publicationIDs = publicationIDs.split(',').collect { Integer.parseInt(it, 10) }
1034
1035                        // First remove the publication that are not present in the array
1036                        flow.study.publications.removeAll { publication -> !publicationIDs.find { id -> id == publication.id } }
1037
1038                        // Add those publications not yet present in the database
1039                        publicationIDs.each { id ->
1040                                if (!flow.study.publications.find { publication -> id == publication.id }) {
1041                                        def publication = Publication.get(id)
1042                                        if (publication) {
1043                                                flow.study.addToPublications(publication)
1044                                        } else {
1045                                                log.info('.publication with ID ' + id + ' not found in database.')
1046                                        }
1047                                }
1048                        }
1049
1050                } else {
1051                        log.info('.no publications selected.')
1052                        flow.study.publications.clear()
1053                }
1054
1055        }
1056
1057        /**
1058         * re-usable code for handling contacts form data in a web flow
1059         * @param Map LocalAttributeMap (the flow scope)
1060         * @param Map localAttributeMap (the flash scope)
1061         * @param Map GrailsParameterMap (the flow parameters = form data)
1062         * @return boolean
1063         */
1064        def handleContacts(flow, flash, params) {
1065                if (!flow.study.persons) flow.study.persons = []
1066
1067                // Check the ids of the contacts that should be attached
1068                // to this study. If they are already attached, keep 'm. If
1069                // studies are attached that are not in the selected (i.e. the
1070                // user deleted them), remove them
1071
1072                // Contacts are saved as [person_id]-[role_id]
1073                def contactIDs = params.get('contacts_ids')
1074                if (contactIDs) {
1075                        // Find the individual IDs and make integers
1076                        contactIDs = contactIDs.split(',').collect {
1077                                def parts = it.split('-')
1078                                return [person: Integer.parseInt(parts[0]), role: Integer.parseInt(parts[1])]
1079                        }
1080
1081                        // First remove the contacts that are not present in the array
1082                        flow.study.persons.removeAll {
1083                                studyperson -> !contactIDs.find { ids -> (ids.person == studyperson.person.id) && (ids.role == studyperson.role.id) }
1084                        }
1085
1086                        // Add those contacts not yet present in the database
1087                        contactIDs.each { ids ->
1088                                if (!flow.study.persons.find { studyperson -> (ids.person == studyperson.person.id) && (ids.role == studyperson.role.id) }) {
1089                                        def person = Person.get(ids.person)
1090                                        def role = PersonRole.get(ids.role)
1091                                        if (person && role) {
1092                                                // Find a studyperson object with these parameters
1093                                                def studyPerson = StudyPerson.findAll().find { studyperson -> studyperson.person.id == person.id && studyperson.role.id == role.id }
1094
1095                                                // If if does not yet exist, save the example
1096                                                if (!studyPerson) {
1097                                                        studyPerson = new StudyPerson(
1098                                                                person: person,
1099                                                                role: role
1100                                                        )
1101                                                        studyPerson.save(flush: true)
1102                                                }
1103
1104                                                flow.study.addToPersons(studyPerson)
1105                                        } else {
1106                                                log.info('.person ' + ids.person + ' or Role ' + ids.role + ' not found in database.')
1107                                        }
1108                                }
1109                        }
1110                } else {
1111                        log.info('.no persons selected.')
1112                        flow.study.persons.clear()
1113                }
1114
1115        }
1116
1117        /**
1118         * re-usable code for handling contacts form data in a web flow
1119         * @param Map LocalAttributeMap (the flow scope)
1120         * @param Map localAttributeMap (the flash scope)
1121         * @param Map GrailsParameterMap (the flow parameters = form data)
1122         * @param String    'readers' or 'writers'
1123         * @return boolean
1124         */
1125        def handleUsers(flow, flash, params, type) {
1126                def users = []
1127
1128                if (type == "readers") {
1129                        users = flow.study.readers ?: []
1130                } else if (type == "writers") {
1131                        users = flow.study.writers ?: []
1132                }
1133
1134                // Check the ids of the contacts that should be attached
1135                // to this study. If they are already attached, keep 'm. If
1136                // studies are attached that are not in the selected (i.e. the
1137                // user deleted them), remove them
1138
1139                // Users are saved as user_id
1140                def userIDs = params.get( type + '_ids')
1141                if (userIDs) {
1142                        // Find the individual IDs and make integers
1143                        userIDs = userIDs.split(',').collect { Integer.parseInt(it, 10) }
1144
1145                        // First remove the publication that are not present in the array
1146                        users.removeAll { user -> !userIDs.find { id -> id == user.id } }
1147
1148                        // Add those publications not yet present in the database
1149                        userIDs.each { id ->
1150                                if (!users.find { user -> id == user.id }) {
1151                                        def user = SecUser.get(id)
1152                                        if (user) {
1153                                            users.add(user)
1154                                        } else {
1155                                                log.info('.user with ID ' + id + ' not found in database.')
1156                                        }
1157                                }
1158                        }
1159
1160                } else {
1161                        log.info('.no users selected.')
1162                        users.clear()
1163                }
1164
1165                if (type == "readers") {
1166                        if (flow.study.readers)
1167                        flow.study.readers.clear()
1168                        users.each { flow.study.addToReaders(it) }
1169                } else if (type == "writers") {
1170                        if (flow.study.writers)
1171                        flow.study.writers.clear()
1172
1173                        users.each { flow.study.addToWriters(it) }
1174                }
1175        }
1176
1177        /**
1178         * Handle the wizard subject page
1179         *
1180         * @param Map LocalAttributeMap (the flow scope)
1181         * @param Map localAttributeMap (the flash scope)
1182         * @param Map GrailsParameterMap (the flow parameters = form data)
1183         * @returns boolean
1184         */
1185        def subjectPage(flow, flash, params) {
1186                def errors = false
1187                flash.wizardErrors = [:]
1188
1189                // remember the params in the flash scope
1190                flash.values = params
1191
1192                // iterate through subjects
1193                flow.study.subjects.each() { subject ->
1194                        // iterate through (template and domain) fields
1195                        subject.giveFields().each() { field ->
1196                                // set field
1197                                subject.setFieldValue(
1198                                        field.name,
1199                                        params.get('subject_' + subject.getIdentifier() + '_' + field.escapedName())
1200                                )
1201                        }
1202
1203                        // validate subject
1204                        if (!subject.validate()) {
1205                                errors = true
1206                                this.appendErrors(subject, flash.wizardErrors, 'subject_' + subject.getIdentifier() + '_')
1207                        }
1208                }
1209
1210                return !errors
1211        }
1212
1213        /**
1214         * Add a number of subjects to a study
1215         *
1216         * required params entities:
1217         * -addNumber (int)
1218         * -species   (string)
1219         * -template  (string)
1220         *
1221         * @param Map LocalAttributeMap (the flow scope)
1222         * @param Map localAttributeMap (the flash scope)
1223         * @param Map GrailsParameterMap (the flow parameters = form data)
1224         * @returns boolean
1225         */
1226        def addSubjects(flow, flash, params) {
1227                // remember the params in the flash scope
1228                flash.values = params
1229
1230                // handle the subject page
1231                subjectPage(flow, flash, params)
1232
1233                // (re)set error message
1234                flash.wizardErrors = [:]
1235
1236                // set work variables
1237                def errors              = false
1238                def number              = params.get('addNumber') as int
1239                def species             = Term.findByName(params.get('species'))
1240                def template    = Template.findByName(params.get('template'))
1241
1242                // can we add subjects?
1243                if (number > 0 && species && template) {
1244                        // add subjects to study
1245                        number.times {
1246                                // work variables
1247                                def subjectName = 'Subject '
1248                                def subjectIterator = 1
1249                                def tempSubjectName = subjectName + subjectIterator
1250
1251                                // make sure subject name is unique
1252                                if (flow.study.subjects) {
1253                                        while (flow.study.subjects.find { it.name == tempSubjectName }) {
1254                                                subjectIterator++
1255                                                tempSubjectName = subjectName + subjectIterator
1256                                        }
1257                                }
1258                                subjectName = tempSubjectName
1259
1260                                // create a subject instance
1261                                def subject = new Subject(
1262                                        name            : subjectName,
1263                                        species         : species,
1264                                        template        : template
1265                                )
1266
1267                                // add it to the study
1268                                flow.study.addToSubjects( subject )
1269
1270                                // validate subject
1271                                if (subject.validate()) {
1272                                        log.info ".added subject "+subject
1273                                } else {
1274                                        // whoops?
1275                                        flow.study.removeFromSubjects( subject )
1276
1277                                        // append errors
1278                                        this.appendErrors(subject, flash.wizardErrors)
1279                                        errors = true
1280                                }
1281                        }
1282                } else {
1283                        // add feedback
1284                        errors = true
1285                        if (number < 1) this.appendErrorMap(['addNumber': 'Enter a positive number of subjects to add'], flash.wizardErrors)
1286                        if (!species)   this.appendErrorMap(['species': 'You need to select a species, or add one if it is not yet present'], flash.wizardErrors)
1287                        if (!template)  this.appendErrorMap(['template': 'You need to select a template, or add one if it is not yet present'], flash.wizardErrors)
1288                }
1289
1290                return !errors
1291        }
1292
1293        /**
1294         * Handle the wizard event page
1295         *
1296         * @param Map LocalAttributeMap (the flow scope)
1297         * @param Map localAttributeMap (the flash scope)
1298         * @param Map GrailsParameterMap (the flow parameters = form data)
1299         * @returns boolean
1300         */
1301        def eventPage(flow, flash, params) {
1302                def errors = false
1303                flash.wizardErrors = [:]
1304
1305                // remember the params in the flash scope
1306                flash.values = params
1307
1308                // handle the 'add event' form
1309                if (flow.event) {
1310                        flow.event.giveFields().each() { field ->
1311                                // set field
1312                                flow.event.setFieldValue(
1313                                        field.name,
1314                                        params.get(field.escapedName())
1315                                )
1316                        }
1317                }
1318
1319                // handle the eventGroup names and grouping
1320                def name        = ""
1321                def tempName= ""
1322                flow.study.eventGroups.each() { eventGroup ->
1323                        // iterate through templates
1324                        flow.study.giveAllEventTemplates().each() { template ->
1325                                tempName = params.get( 'eventGroup_' + eventGroup.getIdentifier() + '_' + template.getIdentifier() )
1326
1327                                // is the name different?
1328                                if (tempName != eventGroup.name) {
1329                                        name = tempName
1330                                }
1331                        }
1332
1333                        // should the name change?
1334                        if (name) {
1335                                // yes, change it
1336                                eventGroup.name = name
1337                                name = ""
1338                        }
1339
1340                        // handle eventGrouping
1341                        ( ((flow.study.events) ? flow.study.events : []) + ((flow.study.samplingEvents) ? flow.study.samplingEvents : []) ) .each() { event ->
1342                                if (params.get( 'event_' + event.getIdentifier() + '_group_' + eventGroup.getIdentifier() )) {
1343                                        // add to eventGroup
1344                                        if (event instanceof SamplingEvent) {
1345                                                // check if we are already in this eventGroup
1346                                                if (!eventGroup.samplingEvents.find { it.equals(event) }) {
1347                                                        // no, add it
1348                                                        eventGroup.addToSamplingEvents(event)
1349
1350                                                        // iterate through subjects for this eventGroup
1351                                                        eventGroup.subjects.each() { subject ->
1352                                                                // instantiate a sample for this subject / event
1353                                                                def samplingEventName = this.ucwords(event.template.name)
1354                                                                def eventGroupName = this.ucwords(eventGroup.name).replaceAll("([ ]{1,})", "")
1355                                                                def sampleName = (this.ucwords(subject.name) + '_' + samplingEventName + '_' + eventGroupName + '_' + new RelTime(event.startTime).toString()).replaceAll("([ ]{1,})", "")
1356                                                                def tempSampleIterator = 0
1357                                                                def tempSampleName = sampleName
1358
1359                                                                // make sure sampleName is unique
1360                                                                if (flow.study.samples) {
1361                                                                        while (flow.study.samples.find { it.name == tempSampleName }) {
1362                                                                                tempSampleIterator++
1363                                                                                tempSampleName = sampleName + "_" + tempSampleIterator
1364                                                                        }
1365                                                                        sampleName = tempSampleName
1366                                                                }
1367
1368                                                                // instantiate a sample
1369                                                                flow.study.addToSamples(
1370                                                                        new Sample(
1371                                                                                parentSubject   : subject,
1372                                                                                parentEvent             : event,
1373                                                                                parentEventGroup: eventGroup,
1374                                                                                name                    : sampleName,
1375                                                                                template                : (event.sampleTemplate) ? event.sampleTemplate : ''
1376                                                                        )
1377                                                                )
1378                                                        }
1379                                                }
1380                                        } else {
1381                                                eventGroup.addToEvents(event)
1382                                        }
1383                                } else {
1384                                        // remove from eventGroup
1385                                        if (event instanceof SamplingEvent) {
1386                                                // iterate through subjects (if we have them)
1387                                                eventGroup.subjects.each() { subject ->
1388                                                        // find all samples for this subject / event
1389                                                        flow.study.samples.findAll { (it.parentEvent.equals(event) && it.parentSubject.equals(subject) ) }.each() {
1390                                                                // delete this sample
1391                                                                flow.study.removeFromSamples( it )
1392                                                                it.delete()
1393                                                        }
1394                                                }
1395
1396                                                eventGroup.removeFromSamplingEvents(event)
1397                                        } else {
1398                                                eventGroup.removeFromEvents(event)
1399                                        }
1400                                }
1401                        }
1402                }
1403
1404                // handle the (sampling) events
1405                ( ((flow.study.events) ? flow.study.events : []) + ((flow.study.samplingEvents) ? flow.study.samplingEvents : []) ) .each() { event ->
1406                        event.giveFields().each() { field ->
1407                                event.setFieldValue(
1408                                        field.name,
1409                                        params.get( 'event_' + event.getIdentifier() + '_' + field.escapedName() )
1410                                )
1411                        }
1412
1413                        // validate event
1414                        if (!event.validate()) {
1415                                errors = true
1416                                this.appendErrors(event, flash.wizardErrors)
1417                        }
1418                }
1419
1420                return !errors
1421        }
1422
1423        /**
1424         * Handle the wizard group page
1425         *
1426         * @param Map LocalAttributeMap (the flow scope)
1427         * @param Map localAttributeMap (the flash scope)
1428         * @param Map GrailsParameterMap (the flow parameters = form data)
1429         * @returns boolean
1430         */
1431        def groupPage(flow, flash, params) {
1432                def errors = false
1433                flash.wizardErrors = [:]
1434
1435                // remember the params in the flash scope
1436                flash.values = params
1437
1438                // iterate through groups
1439                flow.study.eventGroups.each() { eventGroup ->
1440                        // iterate through subjects
1441                        flow.study.subjects.each() { subject ->
1442                                if (params.get('subject_' + subject.getIdentifier() + '_group_' + eventGroup.getIdentifier() )) {
1443                                        // check if this subject is already part of this eventGroup
1444                                        if ( !eventGroup.subjects.find { it.equals(subject) } ) {
1445                                                // add to eventGroup
1446                                                eventGroup.addToSubjects(subject)
1447
1448                                                // iterate through samplingEvents
1449                                                eventGroup.samplingEvents.each() { samplingEvent ->
1450                                                        def samplingEventName = this.ucwords(samplingEvent.template.name)
1451                                                        def eventGroupName = this.ucwords(eventGroup.name)
1452                                                        def sampleName = (this.ucwords(subject.name) + '_' + samplingEventName + '_' + eventGroupName + '_' + new RelTime(samplingEvent.startTime).toString()).replaceAll("([ ]{1,})", "")
1453                                                        def tempSampleIterator = 0
1454                                                        def tempSampleName = sampleName
1455
1456                                                        // make sure sampleName is unique
1457                                                        if (flow.study.samples) {
1458                                                                while (flow.study.samples.find { it.name == tempSampleName }) {
1459                                                                        tempSampleIterator++
1460                                                                        tempSampleName = sampleName + "_" + tempSampleIterator
1461                                                                }
1462                                                                sampleName = tempSampleName
1463                                                        }
1464
1465                                                        // instantiate a sample
1466                                                        flow.study.addToSamples(
1467                                                                new Sample(
1468                                                                        parentSubject   : subject,
1469                                                                        parentEvent             : samplingEvent,
1470                                                                        parentEventGroup: eventGroup,
1471                                                                        name                    : sampleName,
1472                                                                        template                : (samplingEvent.sampleTemplate) ? samplingEvent.sampleTemplate : ''
1473                                                                )
1474                                                        )
1475                                                }
1476                                        } else {
1477                                        }
1478                                } else {
1479                                        // check if this subject is a member of this eventGroup
1480                                        if (eventGroup.subjects.find { it.equals(subject) }) {
1481                                                // remove from eventGroup
1482                                                eventGroup.removeFromSubjects(subject)
1483
1484                                                // iterate through samplingEvents
1485                                                eventGroup.samplingEvents.each() { samplingEvent ->
1486                                                        flow.study.samples.findAll { (it.parentEvent.equals(samplingEvent) && it.parentSubject.equals(subject) && it.parentEventGroup.equals(eventGroup)) }.each() {
1487                                                                // delete this sample
1488                                                                flow.study.removeFromSamples(it)
1489                                                                it.delete()
1490                                                        }
1491                                                }
1492                                        }
1493                                }
1494                        }
1495                }
1496        }
1497
1498        /**
1499         * Handle the wizard samples page
1500         *
1501         * @param Map LocalAttributeMap (the flow scope)
1502         * @param Map localAttributeMap (the flash scope)
1503         * @param Map GrailsParameterMap (the flow parameters = form data)
1504         * @returns boolean
1505         */
1506        def samplePage(flow, flash, params) {
1507                def errors = false
1508                flash.wizardErrors = [:]
1509
1510                // remember the params in the flash scope
1511                flash.values = params
1512
1513                // iterate through samples
1514                flow.study.samples.each() { sample ->
1515                        // iterate through sample fields
1516                        sample.giveFields().each() { field ->
1517                                def value = params.get('sample_'+sample.getIdentifier()+'_'+field.escapedName())
1518
1519                                // set field value
1520                                if (!(field.name == 'name' && !value)) {
1521                                        log.info "setting "+field.name+" to "+value
1522                                        sample.setFieldValue(field.name, value)
1523                                }
1524                        }
1525
1526                        // has the template changed?
1527                        def templateName = params.get('template_' + sample.getIdentifier())
1528                        if (templateName && sample.template?.name != templateName) {
1529                                sample.template = Template.findByName(templateName)
1530                        }
1531
1532                        // validate sample
1533                        if (!sample.validate()) {
1534                                errors = true
1535                                this.appendErrors(sample, flash.wizardErrors, 'sample_' + sample.getIdentifier() + '_' )
1536                                log.info 'error-> sample_'+sample.getIdentifier()
1537                        }
1538                }
1539
1540                return !errors
1541        }
1542
1543        /**
1544         * Handle the wizard assays page
1545         *
1546         * @param Map LocalAttributeMap (the flow scope)
1547         * @param Map localAttributeMap (the flash scope)
1548         * @param Map GrailsParameterMap (the flow parameters = form data)
1549         * @returns boolean
1550         */
1551        def assayPage(flow, flash, params) {
1552                def errors = false
1553                flash.wizardErrors = [:]
1554
1555                // remember the params in the flash scope
1556                flash.values = params
1557
1558                // handle the 'add assay' form
1559                if (flow.assay) {
1560                        flow.assay.giveFields().each() { field ->
1561                                // set field
1562                                flow.assay.setFieldValue(
1563                                        field.name,
1564                                        params.get(field.escapedName())
1565                                )
1566                        }
1567                }
1568
1569                // handle the assay data
1570                flow.study.assays.each() { assay ->
1571                        // set data
1572                        assay.giveFields().each() { field ->
1573                                assay.setFieldValue(
1574                                        field.name,
1575                                        params.get( 'assay_' + assay.getIdentifier() + '_' + field.escapedName() )
1576                                )
1577                        }
1578
1579                        // validate assay
1580                        if (!assay.validate()) {
1581                                errors = true
1582                                this.appendErrors(assay, flash.wizardErrors, 'assay_' + assay.getIdentifier() + '_')
1583                        }
1584                }
1585
1586                return !errors
1587        }
1588
1589        /**
1590         * Handle the wizard assayGroups page
1591         *
1592         * @param Map LocalAttributeMap (the flow scope)
1593         * @param Map localAttributeMap (the flash scope)
1594         * @param Map GrailsParameterMap (the flow parameters = form data)
1595         * @returns boolean
1596         */
1597        def assayGroupPage(flow, flash, params) {
1598                def errors = false
1599                flash.wizardErrors = [:]
1600
1601                // remember the params in the flash scope
1602                flash.values = params
1603
1604                // iterate through samples
1605                flow.study.samples.each() { sample ->
1606                        // iterate through assays
1607                        flow.study.assays.each() { assay ->
1608                                if (params.get( 'sample_' + sample.getIdentifier() + '_assay_' + assay.getIdentifier() )) {
1609                                        println "add sample "+sample.getIdentifier()+" to assay "+assay.getIdentifier()
1610                                        // add sample to assay
1611                                        assay.addToSamples( sample )
1612                                } else {
1613                                        // remove sample from assay
1614                                        assay.removeFromSamples( sample )
1615                                }
1616                                println assay.samples
1617                        }
1618                }
1619
1620                return !errors
1621        }
1622
1623        /**
1624         * groovy / java equivalent of php's ucwords function
1625         *
1626         * Capitalize all first letters of separate words
1627         *
1628         * @param String
1629         * @return String
1630         */
1631        def ucwords(String text) {
1632                def newText = ''
1633
1634                // change case to lowercase
1635                text = text.toLowerCase()
1636
1637                // iterate through words
1638                text.split(" ").each() {
1639                        newText += it[0].toUpperCase() + it.substring(1) + " "
1640                }
1641
1642                return newText.substring(0, newText.size()-1)
1643        }
1644
1645        /**
1646         * return the object from a map of objects by searching for a name
1647         * @param String name
1648         * @param Map map of objects
1649         * @return Object
1650         */
1651        def getObjectByName(name, map) {
1652                def result = null
1653                map.each() {
1654                        if (it.name == name) {
1655                                result = it
1656                        }
1657                }
1658
1659                return result
1660        }
1661
1662        /**
1663         * transform domain class validation errors into a human readable
1664         * linked hash map
1665         * @param object validated domain class
1666         * @return object  linkedHashMap
1667         */
1668        def getHumanReadableErrors(object) {
1669                def errors = [:]
1670                object.errors.getAllErrors().each() {
1671                        def message = it.toString()
1672
1673                        //errors[it.getArguments()[0]] = it.getDefaultMessage()
1674                        errors[it.getArguments()[0]] = message.substring(0, message.indexOf(';'))
1675                }
1676
1677                return errors
1678        }
1679
1680        /**
1681         * append errors of a particular object to a map
1682         * @param object
1683         * @param map linkedHashMap
1684         * @void
1685         */
1686        def appendErrors(object, map) {
1687                this.appendErrorMap(this.getHumanReadableErrors(object), map)
1688        }
1689
1690        def appendErrors(object, map, prepend) {
1691                this.appendErrorMap(this.getHumanReadableErrors(object), map, prepend)
1692        }
1693
1694        /**
1695         * append errors of one map to another map
1696         * @param map linkedHashMap
1697         * @param map linkedHashMap
1698         * @void
1699         */
1700        def appendErrorMap(map, mapToExtend) {
1701                map.each() {key, value ->
1702                        mapToExtend[key] = ['key': key, 'value': value, 'dynamic': false]
1703                }
1704        }
1705
1706        def appendErrorMap(map, mapToExtend, prepend) {
1707                map.each() {key, value ->
1708                        mapToExtend[prepend + key] = ['key': key, 'value': value, 'dynamic': true]
1709                }
1710        }
1711
1712        /**
1713         * Parses a RelTime string and returns a nice human readable string
1714         *
1715         * @return Human Readable string or a HTTP response code 400 on error
1716         */
1717        def ajaxParseRelTime = {
1718                if (params.reltime == null) {
1719                        response.status = 400
1720                        render('reltime parameter is expected')
1721                }
1722
1723                try {
1724                        def reltime = RelTime.parseRelTime(params.reltime)
1725                        render reltime.toPrettyString()
1726                } catch (IllegalArgumentException e) {
1727                        response.status = 400
1728                        render(e.getMessage())
1729                }
1730        }
1731
1732        /**
1733         * Proxy for searching PubMed articles (or other articles from the Entrez DB).
1734         *
1735         * This proxy is needed because it is not allowed to fetch XML directly from a different
1736         * domain using javascript. So we have the javascript call a function on our own domain
1737         * and the proxy will fetch the data from Entrez
1738         *
1739         * @since       20100609
1740         * @param       _utility        The name of the utility, without the complete path. Example: 'esearch.fcgi'
1741         * @return      XML
1742         */
1743        def entrezProxy = {
1744                // Remove unnecessary parameters
1745                params.remove( "action" )
1746                params.remove( "controller" )
1747
1748                def url = "http://eutils.ncbi.nlm.nih.gov/entrez/eutils";
1749                def util = params.remove( "_utility" )
1750                def paramString = params.collect { k, v -> k + '=' + v.encodeAsURL() }.join( '&' );
1751
1752                def fullUrl = url + '/' + util + '?' + paramString;
1753
1754                // Return the output of the request
1755                // render fullUrl;
1756                render(
1757                    text:           new URL( fullUrl ).getText(),
1758                    contentType:    "text/xml",
1759                    encoding:       "UTF-8"
1760                );
1761        }
1762}
Note: See TracBrowser for help on using the browser.