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
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.is(s) || 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.