Project

General

Profile

Download (50.4 KB) Statistics
| Branch: | Tag: | Revision:
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 xxx)
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);
342
//        List<StateData> sdAlpinaLeafColor = verifyCategoricalData(uuidFeatureLeafColor, 0, aggrDescLapsanaCommunisAlpina, false);
343
//        verifyState(sdAlpinaLeafColor, uuidLeafColorBlue, 0);
344
//        verifyState(sdAlpinaLeafColor, uuidLeafColorYellow, 0);
345

    
346
        //test duplicates
347
        specDescAlpina1 = (SpecimenDescription)descriptionService.find(specDescAlpina1.getUuid());
348
        addCategoricalData(specDescAlpina1, uuidFeatureLeafColor, uuidLeafColorBlue);
349
        addCategoricalData(specDescAlpina1, uuidFeatureLeafColor, uuidLeafColorBlue);
350
        commitAndStartNewTransaction();
351
        result = engine.invoke(config, repository);
352

    
353
        verifyStatusOk(result);
354
        taxLapsanaCommunisAlpina = (Taxon)taxonService.find(T_LAPSANA_COMMUNIS_ALPINA_UUID);
355
        aggrDescLapsanaCommunisAlpina = verifyTaxonDescriptions(taxLapsanaCommunisAlpina, 2);  //for leafPA and for color
356
        List<StateData> sdAlpinaLeafColor = verifyCategoricalData(uuidFeatureLeafColor, 1, aggrDescLapsanaCommunisAlpina, false);
357
        verifyState(sdAlpinaLeafColor, uuidLeafColorBlue, 2);
358
        verifyState(sdAlpinaLeafColor, uuidLeafColorYellow, 0);
359

    
360
    }
361

    
362
    @Test
363
    @DataSets({
364
        @DataSet(loadStrategy=CleanSweepInsertLoadStrategy.class, value="/eu/etaxonomy/cdm/database/ClearDB_with_Terms_DataSet.xml"),
365
        @DataSet(value="/eu/etaxonomy/cdm/database/TermsDataSet-with_auditing_info.xml"),
366
        @DataSet(value="StructuredDescriptionAggregationTest.xml"),
367
    })
368
    public void testSourceModes() {
369
        createDefaultFeatureTree();
370
        DescriptiveDataSet dataSet = createTestData();
371
        commitAndStartNewTransaction();
372

    
373
        StructuredDescriptionAggregationConfiguration config = createConfig(dataSet);
374
        config.setWithinTaxonSourceMode(AggregationSourceMode.NONE);
375
        config.setToParentSourceMode(AggregationSourceMode.NONE);
376

    
377
        // 1st aggregation
378
        UpdateResult result = engine.invoke(config, repository);
379
        verifyStatusOk(result);
380
        commitAndStartNewTransaction();
381
        verifyAggregatedDescription(new TestConfig(config));
382

    
383
        config.setWithinTaxonSourceMode(AggregationSourceMode.DESCRIPTION);
384
        config.setToParentSourceMode(AggregationSourceMode.NONE);
385
        result = engine.invoke(config, repository);
386
        verifyStatusOk(result);
387
        commitAndStartNewTransaction();
388
        verifyAggregatedDescription(new TestConfig(config));
389

    
390
        config.setWithinTaxonSourceMode(AggregationSourceMode.NONE);
391
        config.setToParentSourceMode(AggregationSourceMode.DESCRIPTION);
392
        result = engine.invoke(config, repository);
393
        verifyStatusOk(result);
394
        commitAndStartNewTransaction();
395
        verifyAggregatedDescription(new TestConfig(config));
396

    
397
        config.setWithinTaxonSourceMode(AggregationSourceMode.DESCRIPTION);
398
        config.setToParentSourceMode(AggregationSourceMode.TAXON);
399
        result = engine.invoke(config, repository);
400
        verifyStatusOk(result);
401
        commitAndStartNewTransaction();
402
        verifyAggregatedDescription(new TestConfig(config));
403

    
404
        config.setWithinTaxonSourceMode(AggregationSourceMode.NONE);
405
        config.setToParentSourceMode(AggregationSourceMode.TAXON);
406
        result = engine.invoke(config, repository);
407
        verifyStatusOk(result);
408
        commitAndStartNewTransaction();
409
        verifyAggregatedDescription(new TestConfig(config));
410

    
411
        config.setWithinTaxonSourceMode(AggregationSourceMode.ALL);
412
        config.setToParentSourceMode(AggregationSourceMode.DESCRIPTION);
413
        result = engine.invoke(config, repository);
414
        verifyStatusError(result, "Unsupported source mode for within-taxon aggregation: ALL");
415
        commitAndStartNewTransaction();
416

    
417
        config.setWithinTaxonSourceMode(AggregationSourceMode.DESCRIPTION);
418
        config.setToParentSourceMode(AggregationSourceMode.ALL);
419
        result = engine.invoke(config, repository);
420
        verifyStatusError(result, "Unsupported source mode for to-parent aggregation: ALL");
421
        commitAndStartNewTransaction();
422

    
423
        config.setWithinTaxonSourceMode(AggregationSourceMode.ALL_SAMEVALUE);
424
        config.setToParentSourceMode(AggregationSourceMode.DESCRIPTION);
425
        result = engine.invoke(config, repository);
426
        verifyStatusError(result, "Unsupported source mode for within-taxon aggregation: ALL_SAMEVALUE");
427
        commitAndStartNewTransaction();
428

    
429
//        removeSomeDataFromFirstAggregation();
430
//        commitAndStartNewTransaction();
431
//        Assert.assertEquals("Should have 3 specimen desc, 1 literature desc, 2 individual association holder, "
432
//                + "4 aggregated descriptions, 4 cloned specimen descriptions (still not deleted), (3 cloned aggregated descriptions?) = 17",
433
//                17, descriptionService.count(null));
434
//
435
//        // 2nd aggregation
436
//        result = engine.invoke(config, repository);
437
//        testStatusOk(result);
438
//        commitAndStartNewTransaction();
439
//        testAggregatedDescription(false, false, true);
440
    }
441

    
442
    @Test
443
    @DataSets({
444
        @DataSet(loadStrategy=CleanSweepInsertLoadStrategy.class, value="/eu/etaxonomy/cdm/database/ClearDB_with_Terms_DataSet.xml"),
445
        @DataSet(value="/eu/etaxonomy/cdm/database/TermsDataSet-with_auditing_info.xml"),
446
        @DataSet(value="StructuredDescriptionAggregationTest.xml"),
447
    })
448
    public void testAggregation() {
449
        createDefaultFeatureTree();
450
        DescriptiveDataSet dataSet = createTestData();
451
        commitAndStartNewTransaction();
452

    
453
        StructuredDescriptionAggregationConfiguration config = createConfig(dataSet);
454

    
455
        UpdateResult result = engine.invoke(config, repository);
456
        commitAndStartNewTransaction();
457
        verifyStatusOk(result);
458
        verifyAggregatedDescription(new TestConfig(config));
459

    
460
        config.setIncludeLiterature(true);
461

    
462
        result = engine.invoke(config, repository);
463
        commitAndStartNewTransaction();
464
        verifyStatusOk(result);
465
        verifyAggregatedDescription(new TestConfig(config));
466
    }
467

    
468
    private void verifyStatusOk(UpdateResult result) {
469
        if (result.getStatus() != UpdateResult.Status.OK){
470
            Assert.fail("Aggregation should have status OK but was " + result.toString());
471
            for (Exception ex : result.getExceptions()){
472
                ex.printStackTrace();
473
            }
474
        }
475
    }
476

    
477
    private void verifyStatusError(UpdateResult result, String expectedMessage) {
478
        if (result.getStatus() != UpdateResult.Status.ERROR){
479
            Assert.fail("Aggregation should fail with status error " + result.toString());
480
        }
481
        if (result.getExceptions().isEmpty()){
482
            Assert.fail("Failing aggregation include exception " + result.toString());
483
        }
484
        Exception e = result.getExceptions().iterator().next();
485
        if (! (e instanceof AggregationException)){
486
            Assert.fail("Exception should be of type AggregationException " + result.toString());
487
        }else if (expectedMessage != null){
488
            Assert.assertEquals(expectedMessage, e.getMessage());
489
        }
490
    }
491

    
492
    private void addLiterature(DescriptiveDataSet dataSet) {
493

    
494
        //literature description
495
        Taxon taxon = (Taxon)taxonService.find(T_LAPSANA_COMMUNIS_ALPINA_UUID);
496
        TaxonDescription literatureDescription = TaxonDescription.NewInstance(taxon);
497
        literatureDescription.addType(DescriptionType.SECONDARY_DATA);
498
        addQuantitativeData(literatureDescription, uuidFeatureLeafLength, new BigDecimal("4.5"), new BigDecimal("6.5"));
499
        addCategoricalData(literatureDescription, uuidFeatureLeafColor, uuidLeafColorBlue);
500
        dataSet.addDescription(literatureDescription);
501
    }
502

    
503
    private class TestConfig{
504
        boolean withAddedData;
505
        boolean withRemovedData;
506
        boolean withFeature;
507
        final boolean withLiterature;
508
        final boolean cloneAggSourceDesc;
509
        final AggregationSourceMode withinTaxonSourceMode;
510
        final AggregationSourceMode toParentSourceMode;
511

    
512

    
513
        private TestConfig(StructuredDescriptionAggregationConfiguration config) {
514
            withinTaxonSourceMode = config.getWithinTaxonSourceMode();
515
            toParentSourceMode = config.getToParentSourceMode();
516
            cloneAggSourceDesc = config.isCloneAggregatedSourceDescriptions();
517
            withLiterature = config.isIncludeLiterature();
518
        }
519
        private TestConfig setWithAddedData() {withAddedData = true; return this;}
520
        private TestConfig setWithRemoved() {withRemovedData = true; return this;}
521
        private TestConfig setWithFeature() {withFeature = true; return this;}
522
    }
523

    
524
    private void verifyAggregatedDescription(TestConfig config) {
525

    
526
        boolean withRemovedData = config.withRemovedData;
527
        boolean withLiterature = config.withLiterature;
528
        boolean withAddedData = config.withAddedData;
529
        boolean isWithinNone = config.withinTaxonSourceMode.isNone();
530
        boolean withFeature = config.withFeature;
531
        boolean isToParentNone = config.toParentSourceMode.isNone();
532
        boolean isToParentTaxon = config.toParentSourceMode.isTaxon();
533
        boolean cloneAggSourceDesc = config.cloneAggSourceDesc;
534

    
535
        int intDel = withRemovedData? -1 : 0;
536
        int intLit = withLiterature? 1 : 0;
537

    
538
        //L. communis alpina
539
        Taxon taxLapsanaCommunisAlpina = (Taxon)taxonService.find(T_LAPSANA_COMMUNIS_ALPINA_UUID);
540
        int nElement = withAddedData ? 4 : 3;
541
        TaxonDescription aggrDescLapsanaCommunisAlpina = verifyTaxonDescriptions(taxLapsanaCommunisAlpina, nElement);
542

    
543
        List<StateData> stateData = verifyCategoricalData(uuidFeatureLeafPA, 1, aggrDescLapsanaCommunisAlpina, withAddedData);
544
        verifyState(stateData, State.uuidPresent, 3+intDel);
545
        List<StateData> sdAlpinaLeafColor = verifyCategoricalData(uuidFeatureLeafColor, 1, aggrDescLapsanaCommunisAlpina, false);
546
        int litLeafColorBlue = withLiterature? 1: 0;
547
        verifyState(sdAlpinaLeafColor, uuidLeafColorBlue, 2+litLeafColorBlue);
548
        verifyState(sdAlpinaLeafColor, uuidLeafColorYellow, 0);
549
        BigDecimal count = withLiterature? null : withRemovedData ? new BigDecimal("2"): new BigDecimal("3");
550
        BigDecimal avg = withLiterature? null : withRemovedData ? new BigDecimal("6"): new BigDecimal("6.666667");
551
        BigDecimal min = withLiterature? new BigDecimal("4.5") : new BigDecimal("5.0");
552
        BigDecimal max = withRemovedData ? new BigDecimal("7.0") : new BigDecimal("8.0");
553
        verifyQuantitativeData(uuidFeatureLeafLength, count, min, max, avg, aggrDescLapsanaCommunisAlpina);
554
        //... sources
555
        int nWithinSources = isWithinNone ? 0 : 3+intLit+intDel;
556
        Assert.assertEquals(nWithinSources, aggrDescLapsanaCommunisAlpina.getSources().size());
557
        SpecimenOrObservationBase<?> specLcommunisAlpina1 = CdmBase.deproxy(occurrenceService.find(T_LAPSANA_COMMUNIS_ALPINA_SPEC1_UUID));
558
        int nCloned = isWithinNone ? 0 : 1;
559
        Assert.assertEquals("Spec1 must have 2 descriptions now. The primary one and the cloned.", 1+nCloned, specLcommunisAlpina1.getSpecimenDescriptions().size());
560
        Assert.assertEquals(nCloned, specLcommunisAlpina1.getSpecimenDescriptions().stream().filter(d->d.isCloneForSource()).count());
561
        if (nCloned > 0){
562
            DescriptionBase<?> clonedDesc = specLcommunisAlpina1.getDescriptions().stream().filter(d->d.isCloneForSource()).findFirst().get();
563
            DescriptionBase<?> sourceDescription = getSingleSpecimenDescriptionSource(aggrDescLapsanaCommunisAlpina, T_LAPSANA_COMMUNIS_ALPINA_SPEC1_UUID);
564
            Assert.assertEquals(clonedDesc, sourceDescription);
565
        }
566

    
567
        //L. communis adenophora
568
        Taxon taxLapsanaCommunisAdenophora = (Taxon)taxonService.find(T_LAPSANA_COMMUNIS_ADENOPHORA_UUID);
569
        int nEl = withFeature ? 4 : 3;
570
        TaxonDescription aggrDescLapsanaCommunisAdenophora = verifyTaxonDescriptions(taxLapsanaCommunisAdenophora, nEl);
571
        verifyState(verifyCategoricalData(uuidFeatureLeafPA, 1, aggrDescLapsanaCommunisAdenophora, false), State.uuidPresent, 1);
572
        List<StateData> sdAdenophoraLeafColor = verifyCategoricalData(uuidFeatureLeafColor, 1, aggrDescLapsanaCommunisAdenophora, false);
573
        verifyState(sdAdenophoraLeafColor, uuidLeafColorBlue, 0);
574
        verifyState(sdAdenophoraLeafColor, uuidLeafColorYellow, 1);
575
        verifyQuantitativeData(uuidFeatureLeafLength, new BigDecimal("1"), new BigDecimal("10.0"),
576
                new BigDecimal("10.0"), new BigDecimal("10.0"), aggrDescLapsanaCommunisAdenophora);
577

    
578
        //L. communis
579
        Taxon taxLapsanaCommunis = (Taxon)taxonService.find(T_LAPSANA_COMMUNIS_UUID);
580
        TaxonDescription aggrDescLapsanaCommunis = verifyTaxonDescriptions(taxLapsanaCommunis, nEl);
581
        verifyState(verifyCategoricalData(uuidFeatureLeafPA, 1, aggrDescLapsanaCommunis, false), State.uuidPresent, 4+intDel);
582
        List<StateData> sdCommunisLeafColor = verifyCategoricalData(uuidFeatureLeafColor, 2, aggrDescLapsanaCommunis, false);
583
        verifyState(sdCommunisLeafColor, uuidLeafColorBlue, 2 + intLit);
584
        verifyState(sdCommunisLeafColor, uuidLeafColorYellow, 1);
585
        count = withLiterature? null : withRemovedData ? new BigDecimal("3") : new BigDecimal("4");
586
        avg = withLiterature? null : withRemovedData ? new BigDecimal("7.333333") : new BigDecimal("7.5");
587
        verifyQuantitativeData(uuidFeatureLeafLength, count, min,
588
                new BigDecimal("10.0"), avg, aggrDescLapsanaCommunis);
589
        //... sources
590
        int nToParent = isToParentNone ? 0 : 2;
591
        Assert.assertEquals(nToParent, aggrDescLapsanaCommunis.getSources().size());
592
        Map<UUID, List<TaxonDescription>> taxonDescriptionMap = getSourceTaxonDescriptionMap(aggrDescLapsanaCommunis);
593
        int nToParentDescs = isToParentTaxon? 0 : nToParent;
594
        Assert.assertEquals(nToParentDescs, taxonDescriptionMap.size());
595
        if (nToParentDescs > 0){
596
            Assert.assertEquals(1, taxonDescriptionMap.get(T_LAPSANA_COMMUNIS_ALPINA_UUID).size());
597
            Assert.assertEquals(1, taxonDescriptionMap.get(T_LAPSANA_COMMUNIS_ADENOPHORA_UUID).size());
598
            if (cloneAggSourceDesc){
599
                Assert.assertNotEquals(aggrDescLapsanaCommunisAlpina, taxonDescriptionMap.get(T_LAPSANA_COMMUNIS_ALPINA_UUID).get(0));
600
            }else{
601
                Assert.assertEquals(aggrDescLapsanaCommunisAlpina, taxonDescriptionMap.get(T_LAPSANA_COMMUNIS_ALPINA_UUID).get(0));
602
            }
603
        }else if (isToParentTaxon){
604
            Map<UUID, List<Taxon>> taxonToTaxonSourceMap = getSourceTaxonMap(aggrDescLapsanaCommunis);
605
            Assert.assertEquals(1, taxonToTaxonSourceMap.get(T_LAPSANA_COMMUNIS_ALPINA_UUID).size());
606
            Assert.assertEquals(1, taxonToTaxonSourceMap.get(T_LAPSANA_COMMUNIS_ADENOPHORA_UUID).size());
607
            Assert.assertEquals(taxLapsanaCommunisAlpina, taxonToTaxonSourceMap.get(T_LAPSANA_COMMUNIS_ALPINA_UUID).get(0));
608
        }
609

    
610
        //Lapsana
611
        Taxon taxLapsana = (Taxon)taxonService.find(T_LAPSANA_UUID);
612
        TaxonDescription aggrDescLapsana = verifyTaxonDescriptions(taxLapsana, nEl);
613
        verifyState(verifyCategoricalData(uuidFeatureLeafPA, 1, aggrDescLapsana, false), State.uuidPresent, 4+intDel);
614
        List<StateData> sdLapsanLeafColor = verifyCategoricalData(uuidFeatureLeafColor, 2, aggrDescLapsana, false);
615
        verifyState(sdLapsanLeafColor, uuidLeafColorBlue, 2 + intLit);
616
        verifyState(sdLapsanLeafColor, uuidLeafColorYellow, 1);
617
        verifyQuantitativeData(uuidFeatureLeafLength, count, min,
618
                new BigDecimal("10.0"), avg, aggrDescLapsana);
619
        //... sources
620
        nToParent = isToParentNone ? 0 : 1;
621
        Assert.assertEquals(nToParent, aggrDescLapsana.getSources().size());
622
        nToParentDescs = isToParentTaxon? 0 : nToParent;
623
        taxonDescriptionMap = getSourceTaxonDescriptionMap(aggrDescLapsana);
624
        Assert.assertEquals(nToParentDescs, taxonDescriptionMap.size());
625
        if (nToParentDescs > 0){
626
            Assert.assertEquals(1, taxonDescriptionMap.get(T_LAPSANA_COMMUNIS_UUID).size());
627
            if (cloneAggSourceDesc){
628
                Assert.assertNotEquals(aggrDescLapsanaCommunis, taxonDescriptionMap.get(T_LAPSANA_COMMUNIS_UUID).get(0));
629
            }else{
630
                Assert.assertEquals(aggrDescLapsanaCommunis, taxonDescriptionMap.get(T_LAPSANA_COMMUNIS_UUID).get(0));
631
            }
632

    
633
        }else if (isToParentTaxon){
634
            Map<UUID, List<Taxon>> taxonToTaxonSourceMap = getSourceTaxonMap(aggrDescLapsana);
635
            Assert.assertEquals(1, taxonToTaxonSourceMap.get(T_LAPSANA_COMMUNIS_UUID).size());
636
            Assert.assertEquals(taxLapsanaCommunis, taxonToTaxonSourceMap.get(T_LAPSANA_COMMUNIS_UUID).get(0));
637
        }
638

    
639
        //total description count
640
        nCloned = (isToParentNone || isToParentTaxon || !cloneAggSourceDesc ? 0 : 3) + (isWithinNone ? 0 : 4);
641
        Assert.assertEquals("Should have 4 specimen desc, 1 literature desc, 2 individual association holder, "
642
                + "4 aggregated descriptions, 4/0 cloned specimen descriptions, (3/4/0 cloned aggregated descriptions?) = 18/19",
643
                11+nCloned+intLit+(intDel*2), descriptionService.count(null));
644

    
645
    }
646

    
647
    //a map of the taxon uuid to the attached source descriptions
648
    private Map<UUID, List<TaxonDescription>> getSourceTaxonDescriptionMap(TaxonDescription desc) {
649
        return desc.getSources().stream().filter(s->(s.getCdmSource() instanceof TaxonDescription))
650
            .map(s->CdmBase.deproxy(s.getCdmSource(), TaxonDescription.class))
651
            .collect(Collectors.groupingBy(fDescToDescribedUuid));
652
    }
653

    
654
    private static Function<DescriptionBase<?>, UUID> fDescToDescribedUuid =
655
            ((Function<DescriptionBase<?>, IDescribable<?>>)(d->d.isInstanceOf(SpecimenDescription.class)? d.getDescribedSpecimenOrObservation(): CdmBase.deproxy(d, TaxonDescription.class).getTaxon()))
656
            .andThen(IDescribable::getUuid);
657

    
658
    //a map of the taxon to the attached taxon (source link)
659
    private Map<UUID, List<Taxon>> getSourceTaxonMap(TaxonDescription desc) {
660
        return desc.getSources().stream().filter(s->(s.getCdmSource() instanceof Taxon))
661
            .map(s->CdmBase.deproxy(s.getCdmSource(), Taxon.class))
662
            .collect(Collectors.groupingBy(t->t.getUuid()));
663
    }
664

    
665

    
666
    private DescriptionBase<?> getSingleSpecimenDescriptionSource(
667
            TaxonDescription aggrDescLapsanaCommunisAlpina, UUID specimenUuid) {
668

    
669
        Map<UUID, List<DescriptionBase<?>>> map = aggrDescLapsanaCommunisAlpina.getSources().stream()
670
            .map(src->(DescriptionBase<?>)src.getCdmSource())
671
            .filter(o-> o != null)
672
            .collect(Collectors.groupingBy(fDescToDescribedUuid));
673
        Assert.assertEquals(1, map.get(specimenUuid).size());
674
        return map.get(specimenUuid).get(0);
675

    
676
    }
677

    
678
    private StructuredDescriptionAggregationConfiguration createConfig(DescriptiveDataSet dataSet) {
679
        TaxonNodeFilter filter = TaxonNodeFilter.NewSubtreeInstance(TN_LAPSANA_UUID);
680
        StructuredDescriptionAggregationConfiguration config =
681
                StructuredDescriptionAggregationConfiguration.NewInstance(filter, monitor);
682
        config.setDatasetUuid(dataSet.getUuid());
683
        config.setAggregationMode(AggregationMode.byWithinTaxonAndToParent());
684
        config.setIncludeLiterature(false);
685
        config.setToParentSourceMode(AggregationSourceMode.DESCRIPTION);  //test where written against DESCRIPTION at the beginning so we use this as default for the tests
686

    
687
        return config;
688
    }
689

    
690
    private DescriptiveDataSet createTestData() {
691
        DescriptiveDataSet dataSet = DescriptiveDataSet.NewInstance();
692
        dataSet.setLabel("Test dataset");
693
        datasetService.save(dataSet);
694

    
695
        //L. communis alpina spec1
696
        SpecimenDescription specDescAlpina1 = createSpecimenDescription(dataSet, T_LAPSANA_COMMUNIS_ALPINA_UUID, "alpina specimen1", T_LAPSANA_COMMUNIS_ALPINA_SPEC1_UUID);
697
        addCategoricalData(specDescAlpina1, uuidFeatureLeafPA, State.uuidPresent);
698
        addQuantitativeData(specDescAlpina1, uuidFeatureLeafLength, StatisticalMeasure.EXACT_VALUE(), new BigDecimal("5.0"));
699
        addCategoricalData(specDescAlpina1, uuidFeatureLeafColor, uuidLeafColorBlue);
700

    
701
        //L. communis alpina spec2
702
        SpecimenDescription specDescAlpina2 = createSpecimenDescription(dataSet, T_LAPSANA_COMMUNIS_ALPINA_UUID, "alpina specimen2", T_LAPSANA_COMMUNIS_ALPINA_SPEC2_UUID);
703
        addCategoricalData(specDescAlpina2, uuidFeatureLeafPA, State.uuidPresent);
704
        addQuantitativeData(specDescAlpina2, uuidFeatureLeafLength, StatisticalMeasure.EXACT_VALUE(), new BigDecimal("7.0"));
705
        addCategoricalData(specDescAlpina2, uuidFeatureLeafColor, uuidLeafColorBlue);
706

    
707
        //L. communis alpina spec3
708
        SpecimenDescription specDescAlpina3 = createSpecimenDescription(dataSet, T_LAPSANA_COMMUNIS_ALPINA_UUID, "alpina specimen3", T_LAPSANA_COMMUNIS_ALPINA_SPEC3_UUID);
709
        addCategoricalData(specDescAlpina3, uuidFeatureLeafPA, State.uuidPresent);
710
        addQuantitativeData(specDescAlpina3, uuidFeatureLeafLength, StatisticalMeasure.EXACT_VALUE(), new BigDecimal("8.0"));
711

    
712
        //L. communis adenophora
713
        SpecimenDescription specDescAdenophora = createSpecimenDescription(dataSet, T_LAPSANA_COMMUNIS_ADENOPHORA_UUID, "adenophora specimen", T_LAPSANA_COMMUNIS_ADENOPHORA_SPEC1_UUID);
714
        addCategoricalData(specDescAdenophora, uuidFeatureLeafPA, State.uuidPresent);
715
        addQuantitativeData(specDescAdenophora, uuidFeatureLeafLength, StatisticalMeasure.EXACT_VALUE(), new BigDecimal("10.0"));
716
        addCategoricalData(specDescAdenophora, uuidFeatureLeafColor, uuidLeafColorYellow);
717

    
718
        TaxonNode tnLapsana = taxonNodeService.find(TN_LAPSANA_UUID);
719
        Assert.assertNotNull(tnLapsana);
720
        dataSet.addTaxonSubtree(tnLapsana);
721

    
722
        @SuppressWarnings("unchecked")
723
        TermTree<Feature> descriptiveSystem = termTreeService.find(uuidFeatureTree);
724
        dataSet.setDescriptiveSystem(descriptiveSystem);
725

    
726
        addLiterature(dataSet);
727
        return dataSet;
728
    }
729

    
730
    private TaxonDescription verifyTaxonDescriptions(Taxon taxon, int elementCount){
731
        List<TaxonDescription> aggrNonCloneTaxonDescriptions = taxon.getDescriptions().stream()
732
                .filter(desc->desc.getTypes().contains(DescriptionType.AGGREGATED_STRUC_DESC))
733
                .filter(desc->!desc.getTypes().contains(DescriptionType.CLONE_FOR_SOURCE))
734
                .collect(Collectors.toList());
735

    
736
        Assert.assertEquals(1, aggrNonCloneTaxonDescriptions.size());
737
        TaxonDescription aggrDesc = aggrNonCloneTaxonDescriptions.iterator().next();
738
        Set<DescriptionElementBase> elements = aggrDesc.getElements();
739
        Assert.assertEquals(elementCount, elements.size());
740
        return aggrDesc;
741
    }
742

    
743
    private void verifyNumberTaxonDescriptions(Taxon taxon, long descriptionCount){
744
        long n = taxon.getDescriptions().stream()
745
            .filter(desc->desc.getTypes().contains(DescriptionType.AGGREGATED_STRUC_DESC))
746
            .filter(desc->!desc.getTypes().contains(DescriptionType.CLONE_FOR_SOURCE)).count();
747
        Assert.assertEquals(descriptionCount, n);
748
    }
749

    
750
    private void verifyNumberDescriptionElements(DescriptionBase<?> description, UUID featureUuid, long elementCount){
751
        long n = description.getElements().stream()
752
                .filter(element->element.getFeature().getUuid().equals(featureUuid))
753
                .map(catData->CdmBase.deproxy(catData, CategoricalData.class))
754
                .count();
755
        Assert.assertEquals(elementCount, n);
756
    }
757

    
758
    private void verifyQuantitativeData(UUID featureUuid, BigDecimal sampleSize, BigDecimal min,
759
            BigDecimal max, BigDecimal avg, TaxonDescription aggrDesc) {
760
        List<QuantitativeData> quantitativeDatas = aggrDesc.getElements().stream()
761
                .filter(element->element.getFeature().getUuid().equals(featureUuid))
762
                .map(catData->CdmBase.deproxy(catData, QuantitativeData.class))
763
                .collect(Collectors.toList());
764
        Assert.assertEquals(1, quantitativeDatas.size());
765
        QuantitativeData leafLength = quantitativeDatas.iterator().next();
766
        Assert.assertEquals(sampleSize, leafLength.getSampleSize());
767
        Assert.assertEquals(min, leafLength.getMin());
768
        Assert.assertEquals(max, leafLength.getMax());
769
        Assert.assertEquals(avg, leafLength.getAverage());
770
        Assert.assertEquals(MeasurementUnit.METER(), leafLength.getUnit());
771
        Assert.assertNotNull(leafLength.getUnit());
772
    }
773

    
774

    
775
    private List<StateData> verifyCategoricalData(UUID featureUuid, int stateDataCount, TaxonDescription taxonDescription, boolean withAddedData) {
776
        List<CategoricalData> categoricalDatas = taxonDescription.getElements().stream()
777
                .filter(element->element.getFeature().getUuid().equals(featureUuid))
778
                .map(catData->CdmBase.deproxy(catData, CategoricalData.class))
779
                .collect(Collectors.toList());
780
        int nCD = withAddedData ? 2 : 1;
781
        Assert.assertEquals(nCD, categoricalDatas.size());
782
        CategoricalData categoricalData;
783
        if (withAddedData){
784
            categoricalData = categoricalDatas.stream().filter(cd->cd.getStateData().get(0).getCount() != null ).findFirst().get();
785
        }else{
786
            categoricalData = categoricalDatas.iterator().next(); // categoricalDatas.stream().filter(cd->cd.getStateData().size() != 1).collect(Collectors.toList());
787
        }
788
        List<StateData> stateDatas = categoricalData.getStateData();
789
        Assert.assertEquals(stateDataCount, stateDatas.size());
790
        return stateDatas;
791
    }
792

    
793
    private void verifyState(List<StateData> stateDatas, UUID stateUuid, Integer stateDataCount){
794
        List<StateData> filteredStateDatas = stateDatas.stream()
795
                .filter(stateData->stateData.getState()!=null && stateData.getState().getUuid().equals(stateUuid))
796
                .collect(Collectors.toList());
797
        if(stateDataCount==0){
798
            // non-existence test
799
            Assert.assertEquals(0, filteredStateDatas.size());
800
            return;
801
        }
802
        Assert.assertEquals(1, filteredStateDatas.size());
803
        StateData stateData = filteredStateDatas.iterator().next();
804
        Assert.assertEquals(stateDataCount, stateData.getCount());
805
        Assert.assertEquals(stateUuid, stateData.getState().getUuid());
806
    }
807

    
808
    private void addQuantitativeData(DescriptionBase<?> desc, UUID uuidFeature, StatisticalMeasure type, BigDecimal value) {
809
        Feature feature = (Feature)termService.find(uuidFeature);
810
        QuantitativeData qd = QuantitativeData.NewInstance(feature);
811
        StatisticalMeasurementValue smv = StatisticalMeasurementValue.NewInstance(type, value);
812
        qd.addStatisticalValue(smv);
813
        Assert.assertNotNull(MeasurementUnit.METER());
814
        qd.setUnit(MeasurementUnit.METER());
815
        desc.addElement(qd);
816
    }
817

    
818
    private void addQuantitativeData(DescriptionBase<?> desc, UUID uuidFeature, BigDecimal min, BigDecimal max) {
819
        Feature feature = (Feature)termService.find(uuidFeature);
820
        QuantitativeData qd = QuantitativeData.NewInstance(feature);
821
        StatisticalMeasurementValue smv = StatisticalMeasurementValue.NewInstance(StatisticalMeasure.MIN(), min);
822
        qd.addStatisticalValue(smv);
823
        smv = StatisticalMeasurementValue.NewInstance(StatisticalMeasure.MAX(), max);
824
        qd.addStatisticalValue(smv);
825
        desc.addElement(qd);
826
    }
827

    
828
    private void addCategoricalData(DescriptionBase<?> desc, UUID featureUuid, UUID stateUUID) {
829
        Feature feature = (Feature)termService.find(featureUuid);
830
        State state = (State)termService.find(stateUUID);
831
        CategoricalData cd = CategoricalData.NewInstance(state, feature);
832
        desc.addElement(cd);
833
    }
834

    
835
    private SpecimenDescription createSpecimenDescription(DescriptiveDataSet dataSet, UUID taxonUuid, String specLabel, UUID specimenUuid ) {
836
        Taxon taxon = (Taxon)taxonService.find(taxonUuid);
837
        DerivedUnit specimen = DerivedUnit.NewPreservedSpecimenInstance();
838
        specimen.setTitleCache(specLabel, true);
839
        specimen.setUuid(specimenUuid);
840
        TaxonDescription taxonDescription = taxon.getDescriptions(DescriptionType.INDIVIDUALS_ASSOCIATION).stream()
841
            .findFirst()
842
            .orElseGet(()->{
843
                TaxonDescription td = TaxonDescription.NewInstance(taxon);
844
                td.addType(DescriptionType.INDIVIDUALS_ASSOCIATION);
845
                td.setTitleCache("Specimens used by " + dataSet.getTitleCache() + " for " + getTaxonLabel(taxon), true);
846
                return td;}
847
             );
848
        IndividualsAssociation individualsAssociation = IndividualsAssociation.NewInstance(specimen);
849
        // TODO this has to be discussed; currently the description with the InidividualsAssociation is
850
        // needed in the dataset for performance reasons
851
        taxonDescription.addElement(individualsAssociation);
852
        dataSet.addDescription(taxonDescription);
853
        SpecimenDescription specDesc = SpecimenDescription.NewInstance(specimen);
854

    
855
        dataSet.addDescription(specDesc);
856
        return specDesc;
857
    }
858

    
859
    private String getTaxonLabel(Taxon taxon) {
860
        if (taxon.getName() != null){
861
            return taxon.getName().getTitleCache();
862
        }else{
863
            return taxon.getTitleCache();
864
        }
865
    }
866

    
867
    private Feature createFeature(UUID uuid, String label, boolean isQuantitative) {
868
        Feature feature = Feature.NewInstance("", label, null);
869
        feature.setUuid(uuid);
870
        feature.setSupportsQuantitativeData(isQuantitative);
871
        feature.setSupportsCategoricalData(!isQuantitative);
872
        feature.setSupportsTextData(false);
873
        termService.save(feature);
874
        return feature;
875
    }
876

    
877
    private State createState(String label, UUID uuid) {
878
        State state = State.NewInstance("", label, "");
879
        state.getTitleCache();  //for better debugging
880
        state.setUuid(uuid);
881
        termService.save(state);
882
        return state;
883
    }
884

    
885
    private void createDefaultFeatureTree() {
886
        //feature tree
887
        //leaf p/a
888
        //  leaf length
889
        //  leaf color
890

    
891
        //tree
892
        TermTree<Feature> featureTree = TermTree.NewFeatureInstance();
893
        featureTree.setUuid(uuidFeatureTree);
894

    
895
        //leaf p/a
896
        Feature featureLeafPA = createFeature(uuidFeatureLeafPA, "LeafPA", false);
897
        TermNode<Feature> leafPANode = featureTree.getRoot().addChild(featureLeafPA);
898

    
899
        //leaf length
900
        Feature featureLeafLength = createFeature(uuidFeatureLeafLength, "LeafLength", true);
901
        leafPANode.addChild(featureLeafLength);
902

    
903
        //leaf color
904
        Feature featureLeafColor = createFeature(uuidFeatureLeafColor, "LeafColor", false);
905
        leafPANode.addChild(featureLeafColor);
906
        TermVocabulary<State> stateVoc = TermVocabulary.NewInstance(TermType.State, State.class, "", "Colors", null, null);
907
        State yellow = createState("Yellow", uuidLeafColorYellow);
908
        State blue = createState("Blue", uuidLeafColorBlue);
909
        stateVoc.addTerm(yellow);
910
        stateVoc.addTerm(blue);
911
        featureLeafColor.addSupportedCategoricalEnumeration(stateVoc);
912

    
913
        //additional feature
914
        Feature featureLeafAdd = createFeature(uuidFeatureLeafAdd, "Add", true);
915
        leafPANode.addChild(featureLeafAdd);
916

    
917
        vocabularyService.save(stateVoc);
918
    }
919

    
920
//    @Test
921
    //to create the taxonomic classification available also as .xml file
922
    @Override
923
    public void createTestDataSet() throws FileNotFoundException {
924

    
925
        // --- References --- //
926
        Reference sec = ReferenceFactory.newDatabase();
927
        sec.setTitleCache("Test", true);
928
        Reference nomRef = ReferenceFactory.newBook();
929
        sec.setTitleCache("Sp.Pl.", true);
930

    
931
        referenceService.save(sec);
932
        referenceService.save(nomRef);
933

    
934

    
935
        // --- Taxa --- //
936
        //  Lapsana
937
        //        L. communis
938
        //            L. communis subsp. communis
939
        //            L. communis subsp. adenophora
940
        //            L. communis subsp. alpina
941
        //  Sonchella
942
        //        S. dentata
943
        //        S. stenoma
944
        IBotanicalName n_lapsana = TaxonNameFactory.NewBotanicalInstance(Rank.GENUS());
945
        n_lapsana.setTitleCache("Lapsana", true);
946
        Taxon t_lapsana = Taxon.NewInstance(n_lapsana, sec);
947
        t_lapsana.setUuid(T_LAPSANA_UUID);
948
        taxonService.saveOrUpdate(t_lapsana);
949

    
950
        IBotanicalName n_lapsana_communis = TaxonNameFactory.NewBotanicalInstance(Rank.SPECIES());
951
        n_lapsana_communis.setTitleCache("L. communis", true);
952
        Taxon t_lapsana_communis = Taxon.NewInstance(n_lapsana_communis, sec);
953
        t_lapsana_communis.setUuid(T_LAPSANA_COMMUNIS_UUID);
954
        taxonService.saveOrUpdate(t_lapsana_communis);
955

    
956
        IBotanicalName n_lapsana_communis_communis = TaxonNameFactory.NewBotanicalInstance(Rank.SUBSPECIES());
957
        n_lapsana_communis_communis.setTitleCache("L. communis subsp. communis", true);
958
        Taxon t_lapsana_communis_communis = Taxon.NewInstance(n_lapsana_communis_communis, sec);
959
        t_lapsana_communis_communis.setUuid(T_LAPSANA_COMMUNIS_COMMUNIS_UUID);
960
        taxonService.saveOrUpdate(t_lapsana_communis_communis);
961

    
962
        IBotanicalName n_lapsana_communis_adenophora = TaxonNameFactory.NewBotanicalInstance(Rank.SUBSPECIES());
963
        n_lapsana_communis_adenophora.setTitleCache("L. communis subsp. adenophora", true);
964
        Taxon t_lapsana_communis_adenophora = Taxon.NewInstance(n_lapsana_communis_adenophora, sec);
965
        t_lapsana_communis_adenophora.setUuid(T_LAPSANA_COMMUNIS_ADENOPHORA_UUID);
966
        taxonService.saveOrUpdate(t_lapsana_communis_adenophora);
967

    
968
        IBotanicalName n_lapsana_communis_alpina = TaxonNameFactory.NewBotanicalInstance(Rank.SUBSPECIES());
969
        n_lapsana_communis_alpina.setTitleCache("L. communis subsp. alpina", true);
970
        Taxon t_lapsana_communis_alpina = Taxon.NewInstance(n_lapsana_communis_alpina, sec);
971
        t_lapsana_communis_alpina.setUuid(T_LAPSANA_COMMUNIS_ALPINA_UUID);
972
        taxonService.saveOrUpdate(t_lapsana_communis_alpina);
973

    
974
        // --- Classification --- //
975
        Classification classification = Classification.NewInstance("TestClassification");
976
        classification.setUuid(CLASSIFICATION_UUID);
977
        classificationService.save(classification);
978
        TaxonNode node_lapsana = classification.addChildTaxon(t_lapsana, sec, null);
979
        node_lapsana.setUuid(TN_LAPSANA_UUID);
980
        TaxonNode node_lapsana_communis = node_lapsana.addChildTaxon(t_lapsana_communis, sec, null);
981
        node_lapsana_communis.addChildTaxon(t_lapsana_communis_communis, sec, null);
982
        node_lapsana_communis.addChildTaxon(t_lapsana_communis_adenophora, sec, null);
983
        node_lapsana_communis.addChildTaxon(t_lapsana_communis_alpina, sec, null);
984
        classificationService.saveOrUpdate(classification);
985

    
986
        commitAndStartNewTransaction(null);
987

    
988
        writeDbUnitDataSetFile(new String[] {
989
                "TAXONBASE", "TAXONNAME","CLASSIFICATION",  "TAXONNODE","HOMOTYPICALGROUP",
990
                "REFERENCE", "AGENTBASE",
991
                "DESCRIPTIONELEMENTBASE", "DESCRIPTIONBASE",
992
                "LANGUAGESTRING",
993
                "HIBERNATE_SEQUENCES"
994
         });
995
    }
996

    
997

    
998
}
(3-3/3)