Project

General

Profile

Download (47.8 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(50 * 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
        createDefaultFeatureTree();
305
        DescriptiveDataSet dataSet = DescriptiveDataSet.NewInstance();
306
        datasetService.save(dataSet);
307

    
308
        SpecimenDescription specDescAlpina1 = createSpecimenDescription(dataSet, T_LAPSANA_COMMUNIS_ALPINA_UUID, "alpina specimen1", T_LAPSANA_COMMUNIS_ALPINA_SPEC1_UUID);
309
        addCategoricalData(specDescAlpina1, uuidFeatureLeafColor, null);
310

    
311
        TaxonNode tnLapsana = taxonNodeService.find(TN_LAPSANA_UUID);
312
        Assert.assertNotNull(tnLapsana);
313
        dataSet.addTaxonSubtree(tnLapsana);
314

    
315
        @SuppressWarnings("unchecked")
316
        TermTree<Feature> descriptiveSystem = termTreeService.find(uuidFeatureTree);
317
        dataSet.setDescriptiveSystem(descriptiveSystem);
318
        commitAndStartNewTransaction();
319

    
320
        StructuredDescriptionAggregationConfiguration config = createConfig(dataSet);
321

    
322
        UpdateResult result = engine.invoke(config, repository);
323
        verifyStatusOk(result);
324

    
325
        Taxon taxLapsanaCommunisAlpina = (Taxon)taxonService.find(T_LAPSANA_COMMUNIS_ALPINA_UUID);
326
        TaxonDescription aggrDescLapsanaCommunisAlpina = verifyTaxonDescriptions(taxLapsanaCommunisAlpina, 1);
327
        List<StateData> sdAlpinaLeafColor = verifyCategoricalData(uuidFeatureLeafColor, 1, aggrDescLapsanaCommunisAlpina, false);
328
        verifyState(sdAlpinaLeafColor, uuidLeafColorBlue, 0);
329
        verifyState(sdAlpinaLeafColor, uuidLeafColorYellow, 0);
330
    }
331

    
332
    @Test
333
    @DataSets({
334
        @DataSet(loadStrategy=CleanSweepInsertLoadStrategy.class, value="/eu/etaxonomy/cdm/database/ClearDB_with_Terms_DataSet.xml"),
335
        @DataSet(value="/eu/etaxonomy/cdm/database/TermsDataSet-with_auditing_info.xml"),
336
        @DataSet(value="StructuredDescriptionAggregationTest.xml"),
337
    })
338
    public void testSourceModes() {
339
        createDefaultFeatureTree();
340
        DescriptiveDataSet dataSet = createTestData();
341
        commitAndStartNewTransaction();
342

    
343
        StructuredDescriptionAggregationConfiguration config = createConfig(dataSet);
344
        config.setWithinTaxonSourceMode(AggregationSourceMode.NONE);
345
        config.setToParentSourceMode(AggregationSourceMode.NONE);
346

    
347
        // 1st aggregation
348
        UpdateResult result = engine.invoke(config, repository);
349
        verifyStatusOk(result);
350
        commitAndStartNewTransaction();
351
        verifyAggregatedDescription(new TestConfig(config));
352

    
353
        config.setWithinTaxonSourceMode(AggregationSourceMode.DESCRIPTION);
354
        config.setToParentSourceMode(AggregationSourceMode.NONE);
355
        result = engine.invoke(config, repository);
356
        verifyStatusOk(result);
357
        commitAndStartNewTransaction();
358
        verifyAggregatedDescription(new TestConfig(config));
359

    
360
        config.setWithinTaxonSourceMode(AggregationSourceMode.NONE);
361
        config.setToParentSourceMode(AggregationSourceMode.DESCRIPTION);
362
        result = engine.invoke(config, repository);
363
        verifyStatusOk(result);
364
        commitAndStartNewTransaction();
365
        verifyAggregatedDescription(new TestConfig(config));
366

    
367
        config.setWithinTaxonSourceMode(AggregationSourceMode.DESCRIPTION);
368
        config.setToParentSourceMode(AggregationSourceMode.TAXON);
369
        result = engine.invoke(config, repository);
370
        verifyStatusOk(result);
371
        commitAndStartNewTransaction();
372
        verifyAggregatedDescription(new TestConfig(config));
373

    
374
        config.setWithinTaxonSourceMode(AggregationSourceMode.NONE);
375
        config.setToParentSourceMode(AggregationSourceMode.TAXON);
376
        result = engine.invoke(config, repository);
377
        verifyStatusOk(result);
378
        commitAndStartNewTransaction();
379
        verifyAggregatedDescription(new TestConfig(config));
380

    
381
        config.setWithinTaxonSourceMode(AggregationSourceMode.ALL);
382
        config.setToParentSourceMode(AggregationSourceMode.DESCRIPTION);
383
        result = engine.invoke(config, repository);
384
        verifyStatusError(result, "Unsupported source mode for within-taxon aggregation: ALL");
385
        commitAndStartNewTransaction();
386

    
387
        config.setWithinTaxonSourceMode(AggregationSourceMode.DESCRIPTION);
388
        config.setToParentSourceMode(AggregationSourceMode.ALL);
389
        result = engine.invoke(config, repository);
390
        verifyStatusError(result, "Unsupported source mode for to-parent aggregation: ALL");
391
        commitAndStartNewTransaction();
392

    
393
        config.setWithinTaxonSourceMode(AggregationSourceMode.ALL_SAMEVALUE);
394
        config.setToParentSourceMode(AggregationSourceMode.DESCRIPTION);
395
        result = engine.invoke(config, repository);
396
        verifyStatusError(result, "Unsupported source mode for within-taxon aggregation: ALL_SAMEVALUE");
397
        commitAndStartNewTransaction();
398

    
399
//        removeSomeDataFromFirstAggregation();
400
//        commitAndStartNewTransaction();
401
//        Assert.assertEquals("Should have 3 specimen desc, 1 literature desc, 2 individual association holder, "
402
//                + "4 aggregated descriptions, 4 cloned specimen descriptions (still not deleted), (3 cloned aggregated descriptions?) = 17",
403
//                17, descriptionService.count(null));
404
//
405
//        // 2nd aggregation
406
//        result = engine.invoke(config, repository);
407
//        testStatusOk(result);
408
//        commitAndStartNewTransaction();
409
//        testAggregatedDescription(false, false, true);
410
    }
411

    
412
    @Test
413
    @DataSets({
414
        @DataSet(loadStrategy=CleanSweepInsertLoadStrategy.class, value="/eu/etaxonomy/cdm/database/ClearDB_with_Terms_DataSet.xml"),
415
        @DataSet(value="/eu/etaxonomy/cdm/database/TermsDataSet-with_auditing_info.xml"),
416
        @DataSet(value="StructuredDescriptionAggregationTest.xml"),
417
    })
418
    public void testAggregation() {
419
        createDefaultFeatureTree();
420
        DescriptiveDataSet dataSet = createTestData();
421
        commitAndStartNewTransaction();
422

    
423
        StructuredDescriptionAggregationConfiguration config = createConfig(dataSet);
424

    
425
        UpdateResult result = engine.invoke(config, repository);
426
        commitAndStartNewTransaction();
427
        verifyStatusOk(result);
428
        verifyAggregatedDescription(new TestConfig(config));
429

    
430
        config.setIncludeLiterature(true);
431

    
432
        result = engine.invoke(config, repository);
433
        commitAndStartNewTransaction();
434
        verifyStatusOk(result);
435
        verifyAggregatedDescription(new TestConfig(config));
436
    }
437

    
438
    private void verifyStatusOk(UpdateResult result) {
439
        if (result.getStatus() != UpdateResult.Status.OK){
440
            Assert.fail("Aggregation should have status OK but was " + result.toString());
441
            for (Exception ex : result.getExceptions()){
442
                ex.printStackTrace();
443
            }
444
        }
445
    }
446

    
447
    private void verifyStatusError(UpdateResult result, String expectedMessage) {
448
        if (result.getStatus() != UpdateResult.Status.ERROR){
449
            Assert.fail("Aggregation should fail with status error " + result.toString());
450
        }
451
        if (result.getExceptions().isEmpty()){
452
            Assert.fail("Failing aggregation include exception " + result.toString());
453
        }
454
        Exception e = result.getExceptions().iterator().next();
455
        if (! (e instanceof AggregationException)){
456
            Assert.fail("Exception should be of type AggregationException " + result.toString());
457
        }else if (expectedMessage != null){
458
            Assert.assertEquals(expectedMessage, e.getMessage());
459
        }
460
    }
461

    
462
    private void addLiterature(DescriptiveDataSet dataSet) {
463

    
464
        //literature description
465
        Taxon taxon = (Taxon)taxonService.find(T_LAPSANA_COMMUNIS_ALPINA_UUID);
466
        TaxonDescription literatureDescription = TaxonDescription.NewInstance(taxon);
467
        literatureDescription.addType(DescriptionType.SECONDARY_DATA);
468
        addQuantitativeData(literatureDescription, uuidFeatureLeafLength, new BigDecimal("4.5"), new BigDecimal("6.5"));
469
        addCategoricalData(literatureDescription, uuidFeatureLeafColor, uuidLeafColorBlue);
470
        dataSet.addDescription(literatureDescription);
471
    }
472

    
473
    private class TestConfig{
474
        boolean withAddedData;
475
        boolean withRemovedData;
476
        boolean withFeature;
477
        final boolean withLiterature;
478
        final boolean cloneAggSourceDesc;
479
        final AggregationSourceMode withinTaxonSourceMode;
480
        final AggregationSourceMode toParentSourceMode;
481

    
482

    
483
        private TestConfig(StructuredDescriptionAggregationConfiguration config) {
484
            withinTaxonSourceMode = config.getWithinTaxonSourceMode();
485
            toParentSourceMode = config.getToParentSourceMode();
486
            cloneAggSourceDesc = config.isCloneAggregatedSourceDescriptions();
487
            withLiterature = config.isIncludeLiterature();
488
        }
489
        private TestConfig setWithAddedData() {withAddedData = true; return this;}
490
        private TestConfig setWithRemoved() {withRemovedData = true; return this;}
491
        private TestConfig setWithFeature() {withFeature = true; return this;}
492
    }
493

    
494
    private void verifyAggregatedDescription(TestConfig config) {
495

    
496
        boolean withRemovedData = config.withRemovedData;
497
        boolean withLiterature = config.withLiterature;
498
        boolean withAddedData = config.withAddedData;
499
        boolean isWithinNone = config.withinTaxonSourceMode.isNone();
500
        boolean withFeature = config.withFeature;
501
        boolean isToParentNone = config.toParentSourceMode.isNone();
502
        boolean isToParentTaxon = config.toParentSourceMode.isTaxon();
503
        boolean cloneAggSourceDesc = config.cloneAggSourceDesc;
504

    
505
        int intDel = withRemovedData? -1 : 0;
506
        int intLit = withLiterature? 1 : 0;
507

    
508
        //L. communis alpina
509
        Taxon taxLapsanaCommunisAlpina = (Taxon)taxonService.find(T_LAPSANA_COMMUNIS_ALPINA_UUID);
510
        int nElement = withAddedData ? 4 : 3;
511
        TaxonDescription aggrDescLapsanaCommunisAlpina = verifyTaxonDescriptions(taxLapsanaCommunisAlpina, nElement);
512

    
513
        List<StateData> stateData = verifyCategoricalData(uuidFeatureLeafPA, 1, aggrDescLapsanaCommunisAlpina, withAddedData);
514
        verifyState(stateData, State.uuidPresent, 3+intDel);
515
        List<StateData> sdAlpinaLeafColor = verifyCategoricalData(uuidFeatureLeafColor, 1, aggrDescLapsanaCommunisAlpina, false);
516
        int litLeafColorBlue = withLiterature? 1: 0;
517
        verifyState(sdAlpinaLeafColor, uuidLeafColorBlue, 2+litLeafColorBlue);
518
        verifyState(sdAlpinaLeafColor, uuidLeafColorYellow, 0);
519
        BigDecimal count = withLiterature? null : withRemovedData ? new BigDecimal("2"): new BigDecimal("3");
520
        BigDecimal avg = withLiterature? null : withRemovedData ? new BigDecimal("6"): new BigDecimal("6.666667");
521
        BigDecimal min = withLiterature? new BigDecimal("4.5") : new BigDecimal("5.0");
522
        BigDecimal max = withRemovedData ? new BigDecimal("7.0") : new BigDecimal("8.0");
523
        verifyQuantitativeData(uuidFeatureLeafLength, count, min, max, avg, aggrDescLapsanaCommunisAlpina);
524
        //... sources
525
        int nWithinSources = isWithinNone ? 0 : 3+intLit+intDel;
526
        Assert.assertEquals(nWithinSources, aggrDescLapsanaCommunisAlpina.getSources().size());
527
        SpecimenOrObservationBase<?> specLcommunisAlpina1 = CdmBase.deproxy(occurrenceService.find(T_LAPSANA_COMMUNIS_ALPINA_SPEC1_UUID));
528
        int nCloned = isWithinNone ? 0 : 1;
529
        Assert.assertEquals("Spec1 must have 2 descriptions now. The primary one and the cloned.", 1+nCloned, specLcommunisAlpina1.getSpecimenDescriptions().size());
530
        Assert.assertEquals(nCloned, specLcommunisAlpina1.getSpecimenDescriptions().stream().filter(d->d.isCloneForSource()).count());
531
        if (nCloned > 0){
532
            DescriptionBase<?> clonedDesc = specLcommunisAlpina1.getDescriptions().stream().filter(d->d.isCloneForSource()).findFirst().get();
533
            DescriptionBase<?> sourceDescription = getSingleSpecimenDescriptionSource(aggrDescLapsanaCommunisAlpina, T_LAPSANA_COMMUNIS_ALPINA_SPEC1_UUID);
534
            Assert.assertEquals(clonedDesc, sourceDescription);
535
        }
536

    
537
        //L. communis adenophora
538
        Taxon taxLapsanaCommunisAdenophora = (Taxon)taxonService.find(T_LAPSANA_COMMUNIS_ADENOPHORA_UUID);
539
        int nEl = withFeature ? 4 : 3;
540
        TaxonDescription aggrDescLapsanaCommunisAdenophora = verifyTaxonDescriptions(taxLapsanaCommunisAdenophora, nEl);
541
        verifyState(verifyCategoricalData(uuidFeatureLeafPA, 1, aggrDescLapsanaCommunisAdenophora, false), State.uuidPresent, 1);
542
        List<StateData> sdAdenophoraLeafColor = verifyCategoricalData(uuidFeatureLeafColor, 1, aggrDescLapsanaCommunisAdenophora, false);
543
        verifyState(sdAdenophoraLeafColor, uuidLeafColorBlue, 0);
544
        verifyState(sdAdenophoraLeafColor, uuidLeafColorYellow, 1);
545
        verifyQuantitativeData(uuidFeatureLeafLength, new BigDecimal("1"), new BigDecimal("10.0"),
546
                new BigDecimal("10.0"), new BigDecimal("10.0"), aggrDescLapsanaCommunisAdenophora);
547

    
548
        //L. communis
549
        Taxon taxLapsanaCommunis = (Taxon)taxonService.find(T_LAPSANA_COMMUNIS_UUID);
550
        TaxonDescription aggrDescLapsanaCommunis = verifyTaxonDescriptions(taxLapsanaCommunis, nEl);
551
        verifyState(verifyCategoricalData(uuidFeatureLeafPA, 1, aggrDescLapsanaCommunis, false), State.uuidPresent, 4+intDel);
552
        List<StateData> sdCommunisLeafColor = verifyCategoricalData(uuidFeatureLeafColor, 2, aggrDescLapsanaCommunis, false);
553
        verifyState(sdCommunisLeafColor, uuidLeafColorBlue, 2 + intLit);
554
        verifyState(sdCommunisLeafColor, uuidLeafColorYellow, 1);
555
        count = withLiterature? null : withRemovedData ? new BigDecimal("3") : new BigDecimal("4");
556
        avg = withLiterature? null : withRemovedData ? new BigDecimal("7.333333") : new BigDecimal("7.5");
557
        verifyQuantitativeData(uuidFeatureLeafLength, count, min,
558
                new BigDecimal("10.0"), avg, aggrDescLapsanaCommunis);
559
        //... sources
560
        int nToParent = isToParentNone ? 0 : 2;
561
        Assert.assertEquals(nToParent, aggrDescLapsanaCommunis.getSources().size());
562
        Map<UUID, List<TaxonDescription>> taxonDescriptionMap = getSourceTaxonDescriptionMap(aggrDescLapsanaCommunis);
563
        int nToParentDescs = isToParentTaxon? 0 : nToParent;
564
        Assert.assertEquals(nToParentDescs, taxonDescriptionMap.size());
565
        if (nToParentDescs > 0){
566
            Assert.assertEquals(1, taxonDescriptionMap.get(T_LAPSANA_COMMUNIS_ALPINA_UUID).size());
567
            Assert.assertEquals(1, taxonDescriptionMap.get(T_LAPSANA_COMMUNIS_ADENOPHORA_UUID).size());
568
            if (cloneAggSourceDesc){
569
                Assert.assertNotEquals(aggrDescLapsanaCommunisAlpina, taxonDescriptionMap.get(T_LAPSANA_COMMUNIS_ALPINA_UUID).get(0));
570
            }else{
571
                Assert.assertEquals(aggrDescLapsanaCommunisAlpina, taxonDescriptionMap.get(T_LAPSANA_COMMUNIS_ALPINA_UUID).get(0));
572
            }
573
        }else if (isToParentTaxon){
574
            Map<UUID, List<Taxon>> taxonToTaxonSourceMap = getSourceTaxonMap(aggrDescLapsanaCommunis);
575
            Assert.assertEquals(1, taxonToTaxonSourceMap.get(T_LAPSANA_COMMUNIS_ALPINA_UUID).size());
576
            Assert.assertEquals(1, taxonToTaxonSourceMap.get(T_LAPSANA_COMMUNIS_ADENOPHORA_UUID).size());
577
            Assert.assertEquals(taxLapsanaCommunisAlpina, taxonToTaxonSourceMap.get(T_LAPSANA_COMMUNIS_ALPINA_UUID).get(0));
578
        }
579

    
580
        //Lapsana
581
        Taxon taxLapsana = (Taxon)taxonService.find(T_LAPSANA_UUID);
582
        TaxonDescription aggrDescLapsana = verifyTaxonDescriptions(taxLapsana, nEl);
583
        verifyState(verifyCategoricalData(uuidFeatureLeafPA, 1, aggrDescLapsana, false), State.uuidPresent, 4+intDel);
584
        List<StateData> sdLapsanLeafColor = verifyCategoricalData(uuidFeatureLeafColor, 2, aggrDescLapsana, false);
585
        verifyState(sdLapsanLeafColor, uuidLeafColorBlue, 2 + intLit);
586
        verifyState(sdLapsanLeafColor, uuidLeafColorYellow, 1);
587
        verifyQuantitativeData(uuidFeatureLeafLength, count, min,
588
                new BigDecimal("10.0"), avg, aggrDescLapsana);
589
        //... sources
590
        nToParent = isToParentNone ? 0 : 1;
591
        Assert.assertEquals(nToParent, aggrDescLapsana.getSources().size());
592
        nToParentDescs = isToParentTaxon? 0 : nToParent;
593
        taxonDescriptionMap = getSourceTaxonDescriptionMap(aggrDescLapsana);
594
        Assert.assertEquals(nToParentDescs, taxonDescriptionMap.size());
595
        if (nToParentDescs > 0){
596
            Assert.assertEquals(1, taxonDescriptionMap.get(T_LAPSANA_COMMUNIS_UUID).size());
597
            if (cloneAggSourceDesc){
598
                Assert.assertNotEquals(aggrDescLapsanaCommunis, taxonDescriptionMap.get(T_LAPSANA_COMMUNIS_UUID).get(0));
599
            }else{
600
                Assert.assertEquals(aggrDescLapsanaCommunis, taxonDescriptionMap.get(T_LAPSANA_COMMUNIS_UUID).get(0));
601
            }
602

    
603
        }else if (isToParentTaxon){
604
            Map<UUID, List<Taxon>> taxonToTaxonSourceMap = getSourceTaxonMap(aggrDescLapsana);
605
            Assert.assertEquals(1, taxonToTaxonSourceMap.get(T_LAPSANA_COMMUNIS_UUID).size());
606
            Assert.assertEquals(taxLapsanaCommunis, taxonToTaxonSourceMap.get(T_LAPSANA_COMMUNIS_UUID).get(0));
607
        }
608

    
609
        //total description count
610
        nCloned = (isToParentNone || isToParentTaxon || !cloneAggSourceDesc ? 0 : 3) + (isWithinNone ? 0 : 4);
611
        Assert.assertEquals("Should have 4 specimen desc, 1 literature desc, 2 individual association holder, "
612
                + "4 aggregated descriptions, 4/0 cloned specimen descriptions, (3/4/0 cloned aggregated descriptions?) = 18/19",
613
                11+nCloned+intLit+(intDel*2), descriptionService.count(null));
614

    
615
    }
616

    
617
    //a map of the taxon uuid to the attached source descriptions
618
    private Map<UUID, List<TaxonDescription>> getSourceTaxonDescriptionMap(TaxonDescription desc) {
619
        return desc.getSources().stream().filter(s->(s.getCdmSource() instanceof TaxonDescription))
620
            .map(s->CdmBase.deproxy(s.getCdmSource(), TaxonDescription.class))
621
            .collect(Collectors.groupingBy(fDescToDescribedUuid));
622
    }
623

    
624
    private static Function<DescriptionBase<?>, UUID> fDescToDescribedUuid =
625
            ((Function<DescriptionBase<?>, IDescribable<?>>)(d->d.isInstanceOf(SpecimenDescription.class)? d.getDescribedSpecimenOrObservation(): CdmBase.deproxy(d, TaxonDescription.class).getTaxon()))
626
            .andThen(IDescribable::getUuid);
627

    
628
    //a map of the taxon to the attached taxon (source link)
629
    private Map<UUID, List<Taxon>> getSourceTaxonMap(TaxonDescription desc) {
630
        return desc.getSources().stream().filter(s->(s.getCdmSource() instanceof Taxon))
631
            .map(s->CdmBase.deproxy(s.getCdmSource(), Taxon.class))
632
            .collect(Collectors.groupingBy(t->t.getUuid()));
633
    }
634

    
635

    
636
    private DescriptionBase<?> getSingleSpecimenDescriptionSource(
637
            TaxonDescription aggrDescLapsanaCommunisAlpina, UUID specimenUuid) {
638

    
639
        Map<UUID, List<DescriptionBase<?>>> map = aggrDescLapsanaCommunisAlpina.getSources().stream()
640
            .map(src->(DescriptionBase<?>)src.getCdmSource())
641
            .filter(o-> o != null)
642
            .collect(Collectors.groupingBy(fDescToDescribedUuid));
643
        Assert.assertEquals(1, map.get(specimenUuid).size());
644
        return map.get(specimenUuid).get(0);
645

    
646
    }
647

    
648
    private StructuredDescriptionAggregationConfiguration createConfig(DescriptiveDataSet dataSet) {
649
        TaxonNodeFilter filter = TaxonNodeFilter.NewSubtreeInstance(TN_LAPSANA_UUID);
650
        StructuredDescriptionAggregationConfiguration config =
651
                StructuredDescriptionAggregationConfiguration.NewInstance(filter, monitor);
652
        config.setDatasetUuid(dataSet.getUuid());
653
        config.setAggregationMode(AggregationMode.byWithinTaxonAndToParent());
654
        config.setIncludeLiterature(false);
655
        config.setToParentSourceMode(AggregationSourceMode.DESCRIPTION);  //test where written against DESCRIPTION at the beginning so we use this as default for the tests
656

    
657
        return config;
658
    }
659

    
660
    private DescriptiveDataSet createTestData() {
661
        DescriptiveDataSet dataSet = DescriptiveDataSet.NewInstance();
662
        dataSet.setLabel("Test dataset");
663
        datasetService.save(dataSet);
664

    
665
        //L. communis alpina spec1
666
        SpecimenDescription specDescAlpina1 = createSpecimenDescription(dataSet, T_LAPSANA_COMMUNIS_ALPINA_UUID, "alpina specimen1", T_LAPSANA_COMMUNIS_ALPINA_SPEC1_UUID);
667
        addCategoricalData(specDescAlpina1, uuidFeatureLeafPA, State.uuidPresent);
668
        addQuantitativeData(specDescAlpina1, uuidFeatureLeafLength, StatisticalMeasure.EXACT_VALUE(), new BigDecimal("5.0"));
669
        addCategoricalData(specDescAlpina1, uuidFeatureLeafColor, uuidLeafColorBlue);
670

    
671
        //L. communis alpina spec2
672
        SpecimenDescription specDescAlpina2 = createSpecimenDescription(dataSet, T_LAPSANA_COMMUNIS_ALPINA_UUID, "alpina specimen2", T_LAPSANA_COMMUNIS_ALPINA_SPEC2_UUID);
673
        addCategoricalData(specDescAlpina2, uuidFeatureLeafPA, State.uuidPresent);
674
        addQuantitativeData(specDescAlpina2, uuidFeatureLeafLength, StatisticalMeasure.EXACT_VALUE(), new BigDecimal("7.0"));
675
        addCategoricalData(specDescAlpina2, uuidFeatureLeafColor, uuidLeafColorBlue);
676

    
677
        //L. communis alpina spec3
678
        SpecimenDescription specDescAlpina3 = createSpecimenDescription(dataSet, T_LAPSANA_COMMUNIS_ALPINA_UUID, "alpina specimen3", T_LAPSANA_COMMUNIS_ALPINA_SPEC3_UUID);
679
        addCategoricalData(specDescAlpina3, uuidFeatureLeafPA, State.uuidPresent);
680
        addQuantitativeData(specDescAlpina3, uuidFeatureLeafLength, StatisticalMeasure.EXACT_VALUE(), new BigDecimal("8.0"));
681

    
682
        //L. communis adenophora
683
        SpecimenDescription specDescAdenophora = createSpecimenDescription(dataSet, T_LAPSANA_COMMUNIS_ADENOPHORA_UUID, "adenophora specimen", T_LAPSANA_COMMUNIS_ADENOPHORA_SPEC1_UUID);
684
        addCategoricalData(specDescAdenophora, uuidFeatureLeafPA, State.uuidPresent);
685
        addQuantitativeData(specDescAdenophora, uuidFeatureLeafLength, StatisticalMeasure.EXACT_VALUE(), new BigDecimal("10.0"));
686
        addCategoricalData(specDescAdenophora, uuidFeatureLeafColor, uuidLeafColorYellow);
687

    
688
        TaxonNode tnLapsana = taxonNodeService.find(TN_LAPSANA_UUID);
689
        Assert.assertNotNull(tnLapsana);
690
        dataSet.addTaxonSubtree(tnLapsana);
691

    
692
        @SuppressWarnings("unchecked")
693
        TermTree<Feature> descriptiveSystem = termTreeService.find(uuidFeatureTree);
694
        dataSet.setDescriptiveSystem(descriptiveSystem);
695

    
696
        addLiterature(dataSet);
697
        return dataSet;
698
    }
699

    
700
    private TaxonDescription verifyTaxonDescriptions(Taxon taxon, int elementSize){
701
        List<TaxonDescription> taxonDescriptions = taxon.getDescriptions().stream()
702
                .filter(desc->desc.getTypes().contains(DescriptionType.AGGREGATED_STRUC_DESC))
703
                .filter(desc->!desc.getTypes().contains(DescriptionType.CLONE_FOR_SOURCE))
704
                .collect(Collectors.toList());
705

    
706
        Assert.assertEquals(1, taxonDescriptions.size());
707
        TaxonDescription aggrDesc = taxonDescriptions.iterator().next();
708
        Set<DescriptionElementBase> elements = aggrDesc.getElements();
709
        Assert.assertEquals(elementSize, elements.size());
710
        return aggrDesc;
711
    }
712

    
713
    private void verifyQuantitativeData(UUID featureUuid, BigDecimal sampleSize, BigDecimal min,
714
            BigDecimal max, BigDecimal avg, TaxonDescription aggrDesc) {
715
        List<QuantitativeData> quantitativeDatas = aggrDesc.getElements().stream()
716
                .filter(element->element.getFeature().getUuid().equals(featureUuid))
717
                .map(catData->CdmBase.deproxy(catData, QuantitativeData.class))
718
                .collect(Collectors.toList());
719
        Assert.assertEquals(1, quantitativeDatas.size());
720
        QuantitativeData leafLength = quantitativeDatas.iterator().next();
721
        Assert.assertEquals(sampleSize, leafLength.getSampleSize());
722
        Assert.assertEquals(min, leafLength.getMin());
723
        Assert.assertEquals(max, leafLength.getMax());
724
        Assert.assertEquals(avg, leafLength.getAverage());
725
        Assert.assertEquals(MeasurementUnit.METER(), leafLength.getUnit());
726
        Assert.assertNotNull(leafLength.getUnit());
727
    }
728

    
729

    
730
    private List<StateData> verifyCategoricalData(UUID featureUuid, int stateDataCount, TaxonDescription taxonDescription, boolean withAddedData) {
731
        List<CategoricalData> categoricalDatas = taxonDescription.getElements().stream()
732
                .filter(element->element.getFeature().getUuid().equals(featureUuid))
733
                .map(catData->CdmBase.deproxy(catData, CategoricalData.class))
734
                .collect(Collectors.toList());
735
        int nCD = withAddedData ? 2 : 1;
736
        Assert.assertEquals(nCD, categoricalDatas.size());
737
        CategoricalData categoricalData;
738
        if (withAddedData){
739
            categoricalData = categoricalDatas.stream().filter(cd->cd.getStateData().get(0).getCount() != null ).findFirst().get();
740
        }else{
741
            categoricalData = categoricalDatas.iterator().next(); // categoricalDatas.stream().filter(cd->cd.getStateData().size() != 1).collect(Collectors.toList());
742
        }
743
        List<StateData> stateDatas = categoricalData.getStateData();
744
        Assert.assertEquals(stateDataCount, stateDatas.size());
745
        return stateDatas;
746
    }
747

    
748
    private void verifyState(List<StateData> stateDatas, UUID stateUuid, Integer stateDataCount){
749
        List<StateData> filteredStateDatas = stateDatas.stream()
750
                .filter(stateData->stateData.getState()!=null && stateData.getState().getUuid().equals(stateUuid))
751
                .collect(Collectors.toList());
752
        if(stateDataCount==0){
753
            // non-existence test
754
            Assert.assertEquals(0, filteredStateDatas.size());
755
            return;
756
        }
757
        Assert.assertEquals(1, filteredStateDatas.size());
758
        StateData stateData = filteredStateDatas.iterator().next();
759
        Assert.assertEquals(stateDataCount, stateData.getCount());
760
        Assert.assertEquals(stateUuid, stateData.getState().getUuid());
761
    }
762

    
763
    private void addQuantitativeData(DescriptionBase<?> desc, UUID uuidFeature, StatisticalMeasure type, BigDecimal value) {
764
        Feature feature = (Feature)termService.find(uuidFeature);
765
        QuantitativeData qd = QuantitativeData.NewInstance(feature);
766
        StatisticalMeasurementValue smv = StatisticalMeasurementValue.NewInstance(type, value);
767
        qd.addStatisticalValue(smv);
768
        Assert.assertNotNull(MeasurementUnit.METER());
769
        qd.setUnit(MeasurementUnit.METER());
770
        desc.addElement(qd);
771
    }
772

    
773
    private void addQuantitativeData(DescriptionBase<?> desc, UUID uuidFeature, BigDecimal min, BigDecimal max) {
774
        Feature feature = (Feature)termService.find(uuidFeature);
775
        QuantitativeData qd = QuantitativeData.NewInstance(feature);
776
        StatisticalMeasurementValue smv = StatisticalMeasurementValue.NewInstance(StatisticalMeasure.MIN(), min);
777
        qd.addStatisticalValue(smv);
778
        smv = StatisticalMeasurementValue.NewInstance(StatisticalMeasure.MAX(), max);
779
        qd.addStatisticalValue(smv);
780
        desc.addElement(qd);
781
    }
782

    
783
    private void addCategoricalData(DescriptionBase<?> desc, UUID featureUuid, UUID stateUUID) {
784
        Feature feature = (Feature)termService.find(featureUuid);
785
        State state = (State)termService.find(stateUUID);
786
        CategoricalData cd = CategoricalData.NewInstance(state, feature);
787
        desc.addElement(cd);
788
    }
789

    
790
    private SpecimenDescription createSpecimenDescription(DescriptiveDataSet dataSet, UUID taxonUuid, String specLabel, UUID specimenUuid ) {
791
        Taxon taxon = (Taxon)taxonService.find(taxonUuid);
792
        DerivedUnit specimen = DerivedUnit.NewPreservedSpecimenInstance();
793
        specimen.setTitleCache(specLabel, true);
794
        specimen.setUuid(specimenUuid);
795
        TaxonDescription taxonDescription = taxon.getDescriptions(DescriptionType.INDIVIDUALS_ASSOCIATION).stream()
796
            .findFirst()
797
            .orElseGet(()->{
798
                TaxonDescription td = TaxonDescription.NewInstance(taxon);
799
                td.addType(DescriptionType.INDIVIDUALS_ASSOCIATION);
800
                td.setTitleCache("Specimens used by " + dataSet.getTitleCache() + " for " + getTaxonLabel(taxon), true);
801
                return td;}
802
             );
803
        IndividualsAssociation individualsAssociation = IndividualsAssociation.NewInstance(specimen);
804
        // TODO this has to be discussed; currently the description with the InidividualsAssociation is
805
        // needed in the dataset for performance reasons
806
        taxonDescription.addElement(individualsAssociation);
807
        dataSet.addDescription(taxonDescription);
808
        SpecimenDescription specDesc = SpecimenDescription.NewInstance(specimen);
809

    
810
        dataSet.addDescription(specDesc);
811
        return specDesc;
812
    }
813

    
814
    private String getTaxonLabel(Taxon taxon) {
815
        if (taxon.getName() != null){
816
            return taxon.getName().getTitleCache();
817
        }else{
818
            return taxon.getTitleCache();
819
        }
820
    }
821

    
822
    private Feature createFeature(UUID uuid, String label, boolean isQuantitative) {
823
        Feature feature = Feature.NewInstance("", label, null);
824
        feature.setUuid(uuid);
825
        feature.setSupportsQuantitativeData(isQuantitative);
826
        feature.setSupportsCategoricalData(!isQuantitative);
827
        feature.setSupportsTextData(false);
828
        termService.save(feature);
829
        return feature;
830
    }
831

    
832
    private State createState(String label, UUID uuid) {
833
        State state = State.NewInstance("", label, "");
834
        state.getTitleCache();  //for better debugging
835
        state.setUuid(uuid);
836
        termService.save(state);
837
        return state;
838
    }
839

    
840
    private void createDefaultFeatureTree() {
841
        //feature tree
842
        //leaf p/a
843
        //  leaf length
844
        //  leaf color
845

    
846
        //tree
847
        TermTree<Feature> featureTree = TermTree.NewFeatureInstance();
848
        featureTree.setUuid(uuidFeatureTree);
849

    
850
        //leaf p/a
851
        Feature featureLeafPA = createFeature(uuidFeatureLeafPA, "LeafPA", false);
852
        TermNode<Feature> leafPANode = featureTree.getRoot().addChild(featureLeafPA);
853

    
854
        //leaf length
855
        Feature featureLeafLength = createFeature(uuidFeatureLeafLength, "LeafLength", true);
856
        leafPANode.addChild(featureLeafLength);
857

    
858
        //leaf color
859
        Feature featureLeafColor = createFeature(uuidFeatureLeafColor, "LeafColor", false);
860
        leafPANode.addChild(featureLeafColor);
861
        TermVocabulary<State> stateVoc = TermVocabulary.NewInstance(TermType.State, State.class, "", "Colors", null, null);
862
        State yellow = createState("Yellow", uuidLeafColorYellow);
863
        State blue = createState("Blue", uuidLeafColorBlue);
864
        stateVoc.addTerm(yellow);
865
        stateVoc.addTerm(blue);
866
        featureLeafColor.addSupportedCategoricalEnumeration(stateVoc);
867

    
868
        //additional feature
869
        Feature featureLeafAdd = createFeature(uuidFeatureLeafAdd, "Add", true);
870
        leafPANode.addChild(featureLeafAdd);
871

    
872
        vocabularyService.save(stateVoc);
873
    }
874

    
875
//    @Test
876
    //to create the taxonomic classification available also as .xml file
877
    @Override
878
    public void createTestDataSet() throws FileNotFoundException {
879

    
880
        // --- References --- //
881
        Reference sec = ReferenceFactory.newDatabase();
882
        sec.setTitleCache("Test", true);
883
        Reference nomRef = ReferenceFactory.newBook();
884
        sec.setTitleCache("Sp.Pl.", true);
885

    
886
        referenceService.save(sec);
887
        referenceService.save(nomRef);
888

    
889

    
890
        // --- Taxa --- //
891
        //  Lapsana
892
        //        L. communis
893
        //            L. communis subsp. communis
894
        //            L. communis subsp. adenophora
895
        //            L. communis subsp. alpina
896
        //  Sonchella
897
        //        S. dentata
898
        //        S. stenoma
899
        IBotanicalName n_lapsana = TaxonNameFactory.NewBotanicalInstance(Rank.GENUS());
900
        n_lapsana.setTitleCache("Lapsana", true);
901
        Taxon t_lapsana = Taxon.NewInstance(n_lapsana, sec);
902
        t_lapsana.setUuid(T_LAPSANA_UUID);
903
        taxonService.saveOrUpdate(t_lapsana);
904

    
905
        IBotanicalName n_lapsana_communis = TaxonNameFactory.NewBotanicalInstance(Rank.SPECIES());
906
        n_lapsana_communis.setTitleCache("L. communis", true);
907
        Taxon t_lapsana_communis = Taxon.NewInstance(n_lapsana_communis, sec);
908
        t_lapsana_communis.setUuid(T_LAPSANA_COMMUNIS_UUID);
909
        taxonService.saveOrUpdate(t_lapsana_communis);
910

    
911
        IBotanicalName n_lapsana_communis_communis = TaxonNameFactory.NewBotanicalInstance(Rank.SUBSPECIES());
912
        n_lapsana_communis_communis.setTitleCache("L. communis subsp. communis", true);
913
        Taxon t_lapsana_communis_communis = Taxon.NewInstance(n_lapsana_communis_communis, sec);
914
        t_lapsana_communis_communis.setUuid(T_LAPSANA_COMMUNIS_COMMUNIS_UUID);
915
        taxonService.saveOrUpdate(t_lapsana_communis_communis);
916

    
917
        IBotanicalName n_lapsana_communis_adenophora = TaxonNameFactory.NewBotanicalInstance(Rank.SUBSPECIES());
918
        n_lapsana_communis_adenophora.setTitleCache("L. communis subsp. adenophora", true);
919
        Taxon t_lapsana_communis_adenophora = Taxon.NewInstance(n_lapsana_communis_adenophora, sec);
920
        t_lapsana_communis_adenophora.setUuid(T_LAPSANA_COMMUNIS_ADENOPHORA_UUID);
921
        taxonService.saveOrUpdate(t_lapsana_communis_adenophora);
922

    
923
        IBotanicalName n_lapsana_communis_alpina = TaxonNameFactory.NewBotanicalInstance(Rank.SUBSPECIES());
924
        n_lapsana_communis_alpina.setTitleCache("L. communis subsp. alpina", true);
925
        Taxon t_lapsana_communis_alpina = Taxon.NewInstance(n_lapsana_communis_alpina, sec);
926
        t_lapsana_communis_alpina.setUuid(T_LAPSANA_COMMUNIS_ALPINA_UUID);
927
        taxonService.saveOrUpdate(t_lapsana_communis_alpina);
928

    
929
        // --- Classification --- //
930
        Classification classification = Classification.NewInstance("TestClassification");
931
        classification.setUuid(CLASSIFICATION_UUID);
932
        classificationService.save(classification);
933
        TaxonNode node_lapsana = classification.addChildTaxon(t_lapsana, sec, null);
934
        node_lapsana.setUuid(TN_LAPSANA_UUID);
935
        TaxonNode node_lapsana_communis = node_lapsana.addChildTaxon(t_lapsana_communis, sec, null);
936
        node_lapsana_communis.addChildTaxon(t_lapsana_communis_communis, sec, null);
937
        node_lapsana_communis.addChildTaxon(t_lapsana_communis_adenophora, sec, null);
938
        node_lapsana_communis.addChildTaxon(t_lapsana_communis_alpina, sec, null);
939
        classificationService.saveOrUpdate(classification);
940

    
941
        commitAndStartNewTransaction(null);
942

    
943
        writeDbUnitDataSetFile(new String[] {
944
                "TAXONBASE", "TAXONNAME","CLASSIFICATION",  "TAXONNODE","HOMOTYPICALGROUP",
945
                "REFERENCE", "AGENTBASE",
946
                "DESCRIPTIONELEMENTBASE", "DESCRIPTIONBASE",
947
                "LANGUAGESTRING",
948
                "HIBERNATE_SEQUENCES"
949
         });
950
    }
951

    
952

    
953
}
(2-2/2)