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

Last change on this file since 862 was 862, checked in by duh, 10 years ago
  • adding assay grouping page
  • Property svn:keywords set to Author Rev Date
File size: 10.1 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: 862 $
10 * $Author: duh $
11 * $Date: 2010-08-30 14:16:31 +0000 (ma, 30 aug 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                new TemplateField(
70                        name: 'code',
71                        type: TemplateFieldType.STRING,
72                        preferredIdentifier:true,
73                        comment: 'Fill out the code by which many people will recognize your study'),
74                new TemplateField(
75                        name: 'startDate',
76                        type: TemplateFieldType.DATE,
77                        comment: 'Fill out the official start date or date of first action')
78        ]
79
80        /**
81         * return the title of this study
82         */
83        def String toString() {
84                return title
85        }
86
87        /**
88         * returns all events and sampling events that do not belong to a group
89         */
90        def Set<Event> getOrphanEvents() {
91                def orphans =   events.findAll { event -> !event.belongsToGroup(eventGroups) } +
92                                                samplingEvents.findAll { event -> !event.belongsToGroup(eventGroups) }
93
94                return orphans
95        }
96
97        /**
98         * Return the unique Subject templates that are used in this study
99         */
100        def Set<Template> giveSubjectTemplates() {
101                TemplateEntity.giveTemplates(subjects)
102        }
103
104        /**
105         * Return all subjects for a specific template
106         * @param Template
107         * @return ArrayList
108         */
109        def ArrayList<Subject> giveSubjectsForTemplate(Template template) {
110                subjects.findAll { it.template.equals(template) }
111        }
112
113        /**
114         * Return all unique assay templates
115         * @return Set
116         */
117        Set<Template> giveAllAssayTemplates() {
118                TemplateEntity.giveTemplates(( (assays) ? assays : [] ))
119        }
120
121        /**
122         * Return all assays for a particular template
123         * @return ArrayList
124         */
125        def ArrayList giveAssaysForTemplate(Template template) {
126                assays.findAll { it.template.equals(template) }
127        }
128
129        /**
130         * Return the unique Event and SamplingEvent templates that are used in this study
131         */
132        Set<Template> giveAllEventTemplates() {
133                // For some reason, giveAllEventTemplates() + giveAllSamplingEventTemplates()
134                // gives trouble when asking .size() to the result
135                // So we also use giveTemplates here
136                TemplateEntity.giveTemplates( ((events) ? events : []) + ((samplingEvents) ? samplingEvents : []) )
137        }
138
139
140        /**
141         * Return all events and samplingEvenets for a specific template
142         * @param Template
143         * @return ArrayList
144         */
145        def ArrayList giveEventsForTemplate(Template template) {
146                def events = events.findAll { it.template.equals(template) }
147                def samplingEvents = samplingEvents.findAll { it.template.equals(template) }
148
149                return (events) ? events : samplingEvents
150        }
151
152        /**
153         * Return the unique Event templates that are used in this study
154         */
155        Set<Template> giveEventTemplates() {
156                TemplateEntity.giveTemplates(events)
157        }
158
159        /**
160         * Return the unique SamplingEvent templates that are used in this study
161         */
162        Set<Template> giveSamplingEventTemplates() {
163                TemplateEntity.giveTemplates(samplingEvents)
164        }
165
166        /**
167         * Returns the unique Sample templates that are used in the study
168         */
169        Set<Template> giveSampleTemplates() {
170                TemplateEntity.giveTemplates(samples)
171        }
172
173        /**
174         * Return all samples for a specific template
175         * @param Template
176         * @return ArrayList
177         */
178        def ArrayList<Subject> giveSamplesForTemplate(Template template) {
179                samples.findAll { it.template.equals(template) }
180        }
181
182        /**
183         * Returns the template of the study
184         */
185        Template giveStudyTemplate() {
186                return this.template
187        }
188
189
190        /**
191         * Delete a specific subject from this study, including all its relations
192         * @param subject The subject to be deleted
193         * @return A String which contains a (user-readable) message describing the changes to the database
194         */
195        String deleteSubject(Subject subject) {
196                String msg = "Subject ${subject.name} was deleted"
197
198                // Delete the subject from the event groups it was referenced in
199                this.eventGroups.each {
200                        if (it.subjects.contains(subject)) {
201                                it.removeFromSubjects(subject)
202                                msg += ", deleted from event group '${it.name}'"
203                        }
204                }
205
206                // Delete the samples that have this subject as parent
207                this.samples.findAll { it.parentSubject.equals(subject) }.each {
208                        // This should remove the sample itself too, because of the cascading belongsTo relation
209                        this.removeFromSamples(it)
210                        // But apparently it needs an explicit delete() too
211                        it.delete()
212                        msg += ", sample '${it.name}' was deleted"
213                }
214
215                // This should remove the subject itself too, because of the cascading belongsTo relation
216                this.removeFromSubjects(subject)
217                // But apparently it needs an explicit delete() too
218                subject.delete()
219
220                return msg
221        }
222
223        /**
224         * Delete an assay from the study
225         * @param Assay
226         * @void
227         */
228        def deleteAssay(Assay assay) {
229                // remove this assay from the study
230                this.removeFromAssays( assay )
231        }
232
233        /**
234         * Delete an event from the study, including all its relations
235         * @param Event
236         * @return String
237         */
238        String deleteEvent(Event event) {
239                String msg = "Event ${event} was deleted"
240
241                // remove event from the study
242                this.removeFromEvents(event)
243
244                // remove event from eventGroups
245                this.eventGroups.each() { eventGroup ->
246                        eventGroup.removeFromEvents(event)
247                }
248
249                return msg
250        }
251
252        /**
253         * Delete a samplingEvent from the study, including all its relations
254         * @param SamplingEvent
255         * @return String
256         */
257        String deleteSamplingEvent(SamplingEvent samplingEvent) {
258                String msg = "SamplingEvent ${samplingEvent} was deleted"
259
260                // remove event from eventGroups
261                this.eventGroups.each() { eventGroup ->
262                        eventGroup.removeFromSamplingEvents(samplingEvent)
263                }
264
265                // Delete the samples that have this sampling event as parent
266                this.samples.findAll { it.parentEvent.equals(samplingEvent) }.each {
267                        // This should remove the sample itself too, because of the cascading belongsTo relation
268                        this.removeFromSamples(it)
269                        // But apparently it needs an explicit delete() too
270                        it.delete()
271                        msg += ", sample '${it.name}' was deleted"
272                }
273
274                // Remove event from the study
275                // This should remove the event group itself too, because of the cascading belongsTo relation
276                this.removeFromSamplingEvents(samplingEvent)
277
278                // But apparently it needs an explicit delete() too
279                // (Which can be verified by outcommenting this line, then SampleTests.testDeleteViaParentSamplingEvent fails
280                samplingEvent.delete()
281
282                return msg
283        }
284       
285        /**
286         * Delete an eventGroup from the study, including all its relations
287         * @param EventGroup
288         * @return String
289         */
290        String deleteEventGroup(EventGroup eventGroup) {
291                String msg = "EventGroup ${eventGroup} was deleted"
292
293                // If the event group contains sampling events
294                if (eventGroup.samplingEvents) {
295                        // remove all samples that originate from this eventGroup
296                        if (eventGroup.samplingEvents.size()) {
297                                // find all samples related to this eventGroup
298                                // - subject comparison is relatively straightforward and
299                                //   behaves as expected
300                                // - event comparison behaves strange, so now we compare
301                                //              1. database id's or,
302                                //              2. object identifiers or,
303                                //              3. objects itself
304                                //   this seems now to work as expected
305                                this.samples.findAll { sample ->
306                                        (
307                                                (eventGroup.subjects.findAll {
308                                                        it.equals(sample.parentSubject)
309                                                })
310                                                &&
311                                                (eventGroup.samplingEvents.findAll {
312                                                        (
313                                                                (it.id && sample.parentEvent.id && it.id==sample.parentEvent.id)
314                                                                ||
315                                                                (it.getIdentifier() == sample.parentEvent.getIdentifier())
316                                                                ||
317                                                                it.equals(sample.parentEvent)
318                                                        )
319                                                })
320                                        )
321                                }.each() {
322                                        // remove sample from study
323
324                                        // -------
325                                        // NOTE, the right samples are found, but the don't
326                                        // get deleted from the database!
327                                        // -------
328
329                                        println ".removing sample '${it.name}' from study '${this.title}'"
330                                        msg += ", sample '${it.name}' was deleted"
331                                        this.removeFromSamples( it )
332
333                                        // Also here, contrary to documentation, an extra delete() is needed
334                                        // otherwise date is not properly deleted!
335                                        it.delete()
336                                }
337                        }
338
339                        // remove all samplingEvents from this eventGroup
340                        eventGroup.samplingEvents.findAll{}.each() {
341                                eventGroup.removeFromSamplingEvents(it)
342                                println ".removed samplingEvent '${it.name}' from eventGroup '${eventGroup.name}'"
343                                msg += ", samplingEvent '${it.name}' was removed from eventGroup '${eventGroup.name}'"
344                        }
345                }
346
347                // If the event group contains subjects
348                if (eventGroup.subjects) {
349                        // remove all subject from this eventGroup
350                        eventGroup.subjects.findAll{}.each() {
351                                eventGroup.removeFromSubjects(it)
352                                println ".removed subject '${it.name}' from eventGroup '${eventGroup.name}'"
353                                msg += ", subject '${it.name}' was removed from eventGroup '${eventGroup.name}'"
354                        }
355                }
356
357                // remove the eventGroup from the study
358                println ".remove eventGroup '${eventGroup.name}' from study '${this.title}'"
359                this.removeFromEventGroups(eventGroup)
360
361                // Also here, contrary to documentation, an extra delete() is needed
362                // otherwise cascaded deletes are not properly performed
363                eventGroup.delete()
364
365                return msg
366        }
367}
Note: See TracBrowser for help on using the repository browser.