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

Revision 1945, 5.9 KB (checked in by robert@…, 3 years ago)

- Updated the import algorithm in the simpleWizard, so it won't be too slow with > 200 samples
- Changed the warning added in r1944 so the user knows why the import will be slow

  • Property svn:keywords set to Rev Author Date
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$
11 * $Author$
12 * $Date$
[84]13 */
[1452]14class Sample extends TemplateEntity {
[1034]15        static belongsTo = [
[1169]16                // A Sample always belongs to one study.
[1034]17                parent                  : Study,
[1169]18
19                // A Sample optionally has a parent Subject from which it was taken, this Subject should be in the same parent study.
[1034]20                parentSubject   : Subject,
[1169]21
22                // Also, it has a parent SamplingEvent describing the actual sampling, also within the same parent study.
[1034]23                parentEvent             : SamplingEvent,
[1169]24
25                // And it has a parent EventGroup which tied it to its parent subject and parent event
[1034]26                parentEventGroup: EventGroup
[1169]27
28                // We can't have parentAssay since a Sample can belong to multiple Assays
[1034]29        ]
[654]30
[652]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)
[1440]33       
[397]34        /**
[1440]35         * UUID of this sample
36         */
37        String sampleUUID
38       
39        /**
[397]40         * return the domain fields for this domain class
41         * @return List
42         */
[506]43        static List<TemplateField> giveDomainFields() { return Sample.domainFields }
[754]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.
[540]47        static List<TemplateField> domainFields = [
48                new TemplateField(
49                        name: 'name',
50                        type: TemplateFieldType.STRING,
[654]51                        preferredIdentifier: true,
52                        required: true
[540]53                ),
54                new TemplateField(
55                        name: 'material',
[1314]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'"
[540]58                )
59        ]
[375]60
[232]61        static constraints = {
[754]62                // The parent subject is optional, e.g. in a biobank of samples the subject could be unknown or non-existing.
[288]63                parentSubject(nullable:true)
[1034]64
[960]65                // The same holds for parentEvent
[1169]66                parentEvent(nullable:true)
[1034]67
68                // and for parentEventGroup
69                parentEventGroup(nullable:true)
[1169]70
[754]71                // The material domain field is optional
[654]72                material(nullable: true)
[1588]73
[1440]74                sampleUUID(nullable: true, unique: true)
[654]75
[754]76                // Check if the externalSampleId (currently defined as name) is really unique within each parent study of this sample.
[654]77                // This feature is tested by integration test SampleTests.testSampleUniqueNameConstraint
[689]78                name(unique:['parent'])
[861]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
[1373]88                        // check whether obj.parent.samples is not null at this stage to avoid null pointer exception
[861]89                        if (obj.parent) {
90
[1373]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                                        }
[861]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                })
[232]113        }
[81]114
[1036]115    static mapping = {
116        sort "name"
[1198]117
118        // Workaround for bug http://jira.codehaus.org/browse/GRAILS-6754
119        templateTextFields type: 'text'
[1036]120    }
121
[288]122        static getSamplesFor( event ) {
[540]123                return  Sample.findAll( 'from Sample s where s.parentEvent =:event', [event:event] )
[288]124        }
[651]125
[697]126        def String toString() {
127                return name
128        }
[1482]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           
[1945]144           return this.is(s) || this.id == s.id
[1482]145   }
[1440]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();
[1454]153                        if( !this.save(flush:true) ) {
[1794]154                                //println "Couldn't save sample UUID: " + this.getErrors();
[1454]155                        }
[1440]156                }
157               
158                return this.sampleUUID;
159        }
[1792]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
[81]202}
Note: See TracBrowser for help on using the browser.