source: trunk/grails-app/domain/dbnp/studycapturing/Sample.groovy @ 1373

Last change on this file since 1373 was 1373, checked in by business@…, 10 years ago

added extra check in sample validation to avoid NPEs in case of broken Study objects

  • Property svn:keywords set to Author Date Rev
File size: 4.0 KB
RevLine 
[81]1package dbnp.studycapturing
2
[106]3import dbnp.data.Term
4
[84]5/**
6 * The Sample class describes an actual sample which results from a SamplingEvent.
[822]7 *
8 * Revision information:
9 * $Rev: 1373 $
10 * $Author: business@keesvanbochove.nl $
11 * $Date: 2011-01-12 13:42:06 +0000 (wo, 12 jan 2011) $
[84]12 */
[232]13class Sample extends TemplateEntity {
[667]14        // uncommented due to searchable issue
15        // @see http://jira.codehaus.org/browse/GRAILSPLUGINS-1577
16        //static searchable = { [only: ['name']] }
[654]17
[1034]18        static belongsTo = [
[1169]19                // A Sample always belongs to one study.
[1034]20                parent                  : Study,
[1169]21
22                // A Sample optionally has a parent Subject from which it was taken, this Subject should be in the same parent study.
[1034]23                parentSubject   : Subject,
[1169]24
25                // Also, it has a parent SamplingEvent describing the actual sampling, also within the same parent study.
[1034]26                parentEvent             : SamplingEvent,
[1169]27
28                // And it has a parent EventGroup which tied it to its parent subject and parent event
[1034]29                parentEventGroup: EventGroup
[1169]30
31                // We can't have parentAssay since a Sample can belong to multiple Assays
[1034]32        ]
[654]33
[652]34        String name             // should be unique with respect to the parent study (which can be inferred)
35        Term material           // material of the sample (should normally be bound to the BRENDA ontology)
[224]36
[397]37        /**
38         * return the domain fields for this domain class
39         * @return List
40         */
[506]41        static List<TemplateField> giveDomainFields() { return Sample.domainFields }
[754]42
43        // We have to specify an ontology list for the material property. However, at compile time, this ontology does of course not exist.
44        // Therefore, the ontology is added at runtime in the bootstrap, possibly downloading the ontology properties if it is not present in the database yet.
[540]45        static List<TemplateField> domainFields = [
46                new TemplateField(
47                        name: 'name',
48                        type: TemplateFieldType.STRING,
[654]49                        preferredIdentifier: true,
50                        required: true
[540]51                ),
52                new TemplateField(
53                        name: 'material',
[1314]54                        type: TemplateFieldType.ONTOLOGYTERM,
55                        comment: "The material is based on the BRENDA tissue / enzyme source ontology, a structured controlled vocabulary for the source of an enzyme. It comprises terms for tissues, cell lines, cell types and cell cultures from uni- and multicellular organisms. If a material is missing, please add it by using 'add more'"
[540]56                )
57        ]
[375]58
[232]59        static constraints = {
[754]60                // The parent subject is optional, e.g. in a biobank of samples the subject could be unknown or non-existing.
[288]61                parentSubject(nullable:true)
[1034]62
[960]63                // The same holds for parentEvent
[1169]64                parentEvent(nullable:true)
[1034]65
66                // and for parentEventGroup
67                parentEventGroup(nullable:true)
[1169]68
[754]69                // The material domain field is optional
[654]70                material(nullable: true)
71
[754]72                // Check if the externalSampleId (currently defined as name) is really unique within each parent study of this sample.
[654]73                // This feature is tested by integration test SampleTests.testSampleUniqueNameConstraint
[689]74                name(unique:['parent'])
[861]75
76                // Same, but also when the other sample is not even in the database
77                // This feature is tested by integration test SampleTests.testSampleUniqueNameConstraintAtValidate
78                name(validator: { field, obj, errors ->
79                        // 'obj' refers to the actual Sample object
80
81                        // define a boolean
82                        def error = false
83
[1373]84                        // check whether obj.parent.samples is not null at this stage to avoid null pointer exception
[861]85                        if (obj.parent) {
86
[1373]87                                if (obj.parent.samples) {
88
89                                        // check if there is exactly one sample with this name in the study (this one)
90                                        if (obj.parent.samples.findAll{ it.name == obj.name}.size() > 1) {
91                                                error = true
92                                                errors.rejectValue(
93                                                        'name',
94                                                        'sample.UniqueNameViolation',
95                                                        [obj.name, obj.parent] as Object[],
96                                                        'Sample name {0} appears multiple times in study {1}'
97                                                        )
98                                        }
[861]99                                }
100                        }
101                        else {
102                                // if there is no parent study defined, fail immediately
103                                error = true
104                        }
105
106                        // got an error, or not?
107                        return (!error)
108                })
[232]109        }
[81]110
[1036]111    static mapping = {
112        sort "name"
[1198]113
114        // Workaround for bug http://jira.codehaus.org/browse/GRAILS-6754
115        templateTextFields type: 'text'
[1036]116    }
117
[288]118        static getSamplesFor( event ) {
[540]119                return  Sample.findAll( 'from Sample s where s.parentEvent =:event', [event:event] )
[288]120        }
[651]121
[697]122        def String toString() {
123                return name
124        }
[81]125}
Note: See TracBrowser for help on using the repository browser.