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

Revision 1794, 6.0 KB (checked in by work@…, 3 years ago)

- added support for fuzzymatching (resolves #418)
- removed debug lines
- gdt up to 0.0.38

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