1
|
/**
|
2
|
* Copyright (C) 2019 EDIT
|
3
|
* European Distributed Institute of Taxonomy
|
4
|
* http://www.e-taxonomy.eu
|
5
|
*
|
6
|
* The contents of this file are subject to the Mozilla Public License Version 1.1
|
7
|
* See LICENSE.TXT at the top of this package for the full license terms.
|
8
|
*/
|
9
|
package eu.etaxonomy.cdm.api.service.description;
|
10
|
|
11
|
import java.io.FileNotFoundException;
|
12
|
import java.math.BigDecimal;
|
13
|
import java.util.List;
|
14
|
import java.util.Map;
|
15
|
import java.util.Set;
|
16
|
import java.util.UUID;
|
17
|
import java.util.function.Function;
|
18
|
import java.util.stream.Collectors;
|
19
|
|
20
|
import org.apache.log4j.Logger;
|
21
|
import org.junit.Assert;
|
22
|
import org.junit.Before;
|
23
|
import org.junit.Test;
|
24
|
import org.unitils.dbunit.annotation.DataSet;
|
25
|
import org.unitils.dbunit.annotation.DataSets;
|
26
|
import org.unitils.spring.annotation.SpringBeanByType;
|
27
|
|
28
|
import eu.etaxonomy.cdm.api.application.ICdmRepository;
|
29
|
import eu.etaxonomy.cdm.api.service.IClassificationService;
|
30
|
import eu.etaxonomy.cdm.api.service.IDescriptionService;
|
31
|
import eu.etaxonomy.cdm.api.service.IDescriptiveDataSetService;
|
32
|
import eu.etaxonomy.cdm.api.service.IOccurrenceService;
|
33
|
import eu.etaxonomy.cdm.api.service.IReferenceService;
|
34
|
import eu.etaxonomy.cdm.api.service.ITaxonNodeService;
|
35
|
import eu.etaxonomy.cdm.api.service.ITaxonService;
|
36
|
import eu.etaxonomy.cdm.api.service.ITermService;
|
37
|
import eu.etaxonomy.cdm.api.service.ITermTreeService;
|
38
|
import eu.etaxonomy.cdm.api.service.IVocabularyService;
|
39
|
import eu.etaxonomy.cdm.api.service.UpdateResult;
|
40
|
import eu.etaxonomy.cdm.common.monitor.DefaultProgressMonitor;
|
41
|
import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
|
42
|
import eu.etaxonomy.cdm.filter.TaxonNodeFilter;
|
43
|
import eu.etaxonomy.cdm.model.common.CdmBase;
|
44
|
import eu.etaxonomy.cdm.model.description.CategoricalData;
|
45
|
import eu.etaxonomy.cdm.model.description.DescriptionBase;
|
46
|
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
|
47
|
import eu.etaxonomy.cdm.model.description.DescriptionType;
|
48
|
import eu.etaxonomy.cdm.model.description.DescriptiveDataSet;
|
49
|
import eu.etaxonomy.cdm.model.description.Feature;
|
50
|
import eu.etaxonomy.cdm.model.description.IDescribable;
|
51
|
import eu.etaxonomy.cdm.model.description.IndividualsAssociation;
|
52
|
import eu.etaxonomy.cdm.model.description.MeasurementUnit;
|
53
|
import eu.etaxonomy.cdm.model.description.QuantitativeData;
|
54
|
import eu.etaxonomy.cdm.model.description.SpecimenDescription;
|
55
|
import eu.etaxonomy.cdm.model.description.State;
|
56
|
import eu.etaxonomy.cdm.model.description.StateData;
|
57
|
import eu.etaxonomy.cdm.model.description.StatisticalMeasure;
|
58
|
import eu.etaxonomy.cdm.model.description.StatisticalMeasurementValue;
|
59
|
import eu.etaxonomy.cdm.model.description.TaxonDescription;
|
60
|
import eu.etaxonomy.cdm.model.name.IBotanicalName;
|
61
|
import eu.etaxonomy.cdm.model.name.Rank;
|
62
|
import eu.etaxonomy.cdm.model.name.TaxonNameFactory;
|
63
|
import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
|
64
|
import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
|
65
|
import eu.etaxonomy.cdm.model.reference.Reference;
|
66
|
import eu.etaxonomy.cdm.model.reference.ReferenceFactory;
|
67
|
import eu.etaxonomy.cdm.model.taxon.Classification;
|
68
|
import eu.etaxonomy.cdm.model.taxon.Taxon;
|
69
|
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
|
70
|
import eu.etaxonomy.cdm.model.term.TermNode;
|
71
|
import eu.etaxonomy.cdm.model.term.TermTree;
|
72
|
import eu.etaxonomy.cdm.model.term.TermType;
|
73
|
import eu.etaxonomy.cdm.model.term.TermVocabulary;
|
74
|
import eu.etaxonomy.cdm.test.integration.CdmTransactionalIntegrationTest;
|
75
|
import eu.etaxonomy.cdm.test.unitils.CleanSweepInsertLoadStrategy;
|
76
|
|
77
|
/**
|
78
|
* @author a.mueller
|
79
|
* @since 21.11.2019
|
80
|
*/
|
81
|
public class StructuredDescriptionAggregationTest extends CdmTransactionalIntegrationTest {
|
82
|
|
83
|
@SuppressWarnings("unused")
|
84
|
private static Logger logger = Logger.getLogger(StructuredDescriptionAggregationTest.class);
|
85
|
|
86
|
private static final UUID T_LAPSANA_UUID = UUID.fromString("f65d47bd-4f49-4ab1-bc4a-bc4551eaa1a8");
|
87
|
private static final UUID TN_LAPSANA_UUID = UUID.fromString("f4d29e9f-6484-4184-af2e-9704e96a17e3");
|
88
|
|
89
|
private static final UUID T_LAPSANA_COMMUNIS_UUID = UUID.fromString("2a5ceebb-4830-4524-b330-78461bf8cb6b");
|
90
|
|
91
|
private static final UUID T_LAPSANA_COMMUNIS_COMMUNIS_UUID = UUID.fromString("441a3c40-0c84-11de-8c30-0800200c9a66");
|
92
|
|
93
|
private static final UUID T_LAPSANA_COMMUNIS_ADENOPHORA_UUID = UUID.fromString("e4acf200-63b6-11dd-ad8b-0800200c9a66");
|
94
|
|
95
|
private static final UUID T_LAPSANA_COMMUNIS_ALPINA_UUID = UUID.fromString("596b1325-be50-4b0a-9aa2-3ecd610215f2");
|
96
|
|
97
|
private static final UUID CLASSIFICATION_UUID = UUID.fromString("4b266053-a841-4980-b548-3f21d8d7d712");
|
98
|
|
99
|
private static final UUID T_LAPSANA_COMMUNIS_ALPINA_SPEC1_UUID = UUID.fromString("0de17403-aeef-4138-a220-9914e7c46f5a");
|
100
|
private static final UUID T_LAPSANA_COMMUNIS_ALPINA_SPEC2_UUID = UUID.fromString("9e680e26-9e12-47a9-807c-743525c9171e");
|
101
|
private static final UUID T_LAPSANA_COMMUNIS_ALPINA_SPEC3_UUID = UUID.fromString("3cdaa12f-7508-4073-b8d7-afca77a6be34");
|
102
|
private static final UUID T_LAPSANA_COMMUNIS_ADENOPHORA_SPEC1_UUID = UUID.fromString("83788037-7a03-4e46-98ca-e8801096b216");
|
103
|
|
104
|
|
105
|
private static final UUID uuidFeatureLeafPA = UUID.fromString("c4dfd16f-f2ed-45e0-8f4d-7fe1ae880510");
|
106
|
private static UUID uuidFeatureLeafLength = UUID.fromString("3c19b50b-4a8e-467e-b7d4-89ebc05a33e1");
|
107
|
private static UUID uuidFeatureLeafColor = UUID.fromString("1e8f503c-5aeb-4788-b4f9-84128f7141c7");
|
108
|
private static UUID uuidFeatureLeafAdd = UUID.fromString("774b52a5-c16c-43e5-94e7-3946b506554d");
|
109
|
|
110
|
private static UUID uuidLeafColorBlue = UUID.fromString("9b4df19d-f89d-4788-9d71-d1f6f7cae910");
|
111
|
private static UUID uuidLeafColorYellow = UUID.fromString("4cf0881b-0e7b-489a-9fdb-adbe6ae4e0ae");
|
112
|
|
113
|
private static UUID uuidFeatureTree = UUID.fromString("c8a29a94-2754-4d78-9faa-dff3e1387b2d");
|
114
|
|
115
|
|
116
|
@SpringBeanByType
|
117
|
private ICdmRepository repository;
|
118
|
|
119
|
@SpringBeanByType
|
120
|
private ITermService termService;
|
121
|
|
122
|
@SpringBeanByType
|
123
|
private ITermTreeService termTreeService;
|
124
|
|
125
|
@SpringBeanByType
|
126
|
private IVocabularyService vocabularyService;
|
127
|
|
128
|
@SpringBeanByType
|
129
|
private IDescriptionService descriptionService;
|
130
|
|
131
|
@SpringBeanByType
|
132
|
private ITaxonService taxonService;
|
133
|
|
134
|
@SpringBeanByType
|
135
|
private IOccurrenceService occurrenceService;
|
136
|
|
137
|
@SpringBeanByType
|
138
|
private ITaxonNodeService taxonNodeService;
|
139
|
|
140
|
@SpringBeanByType
|
141
|
private IClassificationService classificationService;
|
142
|
|
143
|
@SpringBeanByType
|
144
|
private IReferenceService referenceService;
|
145
|
|
146
|
@SpringBeanByType
|
147
|
private IDescriptiveDataSetService datasetService;
|
148
|
|
149
|
private StructuredDescriptionAggregation engine;
|
150
|
|
151
|
private IProgressMonitor monitor;
|
152
|
|
153
|
@Before
|
154
|
public void setUp() {
|
155
|
engine = new StructuredDescriptionAggregation();
|
156
|
engine.setBatchMinFreeHeap(35 * 1024 * 1024);
|
157
|
monitor = DefaultProgressMonitor.NewInstance();
|
158
|
}
|
159
|
|
160
|
@Test
|
161
|
@DataSets({
|
162
|
@DataSet(loadStrategy=CleanSweepInsertLoadStrategy.class, value="/eu/etaxonomy/cdm/database/ClearDB_with_Terms_DataSet.xml"),
|
163
|
@DataSet(value="/eu/etaxonomy/cdm/database/TermsDataSet-with_auditing_info.xml"),
|
164
|
@DataSet(value="StructuredDescriptionAggregationTest.xml"),
|
165
|
})
|
166
|
public void testReaggregation(){
|
167
|
createDefaultFeatureTree();
|
168
|
DescriptiveDataSet dataSet = createTestData();
|
169
|
commitAndStartNewTransaction();
|
170
|
|
171
|
StructuredDescriptionAggregationConfiguration config = createConfig(dataSet);
|
172
|
|
173
|
// 1st aggregation
|
174
|
UpdateResult result = engine.invoke(config, repository);
|
175
|
verifyStatusOk(result);
|
176
|
commitAndStartNewTransaction();
|
177
|
verifyAggregatedDescription(new TestConfig(config));
|
178
|
|
179
|
addSomeDataToFirstAggregation();
|
180
|
commitAndStartNewTransaction();
|
181
|
verifyAggregatedDescription(new TestConfig(config).setWithAddedData());
|
182
|
|
183
|
// 2nd aggregation => should be same as originally as data was only added to aggregation to see if it is correctly deleted
|
184
|
result = engine.invoke(config, repository);
|
185
|
verifyStatusOk(result);
|
186
|
commitAndStartNewTransaction();
|
187
|
verifyAggregatedDescription(new TestConfig(config));
|
188
|
|
189
|
// reaggregate with an element having a feature not yet in the existing descriptions
|
190
|
addNewFeature();
|
191
|
commitAndStartNewTransaction();
|
192
|
result = engine.invoke(config, repository);
|
193
|
verifyStatusOk(result);
|
194
|
commitAndStartNewTransaction();
|
195
|
verifyAggregatedDescription(new TestConfig(config).setWithFeature());
|
196
|
|
197
|
}
|
198
|
|
199
|
private void addSomeDataToFirstAggregation() {
|
200
|
Taxon taxLapsanaCommunisAlpina = (Taxon)taxonService.find(T_LAPSANA_COMMUNIS_ALPINA_UUID);
|
201
|
TaxonDescription taxonDescription = taxLapsanaCommunisAlpina.getDescriptions().stream()
|
202
|
.filter(desc->desc.getTypes().contains(DescriptionType.AGGREGATED_STRUC_DESC))
|
203
|
.filter(desc->!desc.getTypes().contains(DescriptionType.CLONE_FOR_SOURCE))
|
204
|
.findFirst().get();
|
205
|
|
206
|
addCategoricalData(taxonDescription, uuidFeatureLeafPA, State.uuidPresent);
|
207
|
}
|
208
|
|
209
|
private void addNewFeature() {
|
210
|
SpecimenOrObservationBase<?> spec1 = occurrenceService.find(T_LAPSANA_COMMUNIS_ADENOPHORA_SPEC1_UUID);
|
211
|
SpecimenDescription specimenDescription = (SpecimenDescription)spec1.getDescriptions().stream()
|
212
|
.filter(desc->!desc.getTypes().contains(DescriptionType.AGGREGATED_STRUC_DESC))
|
213
|
.filter(desc->!desc.getTypes().contains(DescriptionType.CLONE_FOR_SOURCE))
|
214
|
.findFirst().get();
|
215
|
|
216
|
addQuantitativeData(specimenDescription, uuidFeatureLeafAdd, new BigDecimal("2.5"), new BigDecimal("3.6"));
|
217
|
}
|
218
|
|
219
|
@Test
|
220
|
@DataSets({
|
221
|
@DataSet(loadStrategy=CleanSweepInsertLoadStrategy.class, value="/eu/etaxonomy/cdm/database/ClearDB_with_Terms_DataSet.xml"),
|
222
|
@DataSet(value="/eu/etaxonomy/cdm/database/TermsDataSet-with_auditing_info.xml"),
|
223
|
@DataSet(value="StructuredDescriptionAggregationTest.xml"),
|
224
|
})
|
225
|
public void testDeleteTest() {
|
226
|
createDefaultFeatureTree();
|
227
|
DescriptiveDataSet dataSet = createTestData();
|
228
|
commitAndStartNewTransaction();
|
229
|
|
230
|
StructuredDescriptionAggregationConfiguration config = createConfig(dataSet);
|
231
|
|
232
|
// 1st aggregation
|
233
|
UpdateResult result = engine.invoke(config, repository);
|
234
|
verifyStatusOk(result);
|
235
|
commitAndStartNewTransaction();
|
236
|
verifyAggregatedDescription(new TestConfig(config));
|
237
|
|
238
|
removeSomeDataFromFirstAggregation();
|
239
|
commitAndStartNewTransaction();
|
240
|
Assert.assertEquals("Should have 3 specimen desc, 1 literature desc, 2 individual association holder, "
|
241
|
+ "4 aggregated descriptions, 4 cloned specimen descriptions (still not deleted), (0(3) cloned aggregated descriptions?) = 14",
|
242
|
14, descriptionService.count(null));
|
243
|
|
244
|
// 2nd aggregation
|
245
|
result = engine.invoke(config, repository);
|
246
|
verifyStatusOk(result);
|
247
|
commitAndStartNewTransaction();
|
248
|
verifyAggregatedDescription(new TestConfig(config).setWithRemoved());
|
249
|
}
|
250
|
|
251
|
private void removeSomeDataFromFirstAggregation() {
|
252
|
SpecimenOrObservationBase<?> spec3 = occurrenceService.find(T_LAPSANA_COMMUNIS_ALPINA_SPEC3_UUID);
|
253
|
DescriptionBase<?> spec3Desc = spec3.getDescriptions().stream()
|
254
|
.filter(desc->!desc.getTypes().contains(DescriptionType.CLONE_FOR_SOURCE))
|
255
|
.findFirst().get();
|
256
|
|
257
|
spec3.removeDescription(spec3Desc);
|
258
|
descriptionService.delete(spec3Desc);
|
259
|
}
|
260
|
|
261
|
@Test
|
262
|
@DataSets({
|
263
|
@DataSet(loadStrategy=CleanSweepInsertLoadStrategy.class, value="/eu/etaxonomy/cdm/database/ClearDB_with_Terms_DataSet.xml"),
|
264
|
@DataSet(value="/eu/etaxonomy/cdm/database/TermsDataSet-with_auditing_info.xml"),
|
265
|
@DataSet(value="StructuredDescriptionAggregationTest.xml"),
|
266
|
})
|
267
|
public void testIncompleteQuantitativeData() {
|
268
|
createDefaultFeatureTree();
|
269
|
DescriptiveDataSet dataSet = DescriptiveDataSet.NewInstance();
|
270
|
datasetService.save(dataSet);
|
271
|
|
272
|
SpecimenDescription specDescAlpina1 = createSpecimenDescription(dataSet, T_LAPSANA_COMMUNIS_ALPINA_UUID, "alpina specimen1", T_LAPSANA_COMMUNIS_ALPINA_SPEC1_UUID);
|
273
|
addQuantitativeData(specDescAlpina1, uuidFeatureLeafLength, StatisticalMeasure.MIN(), new BigDecimal("5.0"));
|
274
|
|
275
|
SpecimenDescription specDescAlpina2 = createSpecimenDescription(dataSet, T_LAPSANA_COMMUNIS_ALPINA_UUID, "alpina specimen2", T_LAPSANA_COMMUNIS_ALPINA_SPEC2_UUID);
|
276
|
addQuantitativeData(specDescAlpina2, uuidFeatureLeafLength, StatisticalMeasure.MAX(), new BigDecimal("7.0"));
|
277
|
|
278
|
TaxonNode tnLapsana = taxonNodeService.find(TN_LAPSANA_UUID);
|
279
|
Assert.assertNotNull(tnLapsana);
|
280
|
dataSet.addTaxonSubtree(tnLapsana);
|
281
|
|
282
|
@SuppressWarnings("unchecked")
|
283
|
TermTree<Feature> descriptiveSystem = termTreeService.find(uuidFeatureTree);
|
284
|
dataSet.setDescriptiveSystem(descriptiveSystem);
|
285
|
commitAndStartNewTransaction();
|
286
|
|
287
|
StructuredDescriptionAggregationConfiguration config = createConfig(dataSet);
|
288
|
|
289
|
UpdateResult result = engine.invoke(config, repository);
|
290
|
verifyStatusOk(result);
|
291
|
|
292
|
Taxon taxLapsanaCommunisAlpina = (Taxon)taxonService.find(T_LAPSANA_COMMUNIS_ALPINA_UUID);
|
293
|
TaxonDescription aggrDescLapsanaCommunisAlpina = verifyTaxonDescriptions(taxLapsanaCommunisAlpina, 1);
|
294
|
verifyQuantitativeData(uuidFeatureLeafLength, null, new BigDecimal("0.0"), new BigDecimal("7.0"), null, aggrDescLapsanaCommunisAlpina);
|
295
|
}
|
296
|
|
297
|
@Test
|
298
|
@DataSets({
|
299
|
@DataSet(loadStrategy=CleanSweepInsertLoadStrategy.class, value="/eu/etaxonomy/cdm/database/ClearDB_with_Terms_DataSet.xml"),
|
300
|
@DataSet(value="/eu/etaxonomy/cdm/database/TermsDataSet-with_auditing_info.xml"),
|
301
|
@DataSet(value="StructuredDescriptionAggregationTest.xml"),
|
302
|
})
|
303
|
public void testIncompleteCategoricalData() {
|
304
|
|
305
|
//create data
|
306
|
createDefaultFeatureTree();
|
307
|
DescriptiveDataSet dataSet = DescriptiveDataSet.NewInstance();
|
308
|
datasetService.save(dataSet);
|
309
|
|
310
|
SpecimenDescription specDescAlpina1 = createSpecimenDescription(dataSet, T_LAPSANA_COMMUNIS_ALPINA_UUID, "alpina specimen1", T_LAPSANA_COMMUNIS_ALPINA_SPEC1_UUID);
|
311
|
//create empty categorical data
|
312
|
addCategoricalData(specDescAlpina1, uuidFeatureLeafColor, null);
|
313
|
|
314
|
TaxonNode tnLapsana = taxonNodeService.find(TN_LAPSANA_UUID);
|
315
|
Assert.assertNotNull(tnLapsana);
|
316
|
dataSet.addTaxonSubtree(tnLapsana);
|
317
|
|
318
|
@SuppressWarnings("unchecked")
|
319
|
TermTree<Feature> descriptiveSystem = termTreeService.find(uuidFeatureTree);
|
320
|
dataSet.setDescriptiveSystem(descriptiveSystem);
|
321
|
commitAndStartNewTransaction();
|
322
|
|
323
|
//aggregate
|
324
|
StructuredDescriptionAggregationConfiguration config = createConfig(dataSet);
|
325
|
UpdateResult result = engine.invoke(config, repository);
|
326
|
|
327
|
//test aggregation with categorical data without states (empty categorical data)
|
328
|
verifyStatusOk(result);
|
329
|
Taxon taxLapsanaCommunisAlpina = (Taxon)taxonService.find(T_LAPSANA_COMMUNIS_ALPINA_UUID);
|
330
|
//if no state at all exists in description even the description is not created (this was different before but changed with 9b8a8049439 / #9804)
|
331
|
verifyNumberTaxonDescriptions(taxLapsanaCommunisAlpina, 0);
|
332
|
|
333
|
//add data for another feature
|
334
|
specDescAlpina1 = (SpecimenDescription)descriptionService.find(specDescAlpina1.getUuid());
|
335
|
addCategoricalData(specDescAlpina1, uuidFeatureLeafPA, State.uuidPresent);
|
336
|
commitAndStartNewTransaction();
|
337
|
result = engine.invoke(config, repository);
|
338
|
verifyStatusOk(result);
|
339
|
taxLapsanaCommunisAlpina = (Taxon)taxonService.find(T_LAPSANA_COMMUNIS_ALPINA_UUID);
|
340
|
TaxonDescription aggrDescLapsanaCommunisAlpina = verifyTaxonDescriptions(taxLapsanaCommunisAlpina, 1);
|
341
|
verifyNumberDescriptionElements(aggrDescLapsanaCommunisAlpina, uuidFeatureLeafColor, 0); //we don't expect empty categorical data anymore #9804
|
342
|
|
343
|
//test if data for same feature but from >1 description items run into 1 description item when aggregated
|
344
|
specDescAlpina1 = (SpecimenDescription)descriptionService.find(specDescAlpina1.getUuid());
|
345
|
addCategoricalData(specDescAlpina1, uuidFeatureLeafColor, uuidLeafColorBlue);
|
346
|
addCategoricalData(specDescAlpina1, uuidFeatureLeafColor, uuidLeafColorBlue);
|
347
|
commitAndStartNewTransaction();
|
348
|
result = engine.invoke(config, repository);
|
349
|
|
350
|
verifyStatusOk(result);
|
351
|
taxLapsanaCommunisAlpina = (Taxon)taxonService.find(T_LAPSANA_COMMUNIS_ALPINA_UUID);
|
352
|
aggrDescLapsanaCommunisAlpina = verifyTaxonDescriptions(taxLapsanaCommunisAlpina, 2); //for leafPA and for color
|
353
|
List<StateData> sdAlpinaLeafColor = verifyCategoricalData(uuidFeatureLeafColor, 1, aggrDescLapsanaCommunisAlpina, false);
|
354
|
verifyState(sdAlpinaLeafColor, uuidLeafColorBlue, 2);
|
355
|
verifyState(sdAlpinaLeafColor, uuidLeafColorYellow, 0);
|
356
|
}
|
357
|
|
358
|
@Test
|
359
|
@DataSets({
|
360
|
@DataSet(loadStrategy=CleanSweepInsertLoadStrategy.class, value="/eu/etaxonomy/cdm/database/ClearDB_with_Terms_DataSet.xml"),
|
361
|
@DataSet(value="/eu/etaxonomy/cdm/database/TermsDataSet-with_auditing_info.xml"),
|
362
|
@DataSet(value="StructuredDescriptionAggregationTest.xml"),
|
363
|
})
|
364
|
public void testSourceModes() {
|
365
|
createDefaultFeatureTree();
|
366
|
DescriptiveDataSet dataSet = createTestData();
|
367
|
commitAndStartNewTransaction();
|
368
|
|
369
|
StructuredDescriptionAggregationConfiguration config = createConfig(dataSet);
|
370
|
config.setWithinTaxonSourceMode(AggregationSourceMode.NONE);
|
371
|
config.setToParentSourceMode(AggregationSourceMode.NONE);
|
372
|
|
373
|
// 1st aggregation
|
374
|
UpdateResult result = engine.invoke(config, repository);
|
375
|
verifyStatusOk(result);
|
376
|
commitAndStartNewTransaction();
|
377
|
verifyAggregatedDescription(new TestConfig(config));
|
378
|
|
379
|
config.setWithinTaxonSourceMode(AggregationSourceMode.DESCRIPTION);
|
380
|
config.setToParentSourceMode(AggregationSourceMode.NONE);
|
381
|
result = engine.invoke(config, repository);
|
382
|
verifyStatusOk(result);
|
383
|
commitAndStartNewTransaction();
|
384
|
verifyAggregatedDescription(new TestConfig(config));
|
385
|
|
386
|
config.setWithinTaxonSourceMode(AggregationSourceMode.NONE);
|
387
|
config.setToParentSourceMode(AggregationSourceMode.DESCRIPTION);
|
388
|
result = engine.invoke(config, repository);
|
389
|
verifyStatusOk(result);
|
390
|
commitAndStartNewTransaction();
|
391
|
verifyAggregatedDescription(new TestConfig(config));
|
392
|
|
393
|
config.setWithinTaxonSourceMode(AggregationSourceMode.DESCRIPTION);
|
394
|
config.setToParentSourceMode(AggregationSourceMode.TAXON);
|
395
|
result = engine.invoke(config, repository);
|
396
|
verifyStatusOk(result);
|
397
|
commitAndStartNewTransaction();
|
398
|
verifyAggregatedDescription(new TestConfig(config));
|
399
|
|
400
|
config.setWithinTaxonSourceMode(AggregationSourceMode.NONE);
|
401
|
config.setToParentSourceMode(AggregationSourceMode.TAXON);
|
402
|
result = engine.invoke(config, repository);
|
403
|
verifyStatusOk(result);
|
404
|
commitAndStartNewTransaction();
|
405
|
verifyAggregatedDescription(new TestConfig(config));
|
406
|
|
407
|
config.setWithinTaxonSourceMode(AggregationSourceMode.ALL);
|
408
|
config.setToParentSourceMode(AggregationSourceMode.DESCRIPTION);
|
409
|
result = engine.invoke(config, repository);
|
410
|
verifyStatusError(result, "Unsupported source mode for within-taxon aggregation: ALL");
|
411
|
commitAndStartNewTransaction();
|
412
|
|
413
|
config.setWithinTaxonSourceMode(AggregationSourceMode.DESCRIPTION);
|
414
|
config.setToParentSourceMode(AggregationSourceMode.ALL);
|
415
|
result = engine.invoke(config, repository);
|
416
|
verifyStatusError(result, "Unsupported source mode for to-parent aggregation: ALL");
|
417
|
commitAndStartNewTransaction();
|
418
|
|
419
|
config.setWithinTaxonSourceMode(AggregationSourceMode.ALL_SAMEVALUE);
|
420
|
config.setToParentSourceMode(AggregationSourceMode.DESCRIPTION);
|
421
|
result = engine.invoke(config, repository);
|
422
|
verifyStatusError(result, "Unsupported source mode for within-taxon aggregation: ALL_SAMEVALUE");
|
423
|
commitAndStartNewTransaction();
|
424
|
|
425
|
// removeSomeDataFromFirstAggregation();
|
426
|
// commitAndStartNewTransaction();
|
427
|
// Assert.assertEquals("Should have 3 specimen desc, 1 literature desc, 2 individual association holder, "
|
428
|
// + "4 aggregated descriptions, 4 cloned specimen descriptions (still not deleted), (3 cloned aggregated descriptions?) = 17",
|
429
|
// 17, descriptionService.count(null));
|
430
|
//
|
431
|
// // 2nd aggregation
|
432
|
// result = engine.invoke(config, repository);
|
433
|
// testStatusOk(result);
|
434
|
// commitAndStartNewTransaction();
|
435
|
// testAggregatedDescription(false, false, true);
|
436
|
}
|
437
|
|
438
|
@Test
|
439
|
@DataSets({
|
440
|
@DataSet(loadStrategy=CleanSweepInsertLoadStrategy.class, value="/eu/etaxonomy/cdm/database/ClearDB_with_Terms_DataSet.xml"),
|
441
|
@DataSet(value="/eu/etaxonomy/cdm/database/TermsDataSet-with_auditing_info.xml"),
|
442
|
@DataSet(value="StructuredDescriptionAggregationTest.xml"),
|
443
|
})
|
444
|
public void testAggregation() {
|
445
|
createDefaultFeatureTree();
|
446
|
DescriptiveDataSet dataSet = createTestData();
|
447
|
commitAndStartNewTransaction();
|
448
|
|
449
|
StructuredDescriptionAggregationConfiguration config = createConfig(dataSet);
|
450
|
|
451
|
UpdateResult result = engine.invoke(config, repository);
|
452
|
commitAndStartNewTransaction();
|
453
|
verifyStatusOk(result);
|
454
|
verifyAggregatedDescription(new TestConfig(config));
|
455
|
|
456
|
config.setIncludeLiterature(true);
|
457
|
|
458
|
result = engine.invoke(config, repository);
|
459
|
commitAndStartNewTransaction();
|
460
|
verifyStatusOk(result);
|
461
|
verifyAggregatedDescription(new TestConfig(config));
|
462
|
}
|
463
|
|
464
|
private void verifyStatusOk(UpdateResult result) {
|
465
|
if (result.getStatus() != UpdateResult.Status.OK){
|
466
|
Assert.fail("Aggregation should have status OK but was " + result.toString());
|
467
|
for (Exception ex : result.getExceptions()){
|
468
|
ex.printStackTrace();
|
469
|
}
|
470
|
}
|
471
|
}
|
472
|
|
473
|
private void verifyStatusError(UpdateResult result, String expectedMessage) {
|
474
|
if (result.getStatus() != UpdateResult.Status.ERROR){
|
475
|
Assert.fail("Aggregation should fail with status error " + result.toString());
|
476
|
}
|
477
|
if (result.getExceptions().isEmpty()){
|
478
|
Assert.fail("Failing aggregation include exception " + result.toString());
|
479
|
}
|
480
|
Exception e = result.getExceptions().iterator().next();
|
481
|
if (! (e instanceof AggregationException)){
|
482
|
Assert.fail("Exception should be of type AggregationException " + result.toString());
|
483
|
}else if (expectedMessage != null){
|
484
|
Assert.assertEquals(expectedMessage, e.getMessage());
|
485
|
}
|
486
|
}
|
487
|
|
488
|
private void addLiterature(DescriptiveDataSet dataSet) {
|
489
|
|
490
|
//literature description
|
491
|
Taxon taxon = (Taxon)taxonService.find(T_LAPSANA_COMMUNIS_ALPINA_UUID);
|
492
|
TaxonDescription literatureDescription = TaxonDescription.NewInstance(taxon);
|
493
|
literatureDescription.addType(DescriptionType.SECONDARY_DATA);
|
494
|
addQuantitativeData(literatureDescription, uuidFeatureLeafLength, new BigDecimal("4.5"), new BigDecimal("6.5"));
|
495
|
addCategoricalData(literatureDescription, uuidFeatureLeafColor, uuidLeafColorBlue);
|
496
|
dataSet.addDescription(literatureDescription);
|
497
|
}
|
498
|
|
499
|
private class TestConfig{
|
500
|
boolean withAddedData;
|
501
|
boolean withRemovedData;
|
502
|
boolean withFeature;
|
503
|
final boolean withLiterature;
|
504
|
final boolean cloneAggSourceDesc;
|
505
|
final AggregationSourceMode withinTaxonSourceMode;
|
506
|
final AggregationSourceMode toParentSourceMode;
|
507
|
|
508
|
|
509
|
private TestConfig(StructuredDescriptionAggregationConfiguration config) {
|
510
|
withinTaxonSourceMode = config.getWithinTaxonSourceMode();
|
511
|
toParentSourceMode = config.getToParentSourceMode();
|
512
|
cloneAggSourceDesc = config.isCloneAggregatedSourceDescriptions();
|
513
|
withLiterature = config.isIncludeLiterature();
|
514
|
}
|
515
|
private TestConfig setWithAddedData() {withAddedData = true; return this;}
|
516
|
private TestConfig setWithRemoved() {withRemovedData = true; return this;}
|
517
|
private TestConfig setWithFeature() {withFeature = true; return this;}
|
518
|
}
|
519
|
|
520
|
private void verifyAggregatedDescription(TestConfig config) {
|
521
|
|
522
|
boolean withRemovedData = config.withRemovedData;
|
523
|
boolean withLiterature = config.withLiterature;
|
524
|
boolean withAddedData = config.withAddedData;
|
525
|
boolean isWithinNone = config.withinTaxonSourceMode.isNone();
|
526
|
boolean withFeature = config.withFeature;
|
527
|
boolean isToParentNone = config.toParentSourceMode.isNone();
|
528
|
boolean isToParentTaxon = config.toParentSourceMode.isTaxon();
|
529
|
boolean cloneAggSourceDesc = config.cloneAggSourceDesc;
|
530
|
|
531
|
int intDel = withRemovedData? -1 : 0;
|
532
|
int intLit = withLiterature? 1 : 0;
|
533
|
|
534
|
//L. communis alpina
|
535
|
Taxon taxLapsanaCommunisAlpina = (Taxon)taxonService.find(T_LAPSANA_COMMUNIS_ALPINA_UUID);
|
536
|
int nElement = withAddedData ? 4 : 3;
|
537
|
TaxonDescription aggrDescLapsanaCommunisAlpina = verifyTaxonDescriptions(taxLapsanaCommunisAlpina, nElement);
|
538
|
|
539
|
List<StateData> stateData = verifyCategoricalData(uuidFeatureLeafPA, 1, aggrDescLapsanaCommunisAlpina, withAddedData);
|
540
|
verifyState(stateData, State.uuidPresent, 3+intDel);
|
541
|
List<StateData> sdAlpinaLeafColor = verifyCategoricalData(uuidFeatureLeafColor, 1, aggrDescLapsanaCommunisAlpina, false);
|
542
|
int litLeafColorBlue = withLiterature? 1: 0;
|
543
|
verifyState(sdAlpinaLeafColor, uuidLeafColorBlue, 2+litLeafColorBlue);
|
544
|
verifyState(sdAlpinaLeafColor, uuidLeafColorYellow, 0);
|
545
|
BigDecimal count = withLiterature? null : withRemovedData ? new BigDecimal("2"): new BigDecimal("3");
|
546
|
BigDecimal avg = withLiterature? null : withRemovedData ? new BigDecimal("6"): new BigDecimal("6.666667");
|
547
|
BigDecimal min = withLiterature? new BigDecimal("4.5") : new BigDecimal("5.0");
|
548
|
BigDecimal max = withRemovedData ? new BigDecimal("7.0") : new BigDecimal("8.0");
|
549
|
verifyQuantitativeData(uuidFeatureLeafLength, count, min, max, avg, aggrDescLapsanaCommunisAlpina);
|
550
|
//... sources
|
551
|
int nWithinSources = isWithinNone ? 0 : 3+intLit+intDel;
|
552
|
Assert.assertEquals(nWithinSources, aggrDescLapsanaCommunisAlpina.getSources().size());
|
553
|
SpecimenOrObservationBase<?> specLcommunisAlpina1 = CdmBase.deproxy(occurrenceService.find(T_LAPSANA_COMMUNIS_ALPINA_SPEC1_UUID));
|
554
|
int nCloned = isWithinNone ? 0 : 1;
|
555
|
Assert.assertEquals("Spec1 must have 2 descriptions now. The primary one and the cloned.", 1+nCloned, specLcommunisAlpina1.getSpecimenDescriptions().size());
|
556
|
Assert.assertEquals(nCloned, specLcommunisAlpina1.getSpecimenDescriptions().stream().filter(d->d.isCloneForSource()).count());
|
557
|
if (nCloned > 0){
|
558
|
DescriptionBase<?> clonedDesc = specLcommunisAlpina1.getDescriptions().stream().filter(d->d.isCloneForSource()).findFirst().get();
|
559
|
DescriptionBase<?> sourceDescription = getSingleSpecimenDescriptionSource(aggrDescLapsanaCommunisAlpina, T_LAPSANA_COMMUNIS_ALPINA_SPEC1_UUID);
|
560
|
Assert.assertEquals(clonedDesc, sourceDescription);
|
561
|
}
|
562
|
|
563
|
//L. communis adenophora
|
564
|
Taxon taxLapsanaCommunisAdenophora = (Taxon)taxonService.find(T_LAPSANA_COMMUNIS_ADENOPHORA_UUID);
|
565
|
int nEl = withFeature ? 4 : 3;
|
566
|
TaxonDescription aggrDescLapsanaCommunisAdenophora = verifyTaxonDescriptions(taxLapsanaCommunisAdenophora, nEl);
|
567
|
verifyState(verifyCategoricalData(uuidFeatureLeafPA, 1, aggrDescLapsanaCommunisAdenophora, false), State.uuidPresent, 1);
|
568
|
List<StateData> sdAdenophoraLeafColor = verifyCategoricalData(uuidFeatureLeafColor, 1, aggrDescLapsanaCommunisAdenophora, false);
|
569
|
verifyState(sdAdenophoraLeafColor, uuidLeafColorBlue, 0);
|
570
|
verifyState(sdAdenophoraLeafColor, uuidLeafColorYellow, 1);
|
571
|
verifyQuantitativeData(uuidFeatureLeafLength, new BigDecimal("1"), new BigDecimal("10.0"),
|
572
|
new BigDecimal("10.0"), new BigDecimal("10.0"), aggrDescLapsanaCommunisAdenophora);
|
573
|
|
574
|
//L. communis
|
575
|
Taxon taxLapsanaCommunis = (Taxon)taxonService.find(T_LAPSANA_COMMUNIS_UUID);
|
576
|
TaxonDescription aggrDescLapsanaCommunis = verifyTaxonDescriptions(taxLapsanaCommunis, nEl);
|
577
|
verifyState(verifyCategoricalData(uuidFeatureLeafPA, 1, aggrDescLapsanaCommunis, false), State.uuidPresent, 4+intDel);
|
578
|
List<StateData> sdCommunisLeafColor = verifyCategoricalData(uuidFeatureLeafColor, 2, aggrDescLapsanaCommunis, false);
|
579
|
verifyState(sdCommunisLeafColor, uuidLeafColorBlue, 2 + intLit);
|
580
|
verifyState(sdCommunisLeafColor, uuidLeafColorYellow, 1);
|
581
|
count = withLiterature? null : withRemovedData ? new BigDecimal("3") : new BigDecimal("4");
|
582
|
avg = withLiterature? null : withRemovedData ? new BigDecimal("7.333333") : new BigDecimal("7.5");
|
583
|
verifyQuantitativeData(uuidFeatureLeafLength, count, min,
|
584
|
new BigDecimal("10.0"), avg, aggrDescLapsanaCommunis);
|
585
|
//... sources
|
586
|
int nToParent = isToParentNone ? 0 : 2;
|
587
|
Assert.assertEquals(nToParent, aggrDescLapsanaCommunis.getSources().size());
|
588
|
Map<UUID, List<TaxonDescription>> taxonDescriptionMap = getSourceTaxonDescriptionMap(aggrDescLapsanaCommunis);
|
589
|
int nToParentDescs = isToParentTaxon? 0 : nToParent;
|
590
|
Assert.assertEquals(nToParentDescs, taxonDescriptionMap.size());
|
591
|
if (nToParentDescs > 0){
|
592
|
Assert.assertEquals(1, taxonDescriptionMap.get(T_LAPSANA_COMMUNIS_ALPINA_UUID).size());
|
593
|
Assert.assertEquals(1, taxonDescriptionMap.get(T_LAPSANA_COMMUNIS_ADENOPHORA_UUID).size());
|
594
|
if (cloneAggSourceDesc){
|
595
|
Assert.assertNotEquals(aggrDescLapsanaCommunisAlpina, taxonDescriptionMap.get(T_LAPSANA_COMMUNIS_ALPINA_UUID).get(0));
|
596
|
}else{
|
597
|
Assert.assertEquals(aggrDescLapsanaCommunisAlpina, taxonDescriptionMap.get(T_LAPSANA_COMMUNIS_ALPINA_UUID).get(0));
|
598
|
}
|
599
|
}else if (isToParentTaxon){
|
600
|
Map<UUID, List<Taxon>> taxonToTaxonSourceMap = getSourceTaxonMap(aggrDescLapsanaCommunis);
|
601
|
Assert.assertEquals(1, taxonToTaxonSourceMap.get(T_LAPSANA_COMMUNIS_ALPINA_UUID).size());
|
602
|
Assert.assertEquals(1, taxonToTaxonSourceMap.get(T_LAPSANA_COMMUNIS_ADENOPHORA_UUID).size());
|
603
|
Assert.assertEquals(taxLapsanaCommunisAlpina, taxonToTaxonSourceMap.get(T_LAPSANA_COMMUNIS_ALPINA_UUID).get(0));
|
604
|
}
|
605
|
|
606
|
//Lapsana
|
607
|
Taxon taxLapsana = (Taxon)taxonService.find(T_LAPSANA_UUID);
|
608
|
TaxonDescription aggrDescLapsana = verifyTaxonDescriptions(taxLapsana, nEl);
|
609
|
verifyState(verifyCategoricalData(uuidFeatureLeafPA, 1, aggrDescLapsana, false), State.uuidPresent, 4+intDel);
|
610
|
List<StateData> sdLapsanLeafColor = verifyCategoricalData(uuidFeatureLeafColor, 2, aggrDescLapsana, false);
|
611
|
verifyState(sdLapsanLeafColor, uuidLeafColorBlue, 2 + intLit);
|
612
|
verifyState(sdLapsanLeafColor, uuidLeafColorYellow, 1);
|
613
|
verifyQuantitativeData(uuidFeatureLeafLength, count, min,
|
614
|
new BigDecimal("10.0"), avg, aggrDescLapsana);
|
615
|
//... sources
|
616
|
nToParent = isToParentNone ? 0 : 1;
|
617
|
Assert.assertEquals(nToParent, aggrDescLapsana.getSources().size());
|
618
|
nToParentDescs = isToParentTaxon? 0 : nToParent;
|
619
|
taxonDescriptionMap = getSourceTaxonDescriptionMap(aggrDescLapsana);
|
620
|
Assert.assertEquals(nToParentDescs, taxonDescriptionMap.size());
|
621
|
if (nToParentDescs > 0){
|
622
|
Assert.assertEquals(1, taxonDescriptionMap.get(T_LAPSANA_COMMUNIS_UUID).size());
|
623
|
if (cloneAggSourceDesc){
|
624
|
Assert.assertNotEquals(aggrDescLapsanaCommunis, taxonDescriptionMap.get(T_LAPSANA_COMMUNIS_UUID).get(0));
|
625
|
}else{
|
626
|
Assert.assertEquals(aggrDescLapsanaCommunis, taxonDescriptionMap.get(T_LAPSANA_COMMUNIS_UUID).get(0));
|
627
|
}
|
628
|
|
629
|
}else if (isToParentTaxon){
|
630
|
Map<UUID, List<Taxon>> taxonToTaxonSourceMap = getSourceTaxonMap(aggrDescLapsana);
|
631
|
Assert.assertEquals(1, taxonToTaxonSourceMap.get(T_LAPSANA_COMMUNIS_UUID).size());
|
632
|
Assert.assertEquals(taxLapsanaCommunis, taxonToTaxonSourceMap.get(T_LAPSANA_COMMUNIS_UUID).get(0));
|
633
|
}
|
634
|
|
635
|
//total description count
|
636
|
nCloned = (isToParentNone || isToParentTaxon || !cloneAggSourceDesc ? 0 : 3) + (isWithinNone ? 0 : 4);
|
637
|
Assert.assertEquals("Should have 4 specimen desc, 1 literature desc, 2 individual association holder, "
|
638
|
+ "4 aggregated descriptions, 4/0 cloned specimen descriptions, (3/4/0 cloned aggregated descriptions?) = 18/19",
|
639
|
11+nCloned+intLit+(intDel*2), descriptionService.count(null));
|
640
|
|
641
|
}
|
642
|
|
643
|
//a map of the taxon uuid to the attached source descriptions
|
644
|
private Map<UUID, List<TaxonDescription>> getSourceTaxonDescriptionMap(TaxonDescription desc) {
|
645
|
return desc.getSources().stream().filter(s->(s.getCdmSource() instanceof TaxonDescription))
|
646
|
.map(s->CdmBase.deproxy(s.getCdmSource(), TaxonDescription.class))
|
647
|
.collect(Collectors.groupingBy(fDescToDescribedUuid));
|
648
|
}
|
649
|
|
650
|
private static Function<DescriptionBase<?>, UUID> fDescToDescribedUuid =
|
651
|
((Function<DescriptionBase<?>, IDescribable<?>>)(d->d.isInstanceOf(SpecimenDescription.class)? d.getDescribedSpecimenOrObservation(): CdmBase.deproxy(d, TaxonDescription.class).getTaxon()))
|
652
|
.andThen(IDescribable::getUuid);
|
653
|
|
654
|
//a map of the taxon to the attached taxon (source link)
|
655
|
private Map<UUID, List<Taxon>> getSourceTaxonMap(TaxonDescription desc) {
|
656
|
return desc.getSources().stream().filter(s->(s.getCdmSource() instanceof Taxon))
|
657
|
.map(s->CdmBase.deproxy(s.getCdmSource(), Taxon.class))
|
658
|
.collect(Collectors.groupingBy(t->t.getUuid()));
|
659
|
}
|
660
|
|
661
|
|
662
|
private DescriptionBase<?> getSingleSpecimenDescriptionSource(
|
663
|
TaxonDescription aggrDescLapsanaCommunisAlpina, UUID specimenUuid) {
|
664
|
|
665
|
Map<UUID, List<DescriptionBase<?>>> map = aggrDescLapsanaCommunisAlpina.getSources().stream()
|
666
|
.map(src->(DescriptionBase<?>)src.getCdmSource())
|
667
|
.filter(o-> o != null)
|
668
|
.collect(Collectors.groupingBy(fDescToDescribedUuid));
|
669
|
Assert.assertEquals(1, map.get(specimenUuid).size());
|
670
|
return map.get(specimenUuid).get(0);
|
671
|
|
672
|
}
|
673
|
|
674
|
private StructuredDescriptionAggregationConfiguration createConfig(DescriptiveDataSet dataSet) {
|
675
|
TaxonNodeFilter filter = TaxonNodeFilter.NewSubtreeInstance(TN_LAPSANA_UUID);
|
676
|
StructuredDescriptionAggregationConfiguration config =
|
677
|
StructuredDescriptionAggregationConfiguration.NewInstance(filter, monitor);
|
678
|
config.setDatasetUuid(dataSet.getUuid());
|
679
|
config.setAggregationMode(AggregationMode.byWithinTaxonAndToParent());
|
680
|
config.setIncludeLiterature(false);
|
681
|
config.setToParentSourceMode(AggregationSourceMode.DESCRIPTION); //test where written against DESCRIPTION at the beginning so we use this as default for the tests
|
682
|
|
683
|
return config;
|
684
|
}
|
685
|
|
686
|
private DescriptiveDataSet createTestData() {
|
687
|
DescriptiveDataSet dataSet = DescriptiveDataSet.NewInstance();
|
688
|
dataSet.setLabel("Test dataset");
|
689
|
datasetService.save(dataSet);
|
690
|
|
691
|
//L. communis alpina spec1
|
692
|
SpecimenDescription specDescAlpina1 = createSpecimenDescription(dataSet, T_LAPSANA_COMMUNIS_ALPINA_UUID, "alpina specimen1", T_LAPSANA_COMMUNIS_ALPINA_SPEC1_UUID);
|
693
|
addCategoricalData(specDescAlpina1, uuidFeatureLeafPA, State.uuidPresent);
|
694
|
addQuantitativeData(specDescAlpina1, uuidFeatureLeafLength, StatisticalMeasure.EXACT_VALUE(), new BigDecimal("5.0"));
|
695
|
addCategoricalData(specDescAlpina1, uuidFeatureLeafColor, uuidLeafColorBlue);
|
696
|
|
697
|
//L. communis alpina spec2
|
698
|
SpecimenDescription specDescAlpina2 = createSpecimenDescription(dataSet, T_LAPSANA_COMMUNIS_ALPINA_UUID, "alpina specimen2", T_LAPSANA_COMMUNIS_ALPINA_SPEC2_UUID);
|
699
|
addCategoricalData(specDescAlpina2, uuidFeatureLeafPA, State.uuidPresent);
|
700
|
addQuantitativeData(specDescAlpina2, uuidFeatureLeafLength, StatisticalMeasure.EXACT_VALUE(), new BigDecimal("7.0"));
|
701
|
addCategoricalData(specDescAlpina2, uuidFeatureLeafColor, uuidLeafColorBlue);
|
702
|
|
703
|
//L. communis alpina spec3
|
704
|
SpecimenDescription specDescAlpina3 = createSpecimenDescription(dataSet, T_LAPSANA_COMMUNIS_ALPINA_UUID, "alpina specimen3", T_LAPSANA_COMMUNIS_ALPINA_SPEC3_UUID);
|
705
|
addCategoricalData(specDescAlpina3, uuidFeatureLeafPA, State.uuidPresent);
|
706
|
addQuantitativeData(specDescAlpina3, uuidFeatureLeafLength, StatisticalMeasure.EXACT_VALUE(), new BigDecimal("8.0"));
|
707
|
|
708
|
//L. communis adenophora
|
709
|
SpecimenDescription specDescAdenophora = createSpecimenDescription(dataSet, T_LAPSANA_COMMUNIS_ADENOPHORA_UUID, "adenophora specimen", T_LAPSANA_COMMUNIS_ADENOPHORA_SPEC1_UUID);
|
710
|
addCategoricalData(specDescAdenophora, uuidFeatureLeafPA, State.uuidPresent);
|
711
|
addQuantitativeData(specDescAdenophora, uuidFeatureLeafLength, StatisticalMeasure.EXACT_VALUE(), new BigDecimal("10.0"));
|
712
|
addCategoricalData(specDescAdenophora, uuidFeatureLeafColor, uuidLeafColorYellow);
|
713
|
|
714
|
TaxonNode tnLapsana = taxonNodeService.find(TN_LAPSANA_UUID);
|
715
|
Assert.assertNotNull(tnLapsana);
|
716
|
dataSet.addTaxonSubtree(tnLapsana);
|
717
|
|
718
|
@SuppressWarnings("unchecked")
|
719
|
TermTree<Feature> descriptiveSystem = termTreeService.find(uuidFeatureTree);
|
720
|
dataSet.setDescriptiveSystem(descriptiveSystem);
|
721
|
|
722
|
addLiterature(dataSet);
|
723
|
return dataSet;
|
724
|
}
|
725
|
|
726
|
private TaxonDescription verifyTaxonDescriptions(Taxon taxon, int elementCount){
|
727
|
List<TaxonDescription> aggrNonCloneTaxonDescriptions = taxon.getDescriptions().stream()
|
728
|
.filter(desc->desc.getTypes().contains(DescriptionType.AGGREGATED_STRUC_DESC))
|
729
|
.filter(desc->!desc.getTypes().contains(DescriptionType.CLONE_FOR_SOURCE))
|
730
|
.collect(Collectors.toList());
|
731
|
|
732
|
Assert.assertEquals(1, aggrNonCloneTaxonDescriptions.size());
|
733
|
TaxonDescription aggrDesc = aggrNonCloneTaxonDescriptions.iterator().next();
|
734
|
Set<DescriptionElementBase> elements = aggrDesc.getElements();
|
735
|
Assert.assertEquals(elementCount, elements.size());
|
736
|
return aggrDesc;
|
737
|
}
|
738
|
|
739
|
private void verifyNumberTaxonDescriptions(Taxon taxon, long descriptionCount){
|
740
|
long n = taxon.getDescriptions().stream()
|
741
|
.filter(desc->desc.getTypes().contains(DescriptionType.AGGREGATED_STRUC_DESC))
|
742
|
.filter(desc->!desc.getTypes().contains(DescriptionType.CLONE_FOR_SOURCE)).count();
|
743
|
Assert.assertEquals(descriptionCount, n);
|
744
|
}
|
745
|
|
746
|
private void verifyNumberDescriptionElements(DescriptionBase<?> description, UUID featureUuid, long elementCount){
|
747
|
long n = description.getElements().stream()
|
748
|
.filter(element->element.getFeature().getUuid().equals(featureUuid))
|
749
|
.map(catData->CdmBase.deproxy(catData, CategoricalData.class))
|
750
|
.count();
|
751
|
Assert.assertEquals(elementCount, n);
|
752
|
}
|
753
|
|
754
|
private void verifyQuantitativeData(UUID featureUuid, BigDecimal sampleSize, BigDecimal min,
|
755
|
BigDecimal max, BigDecimal avg, TaxonDescription aggrDesc) {
|
756
|
List<QuantitativeData> quantitativeDatas = aggrDesc.getElements().stream()
|
757
|
.filter(element->element.getFeature().getUuid().equals(featureUuid))
|
758
|
.map(catData->CdmBase.deproxy(catData, QuantitativeData.class))
|
759
|
.collect(Collectors.toList());
|
760
|
Assert.assertEquals(1, quantitativeDatas.size());
|
761
|
QuantitativeData leafLength = quantitativeDatas.iterator().next();
|
762
|
Assert.assertEquals(sampleSize, leafLength.getSampleSize());
|
763
|
Assert.assertEquals(min, leafLength.getMin());
|
764
|
Assert.assertEquals(max, leafLength.getMax());
|
765
|
Assert.assertEquals(avg, leafLength.getAverage());
|
766
|
Assert.assertEquals(MeasurementUnit.METER(), leafLength.getUnit());
|
767
|
Assert.assertNotNull(leafLength.getUnit());
|
768
|
}
|
769
|
|
770
|
|
771
|
private List<StateData> verifyCategoricalData(UUID featureUuid, int stateDataCount, TaxonDescription taxonDescription, boolean withAddedData) {
|
772
|
List<CategoricalData> categoricalDatas = taxonDescription.getElements().stream()
|
773
|
.filter(element->element.getFeature().getUuid().equals(featureUuid))
|
774
|
.map(catData->CdmBase.deproxy(catData, CategoricalData.class))
|
775
|
.collect(Collectors.toList());
|
776
|
int nCD = withAddedData ? 2 : 1;
|
777
|
Assert.assertEquals(nCD, categoricalDatas.size());
|
778
|
CategoricalData categoricalData;
|
779
|
if (withAddedData){
|
780
|
categoricalData = categoricalDatas.stream().filter(cd->cd.getStateData().get(0).getCount() != null ).findFirst().get();
|
781
|
}else{
|
782
|
categoricalData = categoricalDatas.iterator().next(); // categoricalDatas.stream().filter(cd->cd.getStateData().size() != 1).collect(Collectors.toList());
|
783
|
}
|
784
|
List<StateData> stateDatas = categoricalData.getStateData();
|
785
|
Assert.assertEquals(stateDataCount, stateDatas.size());
|
786
|
return stateDatas;
|
787
|
}
|
788
|
|
789
|
private void verifyState(List<StateData> stateDatas, UUID stateUuid, Integer stateDataCount){
|
790
|
List<StateData> filteredStateDatas = stateDatas.stream()
|
791
|
.filter(stateData->stateData.getState()!=null && stateData.getState().getUuid().equals(stateUuid))
|
792
|
.collect(Collectors.toList());
|
793
|
if(stateDataCount==0){
|
794
|
// non-existence test
|
795
|
Assert.assertEquals(0, filteredStateDatas.size());
|
796
|
return;
|
797
|
}
|
798
|
Assert.assertEquals(1, filteredStateDatas.size());
|
799
|
StateData stateData = filteredStateDatas.iterator().next();
|
800
|
Assert.assertEquals(stateDataCount, stateData.getCount());
|
801
|
Assert.assertEquals(stateUuid, stateData.getState().getUuid());
|
802
|
}
|
803
|
|
804
|
private void addQuantitativeData(DescriptionBase<?> desc, UUID uuidFeature, StatisticalMeasure type, BigDecimal value) {
|
805
|
Feature feature = (Feature)termService.find(uuidFeature);
|
806
|
QuantitativeData qd = QuantitativeData.NewInstance(feature);
|
807
|
StatisticalMeasurementValue smv = StatisticalMeasurementValue.NewInstance(type, value);
|
808
|
qd.addStatisticalValue(smv);
|
809
|
Assert.assertNotNull(MeasurementUnit.METER());
|
810
|
qd.setUnit(MeasurementUnit.METER());
|
811
|
desc.addElement(qd);
|
812
|
}
|
813
|
|
814
|
private void addQuantitativeData(DescriptionBase<?> desc, UUID uuidFeature, BigDecimal min, BigDecimal max) {
|
815
|
Feature feature = (Feature)termService.find(uuidFeature);
|
816
|
QuantitativeData qd = QuantitativeData.NewInstance(feature);
|
817
|
StatisticalMeasurementValue smv = StatisticalMeasurementValue.NewInstance(StatisticalMeasure.MIN(), min);
|
818
|
qd.addStatisticalValue(smv);
|
819
|
smv = StatisticalMeasurementValue.NewInstance(StatisticalMeasure.MAX(), max);
|
820
|
qd.addStatisticalValue(smv);
|
821
|
desc.addElement(qd);
|
822
|
}
|
823
|
|
824
|
private void addCategoricalData(DescriptionBase<?> desc, UUID featureUuid, UUID stateUUID) {
|
825
|
Feature feature = (Feature)termService.find(featureUuid);
|
826
|
State state = (State)termService.find(stateUUID);
|
827
|
CategoricalData cd = CategoricalData.NewInstance(state, feature);
|
828
|
desc.addElement(cd);
|
829
|
}
|
830
|
|
831
|
private SpecimenDescription createSpecimenDescription(DescriptiveDataSet dataSet, UUID taxonUuid, String specLabel, UUID specimenUuid ) {
|
832
|
Taxon taxon = (Taxon)taxonService.find(taxonUuid);
|
833
|
DerivedUnit specimen = DerivedUnit.NewPreservedSpecimenInstance();
|
834
|
specimen.setTitleCache(specLabel, true);
|
835
|
specimen.setUuid(specimenUuid);
|
836
|
TaxonDescription taxonDescription = taxon.getDescriptions(DescriptionType.INDIVIDUALS_ASSOCIATION).stream()
|
837
|
.findFirst()
|
838
|
.orElseGet(()->{
|
839
|
TaxonDescription td = TaxonDescription.NewInstance(taxon);
|
840
|
td.addType(DescriptionType.INDIVIDUALS_ASSOCIATION);
|
841
|
td.setTitleCache("Specimens used by " + dataSet.getTitleCache() + " for " + getTaxonLabel(taxon), true);
|
842
|
return td;}
|
843
|
);
|
844
|
IndividualsAssociation individualsAssociation = IndividualsAssociation.NewInstance(specimen);
|
845
|
// TODO this has to be discussed; currently the description with the InidividualsAssociation is
|
846
|
// needed in the dataset for performance reasons
|
847
|
taxonDescription.addElement(individualsAssociation);
|
848
|
dataSet.addDescription(taxonDescription);
|
849
|
SpecimenDescription specDesc = SpecimenDescription.NewInstance(specimen);
|
850
|
|
851
|
dataSet.addDescription(specDesc);
|
852
|
return specDesc;
|
853
|
}
|
854
|
|
855
|
private String getTaxonLabel(Taxon taxon) {
|
856
|
if (taxon.getName() != null){
|
857
|
return taxon.getName().getTitleCache();
|
858
|
}else{
|
859
|
return taxon.getTitleCache();
|
860
|
}
|
861
|
}
|
862
|
|
863
|
private Feature createFeature(UUID uuid, String label, boolean isQuantitative) {
|
864
|
Feature feature = Feature.NewInstance("", label, null);
|
865
|
feature.setUuid(uuid);
|
866
|
feature.setSupportsQuantitativeData(isQuantitative);
|
867
|
feature.setSupportsCategoricalData(!isQuantitative);
|
868
|
feature.setSupportsTextData(false);
|
869
|
termService.save(feature);
|
870
|
return feature;
|
871
|
}
|
872
|
|
873
|
private State createState(String label, UUID uuid) {
|
874
|
State state = State.NewInstance("", label, "");
|
875
|
state.getTitleCache(); //for better debugging
|
876
|
state.setUuid(uuid);
|
877
|
termService.save(state);
|
878
|
return state;
|
879
|
}
|
880
|
|
881
|
private void createDefaultFeatureTree() {
|
882
|
//feature tree
|
883
|
//leaf p/a
|
884
|
// leaf length
|
885
|
// leaf color
|
886
|
|
887
|
//tree
|
888
|
TermTree<Feature> featureTree = TermTree.NewFeatureInstance();
|
889
|
featureTree.setUuid(uuidFeatureTree);
|
890
|
|
891
|
//leaf p/a
|
892
|
Feature featureLeafPA = createFeature(uuidFeatureLeafPA, "LeafPA", false);
|
893
|
TermNode<Feature> leafPANode = featureTree.getRoot().addChild(featureLeafPA);
|
894
|
|
895
|
//leaf length
|
896
|
Feature featureLeafLength = createFeature(uuidFeatureLeafLength, "LeafLength", true);
|
897
|
leafPANode.addChild(featureLeafLength);
|
898
|
|
899
|
//leaf color
|
900
|
Feature featureLeafColor = createFeature(uuidFeatureLeafColor, "LeafColor", false);
|
901
|
leafPANode.addChild(featureLeafColor);
|
902
|
TermVocabulary<State> stateVoc = TermVocabulary.NewInstance(TermType.State, State.class, "", "Colors", null, null);
|
903
|
State yellow = createState("Yellow", uuidLeafColorYellow);
|
904
|
State blue = createState("Blue", uuidLeafColorBlue);
|
905
|
stateVoc.addTerm(yellow);
|
906
|
stateVoc.addTerm(blue);
|
907
|
featureLeafColor.addSupportedCategoricalEnumeration(stateVoc);
|
908
|
|
909
|
//additional feature
|
910
|
Feature featureLeafAdd = createFeature(uuidFeatureLeafAdd, "Add", true);
|
911
|
leafPANode.addChild(featureLeafAdd);
|
912
|
|
913
|
vocabularyService.save(stateVoc);
|
914
|
}
|
915
|
|
916
|
// @Test
|
917
|
//to create the taxonomic classification available also as .xml file
|
918
|
@Override
|
919
|
public void createTestDataSet() throws FileNotFoundException {
|
920
|
|
921
|
// --- References --- //
|
922
|
Reference sec = ReferenceFactory.newDatabase();
|
923
|
sec.setTitleCache("Test", true);
|
924
|
Reference nomRef = ReferenceFactory.newBook();
|
925
|
sec.setTitleCache("Sp.Pl.", true);
|
926
|
|
927
|
referenceService.save(sec);
|
928
|
referenceService.save(nomRef);
|
929
|
|
930
|
|
931
|
// --- Taxa --- //
|
932
|
// Lapsana
|
933
|
// L. communis
|
934
|
// L. communis subsp. communis
|
935
|
// L. communis subsp. adenophora
|
936
|
// L. communis subsp. alpina
|
937
|
// Sonchella
|
938
|
// S. dentata
|
939
|
// S. stenoma
|
940
|
IBotanicalName n_lapsana = TaxonNameFactory.NewBotanicalInstance(Rank.GENUS());
|
941
|
n_lapsana.setTitleCache("Lapsana", true);
|
942
|
Taxon t_lapsana = Taxon.NewInstance(n_lapsana, sec);
|
943
|
t_lapsana.setUuid(T_LAPSANA_UUID);
|
944
|
taxonService.saveOrUpdate(t_lapsana);
|
945
|
|
946
|
IBotanicalName n_lapsana_communis = TaxonNameFactory.NewBotanicalInstance(Rank.SPECIES());
|
947
|
n_lapsana_communis.setTitleCache("L. communis", true);
|
948
|
Taxon t_lapsana_communis = Taxon.NewInstance(n_lapsana_communis, sec);
|
949
|
t_lapsana_communis.setUuid(T_LAPSANA_COMMUNIS_UUID);
|
950
|
taxonService.saveOrUpdate(t_lapsana_communis);
|
951
|
|
952
|
IBotanicalName n_lapsana_communis_communis = TaxonNameFactory.NewBotanicalInstance(Rank.SUBSPECIES());
|
953
|
n_lapsana_communis_communis.setTitleCache("L. communis subsp. communis", true);
|
954
|
Taxon t_lapsana_communis_communis = Taxon.NewInstance(n_lapsana_communis_communis, sec);
|
955
|
t_lapsana_communis_communis.setUuid(T_LAPSANA_COMMUNIS_COMMUNIS_UUID);
|
956
|
taxonService.saveOrUpdate(t_lapsana_communis_communis);
|
957
|
|
958
|
IBotanicalName n_lapsana_communis_adenophora = TaxonNameFactory.NewBotanicalInstance(Rank.SUBSPECIES());
|
959
|
n_lapsana_communis_adenophora.setTitleCache("L. communis subsp. adenophora", true);
|
960
|
Taxon t_lapsana_communis_adenophora = Taxon.NewInstance(n_lapsana_communis_adenophora, sec);
|
961
|
t_lapsana_communis_adenophora.setUuid(T_LAPSANA_COMMUNIS_ADENOPHORA_UUID);
|
962
|
taxonService.saveOrUpdate(t_lapsana_communis_adenophora);
|
963
|
|
964
|
IBotanicalName n_lapsana_communis_alpina = TaxonNameFactory.NewBotanicalInstance(Rank.SUBSPECIES());
|
965
|
n_lapsana_communis_alpina.setTitleCache("L. communis subsp. alpina", true);
|
966
|
Taxon t_lapsana_communis_alpina = Taxon.NewInstance(n_lapsana_communis_alpina, sec);
|
967
|
t_lapsana_communis_alpina.setUuid(T_LAPSANA_COMMUNIS_ALPINA_UUID);
|
968
|
taxonService.saveOrUpdate(t_lapsana_communis_alpina);
|
969
|
|
970
|
// --- Classification --- //
|
971
|
Classification classification = Classification.NewInstance("TestClassification");
|
972
|
classification.setUuid(CLASSIFICATION_UUID);
|
973
|
classificationService.save(classification);
|
974
|
TaxonNode node_lapsana = classification.addChildTaxon(t_lapsana, sec, null);
|
975
|
node_lapsana.setUuid(TN_LAPSANA_UUID);
|
976
|
TaxonNode node_lapsana_communis = node_lapsana.addChildTaxon(t_lapsana_communis, sec, null);
|
977
|
node_lapsana_communis.addChildTaxon(t_lapsana_communis_communis, sec, null);
|
978
|
node_lapsana_communis.addChildTaxon(t_lapsana_communis_adenophora, sec, null);
|
979
|
node_lapsana_communis.addChildTaxon(t_lapsana_communis_alpina, sec, null);
|
980
|
classificationService.saveOrUpdate(classification);
|
981
|
|
982
|
commitAndStartNewTransaction(null);
|
983
|
|
984
|
writeDbUnitDataSetFile(new String[] {
|
985
|
"TAXONBASE", "TAXONNAME","CLASSIFICATION", "TAXONNODE","HOMOTYPICALGROUP",
|
986
|
"REFERENCE", "AGENTBASE",
|
987
|
"DESCRIPTIONELEMENTBASE", "DESCRIPTIONBASE",
|
988
|
"LANGUAGESTRING",
|
989
|
"HIBERNATE_SEQUENCES"
|
990
|
});
|
991
|
}
|
992
|
|
993
|
|
994
|
}
|