Project

General

Profile

Download (35.1 KB) Statistics
| Branch: | Revision:
1
// $Id$
2
/**
3
* Copyright (C) 2017 EDIT
4
* European Distributed Institute of Taxonomy
5
* http://www.e-taxonomy.eu
6
*
7
* The contents of this file are subject to the Mozilla Public License Version 1.1
8
* See LICENSE.TXT at the top of this package for the full license terms.
9
*/
10
package eu.etaxonomy.cdm.io.bogota;
11

    
12
import java.util.Arrays;
13
import java.util.HashMap;
14
import java.util.List;
15
import java.util.Map;
16
import java.util.UUID;
17

    
18
import org.apache.log4j.Logger;
19
import org.joda.time.Partial;
20
import org.springframework.stereotype.Component;
21

    
22
import eu.etaxonomy.cdm.api.facade.DerivedUnitFacade;
23
import eu.etaxonomy.cdm.common.CdmUtils;
24
import eu.etaxonomy.cdm.io.common.utils.ImportDeduplicationHelper;
25
import eu.etaxonomy.cdm.model.agent.AgentBase;
26
import eu.etaxonomy.cdm.model.agent.Institution;
27
import eu.etaxonomy.cdm.model.agent.Person;
28
import eu.etaxonomy.cdm.model.agent.Team;
29
import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;
30
import eu.etaxonomy.cdm.model.common.CdmBase;
31
import eu.etaxonomy.cdm.model.common.DefinedTerm;
32
import eu.etaxonomy.cdm.model.common.ExtensionType;
33
import eu.etaxonomy.cdm.model.common.IdentifiableSource;
34
import eu.etaxonomy.cdm.model.common.Language;
35
import eu.etaxonomy.cdm.model.common.TimePeriod;
36
import eu.etaxonomy.cdm.model.description.IndividualsAssociation;
37
import eu.etaxonomy.cdm.model.description.TaxonDescription;
38
import eu.etaxonomy.cdm.model.location.Country;
39
import eu.etaxonomy.cdm.model.location.NamedArea;
40
import eu.etaxonomy.cdm.model.location.NamedAreaLevel;
41
import eu.etaxonomy.cdm.model.location.NamedAreaType;
42
import eu.etaxonomy.cdm.model.location.Point;
43
import eu.etaxonomy.cdm.model.location.ReferenceSystem;
44
import eu.etaxonomy.cdm.model.name.NomenclaturalCode;
45
import eu.etaxonomy.cdm.model.name.Rank;
46
import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignation;
47
import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignationStatus;
48
import eu.etaxonomy.cdm.model.name.TaxonName;
49
import eu.etaxonomy.cdm.model.name.TaxonNameFactory;
50
import eu.etaxonomy.cdm.model.occurrence.Collection;
51
import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
52
import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent;
53
import eu.etaxonomy.cdm.model.occurrence.FieldUnit;
54
import eu.etaxonomy.cdm.model.reference.Reference;
55
import eu.etaxonomy.cdm.model.taxon.Synonym;
56
import eu.etaxonomy.cdm.model.taxon.Taxon;
57
import eu.etaxonomy.cdm.model.taxon.TaxonBase;
58
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
59
import eu.etaxonomy.cdm.strategy.exceptions.UnknownCdmTypeException;
60
import eu.etaxonomy.cdm.strategy.parser.DeterminationModifierParser;
61
import eu.etaxonomy.cdm.strategy.parser.INonViralNameParser;
62
import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImpl;
63
import eu.etaxonomy.cdm.strategy.parser.SpecimenTypeParser;
64
import eu.etaxonomy.cdm.strategy.parser.TimePeriodParser;
65

    
66
/**
67
 * @author a.mueller
68
 * @since 21.04.2017
69
 *
70
 */
71
@Component
72
public class BogotaSpecimenImport<CONFIG extends BogotaSpecimenImportConfigurator>
73
        extends SimpleExcelSpecimenImport<CONFIG> {
74

    
75
    private static final long serialVersionUID = -884838817884874228L;
76
    private static final Logger logger = Logger.getLogger(BogotaSpecimenImport.class);
77

    
78
    private static final String COL_TAXON_UUID = "Platform Name ID = cdmID";
79
    private static final String COL_VOUCHER_ID = "Voucher ID";
80

    
81
    private static final String COL_FAMILY = "Family";
82
    private static final String COL_GENUS = "Genus";
83
    private static final String COL_SPECIFIC_EPI = "Specific Epithet";
84
    private static final String COL_BASIONYM_AUTHOR = "Author in parenthesis";
85
    private static final String COL_AUTHOR = "Author";
86
    private static final String COL_IDENTIFIER = "Identifier";
87
    private static final String COL_IDENTIFICATION_DATE = "Identification date";
88
    private static final String COL_IDENTIFICATION_QUALIFIER = "Qualifier";
89
    private static final String COL_TYPUS = "Type";
90
    private static final String COL_IDENTIFICATION_HISTORY = "Identification history";
91
    private static final String COL_COLLECTOR_VERBATIM = "Verbatim Collectors  (Originalfeld JBB)";
92
    private static final String COL_COLLECTOR_LASTNAME = "Primary Collector Last Name  (Originalfeld JBB)";
93
    private static final String COL_COLLECTOR_FIRSTNAME = "Primary Collector First Name Initial (Originalfeld JBB)";
94
    private static final String COL_COLLECTOR_MIDDLENAME = "Primary Collector Middle Name Initial (Originalfeld JBB)";
95
    private static final String COL_COLLECTOR_TYPE = "Primary collector type (Originalfeld JBB)";
96
    private static final String COL_COLLECTOR_NUMBER = "Collector's No";
97
    private static final String COL_COLLECTORS = "Collectors";
98
    private static final String COL_COLLECTION_DATE_FROM = "Collection Date from";
99
    private static final String COL_COLLECTION_DATE_TO = "Collection Date to";
100
    private static final String COL_ALTITUDE_FROM = "Altitude Value from";
101
    private static final String COL_ALTITUDE_TO = "Altitude Value to";
102
    private static final String COL_ALTITUDE_UNIT = "Altitude Unit";
103
    private static final String COL_LOCALITY = "Locality";
104
    private static final String COL_LOCALITY_ID = "LocalityID";
105
    private static final String COL_LATITUDE = "Latitude";
106
    private static final String COL_LONGITUDE = "Longitude";
107
    private static final String COL_ERROR_DISTANCE = "Error distance in m";
108
    private static final String COL_COUNTRY = "Country";
109
    private static final String COL_STATE_AREA = "State/Province/Greater Area";
110
    private static final String COL_GEO_METHOD = "Geocode Method";
111
    private static final String COL_HABITUS = "Habitus";
112
    private static final String COL_COLLECTION = "[Series] Voucher location";
113

    
114
    private static final UUID uuidAnonymous = UUID.fromString("2303f043-6e92-4afa-9082-7719e78a1c8a");
115
    private static final UUID uuidBogota = UUID.fromString("95b6cb03-8452-4439-98bd-8c1aa3c1da4e");
116
    private static final UUID uuidDefaultGeocodMethod = UUID.fromString("0983e680-b0ca-4e46-8df7-0f1d757a2e01");
117
    private static final UUID uuidExtTypeIdentificationHistory = UUID.fromString("7cee5c29-e16b-4e6f-ad57-bf7044259375");
118
    private static final UUID uuidDetQualVelAff = UUID.fromString("511a0c23-2646-4035-b570-36bdc2eb5557");
119

    
120
//    @SuppressWarnings("unchecked")
121
    private ImportDeduplicationHelper<SimpleExcelSpecimenImportState<?>> deduplicationHelper;
122

    
123
    private final Map<String, TaxonNode> taxonNodeMap = new HashMap<>();
124
    private Reference secRef;
125

    
126
    @Override
127
    protected String getWorksheetName() {
128
        return "To be imported";
129
    }
130

    
131
    /**
132
     * {@inheritDoc}
133
     */
134
    @Override
135
    protected void firstPass(SimpleExcelSpecimenImportState<CONFIG> state) {
136

    
137
        HashMap<String, String> record = state.getOriginalRecord();
138

    
139
        String voucherId = getValue(record, COL_VOUCHER_ID);
140
        if (!isInInterval(state)){
141
            return;
142
        }
143
        String line = state.getCurrentLine() + " (id:"+ voucherId+"): ";
144
        if (state.getCurrentLine() % 100 == 0){System.out.println(line);}
145
        try {
146

    
147
            //species
148
            TaxonBase<?> taxonBase = getTaxonByCdmId(state, line, record, voucherId);
149
            if (taxonBase != null){
150
                handleRecordForTaxon(state, voucherId, line, taxonBase);
151
            }else if (record.get(COL_TAXON_UUID)!= null){
152
                //  do nothing
153
            }else{
154
                taxonBase = getOrCreateNewTaxon(state, record, line);
155
                handleRecordForTaxon(state, voucherId, line, taxonBase);
156
            }
157

    
158
        } catch (Exception e) {
159
            state.getResult().addError("An unexpected exception appeared in record", e, null, line);
160
            e.printStackTrace();
161
        }
162

    
163
    }
164

    
165
    /**
166
     * @param state
167
     * @param record
168
     * @param line
169
     * @return
170
     */
171
    private Taxon getOrCreateNewTaxon(SimpleExcelSpecimenImportState<CONFIG> state,
172
            HashMap<String, String> record, String line) {
173
        String familyStr = record.get(COL_FAMILY);
174
        String genusStr = record.get(COL_GENUS);
175
        initTaxonMap(state);
176
        TaxonName speciesName = makeSpeciesName(state, line);
177
        String titleCache = speciesName.getTitleCache();
178
        TaxonNode existingSpeciesNode = taxonNodeMap.get(titleCache);
179
        if (existingSpeciesNode != null){
180
            return existingSpeciesNode.getTaxon();
181
        }else{
182
            Reference sec = getSecReference(state);
183
            Taxon newTaxon = Taxon.NewInstance(speciesName, sec);
184
            newTaxon.addSource(makeOriginalSource(state));
185
            TaxonNode existingGenusNode = taxonNodeMap.get(genusStr);
186
            if (existingGenusNode == null){
187
                TaxonName genusName = TaxonNameFactory.NewBotanicalInstance(Rank.GENUS());
188
                genusName.setGenusOrUninomial(genusStr);
189
                Taxon newGenus = Taxon.NewInstance(genusName, sec);
190
                newGenus.addSource(makeOriginalSource(state));
191
                TaxonNode existingFamilyNode = taxonNodeMap.get(familyStr);
192
                if (existingFamilyNode == null){
193
                    TaxonName familyName = TaxonNameFactory.NewBotanicalInstance(Rank.FAMILY());
194
                    familyName.setGenusOrUninomial(familyStr);
195
                    Taxon newFamily = Taxon.NewInstance(familyName, sec);
196
                    newFamily.addSource(makeOriginalSource(state));
197
                    TaxonNode plantaeNode = taxonNodeMap.get("Plantae");
198
                    existingFamilyNode = plantaeNode.addChildTaxon(newFamily, null, null);
199
                    save(existingFamilyNode);
200
                }
201
                existingGenusNode = existingFamilyNode.addChildTaxon(newGenus, null, null);
202
                save(existingGenusNode);
203
            }
204
            existingSpeciesNode = existingGenusNode.addChildTaxon(newTaxon, null, null);
205
            save(existingSpeciesNode);
206
            return newTaxon;
207
        }
208

    
209
    }
210

    
211
    /**
212
     * @param existingFamilyNode
213
     */
214
    private void save(TaxonNode node) {
215
        getTaxonNodeService().saveOrUpdate(node);
216
        taxonNodeMap.put(node.getTaxon().getName().getTitleCache(), node);
217

    
218
    }
219

    
220
    /**
221
     * @param state
222
     * @return
223
     */
224
    private Reference getSecReference(SimpleExcelSpecimenImportState<CONFIG> state) {
225
        if (this.secRef == null){
226
            Reference sec = state.getConfig().getSecReference();
227
            this.secRef = getReferenceService().find(sec.getUuid());
228
            if (this.secRef == null){
229
                this.secRef = sec;
230
                getReferenceService().save(sec);
231
            }
232
        }
233

    
234

    
235
        return this.secRef;
236
    }
237

    
238
    /**
239
     * @param state
240
     * @param record
241
     * @param line
242
     * @return
243
     */
244
    private TaxonName makeSpeciesName(SimpleExcelSpecimenImportState<CONFIG> state, String line) {
245
        HashMap<String, String> record = state.getOriginalRecord();
246
        String genus = record.get(COL_GENUS);
247
        String species = record.get(COL_SPECIFIC_EPI);
248
        String basionymAuthorStr = record.get(COL_BASIONYM_AUTHOR);
249
        String authorStr = record.get(COL_AUTHOR);
250
        INonViralNameParser<?> parser = NonViralNameParserImpl.NewInstance();
251
        String fullName = genus + " " +  species +
252
                (basionymAuthorStr == null ? "" : " ("+basionymAuthorStr+")")
253
                + " " + authorStr;
254
        TaxonName newName = (TaxonName)parser.parseFullName(fullName , NomenclaturalCode.ICNAFP, Rank.SPECIES());
255
        String titleCache = newName.getTitleCache();
256
        if (newName.isProtectedTitleCache()){
257
            state.getResult().addWarning("Name not parsable: " +  fullName);
258
        }
259
        if (taxonNodeMap.get(titleCache)== null){
260
            getDeduplicationHelper(state).replaceAuthorNamesAndNomRef(state, newName);
261
            newName.addSource(makeOriginalSource(state));
262
        }
263

    
264
        return newName;
265
    }
266

    
267
    /**
268
     * @param state
269
     *
270
     */
271
    private void initTaxonMap(SimpleExcelSpecimenImportState<CONFIG> state) {
272
        if (taxonNodeMap.isEmpty()){
273
            List<String> propertyPaths = Arrays.asList(new String[]{"taxon.name"});
274
            List<TaxonNode> list = getTaxonNodeService().list(null, null, null, null, propertyPaths);
275
            for (TaxonNode node : list){
276
                if (node.getTaxon()!= null){
277
                    String strName = node.getTaxon().getName().getTitleCache();
278
                    TaxonNode existingNode = taxonNodeMap.get(strName);
279
                    if (existingNode != null){
280
                        state.getResult().addWarning("Taxon name exists more than once while initializing taxon map: " + strName, "initTaxonMap");
281
                    }else{
282
                        taxonNodeMap.put(strName, node);
283
                    }
284
                }
285
            }
286
        }
287
    }
288

    
289
    /**
290
     * @param state
291
     * @param record
292
     * @param voucherId
293
     * @param line
294
     * @param taxonBase
295
     * @param taxon
296
     */
297
    protected void handleRecordForTaxon(SimpleExcelSpecimenImportState<CONFIG> state,
298
            String voucherId, String line, TaxonBase<?> taxonBase) {
299

    
300
        HashMap<String, String> record = state.getOriginalRecord();
301
        Taxon taxon = getTaxon(taxonBase);
302

    
303
        TaxonDescription taxonDescription = getTaxonDescription(state, line, taxon);
304

    
305
        DerivedUnit specimen = makeSpecimen(state, line, record, voucherId, taxonBase);
306

    
307
        IndividualsAssociation indAssoc = IndividualsAssociation.NewInstance(specimen);
308
        indAssoc.addImportSource(voucherId, COL_VOUCHER_ID, getSourceCitation(state), null);
309
        taxonDescription.addElement(indAssoc);
310
    }
311

    
312

    
313
    /**
314
     * @param state
315
     * @return
316
     */
317
    private boolean isInInterval(SimpleExcelSpecimenImportState<CONFIG> state) {
318
        Integer min = state.getConfig().getMinLineNumber();
319
        Integer max = state.getConfig().getMaxLineNumber();
320
        Integer current = state.getCurrentLine();
321
        if (current < min || current > max){
322
            return false;
323
        }else{
324
            return true;
325
        }
326
    }
327

    
328

    
329
    /**
330
     * @param state
331
     * @param line
332
     * @param taxon
333
     * @return
334
     */
335
    private TaxonDescription getTaxonDescription(SimpleExcelSpecimenImportState<CONFIG> state, String line,
336
            Taxon taxon) {
337
        Reference ref = getSourceCitation(state);
338
        TaxonDescription desc = this.getTaxonDescription(taxon, ref, ! IMAGE_GALLERY, ! CREATE);
339
        if (desc == null){
340
            //TODO move title creation into base method
341
            desc = this.getTaxonDescription(taxon, ref, ! IMAGE_GALLERY, CREATE);
342
            desc.setTitleCache("Specimen Excel import for " +  taxon.getName().getTitleCache(), true);
343
        }
344
        return desc;
345
    }
346

    
347

    
348
    /**
349
     * @param state
350
     * @param line
351
     * @param record
352
     * @param voucherId
353
     * @return
354
     */
355
    private DerivedUnit makeSpecimen(SimpleExcelSpecimenImportState<CONFIG> state, String line,
356
            HashMap<String, String> record, String voucherId, TaxonBase<?> taxonBase) {
357

    
358
        DerivedUnitFacade facade = DerivedUnitFacade.NewPreservedSpecimenInstance();
359
        facade.setAccessionNumber(voucherId);
360
        makeDetermination(facade.innerDerivedUnit(), state, line, record, taxonBase.getName());
361
        makeTypus(facade.innerDerivedUnit(), state, line, record, taxonBase.getName());
362
        makeCollectorFields(facade, state, line, record);
363
        makeLocationFields(facade, state, line, record);
364
        makeHabitus(facade, state, line, record);
365
        makeCollection(facade, state, line, record);
366
        DerivedUnit specimen = facade.innerDerivedUnit();
367
        specimen.addSource(makeOriginalSource(state));
368
        FieldUnit fieldUnit = facade.innerFieldUnit();
369
        fieldUnit.addSource(makeOriginalSource(state));
370
        return specimen;
371
    }
372

    
373

    
374
    /**
375
     * @param innerDerivedUnit
376
     * @param state
377
     * @param line
378
     * @param record
379
     * @param name
380
     */
381
    private void makeTypus(DerivedUnit specimen, SimpleExcelSpecimenImportState<CONFIG> state, String line,
382
            HashMap<String, String> record, TaxonName name) {
383
        String typus = record.get(COL_TYPUS);
384
        if (typus != null){
385
            SpecimenTypeDesignationStatus status;
386
            try {
387
                status = SpecimenTypeParser.parseSpecimenTypeStatus(typus);
388
                SpecimenTypeDesignation designation = SpecimenTypeDesignation.NewInstance();
389
                designation.setTypeStatus(status);
390
                name.addSpecimenTypeDesignation(specimen, status, null, null, null, false, false);
391
            } catch (UnknownCdmTypeException e) {
392
                state.getResult().addWarning("Type designation could not be parsed", null, line);
393
            }
394
        }
395
    }
396

    
397

    
398
    /**
399
     * @param facade
400
     * @param state
401
     * @param line
402
     * @param record
403
     */
404
    private void makeCollection(DerivedUnitFacade facade, SimpleExcelSpecimenImportState<CONFIG> state,
405
            String line, HashMap<String, String> record) {
406
        String strCollection = record.get(COL_COLLECTION);
407
        String collectionFormat = ".*\\([A-Z]{2,4}\\)";
408
        if (!strCollection.matches(collectionFormat)){
409
            String message = "Voucher location format does not match the expected format. Voucher '(" + strCollection + ")' location not added.";
410
            state.getResult().addError(message, null, line);
411
            return;
412
        }
413
        String[] splits = strCollection.split("\\(");
414
        String collectionName = splits[0];
415
        String collectionCode = splits[1].replace(")", "");
416
        Collection collection = Collection.NewInstance();
417
        collection.setName(collectionName);
418
        collection.setCode(collectionCode);
419
        collection = getDeduplicationHelper(state).getExistingCollection(state, collection);
420
        facade.setCollection(collection);
421
    }
422

    
423

    
424
    /**
425
     * @param state
426
     * @return
427
     */
428
    private ImportDeduplicationHelper<SimpleExcelSpecimenImportState<?>> getDeduplicationHelper(SimpleExcelSpecimenImportState<CONFIG> state) {
429
        if (deduplicationHelper == null){
430
            deduplicationHelper = ImportDeduplicationHelper.NewInstance(this, state);
431
        }
432
        return deduplicationHelper;
433
    }
434

    
435

    
436
    /**
437
     * @param facade
438
     * @param state
439
     * @param line
440
     * @param record
441
     */
442
    private void makeHabitus(DerivedUnitFacade facade, SimpleExcelSpecimenImportState<CONFIG> state, String line,
443
            HashMap<String, String> record) {
444
        String habitus = record.get(COL_HABITUS);
445
        if (habitus != null){
446
            facade.setPlantDescription(habitus);
447
        }
448
    }
449

    
450

    
451
    /**
452
     * @param facade
453
     * @param state
454
     * @param line
455
     * @param record
456
     * @param voucherId
457
     */
458
    private void makeLocationFields(DerivedUnitFacade facade, SimpleExcelSpecimenImportState<CONFIG> state, String line,
459
            HashMap<String, String> record) {
460
        //Altitude
461
        String strAltitudeFrom = record.get(COL_ALTITUDE_FROM);
462
        String strAltitudeTo = record.get(COL_ALTITUDE_TO);
463
        Integer intAltitudeFrom = intFromString(state, strAltitudeFrom, line, COL_ALTITUDE_FROM);
464
        Integer intAltitudeTo = intFromString(state, strAltitudeTo, line, COL_ALTITUDE_TO);
465
        if (intAltitudeFrom != null){
466
            facade.setAbsoluteElevation(intAltitudeFrom);
467
            if (!intAltitudeFrom.equals(intAltitudeTo)){
468
                facade.setAbsoluteElevationMax(intAltitudeTo);
469
            }
470
            if (!record.get(COL_ALTITUDE_UNIT).equals("m")){
471
                state.getResult().addWarning("Altitude unit is not m but " + record.get(COL_ALTITUDE_UNIT), "makeLocationFields", line);
472
            }
473
        }
474
        checkNoToIfNoFrom(strAltitudeFrom, strAltitudeTo, state, line, COL_ALTITUDE_TO);
475

    
476
        //locality
477
        String locality = record.get(COL_LOCALITY);
478
        if (locality != null){  //should always exist
479
            facade.setLocality(locality, Language.SPANISH_CASTILIAN());
480
        }
481

    
482
        //Lat + Long
483
        String strLatitude = record.get(COL_LATITUDE);
484
        String strLongitude = record.get(COL_LONGITUDE);
485
        String strError = record.get(COL_ERROR_DISTANCE);
486
        Double dblLatitude = doubleFromString(state, strLatitude, line, COL_LATITUDE);
487
        Double dblLongitude = doubleFromString(state, strLongitude, line, COL_LONGITUDE);
488
        Integer intError = intFromString(state, strError, line, COL_ERROR_DISTANCE);
489
        ReferenceSystem referenceSystem = makeReferenceSystem(state, record, line);
490

    
491
        if (dblLatitude != null || dblLongitude != null || intError != null){ //should always exist
492
            Point exactLocation = Point.NewInstance(dblLongitude, dblLatitude, referenceSystem, intError);
493
            facade.setExactLocation(exactLocation);
494
        }
495

    
496
        //Country
497
        String strCountry = record.get(COL_COUNTRY);
498
        if (strCountry != null){
499
            if (strCountry.equals("Colombia")){
500
                Country colombia = Country.COLOMBIAREPUBLICOF();
501
                colombia.setLabel("Colombia");
502
                getTermService().saveOrUpdate(colombia);
503
                facade.setCountry(colombia);
504
            }else{
505
                state.getResult().addWarning("Country was not Colombia as expected but " +  strCountry,
506
                        "makeLocationFields", line);
507
            }
508
        }
509

    
510
        //State
511
        String strStateArea = record.get(COL_STATE_AREA);
512
        if (strStateArea != null){
513
            if (strStateArea.replaceAll("\\s", "").equalsIgnoreCase("Bogotá,D.C.")){
514
                NamedArea bogota = makeBogota(state, line);
515
                facade.addCollectingArea(bogota);
516
            }else{
517
                state.getResult().addWarning(COL_STATE_AREA + " was not 'Bogotá,  D.C.' as expected but " +  strCountry,
518
                        "makeLocationFields", line);
519
            }
520
        }
521
    }
522

    
523

    
524
    /**
525
     * @param strAltitudeFrom
526
     * @param strAltitudeTo
527
     * @param state
528
     * @param line
529
     * @param colAltitudeTo
530
     */
531
    private void checkNoToIfNoFrom(String strFrom, String strTo,
532
            SimpleExcelSpecimenImportState<CONFIG> state,
533
            String line, String toAttributeName) {
534
        if (isNotBlank(strTo) && isBlank(strFrom)){
535
            String message = "A min-max attribute has a max value (%s) but no min value. This is invalid."
536
                    + " The max value attribute name is %s.";
537
            message = String.format(message, strTo, toAttributeName);
538
            state.getResult().addWarning(message, null, line);
539
        }
540
    }
541

    
542
    private ReferenceSystem defaultGeocodeMethod;
543

    
544
    /**
545
     * @param state
546
     * @param record
547
     * @param line
548
     * @return
549
     */
550
    private ReferenceSystem makeReferenceSystem(SimpleExcelSpecimenImportState<CONFIG> state,
551
            Map<String, String> record, String line) {
552
        String defaultStrRefSys = "Wieczorek, J., Guo, Q., & Hijmans, R. (2004). The point-radius method for georeferencing locality descriptions and calculating associated uncertainty. International journal of geographical information science, 18(8), 745-767.; Escobar D, Díaz SR, Jojoa LM, Rudas E, Albarracín RD, Ramírez C, Gómez JY, López CR, Saavedra J (2015). Georreferenciación de localidades: Una guía de referencia para colecciones biológicas. Instituto de Investigación de Recursos Biológicos Alexander von Humboldt – Instituto de Ciencias Naturales, Universidad Nacional de Colombia. Bogotá D.C., Colombia. 95 p.";
553
        String strRefSys = record.get(COL_GEO_METHOD);
554
        if (strRefSys == null){
555
            return null;
556
        }else if (!strRefSys.equals(defaultStrRefSys)){
557
            state.getResult().addError("The expected Geocode Method is not the expected default method. Geocode Method was not added.", null, line);
558
            return null;
559
        }else if (defaultGeocodeMethod != null){
560
            return defaultGeocodeMethod;
561
        }else{
562
            String label = "Point radius method";
563
            String description = defaultStrRefSys;
564
            String labelAbbrev = "PRM";
565
            defaultGeocodeMethod = getReferenceSystem(state, uuidDefaultGeocodMethod,
566
                    label, description, labelAbbrev, null);
567
            return defaultGeocodeMethod;
568
        }
569
    }
570

    
571
    private NamedArea bogota;
572
    /**
573
     * @param state
574
     * @param line
575
     * @return
576
     */
577
    private NamedArea makeBogota(SimpleExcelSpecimenImportState<CONFIG> state, String line) {
578
        if (bogota != null){
579
            return bogota;
580
        }else{
581
            String label = "Bogotá, D.C.";
582
            NamedAreaType areaType = NamedAreaType.ADMINISTRATION_AREA();
583
            NamedAreaLevel level = NamedAreaLevel.STATE();
584
            bogota = getNamedArea(state, uuidBogota, label, label, null, areaType,
585
                    level, null, null, null);
586
            return bogota;
587
        }
588
    }
589

    
590

    
591
    /**
592
     * @param facade
593
     * @param state
594
     * @param line
595
     * @param record
596
     */
597
    private void makeCollectorFields(DerivedUnitFacade facade, SimpleExcelSpecimenImportState<CONFIG> state, String line,
598
            HashMap<String, String> record) {
599

    
600
        //collector number
601
        facade.setFieldNumber(record.get(COL_COLLECTOR_NUMBER));
602

    
603
        //gathering date
604
        String dateFrom = unknownToNull((record.get(COL_COLLECTION_DATE_FROM)));
605
        String dateTo = unknownToNull(record.get(COL_COLLECTION_DATE_TO));
606
        checkNoToIfNoFrom(dateFrom, dateTo, state, line, COL_COLLECTION_DATE_TO);
607
        try {
608
            if (dateFrom != null && dateFrom.equals(dateTo)){
609
                dateTo = null;
610
            }
611
            TimePeriod gatheringPeriod = TimePeriodParser.parseEnglishDate(dateFrom, dateTo);
612
            facade.setGatheringPeriod(gatheringPeriod);
613
        } catch (Exception e) {
614
            state.getResult().addError("Error creating gathering date", e, null, line);
615
        }
616

    
617
        //collector
618
        String collectorType = record.get(COL_COLLECTOR_TYPE);
619
        String collectors = record.get(COL_COLLECTORS);
620
        AgentBase<?> collector;
621
        if (collectorType.startsWith("Anonymous")){
622
            collector = getAnonymous();
623
        }else if (collectorType.equals("Brother") || collectorType.equals("Person")){
624
            Person person = Person.NewInstance();
625
            if (collectorType.equals("Person")){
626
                person.setLastname(record.get(COL_COLLECTOR_LASTNAME));
627
                String initials = CdmUtils.concat("", record.get(COL_COLLECTOR_FIRSTNAME), record.get(COL_COLLECTOR_MIDDLENAME));
628
                initials = (initials == null)? null : initials.replaceAll("\\s", "");
629
                person.setInitials(initials);
630
                String full = person.getTitleCache();
631
                if (!full.equals(collectors)){
632
                    person.setTitleCache(collectors, true);
633
                    //TODO use setCollectorTitle in future
634
                }
635
            }else{
636
                person.setTitleCache(collectors, true);
637
                person.setPrefix("Hno.");
638
                person.setFirstname(collectors.replace("Hno.", "").trim());
639
            }
640
            collector = person;
641
        }else if (collectorType.equals("Group")){
642
            collector = Team.NewTitledInstance(collectors, collectors);
643
        }else if (collectorType.equals("Institution")){
644
            collector = Institution.NewNamedInstance(collectors);
645
        }else{
646
            String message = "Collector type " + collectorType + " not yet supported by import. Collector not added.";
647
            state.getResult().addError(message, null, line);
648
            collector = null;
649
        }
650
        collector = getDeduplicationHelper(state).getExistingAgent(state, collector);
651
        facade.setCollector(collector);
652
    }
653

    
654

    
655
    /**
656
     * @param string
657
     * @return
658
     */
659
    private String unknownToNull(String string) {
660
        if (string == null || string.equalsIgnoreCase("unknown")){
661
            return null;
662
        }else{
663
            return string;
664
        }
665
    }
666

    
667
    private Person anonymous;
668
    private Person getAnonymous() {
669
        if (anonymous != null){
670
            return anonymous;
671
        }
672
        anonymous = CdmBase.deproxy(getAgentService().find(uuidAnonymous), Person.class);
673
        if (anonymous == null){
674
            anonymous = Person.NewTitledInstance("Anon.");
675
            anonymous.setUuid(uuidAnonymous);
676
            getAgentService().save(anonymous);
677
        }
678
        return anonymous;
679
    }
680

    
681

    
682
    /**
683
     * @param facade
684
     * @param state
685
     * @param line
686
     * @param record
687
     * @param taxonBase
688
     */
689
    private void makeDetermination(DerivedUnit specimen, SimpleExcelSpecimenImportState<CONFIG> state, String line,
690
            HashMap<String, String> record, TaxonName taxonName) {
691

    
692
        DeterminationEvent determination;
693
        determination = DeterminationEvent.NewInstance(taxonName, specimen);
694
        determination.setPreferredFlag(true);
695

    
696
        //determiner/identifier
697
        TeamOrPersonBase<?> determiner = makeDeterminer(state, record, line);
698
        determination.setDeterminer(determiner);
699

    
700
        //date
701
        TimePeriod date = makeIdentificationDate(state, record, line);
702
        determination.setTimeperiod(date);
703

    
704
        //qualifier
705
        DefinedTerm qualifier = makeDeterminationQualifier(state, record, line);
706
        determination.setModifier(qualifier);
707

    
708
        //history
709
        String history = record.get(COL_IDENTIFICATION_HISTORY);
710
        if (history != null){
711
            String label = "Identification History";
712
            String text = label;
713
            ExtensionType detHistoryType = getExtensionType(state, uuidExtTypeIdentificationHistory, label, text, null);
714
            specimen.addExtension(history, detHistoryType);
715
        }
716
    }
717

    
718

    
719
    /**
720
     * @param state
721
     * @param record
722
     * @param line
723
     * @return
724
     */
725
    private TeamOrPersonBase<?> makeDeterminer(SimpleExcelSpecimenImportState<CONFIG> state,
726
            HashMap<String, String> record, String line) {
727
        String identifier = record.get(COL_IDENTIFIER);
728
        if (identifier == null){
729
            return null;
730
        }else if (identifier.equals("Anon.")){
731
            return getAnonymous();
732
        }else{
733
            Person person = Person.NewInstance();
734
            person.setTitleCache(identifier, true);
735

    
736
            String[] splits = identifier.split("\\.");
737
            int length = splits.length;
738
            if (splits[length - 1].equals("")){
739
                splits[length - 2]= splits[length - 2]+".";
740
                length--;
741
            }
742
            if (splits[length - 1].startsWith("-")){
743
                splits[length - 2]= splits[length - 2]+"." + splits[length - 1];
744
                length--;
745
            }
746
            String lastName = splits[length - 1];
747
            String initials = null;
748
            for (int i= 0; i < length-1;i++){
749
                initials = CdmUtils.concat("", initials, splits[i]+".");
750
            }
751
            person.setLastname(lastName);
752
            person.setInitials(initials);
753
            TeamOrPersonBase<?> result = getDeduplicationHelper(state).getExistingAuthor(state, person);
754
            return result;
755
        }
756
    }
757

    
758

    
759
    /**
760
     * @param state
761
     * @param record
762
     * @param line
763
     * @return
764
     */
765
    private TimePeriod makeIdentificationDate(SimpleExcelSpecimenImportState<CONFIG> state,
766
            HashMap<String, String> record, String line) {
767
        String strDate = record.get(COL_IDENTIFICATION_DATE);
768
        if (strDate == null || strDate.equals("s.n.")){
769
            return null;
770
        }
771
        String[] splits = strDate.split("/");
772
        String strYear = splits[splits.length-1];
773
        String strMonth = splits.length < 2? null:splits[splits.length-2];
774
        String strDay = splits.length < 3? null:splits[splits.length-3];
775

    
776
        Integer year = intFromString(state, strYear, line, COL_IDENTIFICATION_DATE);
777
        Integer month = intFromString(state, strMonth, line, COL_IDENTIFICATION_DATE);
778
        Integer day = intFromString(state, strDay, line, COL_IDENTIFICATION_DATE);
779
        Partial start = TimePeriodParser.makePartialFromDateParts(year, month, day);
780
        return TimePeriod.NewInstance(start);
781
    }
782

    
783

    
784
    /**
785
     * @param state
786
     * @param record
787
     * @param line
788
     * @return
789
     */
790
    private DefinedTerm makeDeterminationQualifier(SimpleExcelSpecimenImportState<CONFIG> state,
791
            HashMap<String, String> record, String line) {
792
        String qualifier = record.get(COL_IDENTIFICATION_QUALIFIER);
793
        if (qualifier != null){
794
            try {
795
                return DeterminationModifierParser.parseDeterminationQualifier(qualifier);
796
            } catch (UnknownCdmTypeException e) {
797
                //TODO add to terms
798
                if (qualifier.equals("vel. aff.")){
799

    
800
                    DefinedTerm velAff = (DefinedTerm)getTermService().find(uuidDetQualVelAff);
801
                    if (velAff == null){
802
                        velAff = DefinedTerm.NewModifierInstance(qualifier, qualifier, qualifier);
803
                        velAff.setUuid(uuidDetQualVelAff);
804
                        getTermService().save(velAff);
805
                    }
806
                    return velAff;
807
                }
808
                state.getResult().addError("Determination qualifier could not be recognized: " + qualifier, null, line);
809
                return null;
810
            }
811
        }else{
812
            return null;
813
        }
814
    }
815

    
816

    
817
    /**
818
     * @param taxonBase
819
     * @return
820
     */
821
    private Taxon getTaxon(TaxonBase<?> taxonBase) {
822
        if (taxonBase.isInstanceOf(Synonym.class)){
823
            return CdmBase.deproxy(taxonBase, Synonym.class).getAcceptedTaxon();
824
        }else{
825
            return CdmBase.deproxy(taxonBase, Taxon.class);
826
        }
827
    }
828

    
829

    
830
    /**
831
     * @param state
832
     * @param line
833
     * @param record
834
     * @param noStr
835
     * @return
836
     */
837
    private TaxonBase<?> getTaxonByCdmId(SimpleExcelSpecimenImportState<CONFIG> state, String line,
838
            HashMap<String, String> record, String noStr) {
839

    
840
        String strUuidTaxon = record.get(COL_TAXON_UUID);
841
        if (strUuidTaxon != null && ! state.getConfig().isOnlyNonCdmTaxa()){
842
            UUID uuidTaxon;
843
            try {
844
                uuidTaxon = UUID.fromString(strUuidTaxon);
845
            } catch (Exception e) {
846
                state.getResult().addError("Taxon uuid has incorrect format. Taxon could not be loaded. Data not imported.", null, line);
847
                return null;
848
            }
849
            TaxonBase<?> result = getTaxonService().find(uuidTaxon);
850
            if (result == null){
851
                state.getResult().addError("Taxon for uuid  "+strUuidTaxon+" could not be found in database. "
852
                        + "Taxon could not be loaded. Data not imported.", null, line);
853

    
854
            }
855
            return result;
856
        }else{
857
            return null;
858
        }
859
    }
860

    
861
    @Override
862
    protected IdentifiableSource makeOriginalSource(SimpleExcelSpecimenImportState<CONFIG> state) {
863
        return IdentifiableSource.NewDataImportInstance(getValue(state.getOriginalRecord(), COL_VOUCHER_ID), COL_VOUCHER_ID, getSourceCitation(state));
864
    }
865

    
866
    /**
867
     * @param state
868
     * @return
869
     */
870
    protected Reference getSourceCitation(SimpleExcelSpecimenImportState<CONFIG> state) {
871
        Reference source = state.getConfig().getSourceReference();
872
        if (source.getId() == 0){
873
            Reference persisted = getReferenceService().find(source.getUuid());
874
            if (persisted == null){
875
                getReferenceService().saveOrUpdate(source);
876
            }else{
877
                state.getConfig().setSourceReference(persisted);
878
                source = persisted;
879
            }
880
        }
881
        return source;
882
    }
883
}
(3-3/6)