Project

General

Profile

Download (37.9 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.Ignore;
24
import org.junit.Test;
25
import org.unitils.dbunit.annotation.DataSet;
26
import org.unitils.dbunit.annotation.DataSets;
27
import org.unitils.spring.annotation.SpringBeanByType;
28

    
29
import eu.etaxonomy.cdm.api.application.ICdmRepository;
30
import eu.etaxonomy.cdm.api.service.IClassificationService;
31
import eu.etaxonomy.cdm.api.service.IDescriptionService;
32
import eu.etaxonomy.cdm.api.service.IDescriptiveDataSetService;
33
import eu.etaxonomy.cdm.api.service.IOccurrenceService;
34
import eu.etaxonomy.cdm.api.service.IReferenceService;
35
import eu.etaxonomy.cdm.api.service.ITaxonNodeService;
36
import eu.etaxonomy.cdm.api.service.ITaxonService;
37
import eu.etaxonomy.cdm.api.service.ITermService;
38
import eu.etaxonomy.cdm.api.service.ITermTreeService;
39
import eu.etaxonomy.cdm.api.service.IVocabularyService;
40
import eu.etaxonomy.cdm.api.service.UpdateResult;
41
import eu.etaxonomy.cdm.common.JvmLimitsException;
42
import eu.etaxonomy.cdm.common.monitor.DefaultProgressMonitor;
43
import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
44
import eu.etaxonomy.cdm.filter.TaxonNodeFilter;
45
import eu.etaxonomy.cdm.model.common.CdmBase;
46
import eu.etaxonomy.cdm.model.description.CategoricalData;
47
import eu.etaxonomy.cdm.model.description.DescriptionBase;
48
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
49
import eu.etaxonomy.cdm.model.description.DescriptionType;
50
import eu.etaxonomy.cdm.model.description.DescriptiveDataSet;
51
import eu.etaxonomy.cdm.model.description.Feature;
52
import eu.etaxonomy.cdm.model.description.IDescribable;
53
import eu.etaxonomy.cdm.model.description.IndividualsAssociation;
54
import eu.etaxonomy.cdm.model.description.QuantitativeData;
55
import eu.etaxonomy.cdm.model.description.SpecimenDescription;
56
import eu.etaxonomy.cdm.model.description.State;
57
import eu.etaxonomy.cdm.model.description.StateData;
58
import eu.etaxonomy.cdm.model.description.StatisticalMeasure;
59
import eu.etaxonomy.cdm.model.description.StatisticalMeasurementValue;
60
import eu.etaxonomy.cdm.model.description.TaxonDescription;
61
import eu.etaxonomy.cdm.model.name.IBotanicalName;
62
import eu.etaxonomy.cdm.model.name.Rank;
63
import eu.etaxonomy.cdm.model.name.TaxonNameFactory;
64
import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
65
import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
66
import eu.etaxonomy.cdm.model.reference.Reference;
67
import eu.etaxonomy.cdm.model.reference.ReferenceFactory;
68
import eu.etaxonomy.cdm.model.taxon.Classification;
69
import eu.etaxonomy.cdm.model.taxon.Taxon;
70
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
71
import eu.etaxonomy.cdm.model.term.TermNode;
72
import eu.etaxonomy.cdm.model.term.TermTree;
73
import eu.etaxonomy.cdm.model.term.TermType;
74
import eu.etaxonomy.cdm.model.term.TermVocabulary;
75
import eu.etaxonomy.cdm.test.integration.CdmTransactionalIntegrationTest;
76
import eu.etaxonomy.cdm.test.unitils.CleanSweepInsertLoadStrategy;
77

    
78
/**
79
 * @author a.mueller
80
 * @since 21.11.2019
81
 */
82
@Ignore //preliminary ignore as it does not always work (depending on other tests)
83
public class StructuredDescriptionAggregationTest extends CdmTransactionalIntegrationTest {
84

    
85
    @SuppressWarnings("unused")
86
    private static Logger logger = Logger.getLogger(StructuredDescriptionAggregationTest.class);
87

    
88
    private static final UUID T_LAPSANA_UUID = UUID.fromString("f65d47bd-4f49-4ab1-bc4a-bc4551eaa1a8");
89
    private static final UUID TN_LAPSANA_UUID = UUID.fromString("f4d29e9f-6484-4184-af2e-9704e96a17e3");
90

    
91
    private static final UUID T_LAPSANA_COMMUNIS_UUID = UUID.fromString("2a5ceebb-4830-4524-b330-78461bf8cb6b");
92

    
93
    private static final UUID T_LAPSANA_COMMUNIS_COMMUNIS_UUID = UUID.fromString("441a3c40-0c84-11de-8c30-0800200c9a66");
94

    
95
    private static final UUID T_LAPSANA_COMMUNIS_ADENOPHORA_UUID = UUID.fromString("e4acf200-63b6-11dd-ad8b-0800200c9a66");
96

    
97
    private static final UUID T_LAPSANA_COMMUNIS_ALPINA_UUID = UUID.fromString("596b1325-be50-4b0a-9aa2-3ecd610215f2");
98

    
99
    private static final UUID CLASSIFICATION_UUID = UUID.fromString("4b266053-a841-4980-b548-3f21d8d7d712");
100

    
101
    private static final UUID T_LAPSANA_COMMUNIS_ALPINA_SPEC1_UUID = UUID.fromString("0de17403-aeef-4138-a220-9914e7c46f5a");
102
    private static final UUID T_LAPSANA_COMMUNIS_ALPINA_SPEC2_UUID = UUID.fromString("9e680e26-9e12-47a9-807c-743525c9171e");
103
    private static final UUID T_LAPSANA_COMMUNIS_ALPINA_SPEC3_UUID = UUID.fromString("3cdaa12f-7508-4073-b8d7-afca77a6be34");
104
    private static final UUID T_LAPSANA_COMMUNIS_ADENOPHORA_SPEC1_UUID = UUID.fromString("83788037-7a03-4e46-98ca-e8801096b216");
105

    
106

    
107
    private static final UUID uuidFeatureLeafPA = UUID.fromString("c4dfd16f-f2ed-45e0-8f4d-7fe1ae880510");
108
    private static UUID uuidFeatureLeafLength = UUID.fromString("3c19b50b-4a8e-467e-b7d4-89ebc05a33e1");
109
    private static UUID uuidFeatureLeafColor = UUID.fromString("1e8f503c-5aeb-4788-b4f9-84128f7141c7");
110

    
111
    private static UUID uuidLeafColorBlue = UUID.fromString("9b4df19d-f89d-4788-9d71-d1f6f7cae910");
112
    private static UUID uuidLeafColorYellow = UUID.fromString("4cf0881b-0e7b-489a-9fdb-adbe6ae4e0ae");
113

    
114
    private static UUID uuidFeatureTree = UUID.fromString("c8a29a94-2754-4d78-9faa-dff3e1387b2d");
115

    
116

    
117
    @SpringBeanByType
118
    private ICdmRepository repository;
119

    
120
    @SpringBeanByType
121
    private ITermService termService;
122

    
123
    @SpringBeanByType
124
    private ITermTreeService termTreeService;
125

    
126
    @SpringBeanByType
127
    private IVocabularyService vocabularyService;
128

    
129
    @SpringBeanByType
130
    private IDescriptionService descriptionService;
131

    
132
    @SpringBeanByType
133
    private ITaxonService taxonService;
134

    
135
    @SpringBeanByType
136
    private IOccurrenceService occurrenceService;
137

    
138
    @SpringBeanByType
139
    private ITaxonNodeService taxonNodeService;
140

    
141
    @SpringBeanByType
142
    private IClassificationService classificationService;
143

    
144
    @SpringBeanByType
145
    private IReferenceService referenceService;
146

    
147
    @SpringBeanByType
148
    private IDescriptiveDataSetService datasetService;
149

    
150
    private StructuredDescriptionAggregation engine;
151

    
152
    private IProgressMonitor monitor;
153

    
154
    @Before
155
    public void setUp() {
156
        engine = new StructuredDescriptionAggregation();
157
//        engine.setBatchMinFreeHeap(100 * 1024 * 1024);
158
        monitor = DefaultProgressMonitor.NewInstance();
159
    }
160

    
161
    @Test
162
    @DataSets({
163
        @DataSet(loadStrategy=CleanSweepInsertLoadStrategy.class, value="/eu/etaxonomy/cdm/database/ClearDB_with_Terms_DataSet.xml"),
164
        @DataSet(value="/eu/etaxonomy/cdm/database/TermsDataSet-with_auditing_info.xml"),
165
        @DataSet(value="StructuredDescriptionAggregationTest.xml"),
166
    })
167
    public void reaggregationTest() throws JvmLimitsException{
168
        createDefaultFeatureTree();
169
        DescriptiveDataSet dataSet = createTestDataset();
170
        commitAndStartNewTransaction();
171

    
172
        StructuredDescriptionAggregationConfiguration config = createConfig(dataSet);
173

    
174
        // 1st aggregation
175
        UpdateResult result = engine.invoke(config, repository);
176
        testStatusOk(result);
177
        commitAndStartNewTransaction();
178
        testAggregatedDescription(false, false, false);
179

    
180
        addSomeDataToFirstAggregation();
181
        commitAndStartNewTransaction();
182
        testAggregatedDescription(true, false, false);
183

    
184
        // 2nd aggregation
185
        result = engine.invoke(config, repository);
186
        testStatusOk(result);
187
        commitAndStartNewTransaction();
188
        testAggregatedDescription(false, false, false);
189
    }
190

    
191
    private void addSomeDataToFirstAggregation() {
192
        Taxon taxLapsanaCommunisAlpina = (Taxon)taxonService.find(T_LAPSANA_COMMUNIS_ALPINA_UUID);
193
        TaxonDescription taxonDescription = taxLapsanaCommunisAlpina.getDescriptions().stream()
194
                .filter(desc->desc.getTypes().contains(DescriptionType.AGGREGATED_STRUC_DESC))
195
                .filter(desc->!desc.getTypes().contains(DescriptionType.CLONE_FOR_SOURCE))
196
                .findFirst().get();
197

    
198
        addCategoricalData(taxonDescription, uuidFeatureLeafPA, State.uuidPresent);
199
    }
200

    
201
    @Test
202
    @DataSets({
203
        @DataSet(loadStrategy=CleanSweepInsertLoadStrategy.class, value="/eu/etaxonomy/cdm/database/ClearDB_with_Terms_DataSet.xml"),
204
        @DataSet(value="/eu/etaxonomy/cdm/database/TermsDataSet-with_auditing_info.xml"),
205
        @DataSet(value="StructuredDescriptionAggregationTest.xml"),
206
    })
207
    public void deleteTest() throws JvmLimitsException{
208
        createDefaultFeatureTree();
209
        DescriptiveDataSet dataSet = createTestDataset();
210
        commitAndStartNewTransaction();
211

    
212
        StructuredDescriptionAggregationConfiguration config = createConfig(dataSet);
213

    
214
        // 1st aggregation
215
        UpdateResult result = engine.invoke(config, repository);
216
        testStatusOk(result);
217
        commitAndStartNewTransaction();
218
        testAggregatedDescription(false, false, false);
219

    
220
        removeSomeDataFromFirstAggregation();
221
        commitAndStartNewTransaction();
222
        Assert.assertEquals("Should have 3 specimen desc, 1 literature desc, 2 individual association holder, "
223
                + "4 aggregated descriptions, 4 cloned specimen descriptions (still not deleted), (3 cloned aggregated descriptions?) = 17",
224
                17, descriptionService.count(null));
225

    
226
        // 2nd aggregation
227
        result = engine.invoke(config, repository);
228
        testStatusOk(result);
229
        commitAndStartNewTransaction();
230
        testAggregatedDescription(false, false, true);
231
    }
232

    
233
    private void removeSomeDataFromFirstAggregation() {
234
        SpecimenOrObservationBase<?> spec3 = occurrenceService.find(T_LAPSANA_COMMUNIS_ALPINA_SPEC3_UUID);
235
        DescriptionBase<?> spec3Desc = spec3.getDescriptions().stream()
236
                .filter(desc->!desc.getTypes().contains(DescriptionType.CLONE_FOR_SOURCE))
237
                .findFirst().get();
238

    
239
        spec3.removeDescription(spec3Desc);
240
        descriptionService.delete(spec3Desc);
241
    }
242

    
243
    @Test
244
    @DataSets({
245
        @DataSet(loadStrategy=CleanSweepInsertLoadStrategy.class, value="/eu/etaxonomy/cdm/database/ClearDB_with_Terms_DataSet.xml"),
246
        @DataSet(value="/eu/etaxonomy/cdm/database/TermsDataSet-with_auditing_info.xml"),
247
        @DataSet(value="StructuredDescriptionAggregationTest.xml"),
248
    })
249
    public void incompleteQuantitativeDataTest() throws JvmLimitsException{
250
        createDefaultFeatureTree();
251
        DescriptiveDataSet dataSet = DescriptiveDataSet.NewInstance();
252
        datasetService.save(dataSet);
253

    
254
        SpecimenDescription specDescAlpina1 = createSpecimenDescription(dataSet, T_LAPSANA_COMMUNIS_ALPINA_UUID, "alpina specimen1", T_LAPSANA_COMMUNIS_ALPINA_SPEC1_UUID);
255
        addQuantitativeData(specDescAlpina1, uuidFeatureLeafLength, StatisticalMeasure.MIN(), new BigDecimal("5.0"));
256

    
257
        SpecimenDescription specDescAlpina2 = createSpecimenDescription(dataSet, T_LAPSANA_COMMUNIS_ALPINA_UUID, "alpina specimen2", T_LAPSANA_COMMUNIS_ALPINA_SPEC2_UUID);
258
        addQuantitativeData(specDescAlpina2, uuidFeatureLeafLength, StatisticalMeasure.MAX(), new BigDecimal("7.0"));
259

    
260
        TaxonNode tnLapsana = taxonNodeService.find(TN_LAPSANA_UUID);
261
        Assert.assertNotNull(tnLapsana);
262
        dataSet.addTaxonSubtree(tnLapsana);
263

    
264
        @SuppressWarnings("unchecked")
265
        TermTree<Feature> descriptiveSystem = termTreeService.find(uuidFeatureTree);
266
        dataSet.setDescriptiveSystem(descriptiveSystem);
267
        commitAndStartNewTransaction();
268

    
269
        StructuredDescriptionAggregationConfiguration config = createConfig(dataSet);
270

    
271
        UpdateResult result = engine.invoke(config, repository);
272
        testStatusOk(result);
273

    
274
        Taxon taxLapsanaCommunisAlpina = (Taxon)taxonService.find(T_LAPSANA_COMMUNIS_ALPINA_UUID);
275
        TaxonDescription aggrDescLapsanaCommunisAlpina = testTaxonDescriptions(taxLapsanaCommunisAlpina, 1);
276
        testQuantitativeData(uuidFeatureLeafLength, null, new BigDecimal("0.0"), new BigDecimal("7.0"), null, aggrDescLapsanaCommunisAlpina);
277
    }
278

    
279
    @Test
280
    @DataSets({
281
        @DataSet(loadStrategy=CleanSweepInsertLoadStrategy.class, value="/eu/etaxonomy/cdm/database/ClearDB_with_Terms_DataSet.xml"),
282
        @DataSet(value="/eu/etaxonomy/cdm/database/TermsDataSet-with_auditing_info.xml"),
283
        @DataSet(value="StructuredDescriptionAggregationTest.xml"),
284
    })
285
    public void incompleteCategoricalDataTest() throws JvmLimitsException{
286
        createDefaultFeatureTree();
287
        DescriptiveDataSet dataSet = DescriptiveDataSet.NewInstance();
288
        datasetService.save(dataSet);
289

    
290
        SpecimenDescription specDescAlpina1 = createSpecimenDescription(dataSet, T_LAPSANA_COMMUNIS_ALPINA_UUID, "alpina specimen1", T_LAPSANA_COMMUNIS_ALPINA_SPEC1_UUID);
291
        addCategoricalData(specDescAlpina1, uuidFeatureLeafColor, null);
292

    
293
        TaxonNode tnLapsana = taxonNodeService.find(TN_LAPSANA_UUID);
294
        Assert.assertNotNull(tnLapsana);
295
        dataSet.addTaxonSubtree(tnLapsana);
296

    
297
        @SuppressWarnings("unchecked")
298
        TermTree<Feature> descriptiveSystem = termTreeService.find(uuidFeatureTree);
299
        dataSet.setDescriptiveSystem(descriptiveSystem);
300
        commitAndStartNewTransaction();
301

    
302
        StructuredDescriptionAggregationConfiguration config = createConfig(dataSet);
303

    
304
        UpdateResult result = engine.invoke(config, repository);
305
        testStatusOk(result);
306

    
307
        Taxon taxLapsanaCommunisAlpina = (Taxon)taxonService.find(T_LAPSANA_COMMUNIS_ALPINA_UUID);
308
        TaxonDescription aggrDescLapsanaCommunisAlpina = testTaxonDescriptions(taxLapsanaCommunisAlpina, 1);
309
        List<StateData> sdAlpinaLeafColor = testCategoricalData(uuidFeatureLeafColor, 1, aggrDescLapsanaCommunisAlpina, false);
310
        testState(sdAlpinaLeafColor, uuidLeafColorBlue, 0);
311
        testState(sdAlpinaLeafColor, uuidLeafColorYellow, 0);
312
    }
313

    
314
    @Test
315
    @DataSets({
316
        @DataSet(loadStrategy=CleanSweepInsertLoadStrategy.class, value="/eu/etaxonomy/cdm/database/ClearDB_with_Terms_DataSet.xml"),
317
        @DataSet(value="/eu/etaxonomy/cdm/database/TermsDataSet-with_auditing_info.xml"),
318
        @DataSet(value="StructuredDescriptionAggregationTest.xml"),
319
    })
320
    public void aggregationTest() throws JvmLimitsException{
321
        createDefaultFeatureTree();
322
        DescriptiveDataSet dataSet = createTestDataset();
323
        commitAndStartNewTransaction();
324

    
325
        StructuredDescriptionAggregationConfiguration config = createConfig(dataSet);
326

    
327
        UpdateResult result = engine.invoke(config, repository);
328
        commitAndStartNewTransaction();
329
        testStatusOk(result);
330
        testAggregatedDescription(false, false, false);
331

    
332
        config.setIncludeLiterature(true);
333

    
334
        result = engine.invoke(config, repository);
335
        commitAndStartNewTransaction();
336
        testStatusOk(result);
337
        testAggregatedDescription(false, true, false);  //with literature
338
    }
339

    
340
    private void testStatusOk(UpdateResult result) {
341
        if (result.getStatus() != UpdateResult.Status.OK){
342
            Assert.fail("Aggregation should have status OK but was " + result.toString());
343
            for (Exception ex : result.getExceptions()){
344
                ex.printStackTrace();
345
            }
346
        }
347
    }
348

    
349
    private void addLiterature(DescriptiveDataSet dataSet) {
350

    
351
        //literature description
352
        Taxon taxon = (Taxon)taxonService.find(T_LAPSANA_COMMUNIS_ALPINA_UUID);
353
        TaxonDescription literatureDescription = TaxonDescription.NewInstance(taxon);
354
        literatureDescription.addType(DescriptionType.SECONDARY_DATA);
355
        addQuantitativeData(literatureDescription, uuidFeatureLeafLength, new BigDecimal("4.5"), new BigDecimal("6.5"));
356
        addCategoricalData(literatureDescription, uuidFeatureLeafColor, uuidLeafColorBlue);
357
        dataSet.addDescription(literatureDescription);
358
    }
359

    
360
    private void testAggregatedDescription(boolean withAddedData, boolean withLiterature, boolean withRemovedData) {
361

    
362
        int intDel = withRemovedData? -1 : 0;
363
        int intLit = withLiterature? 1 : 0;
364

    
365
        //L. communis alpina
366
        Taxon taxLapsanaCommunisAlpina = (Taxon)taxonService.find(T_LAPSANA_COMMUNIS_ALPINA_UUID);
367
        int nElement = withAddedData ? 4 : 3;
368
        TaxonDescription aggrDescLapsanaCommunisAlpina = testTaxonDescriptions(taxLapsanaCommunisAlpina, nElement);
369

    
370
        List<StateData> stateData = testCategoricalData(uuidFeatureLeafPA, 1, aggrDescLapsanaCommunisAlpina, withAddedData);
371
        testState(stateData, State.uuidPresent, 3+intDel);
372
        List<StateData> sdAlpinaLeafColor = testCategoricalData(uuidFeatureLeafColor, 1, aggrDescLapsanaCommunisAlpina, false);
373
        int litLeafColorBlue = withLiterature? 1: 0;
374
        testState(sdAlpinaLeafColor, uuidLeafColorBlue, 2+litLeafColorBlue);
375
        testState(sdAlpinaLeafColor, uuidLeafColorYellow, 0);
376
        BigDecimal count = withLiterature? null : withRemovedData ? new BigDecimal("2"): new BigDecimal("3");
377
        BigDecimal avg = withLiterature? null : withRemovedData ? new BigDecimal("6"): new BigDecimal("6.666667");
378
        BigDecimal min = withLiterature? new BigDecimal("4.5") : new BigDecimal("5.0");
379
        BigDecimal max = withRemovedData ? new BigDecimal("7.0") : new BigDecimal("8.0");
380
        testQuantitativeData(uuidFeatureLeafLength, count, min, max, avg, aggrDescLapsanaCommunisAlpina);
381
        //... sources
382
        Assert.assertEquals(3+intLit+intDel, aggrDescLapsanaCommunisAlpina.getSources().size());
383
        SpecimenOrObservationBase<?> specLcommunisAlpina1 = CdmBase.deproxy(occurrenceService.find(T_LAPSANA_COMMUNIS_ALPINA_SPEC1_UUID));
384
        Assert.assertEquals("Spec1 must have 2 descriptions now. The primary one and the cloned.", 2, specLcommunisAlpina1.getSpecimenDescriptions().size());
385
        Assert.assertEquals(1, specLcommunisAlpina1.getSpecimenDescriptions().stream().filter(d->d.isCloneForSource()).count());
386
        DescriptionBase<?> clonedDesc = specLcommunisAlpina1.getDescriptions().stream().filter(d->d.isCloneForSource()).findFirst().get();
387
        DescriptionBase<?> sourceDescription = getSingleSpecimenDescriptionSource(aggrDescLapsanaCommunisAlpina, T_LAPSANA_COMMUNIS_ALPINA_SPEC1_UUID);
388
        Assert.assertEquals(clonedDesc, sourceDescription);
389

    
390
        //L. communis adenophora
391
        Taxon taxLapsanaCommunisAdenophora = (Taxon)taxonService.find(T_LAPSANA_COMMUNIS_ADENOPHORA_UUID);
392
        TaxonDescription aggrDescLapsanaCommunisAdenophora = testTaxonDescriptions(taxLapsanaCommunisAdenophora, 3);
393
        testState(testCategoricalData(uuidFeatureLeafPA, 1, aggrDescLapsanaCommunisAdenophora, false), State.uuidPresent, 1);
394
        List<StateData> sdAdenophoraLeafColor = testCategoricalData(uuidFeatureLeafColor, 1, aggrDescLapsanaCommunisAdenophora, false);
395
        testState(sdAdenophoraLeafColor, uuidLeafColorBlue, 0);
396
        testState(sdAdenophoraLeafColor, uuidLeafColorYellow, 1);
397
        testQuantitativeData(uuidFeatureLeafLength, new BigDecimal("1"), new BigDecimal("10.0"),
398
                new BigDecimal("10.0"), new BigDecimal("10.0"), aggrDescLapsanaCommunisAdenophora);
399

    
400
        //L. communis
401
        Taxon taxLapsanaCommunis = (Taxon)taxonService.find(T_LAPSANA_COMMUNIS_UUID);
402
        TaxonDescription aggrDescLapsanaCommunis = testTaxonDescriptions(taxLapsanaCommunis, 3);
403
        testState(testCategoricalData(uuidFeatureLeafPA, 1, aggrDescLapsanaCommunis, false), State.uuidPresent, 4+intDel);
404
        List<StateData> sdCommunisLeafColor = testCategoricalData(uuidFeatureLeafColor, 2, aggrDescLapsanaCommunis, false);
405
        testState(sdCommunisLeafColor, uuidLeafColorBlue, 2 + intLit);
406
        testState(sdCommunisLeafColor, uuidLeafColorYellow, 1);
407
        count = withLiterature? null : withRemovedData ? new BigDecimal("3") : new BigDecimal("4");
408
        avg = withLiterature? null : withRemovedData ? new BigDecimal("7.333333") : new BigDecimal("7.5");
409
        testQuantitativeData(uuidFeatureLeafLength, count, min,
410
                new BigDecimal("10.0"), avg, aggrDescLapsanaCommunis);
411
        //... sources
412
        Assert.assertEquals(2, aggrDescLapsanaCommunis.getSources().size());
413
        Map<UUID, List<TaxonDescription>> taxonDescriptionMap = getSourceTaxonDescriptionMap(aggrDescLapsanaCommunis);
414
        Assert.assertEquals(2, taxonDescriptionMap.size());
415
        Assert.assertEquals(1, taxonDescriptionMap.get(T_LAPSANA_COMMUNIS_ALPINA_UUID).size());
416
        Assert.assertEquals(1, taxonDescriptionMap.get(T_LAPSANA_COMMUNIS_ADENOPHORA_UUID).size());
417
        Assert.assertNotEquals(aggrDescLapsanaCommunisAlpina, taxonDescriptionMap.get(T_LAPSANA_COMMUNIS_ALPINA_UUID).get(0));
418

    
419
        //Lapsana
420
        Taxon taxLapsana = (Taxon)taxonService.find(T_LAPSANA_UUID);
421
        TaxonDescription aggrDescLapsana = testTaxonDescriptions(taxLapsana, 3);
422
        testState(testCategoricalData(uuidFeatureLeafPA, 1, aggrDescLapsana, false), State.uuidPresent, 4+intDel);
423
        List<StateData> sdLapsanLeafColor = testCategoricalData(uuidFeatureLeafColor, 2, aggrDescLapsana, false);
424
        testState(sdLapsanLeafColor, uuidLeafColorBlue, 2 + intLit);
425
        testState(sdLapsanLeafColor, uuidLeafColorYellow, 1);
426
        testQuantitativeData(uuidFeatureLeafLength, count, min,
427
                new BigDecimal("10.0"), avg, aggrDescLapsana);
428
        //... sources
429
        Assert.assertEquals(1, aggrDescLapsana.getSources().size());
430
        taxonDescriptionMap = getSourceTaxonDescriptionMap(aggrDescLapsana);
431
        Assert.assertEquals(1, taxonDescriptionMap.size());
432
        Assert.assertEquals(1, taxonDescriptionMap.get(T_LAPSANA_COMMUNIS_UUID).size());
433
        Assert.assertNotEquals(aggrDescLapsanaCommunis, taxonDescriptionMap.get(T_LAPSANA_COMMUNIS_UUID).get(0));
434

    
435
        //total description count
436
        Assert.assertEquals("Should have 4 specimen desc, 1 literature desc, 2 individual association holder, "
437
                + "4 aggregated descriptions, 4 cloned specimen descriptions, (3/4 cloned aggregated descriptions?) = 18/19",
438
                18+intLit+(intDel*2), descriptionService.count(null));
439

    
440
    }
441

    
442
    private Map<UUID, List<TaxonDescription>> getSourceTaxonDescriptionMap(TaxonDescription desc) {
443
        return desc.getSources().stream().filter(s->(s.getCdmSource() instanceof TaxonDescription))
444
            .map(s->CdmBase.deproxy(s.getCdmSource(), TaxonDescription.class))
445
            .collect(Collectors.groupingBy(fDescToDescribedUuid));
446
    }
447

    
448
    private static Function<DescriptionBase<?>, UUID> fDescToDescribedUuid =
449
            ((Function<DescriptionBase<?>, IDescribable<?>>)(d->d.isInstanceOf(SpecimenDescription.class)? d.getDescribedSpecimenOrObservation(): CdmBase.deproxy(d, TaxonDescription.class).getTaxon()))
450
            .andThen(IDescribable::getUuid);
451

    
452
    private DescriptionBase<?> getSingleSpecimenDescriptionSource(
453
            TaxonDescription aggrDescLapsanaCommunisAlpina, UUID specimenUuid) {
454

    
455
        Map<UUID, List<DescriptionBase<?>>> map = aggrDescLapsanaCommunisAlpina.getSources().stream()
456
            .map(src->(DescriptionBase<?>)src.getCdmSource())
457
            .filter(o-> o != null)
458
            .collect(Collectors.groupingBy(fDescToDescribedUuid));
459
        Assert.assertEquals(1, map.get(specimenUuid).size());
460
        return map.get(specimenUuid).get(0);
461

    
462
    }
463

    
464
    private StructuredDescriptionAggregationConfiguration createConfig(DescriptiveDataSet dataSet) {
465
        TaxonNodeFilter filter = TaxonNodeFilter.NewSubtreeInstance(TN_LAPSANA_UUID);
466
        StructuredDescriptionAggregationConfiguration config =
467
                StructuredDescriptionAggregationConfiguration.NewInstance(filter, monitor);
468
        config.setDatasetUuid(dataSet.getUuid());
469
        config.setAggregationMode(AggregationMode.byWithinTaxonAndToParent());
470
        config.setIncludeLiterature(false);
471
        return config;
472
    }
473

    
474
    private DescriptiveDataSet createTestDataset() {
475
        DescriptiveDataSet dataSet = DescriptiveDataSet.NewInstance();
476
        dataSet.setLabel("Test dataset");
477
        datasetService.save(dataSet);
478

    
479
        //L. communis alpina spec1
480
        SpecimenDescription specDescAlpina1 = createSpecimenDescription(dataSet, T_LAPSANA_COMMUNIS_ALPINA_UUID, "alpina specimen1", T_LAPSANA_COMMUNIS_ALPINA_SPEC1_UUID);
481
        addCategoricalData(specDescAlpina1, uuidFeatureLeafPA, State.uuidPresent);
482
        addQuantitativeData(specDescAlpina1, uuidFeatureLeafLength, StatisticalMeasure.EXACT_VALUE(), new BigDecimal("5.0"));
483
        addCategoricalData(specDescAlpina1, uuidFeatureLeafColor, uuidLeafColorBlue);
484

    
485
        //L. communis alpina spec2
486
        SpecimenDescription specDescAlpina2 = createSpecimenDescription(dataSet, T_LAPSANA_COMMUNIS_ALPINA_UUID, "alpina specimen2", T_LAPSANA_COMMUNIS_ALPINA_SPEC2_UUID);
487
        addCategoricalData(specDescAlpina2, uuidFeatureLeafPA, State.uuidPresent);
488
        addQuantitativeData(specDescAlpina2, uuidFeatureLeafLength, StatisticalMeasure.EXACT_VALUE(), new BigDecimal("7.0"));
489
        addCategoricalData(specDescAlpina2, uuidFeatureLeafColor, uuidLeafColorBlue);
490

    
491
        //L. communis alpina spec3
492
        SpecimenDescription specDescAlpina3 = createSpecimenDescription(dataSet, T_LAPSANA_COMMUNIS_ALPINA_UUID, "alpina specimen3", T_LAPSANA_COMMUNIS_ALPINA_SPEC3_UUID);
493
        addCategoricalData(specDescAlpina3, uuidFeatureLeafPA, State.uuidPresent);
494
        addQuantitativeData(specDescAlpina3, uuidFeatureLeafLength, StatisticalMeasure.EXACT_VALUE(), new BigDecimal("8.0"));
495

    
496
        //L. communis adenophora
497
        SpecimenDescription specDescAdenophora = createSpecimenDescription(dataSet, T_LAPSANA_COMMUNIS_ADENOPHORA_UUID, "adenophora specimen", T_LAPSANA_COMMUNIS_ADENOPHORA_SPEC1_UUID);
498
        addCategoricalData(specDescAdenophora, uuidFeatureLeafPA, State.uuidPresent);
499
        addQuantitativeData(specDescAdenophora, uuidFeatureLeafLength, StatisticalMeasure.EXACT_VALUE(), new BigDecimal("10.0"));
500
        addCategoricalData(specDescAdenophora, uuidFeatureLeafColor, uuidLeafColorYellow);
501

    
502
        TaxonNode tnLapsana = taxonNodeService.find(TN_LAPSANA_UUID);
503
        Assert.assertNotNull(tnLapsana);
504
        dataSet.addTaxonSubtree(tnLapsana);
505

    
506
        @SuppressWarnings("unchecked")
507
        TermTree<Feature> descriptiveSystem = termTreeService.find(uuidFeatureTree);
508
        dataSet.setDescriptiveSystem(descriptiveSystem);
509

    
510
        addLiterature(dataSet);
511
        return dataSet;
512
    }
513

    
514
    private TaxonDescription testTaxonDescriptions(Taxon taxon, int elementSize){
515
        List<TaxonDescription> taxonDescriptions = taxon.getDescriptions().stream()
516
                .filter(desc->desc.getTypes().contains(DescriptionType.AGGREGATED_STRUC_DESC))
517
                .filter(desc->!desc.getTypes().contains(DescriptionType.CLONE_FOR_SOURCE))
518
                .collect(Collectors.toList());
519

    
520
        Assert.assertEquals(1, taxonDescriptions.size());
521
        TaxonDescription aggrDesc = taxonDescriptions.iterator().next();
522
        Set<DescriptionElementBase> elements = aggrDesc.getElements();
523
        Assert.assertEquals(elementSize, elements.size());
524
        return aggrDesc;
525
    }
526

    
527
    private void testQuantitativeData(UUID featureUuid, BigDecimal sampleSize, BigDecimal min,
528
            BigDecimal max, BigDecimal avg, TaxonDescription aggrDesc) {
529
        List<QuantitativeData> quantitativeDatas = aggrDesc.getElements().stream()
530
                .filter(element->element.getFeature().getUuid().equals(featureUuid))
531
                .map(catData->CdmBase.deproxy(catData, QuantitativeData.class))
532
                .collect(Collectors.toList());
533
        Assert.assertEquals(1, quantitativeDatas.size());
534
        QuantitativeData leafLength = quantitativeDatas.iterator().next();
535
        Assert.assertEquals(sampleSize, leafLength.getSampleSize());
536
        Assert.assertEquals(min, leafLength.getMin());
537
        Assert.assertEquals(max, leafLength.getMax());
538
        Assert.assertEquals(avg, leafLength.getAverage());
539
    }
540

    
541

    
542
    private List<StateData> testCategoricalData(UUID featureUuid, int stateDataCount, TaxonDescription taxonDescription, boolean withAddedData) {
543
        List<CategoricalData> categoricalDatas = taxonDescription.getElements().stream()
544
                .filter(element->element.getFeature().getUuid().equals(featureUuid))
545
                .map(catData->CdmBase.deproxy(catData, CategoricalData.class))
546
                .collect(Collectors.toList());
547
        int nCD = withAddedData ? 2 : 1;
548
        Assert.assertEquals(nCD, categoricalDatas.size());
549
        CategoricalData categoricalData;
550
        if (withAddedData){
551
            categoricalData = categoricalDatas.stream().filter(cd->cd.getStateData().get(0).getCount() != null ).findFirst().get();
552
        }else{
553
            categoricalData = categoricalDatas.iterator().next(); // categoricalDatas.stream().filter(cd->cd.getStateData().size() != 1).collect(Collectors.toList());
554
        }
555
        List<StateData> stateDatas = categoricalData.getStateData();
556
        Assert.assertEquals(stateDataCount, stateDatas.size());
557
        return stateDatas;
558
    }
559

    
560
    private void testState(List<StateData> stateDatas, UUID stateUuid, Integer stateDataCount){
561
        List<StateData> filteredStateDatas = stateDatas.stream()
562
                .filter(stateData->stateData.getState()!=null && stateData.getState().getUuid().equals(stateUuid))
563
                .collect(Collectors.toList());
564
        if(stateDataCount==0){
565
            // non-existence test
566
            Assert.assertEquals(0, filteredStateDatas.size());
567
            return;
568
        }
569
        Assert.assertEquals(1, filteredStateDatas.size());
570
        StateData stateData = filteredStateDatas.iterator().next();
571
        Assert.assertEquals(stateDataCount, stateData.getCount());
572
        Assert.assertEquals(stateUuid, stateData.getState().getUuid());
573
    }
574

    
575
    private void addQuantitativeData(DescriptionBase<?> desc, UUID uuidFeature, StatisticalMeasure type, BigDecimal value) {
576
        Feature feature = (Feature)termService.find(uuidFeature);
577
        QuantitativeData qd = QuantitativeData.NewInstance(feature);
578
        StatisticalMeasurementValue smv = StatisticalMeasurementValue.NewInstance(type, value);
579
        qd.addStatisticalValue(smv);
580
        desc.addElement(qd);
581
    }
582

    
583
    private void addQuantitativeData(DescriptionBase<?> desc, UUID uuidFeature, BigDecimal min, BigDecimal max) {
584
        Feature feature = (Feature)termService.find(uuidFeature);
585
        QuantitativeData qd = QuantitativeData.NewInstance(feature);
586
        StatisticalMeasurementValue smv = StatisticalMeasurementValue.NewInstance(StatisticalMeasure.MIN(), min);
587
        qd.addStatisticalValue(smv);
588
        smv = StatisticalMeasurementValue.NewInstance(StatisticalMeasure.MAX(), max);
589
        qd.addStatisticalValue(smv);
590
        desc.addElement(qd);
591
    }
592

    
593
    private void addCategoricalData(DescriptionBase<?> desc, UUID featureUuid, UUID stateUUID) {
594
        Feature feature = (Feature)termService.find(featureUuid);
595
        State state = (State)termService.find(stateUUID);
596
        CategoricalData cd = CategoricalData.NewInstance(state, feature);
597
        desc.addElement(cd);
598
    }
599

    
600
    private SpecimenDescription createSpecimenDescription(DescriptiveDataSet dataSet, UUID taxonUuid, String specLabel, UUID specimenUuid ) {
601
        Taxon taxon = (Taxon)taxonService.find(taxonUuid);
602
        DerivedUnit specimen = DerivedUnit.NewPreservedSpecimenInstance();
603
        specimen.setTitleCache(specLabel, true);
604
        specimen.setUuid(specimenUuid);
605
        TaxonDescription taxonDescription = taxon.getDescriptions(DescriptionType.INDIVIDUALS_ASSOCIATION).stream()
606
            .findFirst()
607
            .orElseGet(()->{
608
                TaxonDescription td = TaxonDescription.NewInstance(taxon);
609
                td.addType(DescriptionType.INDIVIDUALS_ASSOCIATION);
610
                td.setTitleCache("Specimens used by " + dataSet.getTitleCache() + " for " + getTaxonLabel(taxon), true);
611
                return td;}
612
             );
613
        IndividualsAssociation individualsAssociation = IndividualsAssociation.NewInstance(specimen);
614
        // TODO this has to be discussed; currently the description with the InidividualsAssociation is
615
        // needed in the dataset for performance reasons
616
        taxonDescription.addElement(individualsAssociation);
617
        dataSet.addDescription(taxonDescription);
618
        SpecimenDescription specDesc = SpecimenDescription.NewInstance(specimen);
619

    
620
        dataSet.addDescription(specDesc);
621
        return specDesc;
622
    }
623

    
624
    private String getTaxonLabel(Taxon taxon) {
625
        if (taxon.getName() != null){
626
            return taxon.getName().getTitleCache();
627
        }else{
628
            return taxon.getTitleCache();
629
        }
630
    }
631

    
632
    private Feature createFeature(UUID uuid, String label, boolean isQuantitative) {
633
        Feature feature = Feature.NewInstance("", label, null);
634
        feature.setUuid(uuid);
635
        feature.setSupportsQuantitativeData(isQuantitative);
636
        feature.setSupportsCategoricalData(!isQuantitative);
637
        feature.setSupportsTextData(false);
638
        termService.save(feature);
639
        return feature;
640
    }
641

    
642
    private State createState(String label, UUID uuid) {
643
        State state = State.NewInstance("", label, "");
644
        state.getTitleCache();  //for better debugging
645
        state.setUuid(uuid);
646
        termService.save(state);
647
        return state;
648
    }
649

    
650
    private void createDefaultFeatureTree() {
651
        //feature tree
652
        //leaf p/a
653
        //  leaf length
654
        //  leaf color
655
        TermTree<Feature> featureTree = TermTree.NewFeatureInstance();
656
        featureTree.setUuid(uuidFeatureTree);
657
        Feature featureLeafPA = createFeature(uuidFeatureLeafPA, "LeafPA", false);
658
        TermNode<Feature> leafPANode = featureTree.getRoot().addChild(featureLeafPA);
659
        Feature featureLeafLength = createFeature(uuidFeatureLeafLength, "LeafLength", true);
660
        leafPANode.addChild(featureLeafLength);
661
        Feature featureLeafColor = createFeature(uuidFeatureLeafColor, "LeafColor", false);
662
        leafPANode.addChild(featureLeafColor);
663
        State yellow = createState("Yellow", uuidLeafColorYellow);
664
        State blue = createState("Blue", uuidLeafColorBlue);
665
        TermVocabulary<State> stateVoc = TermVocabulary.NewInstance(TermType.State, State.class, "", "Colors", null, null);
666
        stateVoc.addTerm(yellow);
667
        stateVoc.addTerm(blue);
668
        featureLeafColor.addSupportedCategoricalEnumeration(stateVoc);
669
        vocabularyService.save(stateVoc);
670
    }
671

    
672
//    @Test
673
    @Override
674
    public void createTestDataSet() throws FileNotFoundException {
675

    
676
        // --- References --- //
677
        Reference sec = ReferenceFactory.newDatabase();
678
        sec.setTitleCache("Test", true);
679
        Reference nomRef = ReferenceFactory.newBook();
680
        sec.setTitleCache("Sp.Pl.", true);
681

    
682
        referenceService.save(sec);
683
        referenceService.save(nomRef);
684

    
685

    
686
        // --- Taxa --- //
687
        //  Lapsana
688
        //        L. communis
689
        //            L. communis subsp. communis
690
        //            L. communis subsp. adenophora
691
        //            L. communis subsp. alpina
692
        //  Sonchella
693
        //        S. dentata
694
        //        S. stenoma
695
        IBotanicalName n_lapsana = TaxonNameFactory.NewBotanicalInstance(Rank.GENUS());
696
        n_lapsana.setTitleCache("Lapsana", true);
697
        Taxon t_lapsana = Taxon.NewInstance(n_lapsana, sec);
698
        t_lapsana.setUuid(T_LAPSANA_UUID);
699
        taxonService.saveOrUpdate(t_lapsana);
700

    
701
        IBotanicalName n_lapsana_communis = TaxonNameFactory.NewBotanicalInstance(Rank.SPECIES());
702
        n_lapsana_communis.setTitleCache("L. communis", true);
703
        Taxon t_lapsana_communis = Taxon.NewInstance(n_lapsana_communis, sec);
704
        t_lapsana_communis.setUuid(T_LAPSANA_COMMUNIS_UUID);
705
        taxonService.saveOrUpdate(t_lapsana_communis);
706

    
707
        IBotanicalName n_lapsana_communis_communis = TaxonNameFactory.NewBotanicalInstance(Rank.SUBSPECIES());
708
        n_lapsana_communis_communis.setTitleCache("L. communis subsp. communis", true);
709
        Taxon t_lapsana_communis_communis = Taxon.NewInstance(n_lapsana_communis_communis, sec);
710
        t_lapsana_communis_communis.setUuid(T_LAPSANA_COMMUNIS_COMMUNIS_UUID);
711
        taxonService.saveOrUpdate(t_lapsana_communis_communis);
712

    
713
        IBotanicalName n_lapsana_communis_adenophora = TaxonNameFactory.NewBotanicalInstance(Rank.SUBSPECIES());
714
        n_lapsana_communis_adenophora.setTitleCache("L. communis subsp. adenophora", true);
715
        Taxon t_lapsana_communis_adenophora = Taxon.NewInstance(n_lapsana_communis_adenophora, sec);
716
        t_lapsana_communis_adenophora.setUuid(T_LAPSANA_COMMUNIS_ADENOPHORA_UUID);
717
        taxonService.saveOrUpdate(t_lapsana_communis_adenophora);
718

    
719
        IBotanicalName n_lapsana_communis_alpina = TaxonNameFactory.NewBotanicalInstance(Rank.SUBSPECIES());
720
        n_lapsana_communis_alpina.setTitleCache("L. communis subsp. alpina", true);
721
        Taxon t_lapsana_communis_alpina = Taxon.NewInstance(n_lapsana_communis_alpina, sec);
722
        t_lapsana_communis_alpina.setUuid(T_LAPSANA_COMMUNIS_ALPINA_UUID);
723
        taxonService.saveOrUpdate(t_lapsana_communis_alpina);
724

    
725
        // --- Classification --- //
726
        Classification classification = Classification.NewInstance("TestClassification");
727
        classification.setUuid(CLASSIFICATION_UUID);
728
        classificationService.save(classification);
729
        TaxonNode node_lapsana = classification.addChildTaxon(t_lapsana, sec, null);
730
        node_lapsana.setUuid(TN_LAPSANA_UUID);
731
        TaxonNode node_lapsana_communis = node_lapsana.addChildTaxon(t_lapsana_communis, sec, null);
732
        node_lapsana_communis.addChildTaxon(t_lapsana_communis_communis, sec, null);
733
        node_lapsana_communis.addChildTaxon(t_lapsana_communis_adenophora, sec, null);
734
        node_lapsana_communis.addChildTaxon(t_lapsana_communis_alpina, sec, null);
735
        classificationService.saveOrUpdate(classification);
736

    
737
        commitAndStartNewTransaction(null);
738

    
739
        writeDbUnitDataSetFile(new String[] {
740
                "TAXONBASE", "TAXONNAME","CLASSIFICATION",  "TAXONNODE","HOMOTYPICALGROUP",
741
                "REFERENCE", "AGENTBASE",
742
                "DESCRIPTIONELEMENTBASE", "DESCRIPTIONBASE",
743
                "LANGUAGESTRING",
744
                "HIBERNATE_SEQUENCES"
745
         });
746
    }
747

    
748

    
749
}
(2-2/2)