1 | package nl.tno.metagenomics.integration |
---|
2 | |
---|
3 | import nl.tno.metagenomics.* |
---|
4 | |
---|
5 | class TrashService { |
---|
6 | |
---|
7 | static transactional = true |
---|
8 | |
---|
9 | /** |
---|
10 | * Moves the valuable data from a study to trash and deletes the study |
---|
11 | * @param Study to move to trash |
---|
12 | */ |
---|
13 | def moveToTrash( Study study ) { |
---|
14 | log.trace "Moving study " + study + " to trash"; |
---|
15 | if( study.trashcan ) |
---|
16 | return |
---|
17 | |
---|
18 | saveDataInTrash( study ); |
---|
19 | |
---|
20 | def l = [] |
---|
21 | if( study.auth ) { |
---|
22 | l += study.auth |
---|
23 | |
---|
24 | l.each { auth -> |
---|
25 | auth.user?.removeFromAuth( auth ); |
---|
26 | study.removeFromAuth( auth ); |
---|
27 | } |
---|
28 | } |
---|
29 | |
---|
30 | study.delete(flush:true); |
---|
31 | } |
---|
32 | |
---|
33 | /** |
---|
34 | * Moves the valuable data from an assay to trash and deletes the assay |
---|
35 | * @param Assay to move to trash |
---|
36 | */ |
---|
37 | def moveToTrash( Assay assay ) { |
---|
38 | saveDataInTrash( assay ); |
---|
39 | |
---|
40 | // Remove associations |
---|
41 | def l = [] |
---|
42 | if( assay.runs ) { |
---|
43 | l += assay.runs |
---|
44 | |
---|
45 | l.each { |
---|
46 | if( it ) { |
---|
47 | assay.removeFromRuns( it ); |
---|
48 | it.removeFromAssays( assay ); |
---|
49 | } |
---|
50 | } |
---|
51 | } |
---|
52 | |
---|
53 | l = [] |
---|
54 | if( assay.assaySamples ) { |
---|
55 | l += assay.assaySamples |
---|
56 | |
---|
57 | l.each { |
---|
58 | it.sample.removeFromAssaySamples( it ); |
---|
59 | assay.removeFromAssaySamples( it ); |
---|
60 | } |
---|
61 | } |
---|
62 | |
---|
63 | def study = assay.study |
---|
64 | if( study ) { |
---|
65 | study.removeFromAssays( assay ); |
---|
66 | } |
---|
67 | } |
---|
68 | |
---|
69 | /** |
---|
70 | * Moves the valuable data from a sample to trash and deletes the sample |
---|
71 | * @param Sample to move to trash |
---|
72 | */ |
---|
73 | def moveToTrash( Sample sample ) { |
---|
74 | saveDataInTrash( sample ); |
---|
75 | |
---|
76 | // Remove associations |
---|
77 | def l = [] |
---|
78 | if( sample.assaySamples ) { |
---|
79 | l += sample.assaySamples |
---|
80 | |
---|
81 | l.each { |
---|
82 | it.assay.removeFromAssaySamples( it ); |
---|
83 | sample.removeFromAssaySamples( it ); |
---|
84 | } |
---|
85 | } |
---|
86 | |
---|
87 | def study = sample.study |
---|
88 | sample.study.removeFromSamples( sample ); |
---|
89 | study.save(); |
---|
90 | } |
---|
91 | |
---|
92 | /** |
---|
93 | * Moves the valuable data from an assaySample to trash and deletes the assay |
---|
94 | * @param Assay to move to trash |
---|
95 | */ |
---|
96 | def moveToTrash( AssaySample assaySample ) { |
---|
97 | saveDataInTrash( assaySample ); |
---|
98 | |
---|
99 | // Remove associations |
---|
100 | if( assaySample.run ) { |
---|
101 | assaySample.run.removeFromAssaySamples( assaySample ); |
---|
102 | } |
---|
103 | |
---|
104 | if( assaySample.assay ) { |
---|
105 | assaySample.assay.removeFromAssaySamples( assaySample ); |
---|
106 | } |
---|
107 | |
---|
108 | if( assaySample.sample ) { |
---|
109 | assaySample.sample.removeFromAssaySamples( assaySample ); |
---|
110 | } |
---|
111 | |
---|
112 | def l = [] |
---|
113 | if( assaySample.sequenceData ) { |
---|
114 | l += assaySample.sequenceData |
---|
115 | |
---|
116 | l.each { |
---|
117 | if( it ) { |
---|
118 | assaySample.removeFromSequenceData( it ); |
---|
119 | } |
---|
120 | } |
---|
121 | } |
---|
122 | |
---|
123 | } |
---|
124 | |
---|
125 | /** |
---|
126 | * Saves data from the study in the trash can (if any data exists) |
---|
127 | * @param study Study to save data from |
---|
128 | * @return |
---|
129 | */ |
---|
130 | def saveDataInTrash( Study study ) { |
---|
131 | Study trashcan = this.giveTrashcan() |
---|
132 | |
---|
133 | if( !trashcan ) { |
---|
134 | log.warn "No trashcan (study with trashcan property set to true) found in the database when deleting study " + study.name + ". Possibly valuable data is deleted forever." |
---|
135 | return; |
---|
136 | } |
---|
137 | |
---|
138 | // Loop through all assays, and see if there are assay samples |
---|
139 | // that have data |
---|
140 | study.assays.each { assay -> |
---|
141 | saveDataInTrash( assay ); |
---|
142 | } |
---|
143 | } |
---|
144 | |
---|
145 | /** |
---|
146 | * Saves data from the assay in the trash can (if any data exists) |
---|
147 | * @param study Assay to save data from |
---|
148 | * @return |
---|
149 | */ |
---|
150 | def saveDataInTrash( Assay assay ) { |
---|
151 | Study trashcan = this.giveTrashcan() |
---|
152 | |
---|
153 | if( !trashcan ) { |
---|
154 | log.warn "No trashcan (study with trashcan property set to true) found in the database when deleting assay " + assay.name + ". Possibly valuable data is deleted forever." |
---|
155 | return; |
---|
156 | } |
---|
157 | |
---|
158 | def assaySamples = assay.assaySamples.findAll { it.containsData() } |
---|
159 | |
---|
160 | // For every assay sample that contains data, save that data in the trashcan |
---|
161 | if( assaySamples.size() > 0 ) { |
---|
162 | // Create a dummy assay copy of the existing assay |
---|
163 | String newAssayToken = 'TrashAssay ' + new Date().format( 'yyyyMMddHHmmssSSS') + ( Math.random() * 10000 ); |
---|
164 | |
---|
165 | Assay dummyAssay = new Assay( assayToken: newAssayToken, name: assay.name, study: trashcan ); |
---|
166 | trashcan.addToAssays( dummyAssay ); |
---|
167 | dummyAssay.save() |
---|
168 | |
---|
169 | assaySamples.each { assaySample -> |
---|
170 | Sample sample = assaySample.sample |
---|
171 | |
---|
172 | // Create dummy sample |
---|
173 | String newSampleToken = 'TrashSample ' + new Date().format( 'yyyyMMddHHmmssSSS') + ( Math.random() * 10000 ); |
---|
174 | Sample dummySample = Sample.cloneSample( sample, newSampleToken, trashcan ); |
---|
175 | trashcan.addToSamples( dummySample ); |
---|
176 | dummySample.save() |
---|
177 | |
---|
178 | // Create dummy assay sample |
---|
179 | AssaySample dummyAssaySample = new AssaySample( assay: dummyAssay, sample: dummySample ); |
---|
180 | |
---|
181 | dummyAssay.addToAssaySamples( dummyAssaySample ); |
---|
182 | dummySample.addToAssaySamples( dummyAssaySample ); |
---|
183 | dummyAssaySample.save(); |
---|
184 | |
---|
185 | // Move data from this assay sample to the trash version of it |
---|
186 | assaySample.moveValuableDataTo( dummyAssaySample ); |
---|
187 | |
---|
188 | // Remove the assaySample from its run, since otherwise the statistics of the run (#sequences for example) |
---|
189 | // are not correct anymore and the assaySample will be resaved after delete |
---|
190 | dummyAssaySample.run?.removeFromAssaySamples( dummyAssaySample ); |
---|
191 | |
---|
192 | dummyAssaySample.save(); |
---|
193 | } |
---|
194 | } |
---|
195 | |
---|
196 | // Remove all assaysamples from this assay from their run |
---|
197 | assay.assaySamples?.each { assaySample -> |
---|
198 | assaySample.run?.removeFromAssaySamples( assaySample ); |
---|
199 | } |
---|
200 | |
---|
201 | // Remove this assay from the runs, since otherwise the samples will be resaved upon delete |
---|
202 | if( assay.runs ) { |
---|
203 | def l = [] + assay.runs |
---|
204 | l.each { |
---|
205 | it.removeFromAssays( assay ); |
---|
206 | } |
---|
207 | } |
---|
208 | } |
---|
209 | |
---|
210 | /** |
---|
211 | * Saves data from the sample in the trash can (if any data exists) |
---|
212 | * @param study Sample to save data from |
---|
213 | * @return |
---|
214 | */ |
---|
215 | def saveDataInTrash( Sample sample ) { |
---|
216 | Study trashcan = this.giveTrashcan() |
---|
217 | |
---|
218 | if( !trashcan ) { |
---|
219 | log.warn "No trashcan (study with trashcan property set to true) found in the database when deleting sample " + sample.name + ". Possibly valuable data is deleted forever." |
---|
220 | return; |
---|
221 | } |
---|
222 | |
---|
223 | def assaySamples = sample.assaySamples.findAll { it.containsData() } |
---|
224 | |
---|
225 | // For every assay sample that contains data, save that data in the trashcan |
---|
226 | if( assaySamples.size() > 0 ) { |
---|
227 | // Create dummy sample |
---|
228 | String newSampleToken = 'TrashSample ' + new Date().format( 'yyyyMMddHHmmssSSS') + ( Math.random() * 10000 ); |
---|
229 | Sample dummySample = Sample.cloneSample( sample, newSampleToken, trashcan ); |
---|
230 | trashcan.addToSamples( dummySample ); |
---|
231 | dummySample.save() |
---|
232 | |
---|
233 | assaySamples.each { assaySample -> |
---|
234 | Assay assay = assaySample.assay; |
---|
235 | |
---|
236 | // Create a dummy assay copy of the existing assay |
---|
237 | String newAssayToken = 'TrashAssay ' + new Date().format( 'yyyyMMddHHmmssSSS') + ( Math.random() * 10000 ); |
---|
238 | |
---|
239 | Assay dummyAssay = new Assay( assayToken: newAssayToken, name: assay.name, study: trashcan ); |
---|
240 | trashcan.addToAssays( dummyAssay ); |
---|
241 | dummyAssay.save() |
---|
242 | |
---|
243 | // Create dummy assay sample |
---|
244 | AssaySample dummyAssaySample = new AssaySample( assay: dummyAssay, sample: dummySample ); |
---|
245 | |
---|
246 | dummyAssay.addToAssaySamples( dummyAssaySample ); |
---|
247 | dummySample.addToAssaySamples( dummyAssaySample ); |
---|
248 | dummyAssaySample.save(); |
---|
249 | |
---|
250 | // Move data from this assay sample to the trash version of it |
---|
251 | assaySample.moveValuableDataTo( dummyAssaySample ); |
---|
252 | |
---|
253 | // Remove the assaySample from its run, since otherwise the statistics of the run (#sequences for example) |
---|
254 | // are not correct anymore |
---|
255 | dummyAssaySample.run?.removeFromAssaySamples( dummyAssaySample ); |
---|
256 | |
---|
257 | dummyAssaySample.save(); |
---|
258 | } |
---|
259 | } |
---|
260 | |
---|
261 | // Remove all assaysamples from this assay from their run |
---|
262 | sample.assaySamples?.each { assaySample -> |
---|
263 | assaySample.run?.removeFromAssaySamples( assaySample ); |
---|
264 | } |
---|
265 | } |
---|
266 | |
---|
267 | |
---|
268 | /** |
---|
269 | * Saves data from the assay-sample in the trash can (if any data exists) |
---|
270 | * @param study Sample to save data from |
---|
271 | * @return |
---|
272 | */ |
---|
273 | def saveDataInTrash( AssaySample assaySample ) { |
---|
274 | Study trashcan = this.giveTrashcan() |
---|
275 | |
---|
276 | if( !trashcan ) { |
---|
277 | log.warn "No trashcan (study with trashcan property set to true) found in the database when deleting sample " + sample.name + ". Possibly valuable data is deleted forever." |
---|
278 | return; |
---|
279 | } |
---|
280 | |
---|
281 | // For every assay sample that contains data, save that data in the trashcan |
---|
282 | if( assaySample.containsData() ) { |
---|
283 | // Create dummy sample |
---|
284 | String newSampleToken = 'TrashSample ' + new Date().format( 'yyyyMMddHHmmssSSS') + ( Math.random() * 10000 ); |
---|
285 | Sample dummySample = Sample.cloneSample( assaySample.sample, newSampleToken, trashcan ); |
---|
286 | trashcan.addToSamples( dummySample ); |
---|
287 | dummySample.save() |
---|
288 | |
---|
289 | Assay assay = assaySample.assay; |
---|
290 | |
---|
291 | // Create a dummy assay copy of the existing assay |
---|
292 | String newAssayToken = 'TrashAssay ' + new Date().format( 'yyyyMMddHHmmssSSS') + ( Math.random() * 10000 ); |
---|
293 | |
---|
294 | Assay dummyAssay = new Assay( assayToken: newAssayToken, name: assay.name, study: trashcan ); |
---|
295 | trashcan.addToAssays( dummyAssay ); |
---|
296 | dummyAssay.save() |
---|
297 | |
---|
298 | // Create dummy assay sample |
---|
299 | AssaySample dummyAssaySample = new AssaySample( assay: dummyAssay, sample: dummySample ); |
---|
300 | |
---|
301 | dummyAssay.addToAssaySamples( dummyAssaySample ); |
---|
302 | dummySample.addToAssaySamples( dummyAssaySample ); |
---|
303 | dummyAssaySample.save(); |
---|
304 | |
---|
305 | // Move data from this assay sample to the trash version of it |
---|
306 | assaySample.moveValuableDataTo( dummyAssaySample ); |
---|
307 | dummyAssaySample.save(); |
---|
308 | } |
---|
309 | } |
---|
310 | |
---|
311 | /** |
---|
312 | * Retrieves the trashcan study from the database |
---|
313 | */ |
---|
314 | def giveTrashcan = { |
---|
315 | def study = Study.findByTrashcan( true ); |
---|
316 | |
---|
317 | if( !study ) |
---|
318 | return null; |
---|
319 | else |
---|
320 | return study |
---|
321 | } |
---|
322 | |
---|
323 | /** |
---|
324 | * Creates a new trashcan study. Should only be used by the bootstrap to create a trashcan |
---|
325 | */ |
---|
326 | def createTrashcan = { |
---|
327 | def study = new Study( name: "Trashcan", studyToken: "trash", trashcan: true ) |
---|
328 | study.save(); |
---|
329 | } |
---|
330 | |
---|
331 | /** |
---|
332 | * Cleans up the trash by removing empty assays or samples. Empty means: |
---|
333 | * - an assay with samples |
---|
334 | * - a sample without sequences, tag sequence and oligo number |
---|
335 | * |
---|
336 | * Also removes samples in trash that are not referenced by an assaysample |
---|
337 | */ |
---|
338 | def cleanTrash = { |
---|
339 | def studies = Study.findAllByTrashcan( true ); |
---|
340 | |
---|
341 | studies.each { study -> |
---|
342 | def numAssays = study.assays?.size() |
---|
343 | def assayList = study.assays?.toList(); |
---|
344 | |
---|
345 | def numSamples |
---|
346 | def sampleList |
---|
347 | |
---|
348 | // Loop backwards through the assays in order to facilitate removing assays |
---|
349 | for( def i = numAssays -1; i >= 0; i-- ) { |
---|
350 | // Loop through all samples and remove the ones that are empty |
---|
351 | if( assayList[ i ].assaySamples != null ) { |
---|
352 | numSamples = assayList[ i ].assaySamples.size() |
---|
353 | sampleList = assayList[ i ].assaySamples.toList(); |
---|
354 | |
---|
355 | for( def j = numSamples - 1; j >= 0; j-- ) { |
---|
356 | def s = sampleList[ j ]; |
---|
357 | if( !s.containsData() ) { |
---|
358 | assayList[ i ].removeFromAssaySamples( s ); |
---|
359 | s.sample?.delete(); |
---|
360 | s.delete(); |
---|
361 | } |
---|
362 | } |
---|
363 | } |
---|
364 | |
---|
365 | // Now check if there are samples left. Otherwise, remove the assay |
---|
366 | if( assayList[i].assaySamples == null || assayList[i].assaySamples.size() == 0 ) { |
---|
367 | study.removeFromAssays( assayList[ i ] ); |
---|
368 | assayList[i].delete(); |
---|
369 | } |
---|
370 | } |
---|
371 | |
---|
372 | // Loop through samples and delete the ones not referenced by an assaysample |
---|
373 | /* |
---|
374 | numSamples = study.samples?.size() |
---|
375 | sampleList = study.samples?.toList(); |
---|
376 | for( def j = numSamples - 1; j >= 0; j-- ) { |
---|
377 | def s = sampleList[ j ]; |
---|
378 | if( s.assaySamples == null || s.assaySamples.size() == 0 ) { |
---|
379 | study.removeFromSamples(s); |
---|
380 | s.delete(flush:true); |
---|
381 | } |
---|
382 | } |
---|
383 | */ |
---|
384 | } |
---|
385 | } |
---|
386 | |
---|
387 | /** |
---|
388 | * Restore an assay from trash and put the contents of the assay in another assay |
---|
389 | * @param assay |
---|
390 | * @param restoreTo |
---|
391 | */ |
---|
392 | public void restoreAssay( Assay assay, Assay restoreTo ) { |
---|
393 | // Loop through all assaysamples and restore the ones that have a matching name |
---|
394 | assay.assaySamples.each { assaySample -> |
---|
395 | if( assaySample ) { |
---|
396 | // Find a sample with the same name |
---|
397 | def restoreSample = restoreTo.assaySamples.find { it.sample?.name == assaySample.sample?.name } |
---|
398 | |
---|
399 | if( restoreSample ) { |
---|
400 | this.restoreSample( assaySample, restoreSample ); |
---|
401 | } |
---|
402 | } |
---|
403 | } |
---|
404 | } |
---|
405 | |
---|
406 | /** |
---|
407 | * Restore a sample from trash and put the contents of the sample into another sample |
---|
408 | * @param sample |
---|
409 | * @param restoreTo |
---|
410 | */ |
---|
411 | public void restoreSample( AssaySample sample, AssaySample restoreTo ) { |
---|
412 | sample.moveValuableDataTo( restoreTo ); |
---|
413 | restoreTo.save(); |
---|
414 | } |
---|
415 | } |
---|