root/trunk/grails-app/domain/dbnp/studycapturing/Sample.groovy @ 1922

Revision 1922, 5.9 KB (checked in by work@…, 3 years ago)

- removed all default instance configuration in favor of just one default.properties file. Instance configuration should be on the server side, not within the code. The setup wizard will help you generate a configuration file...
- removed obsolete references to searchable
- removed obsolete references to grails melody

  • Property svn:keywords set to Rev Author Date
Line 
1package dbnp.studycapturing
2import java.util.ArrayList;
3
4import org.dbnp.gdt.*
5
6/**
7 * The Sample class describes an actual sample which results from a SamplingEvent.
8 *
9 * Revision information:
10 * $Rev$
11 * $Author$
12 * $Date$
13 */
14class Sample extends TemplateEntity {
15        static belongsTo = [
16                // A Sample always belongs to one study.
17                parent                  : Study,
18
19                // A Sample optionally has a parent Subject from which it was taken, this Subject should be in the same parent study.
20                parentSubject   : Subject,
21
22                // Also, it has a parent SamplingEvent describing the actual sampling, also within the same parent study.
23                parentEvent             : SamplingEvent,
24
25                // And it has a parent EventGroup which tied it to its parent subject and parent event
26                parentEventGroup: EventGroup
27
28                // We can't have parentAssay since a Sample can belong to multiple Assays
29        ]
30
31        String name             // should be unique with respect to the parent study (which can be inferred)
32        Term material           // material of the sample (should normally be bound to the BRENDA ontology)
33       
34        /**
35         * UUID of this sample
36         */
37        String sampleUUID
38       
39        /**
40         * return the domain fields for this domain class
41         * @return List
42         */
43        static List<TemplateField> giveDomainFields() { return Sample.domainFields }
44
45        // We have to specify an ontology list for the material property. However, at compile time, this ontology does of course not exist.
46        // Therefore, the ontology is added at runtime in the bootstrap, possibly downloading the ontology properties if it is not present in the database yet.
47        static List<TemplateField> domainFields = [
48                new TemplateField(
49                        name: 'name',
50                        type: TemplateFieldType.STRING,
51                        preferredIdentifier: true,
52                        required: true
53                ),
54                new TemplateField(
55                        name: 'material',
56                        type: TemplateFieldType.ONTOLOGYTERM,
57                        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'"
58                )
59        ]
60
61        static constraints = {
62                // The parent subject is optional, e.g. in a biobank of samples the subject could be unknown or non-existing.
63                parentSubject(nullable:true)
64
65                // The same holds for parentEvent
66                parentEvent(nullable:true)
67
68                // and for parentEventGroup
69                parentEventGroup(nullable:true)
70
71                // The material domain field is optional
72                material(nullable: true)
73
74                sampleUUID(nullable: true, unique: true)
75
76                // Check if the externalSampleId (currently defined as name) is really unique within each parent study of this sample.
77                // This feature is tested by integration test SampleTests.testSampleUniqueNameConstraint
78                name(unique:['parent'])
79
80                // Same, but also when the other sample is not even in the database
81                // This feature is tested by integration test SampleTests.testSampleUniqueNameConstraintAtValidate
82                name(validator: { field, obj, errors ->
83                        // 'obj' refers to the actual Sample object
84
85                        // define a boolean
86                        def error = false
87
88                        // check whether obj.parent.samples is not null at this stage to avoid null pointer exception
89                        if (obj.parent) {
90
91                                if (obj.parent.samples) {
92
93                                        // check if there is exactly one sample with this name in the study (this one)
94                                        if (obj.parent.samples.findAll{ it.name == obj.name}.size() > 1) {
95                                                error = true
96                                                errors.rejectValue(
97                                                        'name',
98                                                        'sample.UniqueNameViolation',
99                                                        [obj.name, obj.parent] as Object[],
100                                                        'Sample name {0} appears multiple times in study {1}'
101                                                        )
102                                        }
103                                }
104                        }
105                        else {
106                                // if there is no parent study defined, fail immediately
107                                error = true
108                        }
109
110                        // got an error, or not?
111                        return (!error)
112                })
113        }
114
115    static mapping = {
116        sort "name"
117
118        // Workaround for bug http://jira.codehaus.org/browse/GRAILS-6754
119        templateTextFields type: 'text'
120    }
121
122        static getSamplesFor( event ) {
123                return  Sample.findAll( 'from Sample s where s.parentEvent =:event', [event:event] )
124        }
125
126        def String toString() {
127                return name
128        }
129
130        /**
131        * Basic equals method to check whether objects are equals, by comparing the ids
132        * @param o              Object to compare with
133        * @return               True iff the id of the given Sample is equal to the id of this Sample
134        */
135   public boolean equals( Object o ) {
136           if( o == null )
137                   return false;
138                   
139           if( !( o instanceof Sample ) )
140                   return false
141           
142           Sample s = (Sample) o;
143           
144           return this.id == s.id
145   }
146       
147        /**
148         * Returns the UUID of this sample and generates one if needed
149         */
150        public String giveUUID() {
151                if( !this.sampleUUID ) {
152                        this.sampleUUID = UUID.randomUUID().toString();
153                        if( !this.save(flush:true) ) {
154                                //println "Couldn't save sample UUID: " + this.getErrors();
155                        }
156                }
157               
158                return this.sampleUUID;
159        }
160       
161        /**
162        * Returns a human readable string of a list of samples, with a maximum number
163        * of characters
164        *
165        * @param sampleList List with Sample objects
166        * @param maxChars maximum number of characters returned
167        * @return human readble string with at most maxChars characters, representing the samples given.
168        */
169   public static String trimSampleNames(ArrayList sampleList, Integer maxChars) {
170           def simpleSamples = sampleList.name.join(', ');
171           def showSamples
172
173           // If the subjects will fit, show them all
174           if (!maxChars || simpleSamples.size() < maxChars) {
175                   showSamples = simpleSamples;
176           } else {
177                   // Always add the first name
178                   def sampleNames = sampleList[0]?.name;
179
180                   // Continue adding names until the length is to long
181                   def id = 0;
182                   sampleList.each { sample ->
183                           if (id > 0) {
184                                   if (sampleNames?.size() + sample.name?.size() < maxChars - 15) {
185                                           sampleNames += ", " + sample.name;
186                                   } else {
187                                           return;
188                                   }
189                           }
190                           id++;
191                   }
192
193                   // Add a postfix
194                   sampleNames += " and " + (sampleList?.size() - id) + " more";
195
196                   showSamples = sampleNames;
197           }
198
199           return showSamples
200   }
201
202}
Note: See TracBrowser for help on using the browser.