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

Last change on this file since 934 was 934, checked in by j.saito@…, 11 years ago

Adjusted CommunicationManager? to match closer with CommunicationManager? in SAM.
Added support for "token" notation of REST services directly in Study and Assay domain classes.
Cleaned up and tested communication further.

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