Project

General

Profile

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

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

    
74
    private static final long serialVersionUID = -884838817884874228L;
75
    @SuppressWarnings("unused")
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
    private final Map<String, TaxonNode> taxonNodeMap = new HashMap<>();
121
    private Reference secRef;
122

    
123
    @Override
124
    protected String getWorksheetName(CONFIG config) {
125
        return "To be imported";
126
    }
127

    
128
    /**
129
     * {@inheritDoc}
130
     */
131
    @Override
132
    protected void firstPass(SimpleExcelSpecimenImportState<CONFIG> state) {
133

    
134
        Map<String, String> record = state.getOriginalRecord();
135

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

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

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

    
160
    }
161

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

    
206
    }
207

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

    
215
    }
216

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

    
231

    
232
        return this.secRef;
233
    }
234

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

    
261
        return newName;
262
    }
263

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

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

    
297
        Map<String, String> record = state.getOriginalRecord();
298
        Taxon taxon = getTaxon(taxonBase);
299

    
300
        TaxonDescription taxonDescription = getTaxonDescription(state, line, taxon);
301

    
302
        DerivedUnit specimen = makeSpecimen(state, line, record, voucherId, taxonBase);
303

    
304
        IndividualsAssociation indAssoc = IndividualsAssociation.NewInstance(specimen);
305
        indAssoc.addImportSource(voucherId, COL_VOUCHER_ID, getSourceCitation(state), null);
306
        taxonDescription.addElement(indAssoc);
307
    }
308

    
309

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

    
325

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

    
344

    
345
    /**
346
     * @param state
347
     * @param line
348
     * @param record
349
     * @param voucherId
350
     * @return
351
     */
352
    private DerivedUnit makeSpecimen(SimpleExcelSpecimenImportState<CONFIG> state, String line,
353
            Map<String, String> record, String voucherId, TaxonBase<?> taxonBase) {
354

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

    
370

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

    
394

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

    
420
    private void makeHabitus(DerivedUnitFacade facade, SimpleExcelSpecimenImportState<CONFIG> state, String line,
421
            Map<String, String> record) {
422
        String habitus = record.get(COL_HABITUS);
423
        if (habitus != null){
424
            facade.setPlantDescription(habitus);
425
        }
426
    }
427

    
428

    
429
    /**
430
     * @param facade
431
     * @param state
432
     * @param line
433
     * @param record
434
     * @param voucherId
435
     */
436
    private void makeLocationFields(DerivedUnitFacade facade, SimpleExcelSpecimenImportState<CONFIG> state, String line,
437
            Map<String, String> record) {
438
        //Altitude
439
        String strAltitudeFrom = record.get(COL_ALTITUDE_FROM);
440
        String strAltitudeTo = record.get(COL_ALTITUDE_TO);
441
        Integer intAltitudeFrom = intFromString(state, strAltitudeFrom, line, COL_ALTITUDE_FROM);
442
        Integer intAltitudeTo = intFromString(state, strAltitudeTo, line, COL_ALTITUDE_TO);
443
        if (intAltitudeFrom != null){
444
            facade.setAbsoluteElevation(intAltitudeFrom);
445
            if (!intAltitudeFrom.equals(intAltitudeTo)){
446
                facade.setAbsoluteElevationMax(intAltitudeTo);
447
            }
448
            if (!record.get(COL_ALTITUDE_UNIT).equals("m")){
449
                state.getResult().addWarning("Altitude unit is not m but " + record.get(COL_ALTITUDE_UNIT), "makeLocationFields", line);
450
            }
451
        }
452
        checkNoToIfNoFrom(strAltitudeFrom, strAltitudeTo, state, line, COL_ALTITUDE_TO);
453

    
454
        //locality
455
        String locality = record.get(COL_LOCALITY);
456
        if (locality != null){  //should always exist
457
            facade.setLocality(locality, Language.SPANISH_CASTILIAN());
458
        }
459

    
460
        //Lat + Long
461
        String strLatitude = record.get(COL_LATITUDE);
462
        String strLongitude = record.get(COL_LONGITUDE);
463
        String strError = record.get(COL_ERROR_DISTANCE);
464
        Double dblLatitude = doubleFromString(state, strLatitude, line, COL_LATITUDE);
465
        Double dblLongitude = doubleFromString(state, strLongitude, line, COL_LONGITUDE);
466
        Integer intError = intFromString(state, strError, line, COL_ERROR_DISTANCE);
467
        ReferenceSystem referenceSystem = makeReferenceSystem(state, record, line);
468

    
469
        if (dblLatitude != null || dblLongitude != null || intError != null){ //should always exist
470
            Point exactLocation = Point.NewInstance(dblLongitude, dblLatitude, referenceSystem, intError);
471
            facade.setExactLocation(exactLocation);
472
        }
473

    
474
        //Country
475
        String strCountry = record.get(COL_COUNTRY);
476
        if (strCountry != null){
477
            if (strCountry.equals("Colombia")){
478
                Country colombia = Country.COLOMBIAREPUBLICOF();
479
                colombia.setLabel("Colombia");
480
                getTermService().saveOrUpdate(colombia);
481
                facade.setCountry(colombia);
482
            }else{
483
                state.getResult().addWarning("Country was not Colombia as expected but " +  strCountry,
484
                        "makeLocationFields", line);
485
            }
486
        }
487

    
488
        //State
489
        String strStateArea = record.get(COL_STATE_AREA);
490
        if (strStateArea != null){
491
            if (strStateArea.replaceAll("\\s", "").equalsIgnoreCase("Bogotá,D.C.")){
492
                NamedArea bogota = makeBogota(state, line);
493
                facade.addCollectingArea(bogota);
494
            }else{
495
                state.getResult().addWarning(COL_STATE_AREA + " was not 'Bogotá,  D.C.' as expected but " +  strCountry,
496
                        "makeLocationFields", line);
497
            }
498
        }
499
    }
500

    
501

    
502
    /**
503
     * @param strAltitudeFrom
504
     * @param strAltitudeTo
505
     * @param state
506
     * @param line
507
     * @param colAltitudeTo
508
     */
509
    private void checkNoToIfNoFrom(String strFrom, String strTo,
510
            SimpleExcelSpecimenImportState<CONFIG> state,
511
            String line, String toAttributeName) {
512
        if (isNotBlank(strTo) && isBlank(strFrom)){
513
            String message = "A min-max attribute has a max value (%s) but no min value. This is invalid."
514
                    + " The max value attribute name is %s.";
515
            message = String.format(message, strTo, toAttributeName);
516
            state.getResult().addWarning(message, null, line);
517
        }
518
    }
519

    
520
    private ReferenceSystem defaultGeocodeMethod;
521

    
522
    /**
523
     * @param state
524
     * @param record
525
     * @param line
526
     * @return
527
     */
528
    private ReferenceSystem makeReferenceSystem(SimpleExcelSpecimenImportState<CONFIG> state,
529
            Map<String, String> record, String line) {
530
        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.";
531
        String strRefSys = record.get(COL_GEO_METHOD);
532
        if (strRefSys == null){
533
            return null;
534
        }else if (!strRefSys.equals(defaultStrRefSys)){
535
            state.getResult().addError("The expected Geocode Method is not the expected default method. Geocode Method was not added.", null, line);
536
            return null;
537
        }else if (defaultGeocodeMethod != null){
538
            return defaultGeocodeMethod;
539
        }else{
540
            String label = "Point radius method";
541
            String description = defaultStrRefSys;
542
            String labelAbbrev = "PRM";
543
            defaultGeocodeMethod = getReferenceSystem(state, uuidDefaultGeocodMethod,
544
                    label, description, labelAbbrev, null);
545
            return defaultGeocodeMethod;
546
        }
547
    }
548

    
549
    private NamedArea bogota;
550
    /**
551
     * @param state
552
     * @param line
553
     * @return
554
     */
555
    private NamedArea makeBogota(SimpleExcelSpecimenImportState<CONFIG> state, String line) {
556
        if (bogota != null){
557
            return bogota;
558
        }else{
559
            String label = "Bogotá, D.C.";
560
            NamedAreaType areaType = NamedAreaType.ADMINISTRATION_AREA();
561
            NamedAreaLevel level = NamedAreaLevel.STATE();
562
            bogota = getNamedArea(state, uuidBogota, label, label, null, areaType,
563
                    level, null, null, null);
564
            return bogota;
565
        }
566
    }
567

    
568

    
569
    /**
570
     * @param facade
571
     * @param state
572
     * @param line
573
     * @param record
574
     */
575
    private void makeCollectorFields(DerivedUnitFacade facade, SimpleExcelSpecimenImportState<CONFIG> state, String line,
576
            Map<String, String> record) {
577

    
578
        //collector number
579
        facade.setFieldNumber(record.get(COL_COLLECTOR_NUMBER));
580

    
581
        //gathering date
582
        String dateFrom = unknownToNull((record.get(COL_COLLECTION_DATE_FROM)));
583
        String dateTo = unknownToNull(record.get(COL_COLLECTION_DATE_TO));
584
        checkNoToIfNoFrom(dateFrom, dateTo, state, line, COL_COLLECTION_DATE_TO);
585
        try {
586
            if (dateFrom != null && dateFrom.equals(dateTo)){
587
                dateTo = null;
588
            }
589
            TimePeriod gatheringPeriod = TimePeriodParser.parseEnglishDate(dateFrom, dateTo);
590
            facade.setGatheringPeriod(gatheringPeriod);
591
        } catch (Exception e) {
592
            state.getResult().addError("Error creating gathering date", e, null, line);
593
        }
594

    
595
        //collector
596
        String collectorType = record.get(COL_COLLECTOR_TYPE);
597
        String collectors = record.get(COL_COLLECTORS);
598
        AgentBase<?> collector;
599
        if (collectorType.startsWith("Anonymous")){
600
            collector = getAnonymous();
601
        }else if (collectorType.equals("Brother") || collectorType.equals("Person")){
602
            Person person = Person.NewInstance();
603
            if (collectorType.equals("Person")){
604
                person.setFamilyName(record.get(COL_COLLECTOR_LASTNAME));
605
                String initials = CdmUtils.concat("", record.get(COL_COLLECTOR_FIRSTNAME), record.get(COL_COLLECTOR_MIDDLENAME));
606
                initials = (initials == null)? null : initials.replaceAll("\\s", "");
607
                person.setInitials(initials);
608
                String full = person.getTitleCache();
609
                if (!full.equals(collectors)){
610
                    person.setTitleCache(collectors, true);
611
                    //TODO use setCollectorTitle in future
612
                }
613
            }else{
614
                person.setTitleCache(collectors, true);
615
                person.setPrefix("Hno.");
616
                person.setGivenName(collectors.replace("Hno.", "").trim());
617
            }
618
            collector = person;
619
        }else if (collectorType.equals("Group")){
620
            collector = Team.NewTitledInstance(collectors, collectors);
621
        }else if (collectorType.equals("Institution")){
622
            collector = Institution.NewNamedInstance(collectors);
623
        }else{
624
            String message = "Collector type " + collectorType + " not yet supported by import. Collector not added.";
625
            state.getResult().addError(message, null, line);
626
            collector = null;
627
        }
628
        collector = state.getDeduplicationHelper().getExistingAgent(collector);
629
        facade.setCollector(collector);
630
    }
631

    
632

    
633
    /**
634
     * @param string
635
     * @return
636
     */
637
    private String unknownToNull(String string) {
638
        if (string == null || string.equalsIgnoreCase("unknown")){
639
            return null;
640
        }else{
641
            return string;
642
        }
643
    }
644

    
645
    private Person anonymous;
646
    private Person getAnonymous() {
647
        if (anonymous != null){
648
            return anonymous;
649
        }
650
        anonymous = CdmBase.deproxy(getAgentService().find(uuidAnonymous), Person.class);
651
        if (anonymous == null){
652
            anonymous = Person.NewTitledInstance("Anon.");
653
            anonymous.setUuid(uuidAnonymous);
654
            getAgentService().save(anonymous);
655
        }
656
        return anonymous;
657
    }
658

    
659

    
660
    /**
661
     * @param facade
662
     * @param state
663
     * @param line
664
     * @param record
665
     * @param taxonBase
666
     */
667
    private void makeDetermination(DerivedUnit specimen, SimpleExcelSpecimenImportState<CONFIG> state, String line,
668
            Map<String, String> record, TaxonName taxonName) {
669

    
670
        DeterminationEvent determination;
671
        determination = DeterminationEvent.NewInstance(taxonName, specimen);
672
        determination.setPreferredFlag(true);
673

    
674
        //determiner/identifier
675
        TeamOrPersonBase<?> determiner = makeDeterminer(state, record, line);
676
        determination.setDeterminer(determiner);
677

    
678
        //date
679
        TimePeriod date = makeIdentificationDate(state, record, line);
680
        determination.setTimeperiod(date);
681

    
682
        //qualifier
683
        DefinedTerm qualifier = makeDeterminationQualifier(state, record, line);
684
        determination.setModifier(qualifier);
685

    
686
        //history
687
        String history = record.get(COL_IDENTIFICATION_HISTORY);
688
        if (history != null){
689
            String label = "Identification History";
690
            String text = label;
691
            ExtensionType detHistoryType = getExtensionType(state, uuidExtTypeIdentificationHistory, label, text, null);
692
            specimen.addExtension(history, detHistoryType);
693
        }
694
    }
695

    
696

    
697
    /**
698
     * @param state
699
     * @param record
700
     * @param line
701
     * @return
702
     */
703
    private TeamOrPersonBase<?> makeDeterminer(SimpleExcelSpecimenImportState<CONFIG> state,
704
            Map<String, String> record, String line) {
705
        String identifier = record.get(COL_IDENTIFIER);
706
        if (identifier == null){
707
            return null;
708
        }else if (identifier.equals("Anon.")){
709
            return getAnonymous();
710
        }else{
711
            Person person = Person.NewInstance();
712
            person.setTitleCache(identifier, true);
713

    
714
            String[] splits = identifier.split("\\.");
715
            int length = splits.length;
716
            if (splits[length - 1].equals("")){
717
                splits[length - 2]= splits[length - 2]+".";
718
                length--;
719
            }
720
            if (splits[length - 1].startsWith("-")){
721
                splits[length - 2]= splits[length - 2]+"." + splits[length - 1];
722
                length--;
723
            }
724
            String familyName = splits[length - 1];
725
            String initials = null;
726
            for (int i= 0; i < length-1;i++){
727
                initials = CdmUtils.concat("", initials, splits[i]+".");
728
            }
729
            person.setFamilyName(familyName);
730
            person.setInitials(initials);
731
            TeamOrPersonBase<?> result = state.getDeduplicationHelper().getExistingAuthor(person);
732
            return result;
733
        }
734
    }
735

    
736

    
737
    /**
738
     * @param state
739
     * @param record
740
     * @param line
741
     * @return
742
     */
743
    private TimePeriod makeIdentificationDate(SimpleExcelSpecimenImportState<CONFIG> state,
744
            Map<String, String> record, String line) {
745
        String strDate = record.get(COL_IDENTIFICATION_DATE);
746
        if (strDate == null || strDate.equals("s.n.")){
747
            return null;
748
        }
749
        String[] splits = strDate.split("/");
750
        String strYear = splits[splits.length-1];
751
        String strMonth = splits.length < 2? null:splits[splits.length-2];
752
        String strDay = splits.length < 3? null:splits[splits.length-3];
753

    
754
        Integer year = intFromString(state, strYear, line, COL_IDENTIFICATION_DATE);
755
        Integer month = intFromString(state, strMonth, line, COL_IDENTIFICATION_DATE);
756
        Integer day = intFromString(state, strDay, line, COL_IDENTIFICATION_DATE);
757
        Partial start = TimePeriodParser.makePartialFromDateParts(year, month, day);
758
        return TimePeriod.NewInstance(start);
759
    }
760

    
761

    
762
    /**
763
     * @param state
764
     * @param record
765
     * @param line
766
     * @return
767
     */
768
    private DefinedTerm makeDeterminationQualifier(SimpleExcelSpecimenImportState<CONFIG> state,
769
            Map<String, String> record, String line) {
770
        String qualifier = record.get(COL_IDENTIFICATION_QUALIFIER);
771
        if (qualifier != null){
772
            try {
773
                return DeterminationModifierParser.parseDeterminationQualifier(qualifier);
774
            } catch (UnknownCdmTypeException e) {
775
                //TODO add to terms
776
                if (qualifier.equals("vel. aff.")){
777

    
778
                    DefinedTerm velAff = (DefinedTerm)getTermService().find(uuidDetQualVelAff);
779
                    if (velAff == null){
780
                        velAff = DefinedTerm.NewModifierInstance(qualifier, qualifier, qualifier);
781
                        velAff.setUuid(uuidDetQualVelAff);
782
                        getTermService().save(velAff);
783
                    }
784
                    return velAff;
785
                }
786
                state.getResult().addError("Determination qualifier could not be recognized: " + qualifier, null, line);
787
                return null;
788
            }
789
        }else{
790
            return null;
791
        }
792
    }
793

    
794

    
795
    /**
796
     * @param taxonBase
797
     * @return
798
     */
799
    private Taxon getTaxon(TaxonBase<?> taxonBase) {
800
        if (taxonBase.isInstanceOf(Synonym.class)){
801
            return CdmBase.deproxy(taxonBase, Synonym.class).getAcceptedTaxon();
802
        }else{
803
            return CdmBase.deproxy(taxonBase, Taxon.class);
804
        }
805
    }
806

    
807

    
808
    /**
809
     * @param state
810
     * @param line
811
     * @param record
812
     * @param noStr
813
     * @return
814
     */
815
    private TaxonBase<?> getTaxonByCdmId(SimpleExcelSpecimenImportState<CONFIG> state, String line,
816
            Map<String, String> record, String noStr) {
817

    
818
        String strUuidTaxon = record.get(COL_TAXON_UUID);
819
        if (strUuidTaxon != null && ! state.getConfig().isOnlyNonCdmTaxa()){
820
            UUID uuidTaxon;
821
            try {
822
                uuidTaxon = UUID.fromString(strUuidTaxon);
823
            } catch (Exception e) {
824
                state.getResult().addError("Taxon uuid has incorrect format. Taxon could not be loaded. Data not imported.", null, line);
825
                return null;
826
            }
827
            TaxonBase<?> result = getTaxonService().find(uuidTaxon);
828
            if (result == null){
829
                state.getResult().addError("Taxon for uuid  "+strUuidTaxon+" could not be found in database. "
830
                        + "Taxon could not be loaded. Data not imported.", null, line);
831

    
832
            }
833
            return result;
834
        }else{
835
            return null;
836
        }
837
    }
838

    
839
    @Override
840
    protected IdentifiableSource makeOriginalSource(SimpleExcelSpecimenImportState<CONFIG> state) {
841
        return IdentifiableSource.NewDataImportInstance(getValue(state.getOriginalRecord(), COL_VOUCHER_ID), COL_VOUCHER_ID, getSourceCitation(state));
842
    }
843

    
844
    /**
845
     * @param state
846
     * @return
847
     */
848
    protected Reference getSourceCitation(SimpleExcelSpecimenImportState<CONFIG> state) {
849
        Reference source = state.getConfig().getSourceReference();
850
        if (source.getId() == 0){
851
            Reference persisted = getReferenceService().find(source.getUuid());
852
            if (persisted == null){
853
                getReferenceService().saveOrUpdate(source);
854
            }else{
855
                state.getConfig().setSourceReference(persisted);
856
                source = persisted;
857
            }
858
        }
859
        return source;
860
    }
861
}
(3-3/6)