source: trunk/grails-app/domain/dbnp/studycapturing/Study.groovy @ 874

Last change on this file since 874 was 874, checked in by duh, 11 years ago
  • Resolved issue #66, marking required fields in wizard
  • Property svn:keywords set to Author Rev Date
File size: 10.4 KB
Line 
1package dbnp.studycapturing
2
3import dbnp.user.User
4
5/**
6 * Domain class describing the basic entity in the study capture part: the Study class.
7 *
8 * Revision information:
9 * $Rev: 874 $
10 * $Author: duh $
11 * $Date: 2010-09-08 11:17:25 +0000 (wo, 08 sep 2010) $
12 */
13class Study extends TemplateEntity {
14        static searchable = {
15        [only: ['title', 'Description']]
16    }
17
18        User owner   // The owner of the study. A new study is automatically owned by its creator.
19        String title        // The title of the study
20        String code             // currently used as the external study ID, e.g. to reference a study in a SAM module
21        Date dateCreated
22        Date lastUpdated
23        Date startDate
24    List subjects
25        List events
26        List samplingEvents
27        List eventGroups
28        List samples
29        List assays
30
31        static hasMany = [
32                editors: User,   // Users with read/write access to the study
33                readers: User,   // Users with only read access to the study
34                subjects: Subject,
35                samplingEvents: SamplingEvent,
36                events: Event,
37                eventGroups: EventGroup,
38                samples: Sample,
39                assays: Assay,
40                persons: StudyPerson,
41                publications: Publication
42        ]
43
44        static constraints = {
45                owner(nullable: true, blank: true)
46                code(nullable:false, blank:true,unique:true)
47        }
48
49        static mapping = {
50                researchQuestion type: 'text'
51                description type: 'text'
52                autoTimestamp true
53        }
54
55        // The external study ID is currently defined as the code of the study.
56        // It is used from within dbNP submodules to refer to particular study in this GSCF instance.
57        def getExternalStudyId() { code }
58
59        /**
60         * return the domain fields for this domain class
61         * @return List
62         */
63        static List<TemplateField> giveDomainFields() { return Study.domainFields }
64
65        static final List<TemplateField> domainFields = [
66                new TemplateField(
67                        name: 'title',
68                        type: TemplateFieldType.STRING,
69                        required: true),
70                new TemplateField(
71                        name: 'code',
72                        type: TemplateFieldType.STRING,
73                        preferredIdentifier:true,
74                        comment: 'Fill out the code by which many people will recognize your study',
75                        required: true),
76                new TemplateField(
77                        name: 'startDate',
78                        type: TemplateFieldType.DATE,
79                        comment: 'Fill out the official start date or date of first action',
80                        required: true)
81        ]
82
83        /**
84         * return the title of this study
85         */
86        def String toString() {
87                return title
88        }
89
90        /**
91         * returns all events and sampling events that do not belong to a group
92         */
93        def Set<Event> getOrphanEvents() {
94                def orphans =   events.findAll { event -> !event.belongsToGroup(eventGroups) } +
95                                                samplingEvents.findAll { event -> !event.belongsToGroup(eventGroups) }
96
97                return orphans
98        }
99
100        /**
101         * Return the unique Subject templates that are used in this study
102         */
103        def Set<Template> giveSubjectTemplates() {
104                TemplateEntity.giveTemplates(subjects)
105        }
106
107        /**
108         * Return all subjects for a specific template
109         * @param Template
110         * @return ArrayList
111         */
112        def ArrayList<Subject> giveSubjectsForTemplate(Template template) {
113                subjects.findAll { it.template.equals(template) }
114        }
115
116        /**
117         * Return all unique assay templates
118         * @return Set
119         */
120        Set<Template> giveAllAssayTemplates() {
121                TemplateEntity.giveTemplates(( (assays) ? assays : [] ))
122        }
123
124        /**
125         * Return all assays for a particular template
126         * @return ArrayList
127         */
128        def ArrayList giveAssaysForTemplate(Template template) {
129                assays.findAll { it.template.equals(template) }
130        }
131
132        /**
133         * Return the unique Event and SamplingEvent templates that are used in this study
134         */
135        Set<Template> giveAllEventTemplates() {
136                // For some reason, giveAllEventTemplates() + giveAllSamplingEventTemplates()
137                // gives trouble when asking .size() to the result
138                // So we also use giveTemplates here
139                TemplateEntity.giveTemplates( ((events) ? events : []) + ((samplingEvents) ? samplingEvents : []) )
140        }
141
142
143        /**
144         * Return all events and samplingEvenets for a specific template
145         * @param Template
146         * @return ArrayList
147         */
148        def ArrayList giveEventsForTemplate(Template template) {
149                def events = events.findAll { it.template.equals(template) }
150                def samplingEvents = samplingEvents.findAll { it.template.equals(template) }
151
152                return (events) ? events : samplingEvents
153        }
154
155        /**
156         * Return the unique Event templates that are used in this study
157         */
158        Set<Template> giveEventTemplates() {
159                TemplateEntity.giveTemplates(events)
160        }
161
162        /**
163         * Return the unique SamplingEvent templates that are used in this study
164         */
165        Set<Template> giveSamplingEventTemplates() {
166                TemplateEntity.giveTemplates(samplingEvents)
167        }
168
169        /**
170         * Returns the unique Sample templates that are used in the study
171         */
172        Set<Template> giveSampleTemplates() {
173                TemplateEntity.giveTemplates(samples)
174        }
175
176        /**
177         * Return all samples for a specific template
178         * @param Template
179         * @return ArrayList
180         */
181        def ArrayList<Subject> giveSamplesForTemplate(Template template) {
182                samples.findAll { it.template.equals(template) }
183        }
184
185        /**
186         * Returns the template of the study
187         */
188        Template giveStudyTemplate() {
189                return this.template
190        }
191
192
193        /**
194         * Delete a specific subject from this study, including all its relations
195         * @param subject The subject to be deleted
196         * @return A String which contains a (user-readable) message describing the changes to the database
197         */
198        String deleteSubject(Subject subject) {
199                String msg = "Subject ${subject.name} was deleted"
200
201                // Delete the subject from the event groups it was referenced in
202                this.eventGroups.each {
203                        if (it.subjects.contains(subject)) {
204                                it.removeFromSubjects(subject)
205                                msg += ", deleted from event group '${it.name}'"
206                        }
207                }
208
209                // Delete the samples that have this subject as parent
210                this.samples.findAll { it.parentSubject.equals(subject) }.each {
211                        // This should remove the sample itself too, because of the cascading belongsTo relation
212                        this.removeFromSamples(it)
213                        // But apparently it needs an explicit delete() too
214                        it.delete()
215                        msg += ", sample '${it.name}' was deleted"
216                }
217
218                // This should remove the subject itself too, because of the cascading belongsTo relation
219                this.removeFromSubjects(subject)
220                // But apparently it needs an explicit delete() too
221                subject.delete()
222
223                return msg
224        }
225
226        /**
227         * Delete an assay from the study
228         * @param Assay
229         * @void
230         */
231        def deleteAssay(Assay assay) {
232                if (assay && assay instanceof Assay) {
233                        // iterate through linked samples
234                        assay.samples.findAll { true }.each() { sample ->
235                                assay.removeFromSamples(sample)
236                        }
237
238                        // remove this assay from the study
239                        this.removeFromAssays(assay)
240
241                        // and delete it explicitly
242                        assay.delete()
243                }
244        }
245
246        /**
247         * Delete an event from the study, including all its relations
248         * @param Event
249         * @return String
250         */
251        String deleteEvent(Event event) {
252                String msg = "Event ${event} was deleted"
253
254                // remove event from the study
255                this.removeFromEvents(event)
256
257                // remove event from eventGroups
258                this.eventGroups.each() { eventGroup ->
259                        eventGroup.removeFromEvents(event)
260                }
261
262                return msg
263        }
264
265        /**
266         * Delete a samplingEvent from the study, including all its relations
267         * @param SamplingEvent
268         * @return String
269         */
270        String deleteSamplingEvent(SamplingEvent samplingEvent) {
271                String msg = "SamplingEvent ${samplingEvent} was deleted"
272
273                // remove event from eventGroups
274                this.eventGroups.each() { eventGroup ->
275                        eventGroup.removeFromSamplingEvents(samplingEvent)
276                }
277
278                // Delete the samples that have this sampling event as parent
279                this.samples.findAll { it.parentEvent.equals(samplingEvent) }.each {
280                        // This should remove the sample itself too, because of the cascading belongsTo relation
281                        this.removeFromSamples(it)
282                        // But apparently it needs an explicit delete() too
283                        it.delete()
284                        msg += ", sample '${it.name}' was deleted"
285                }
286
287                // Remove event from the study
288                // This should remove the event group itself too, because of the cascading belongsTo relation
289                this.removeFromSamplingEvents(samplingEvent)
290
291                // But apparently it needs an explicit delete() too
292                // (Which can be verified by outcommenting this line, then SampleTests.testDeleteViaParentSamplingEvent fails
293                samplingEvent.delete()
294
295                return msg
296        }
297       
298        /**
299         * Delete an eventGroup from the study, including all its relations
300         * @param EventGroup
301         * @return String
302         */
303        String deleteEventGroup(EventGroup eventGroup) {
304                String msg = "EventGroup ${eventGroup} was deleted"
305
306                // If the event group contains sampling events
307                if (eventGroup.samplingEvents) {
308                        // remove all samples that originate from this eventGroup
309                        if (eventGroup.samplingEvents.size()) {
310                                // find all samples related to this eventGroup
311                                // - subject comparison is relatively straightforward and
312                                //   behaves as expected
313                                // - event comparison behaves strange, so now we compare
314                                //              1. database id's or,
315                                //              2. object identifiers or,
316                                //              3. objects itself
317                                //   this seems now to work as expected
318                                this.samples.findAll { sample ->
319                                        (
320                                                (eventGroup.subjects.findAll {
321                                                        it.equals(sample.parentSubject)
322                                                })
323                                                &&
324                                                (eventGroup.samplingEvents.findAll {
325                                                        (
326                                                                (it.id && sample.parentEvent.id && it.id==sample.parentEvent.id)
327                                                                ||
328                                                                (it.getIdentifier() == sample.parentEvent.getIdentifier())
329                                                                ||
330                                                                it.equals(sample.parentEvent)
331                                                        )
332                                                })
333                                        )
334                                }.each() {
335                                        // remove sample from study
336
337                                        // -------
338                                        // NOTE, the right samples are found, but the don't
339                                        // get deleted from the database!
340                                        // -------
341
342                                        println ".removing sample '${it.name}' from study '${this.title}'"
343                                        msg += ", sample '${it.name}' was deleted"
344                                        this.removeFromSamples( it )
345
346                                        // Also here, contrary to documentation, an extra delete() is needed
347                                        // otherwise date is not properly deleted!
348                                        it.delete()
349                                }
350                        }
351
352                        // remove all samplingEvents from this eventGroup
353                        eventGroup.samplingEvents.findAll{}.each() {
354                                eventGroup.removeFromSamplingEvents(it)
355                                println ".removed samplingEvent '${it.name}' from eventGroup '${eventGroup.name}'"
356                                msg += ", samplingEvent '${it.name}' was removed from eventGroup '${eventGroup.name}'"
357                        }
358                }
359
360                // If the event group contains subjects
361                if (eventGroup.subjects) {
362                        // remove all subject from this eventGroup
363                        eventGroup.subjects.findAll{}.each() {
364                                eventGroup.removeFromSubjects(it)
365                                println ".removed subject '${it.name}' from eventGroup '${eventGroup.name}'"
366                                msg += ", subject '${it.name}' was removed from eventGroup '${eventGroup.name}'"
367                        }
368                }
369
370                // remove the eventGroup from the study
371                println ".remove eventGroup '${eventGroup.name}' from study '${this.title}'"
372                this.removeFromEventGroups(eventGroup)
373
374                // Also here, contrary to documentation, an extra delete() is needed
375                // otherwise cascaded deletes are not properly performed
376                eventGroup.delete()
377
378                return msg
379        }
380}
Note: See TracBrowser for help on using the repository browser.