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

Last change on this file since 1794 was 1794, checked in by work@…, 7 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
File size: 6.0 KB
RevLine 
[81]1package dbnp.studycapturing
[1792]2import java.util.ArrayList;
3
[1457]4import org.dbnp.gdt.*
[81]5
[84]6/**
7 * The Sample class describes an actual sample which results from a SamplingEvent.
[822]8 *
9 * Revision information:
10 * $Rev: 1794 $
11 * $Author: work@osx.eu $
12 * $Date: 2011-04-28 13:33:45 +0000 (do, 28 apr 2011) $
[84]13 */
[1452]14class Sample extends TemplateEntity {
[667]15        // uncommented due to searchable issue
16        // @see http://jira.codehaus.org/browse/GRAILSPLUGINS-1577
17        //static searchable = { [only: ['name']] }
[654]18
[1034]19        static belongsTo = [
[1169]20                // A Sample always belongs to one study.
[1034]21                parent                  : Study,
[1169]22
23                // A Sample optionally has a parent Subject from which it was taken, this Subject should be in the same parent study.
[1034]24                parentSubject   : Subject,
[1169]25
26                // Also, it has a parent SamplingEvent describing the actual sampling, also within the same parent study.
[1034]27                parentEvent             : SamplingEvent,
[1169]28
29                // And it has a parent EventGroup which tied it to its parent subject and parent event
[1034]30                parentEventGroup: EventGroup
[1169]31
32                // We can't have parentAssay since a Sample can belong to multiple Assays
[1034]33        ]
[654]34
[652]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)
[1440]37       
[397]38        /**
[1440]39         * UUID of this sample
40         */
41        String sampleUUID
42       
43        /**
[397]44         * return the domain fields for this domain class
45         * @return List
46         */
[506]47        static List<TemplateField> giveDomainFields() { return Sample.domainFields }
[754]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.
[540]51        static List<TemplateField> domainFields = [
52                new TemplateField(
53                        name: 'name',
54                        type: TemplateFieldType.STRING,
[654]55                        preferredIdentifier: true,
56                        required: true
[540]57                ),
58                new TemplateField(
59                        name: 'material',
[1314]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'"
[540]62                )
63        ]
[375]64
[232]65        static constraints = {
[754]66                // The parent subject is optional, e.g. in a biobank of samples the subject could be unknown or non-existing.
[288]67                parentSubject(nullable:true)
[1034]68
[960]69                // The same holds for parentEvent
[1169]70                parentEvent(nullable:true)
[1034]71
72                // and for parentEventGroup
73                parentEventGroup(nullable:true)
[1169]74
[754]75                // The material domain field is optional
[654]76                material(nullable: true)
[1588]77
[1440]78                sampleUUID(nullable: true, unique: true)
[654]79
[754]80                // Check if the externalSampleId (currently defined as name) is really unique within each parent study of this sample.
[654]81                // This feature is tested by integration test SampleTests.testSampleUniqueNameConstraint
[689]82                name(unique:['parent'])
[861]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
[1373]92                        // check whether obj.parent.samples is not null at this stage to avoid null pointer exception
[861]93                        if (obj.parent) {
94
[1373]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                                        }
[861]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                })
[232]117        }
[81]118
[1036]119    static mapping = {
120        sort "name"
[1198]121
122        // Workaround for bug http://jira.codehaus.org/browse/GRAILS-6754
123        templateTextFields type: 'text'
[1036]124    }
125
[288]126        static getSamplesFor( event ) {
[540]127                return  Sample.findAll( 'from Sample s where s.parentEvent =:event', [event:event] )
[288]128        }
[651]129
[697]130        def String toString() {
131                return name
132        }
[1482]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   }
[1440]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();
[1454]157                        if( !this.save(flush:true) ) {
[1794]158                                //println "Couldn't save sample UUID: " + this.getErrors();
[1454]159                        }
[1440]160                }
161               
162                return this.sampleUUID;
163        }
[1792]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
[81]206}
Note: See TracBrowser for help on using the repository browser.