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

Last change on this file since 2153 was 2153, checked in by ferryjagers@…, 8 years ago

studyWizardController.Event - sampleName + templateName fix

  • Property svn:keywords set to Rev Author Date
File size: 7.5 KB
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: 2153 $
11 * $Author: ferryjagers@gmail.com $
12 * $Date: 2012-01-26 16:12:54 +0000 (do, 26 jan 2012) $
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        /**
127         * Returns all assays this samples has been processed in
128         * @return      List of assays
129         */
130        public List getAssays() {
131                return Assay.executeQuery( 'select distinct a from Assay a inner join a.samples s where s = :sample', ['sample': this] )
132        }
133       
134        def String toString() {
135                return name
136        }
137
138        /**
139        * Basic equals method to check whether objects are equals, by comparing the ids
140        * @param o              Object to compare with
141        * @return               True iff the id of the given Sample is equal to the id of this Sample
142        */
143   public boolean equals( Object o ) {
144           if( o == null )
145                   return false;
146                   
147           if( !( o instanceof Sample ) )
148                   return false
149           
150           Sample s = (Sample) o;
151           
152           return this.is(s) || this.id == s.id
153   }
154       
155        /**
156         * Returns the UUID of this sample and generates one if needed
157         */
158        public String giveUUID() {
159                if( !this.sampleUUID ) {
160                        this.sampleUUID = UUID.randomUUID().toString();
161                        if( !this.save(flush:true) ) {
162                                //println "Couldn't save sample UUID: " + this.getErrors();
163                        }
164                }
165               
166                return this.sampleUUID;
167        }
168       
169        /**
170        * Returns a human readable string of a list of samples, with a maximum number
171        * of characters
172        *
173        * @param sampleList List with Sample objects
174        * @param maxChars maximum number of characters returned
175        * @return human readble string with at most maxChars characters, representing the samples given.
176        */
177   public static String trimSampleNames(ArrayList sampleList, Integer maxChars) {
178           def simpleSamples = sampleList.name.join(', ');
179           def showSamples
180
181           // If the subjects will fit, show them all
182           if (!maxChars || simpleSamples.size() < maxChars) {
183                   showSamples = simpleSamples;
184           } else {
185                   // Always add the first name
186                   def sampleNames = sampleList[0]?.name;
187
188                   // Continue adding names until the length is to long
189                   def id = 0;
190                   sampleList.each { sample ->
191                           if (id > 0) {
192                                   if (sampleNames?.size() + sample.name?.size() < maxChars - 15) {
193                                           sampleNames += ", " + sample.name;
194                                   } else {
195                                           return;
196                                   }
197                           }
198                           id++;
199                   }
200
201                   // Add a postfix
202                   sampleNames += " and " + (sampleList?.size() - id) + " more";
203
204                   showSamples = sampleNames;
205           }
206
207           return showSamples
208   }
209
210    public static String generateSampleName(flow, subject, eventGroup, samplingEvent) {
211        def samplingEventName = ucwords(samplingEvent.template.name)
212        def eventGroupName = ucwords(eventGroup.name).replaceAll("([ ]{1,})", "")
213        def sampleTemplateName = (samplingEvent.sampleTemplate) ? ucwords(samplingEvent.sampleTemplate.name) : ''
214        def sampleName = (ucwords(subject.name) + '_' + samplingEventName + '_' + eventGroupName + '_' + new RelTime(samplingEvent.startTime).toString() + '_' + sampleTemplateName).replaceAll("([ ]{1,})", "")
215        def tempSampleIterator = 0
216        def tempSampleName = sampleName
217
218        // make sure sampleName is unique
219        if (flow.study.samples) {
220            while (flow.study.samples.find { it.name == tempSampleName }) {
221                tempSampleIterator++
222                tempSampleName = sampleName + "_" + tempSampleIterator
223            }
224            sampleName = tempSampleName
225        }
226        return sampleName
227    }
228
229    /**
230         * groovy / java equivalent of php's ucwords function
231         *
232         * Capitalize all first letters of separate words
233         *
234         * @param String
235         * @return String
236         */
237        public static ucwords(String text) {
238                def newText = ''
239
240                // change case to lowercase
241                text = text.toLowerCase()
242
243                // iterate through words
244                text.split(" ").each() {
245                        newText += it[0].toUpperCase() + it.substring(1) + " "
246                }
247
248                return newText.substring(0, newText.size()-1)
249        }
250}
Note: See TracBrowser for help on using the repository browser.