3 * Copyright (C) 2017 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
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.
10 package eu
.etaxonomy
.cdm
.io
.bogota
;
12 import java
.util
.Arrays
;
13 import java
.util
.HashMap
;
14 import java
.util
.List
;
16 import java
.util
.UUID
;
18 import org
.apache
.log4j
.Logger
;
19 import org
.joda
.time
.Partial
;
20 import org
.springframework
.stereotype
.Component
;
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
.ExtensionType
;
32 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableSource
;
33 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
34 import eu
.etaxonomy
.cdm
.model
.common
.TimePeriod
;
35 import eu
.etaxonomy
.cdm
.model
.description
.IndividualsAssociation
;
36 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
37 import eu
.etaxonomy
.cdm
.model
.location
.Country
;
38 import eu
.etaxonomy
.cdm
.model
.location
.NamedArea
;
39 import eu
.etaxonomy
.cdm
.model
.location
.NamedAreaLevel
;
40 import eu
.etaxonomy
.cdm
.model
.location
.NamedAreaType
;
41 import eu
.etaxonomy
.cdm
.model
.location
.Point
;
42 import eu
.etaxonomy
.cdm
.model
.location
.ReferenceSystem
;
43 import eu
.etaxonomy
.cdm
.model
.name
.NomenclaturalCode
;
44 import eu
.etaxonomy
.cdm
.model
.name
.Rank
;
45 import eu
.etaxonomy
.cdm
.model
.name
.SpecimenTypeDesignation
;
46 import eu
.etaxonomy
.cdm
.model
.name
.SpecimenTypeDesignationStatus
;
47 import eu
.etaxonomy
.cdm
.model
.name
.TaxonName
;
48 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameFactory
;
49 import eu
.etaxonomy
.cdm
.model
.occurrence
.Collection
;
50 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
;
51 import eu
.etaxonomy
.cdm
.model
.occurrence
.DeterminationEvent
;
52 import eu
.etaxonomy
.cdm
.model
.occurrence
.FieldUnit
;
53 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
54 import eu
.etaxonomy
.cdm
.model
.taxon
.Synonym
;
55 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
56 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
57 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNode
;
58 import eu
.etaxonomy
.cdm
.model
.term
.DefinedTerm
;
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
;
72 public class BogotaSpecimenImport
<CONFIG
extends BogotaSpecimenImportConfigurator
>
73 extends SimpleExcelSpecimenImport
<CONFIG
> {
75 private static final long serialVersionUID
= -884838817884874228L;
76 @SuppressWarnings("unused")
77 private static final Logger logger
= Logger
.getLogger(BogotaSpecimenImport
.class);
79 private static final String COL_TAXON_UUID
= "Platform Name ID = cdmID";
80 private static final String COL_VOUCHER_ID
= "Voucher ID";
82 private static final String COL_FAMILY
= "Family";
83 private static final String COL_GENUS
= "Genus";
84 private static final String COL_SPECIFIC_EPI
= "Specific Epithet";
85 private static final String COL_BASIONYM_AUTHOR
= "Author in parenthesis";
86 private static final String COL_AUTHOR
= "Author";
87 private static final String COL_IDENTIFIER
= "Identifier";
88 private static final String COL_IDENTIFICATION_DATE
= "Identification date";
89 private static final String COL_IDENTIFICATION_QUALIFIER
= "Qualifier";
90 private static final String COL_TYPUS
= "Type";
91 private static final String COL_IDENTIFICATION_HISTORY
= "Identification history";
92 private static final String COL_COLLECTOR_VERBATIM
= "Verbatim Collectors (Originalfeld JBB)";
93 private static final String COL_COLLECTOR_LASTNAME
= "Primary Collector Last Name (Originalfeld JBB)";
94 private static final String COL_COLLECTOR_FIRSTNAME
= "Primary Collector First Name Initial (Originalfeld JBB)";
95 private static final String COL_COLLECTOR_MIDDLENAME
= "Primary Collector Middle Name Initial (Originalfeld JBB)";
96 private static final String COL_COLLECTOR_TYPE
= "Primary collector type (Originalfeld JBB)";
97 private static final String COL_COLLECTOR_NUMBER
= "Collector's No";
98 private static final String COL_COLLECTORS
= "Collectors";
99 private static final String COL_COLLECTION_DATE_FROM
= "Collection Date from";
100 private static final String COL_COLLECTION_DATE_TO
= "Collection Date to";
101 private static final String COL_ALTITUDE_FROM
= "Altitude Value from";
102 private static final String COL_ALTITUDE_TO
= "Altitude Value to";
103 private static final String COL_ALTITUDE_UNIT
= "Altitude Unit";
104 private static final String COL_LOCALITY
= "Locality";
105 private static final String COL_LOCALITY_ID
= "LocalityID";
106 private static final String COL_LATITUDE
= "Latitude";
107 private static final String COL_LONGITUDE
= "Longitude";
108 private static final String COL_ERROR_DISTANCE
= "Error distance in m";
109 private static final String COL_COUNTRY
= "Country";
110 private static final String COL_STATE_AREA
= "State/Province/Greater Area";
111 private static final String COL_GEO_METHOD
= "Geocode Method";
112 private static final String COL_HABITUS
= "Habitus";
113 private static final String COL_COLLECTION
= "[Series] Voucher location";
115 private static final UUID uuidAnonymous
= UUID
.fromString("2303f043-6e92-4afa-9082-7719e78a1c8a");
116 private static final UUID uuidBogota
= UUID
.fromString("95b6cb03-8452-4439-98bd-8c1aa3c1da4e");
117 private static final UUID uuidDefaultGeocodMethod
= UUID
.fromString("0983e680-b0ca-4e46-8df7-0f1d757a2e01");
118 private static final UUID uuidExtTypeIdentificationHistory
= UUID
.fromString("7cee5c29-e16b-4e6f-ad57-bf7044259375");
119 private static final UUID uuidDetQualVelAff
= UUID
.fromString("511a0c23-2646-4035-b570-36bdc2eb5557");
121 // @SuppressWarnings("unchecked")
122 private ImportDeduplicationHelper
<SimpleExcelSpecimenImportState
<?
>> deduplicationHelper
;
124 private final Map
<String
, TaxonNode
> taxonNodeMap
= new HashMap
<>();
125 private Reference secRef
;
128 protected String
getWorksheetName(CONFIG config
) {
129 return "To be imported";
136 protected void firstPass(SimpleExcelSpecimenImportState
<CONFIG
> state
) {
138 Map
<String
, String
> record
= state
.getOriginalRecord();
140 String voucherId
= getValue(record
, COL_VOUCHER_ID
);
141 if (!isInInterval(state
)){
144 String line
= state
.getCurrentLine() + " (id:"+ voucherId
+"): ";
145 if (state
.getCurrentLine() % 100 == 0){System
.out
.println(line
);}
149 TaxonBase
<?
> taxonBase
= getTaxonByCdmId(state
, line
, record
, voucherId
);
150 if (taxonBase
!= null){
151 handleRecordForTaxon(state
, voucherId
, line
, taxonBase
);
152 }else if (record
.get(COL_TAXON_UUID
)!= null){
155 taxonBase
= getOrCreateNewTaxon(state
, record
, line
);
156 handleRecordForTaxon(state
, voucherId
, line
, taxonBase
);
159 } catch (Exception e
) {
160 state
.getResult().addError("An unexpected exception appeared in record", e
, null, line
);
172 private Taxon
getOrCreateNewTaxon(SimpleExcelSpecimenImportState
<CONFIG
> state
,
173 Map
<String
, String
> record
, String line
) {
174 String familyStr
= record
.get(COL_FAMILY
);
175 String genusStr
= record
.get(COL_GENUS
);
177 TaxonName speciesName
= makeSpeciesName(state
, line
);
178 String titleCache
= speciesName
.getTitleCache();
179 TaxonNode existingSpeciesNode
= taxonNodeMap
.get(titleCache
);
180 if (existingSpeciesNode
!= null){
181 return existingSpeciesNode
.getTaxon();
183 Reference sec
= getSecReference(state
);
184 Taxon newTaxon
= Taxon
.NewInstance(speciesName
, sec
);
185 newTaxon
.addSource(makeOriginalSource(state
));
186 TaxonNode existingGenusNode
= taxonNodeMap
.get(genusStr
);
187 if (existingGenusNode
== null){
188 TaxonName genusName
= TaxonNameFactory
.NewBotanicalInstance(Rank
.GENUS());
189 genusName
.setGenusOrUninomial(genusStr
);
190 Taxon newGenus
= Taxon
.NewInstance(genusName
, sec
);
191 newGenus
.addSource(makeOriginalSource(state
));
192 TaxonNode existingFamilyNode
= taxonNodeMap
.get(familyStr
);
193 if (existingFamilyNode
== null){
194 TaxonName familyName
= TaxonNameFactory
.NewBotanicalInstance(Rank
.FAMILY());
195 familyName
.setGenusOrUninomial(familyStr
);
196 Taxon newFamily
= Taxon
.NewInstance(familyName
, sec
);
197 newFamily
.addSource(makeOriginalSource(state
));
198 TaxonNode plantaeNode
= taxonNodeMap
.get("Plantae");
199 existingFamilyNode
= plantaeNode
.addChildTaxon(newFamily
, null, null);
200 save(existingFamilyNode
);
202 existingGenusNode
= existingFamilyNode
.addChildTaxon(newGenus
, null, null);
203 save(existingGenusNode
);
205 existingSpeciesNode
= existingGenusNode
.addChildTaxon(newTaxon
, null, null);
206 save(existingSpeciesNode
);
213 * @param existingFamilyNode
215 private void save(TaxonNode node
) {
216 getTaxonNodeService().saveOrUpdate(node
);
217 taxonNodeMap
.put(node
.getTaxon().getName().getTitleCache(), node
);
225 private Reference
getSecReference(SimpleExcelSpecimenImportState
<CONFIG
> state
) {
226 if (this.secRef
== null){
227 Reference sec
= state
.getConfig().getSecReference();
228 this.secRef
= getReferenceService().find(sec
.getUuid());
229 if (this.secRef
== null){
231 getReferenceService().save(sec
);
245 private TaxonName
makeSpeciesName(SimpleExcelSpecimenImportState
<CONFIG
> state
, String line
) {
246 Map
<String
, String
> record
= state
.getOriginalRecord();
247 String genus
= record
.get(COL_GENUS
);
248 String species
= record
.get(COL_SPECIFIC_EPI
);
249 String basionymAuthorStr
= record
.get(COL_BASIONYM_AUTHOR
);
250 String authorStr
= record
.get(COL_AUTHOR
);
251 INonViralNameParser
<?
> parser
= NonViralNameParserImpl
.NewInstance();
252 String fullName
= genus
+ " " + species
+
253 (basionymAuthorStr
== null ?
"" : " ("+basionymAuthorStr
+")")
255 TaxonName newName
= (TaxonName
)parser
.parseFullName(fullName
, NomenclaturalCode
.ICNAFP
, Rank
.SPECIES());
256 String titleCache
= newName
.getTitleCache();
257 if (newName
.isProtectedTitleCache()){
258 state
.getResult().addWarning("Name not parsable: " + fullName
);
260 if (taxonNodeMap
.get(titleCache
)== null){
261 getDeduplicationHelper(state
).replaceAuthorNamesAndNomRef(state
, newName
);
262 newName
.addSource(makeOriginalSource(state
));
272 private void initTaxonMap(SimpleExcelSpecimenImportState
<CONFIG
> state
) {
273 if (taxonNodeMap
.isEmpty()){
274 List
<String
> propertyPaths
= Arrays
.asList(new String
[]{"taxon.name"});
275 List
<TaxonNode
> list
= getTaxonNodeService().list(null, null, null, null, propertyPaths
);
276 for (TaxonNode node
: list
){
277 if (node
.getTaxon()!= null){
278 String strName
= node
.getTaxon().getName().getTitleCache();
279 TaxonNode existingNode
= taxonNodeMap
.get(strName
);
280 if (existingNode
!= null){
281 state
.getResult().addWarning("Taxon name exists more than once while initializing taxon map: " + strName
, "initTaxonMap");
283 taxonNodeMap
.put(strName
, node
);
298 protected void handleRecordForTaxon(SimpleExcelSpecimenImportState
<CONFIG
> state
,
299 String voucherId
, String line
, TaxonBase
<?
> taxonBase
) {
301 Map
<String
, String
> record
= state
.getOriginalRecord();
302 Taxon taxon
= getTaxon(taxonBase
);
304 TaxonDescription taxonDescription
= getTaxonDescription(state
, line
, taxon
);
306 DerivedUnit specimen
= makeSpecimen(state
, line
, record
, voucherId
, taxonBase
);
308 IndividualsAssociation indAssoc
= IndividualsAssociation
.NewInstance(specimen
);
309 indAssoc
.addImportSource(voucherId
, COL_VOUCHER_ID
, getSourceCitation(state
), null);
310 taxonDescription
.addElement(indAssoc
);
318 private boolean isInInterval(SimpleExcelSpecimenImportState
<CONFIG
> state
) {
319 Integer min
= state
.getConfig().getMinLineNumber();
320 Integer max
= state
.getConfig().getMaxLineNumber();
321 Integer current
= state
.getCurrentLine();
322 if (current
< min
|| current
> max
){
336 private TaxonDescription
getTaxonDescription(SimpleExcelSpecimenImportState
<CONFIG
> state
, String line
,
338 Reference ref
= getSourceCitation(state
);
339 TaxonDescription desc
= this.getTaxonDescription(taxon
, ref
, ! IMAGE_GALLERY
, ! CREATE
);
341 //TODO move title creation into base method
342 desc
= this.getTaxonDescription(taxon
, ref
, ! IMAGE_GALLERY
, CREATE
);
343 desc
.setTitleCache("Specimen Excel import for " + taxon
.getName().getTitleCache(), true);
356 private DerivedUnit
makeSpecimen(SimpleExcelSpecimenImportState
<CONFIG
> state
, String line
,
357 Map
<String
, String
> record
, String voucherId
, TaxonBase
<?
> taxonBase
) {
359 DerivedUnitFacade facade
= DerivedUnitFacade
.NewPreservedSpecimenInstance();
360 facade
.setAccessionNumber(voucherId
);
361 makeDetermination(facade
.innerDerivedUnit(), state
, line
, record
, taxonBase
.getName());
362 makeTypus(facade
.innerDerivedUnit(), state
, line
, record
, taxonBase
.getName());
363 makeCollectorFields(facade
, state
, line
, record
);
364 makeLocationFields(facade
, state
, line
, record
);
365 makeHabitus(facade
, state
, line
, record
);
366 makeCollection(facade
, state
, line
, record
);
367 DerivedUnit specimen
= facade
.innerDerivedUnit();
368 specimen
.addSource(makeOriginalSource(state
));
369 FieldUnit fieldUnit
= facade
.innerFieldUnit();
370 fieldUnit
.addSource(makeOriginalSource(state
));
376 * @param innerDerivedUnit
382 private void makeTypus(DerivedUnit specimen
, SimpleExcelSpecimenImportState
<CONFIG
> state
, String line
,
383 Map
<String
, String
> record
, TaxonName name
) {
384 String typus
= record
.get(COL_TYPUS
);
386 SpecimenTypeDesignationStatus status
;
388 status
= SpecimenTypeParser
.parseSpecimenTypeStatus(typus
);
389 SpecimenTypeDesignation designation
= SpecimenTypeDesignation
.NewInstance();
390 designation
.setTypeStatus(status
);
391 name
.addSpecimenTypeDesignation(specimen
, status
, null, null, null, false, false);
392 } catch (UnknownCdmTypeException e
) {
393 state
.getResult().addWarning("Type designation could not be parsed", null, line
);
405 private void makeCollection(DerivedUnitFacade facade
, SimpleExcelSpecimenImportState
<CONFIG
> state
,
406 String line
, Map
<String
, String
> record
) {
407 String strCollection
= record
.get(COL_COLLECTION
);
408 String collectionFormat
= ".*\\([A-Z]{2,4}\\)";
409 if (!strCollection
.matches(collectionFormat
)){
410 String message
= "Voucher location format does not match the expected format. Voucher '(" + strCollection
+ ")' location not added.";
411 state
.getResult().addError(message
, null, line
);
414 String
[] splits
= strCollection
.split("\\(");
415 String collectionName
= splits
[0];
416 String collectionCode
= splits
[1].replace(")", "");
417 Collection collection
= Collection
.NewInstance();
418 collection
.setName(collectionName
);
419 collection
.setCode(collectionCode
);
420 collection
= getDeduplicationHelper(state
).getExistingCollection(state
, collection
);
421 facade
.setCollection(collection
);
429 private ImportDeduplicationHelper
<SimpleExcelSpecimenImportState
<?
>> getDeduplicationHelper(SimpleExcelSpecimenImportState
<CONFIG
> state
) {
430 if (deduplicationHelper
== null){
431 deduplicationHelper
= ImportDeduplicationHelper
.NewInstance(this, state
);
433 return deduplicationHelper
;
443 private void makeHabitus(DerivedUnitFacade facade
, SimpleExcelSpecimenImportState
<CONFIG
> state
, String line
,
444 Map
<String
, String
> record
) {
445 String habitus
= record
.get(COL_HABITUS
);
446 if (habitus
!= null){
447 facade
.setPlantDescription(habitus
);
459 private void makeLocationFields(DerivedUnitFacade facade
, SimpleExcelSpecimenImportState
<CONFIG
> state
, String line
,
460 Map
<String
, String
> record
) {
462 String strAltitudeFrom
= record
.get(COL_ALTITUDE_FROM
);
463 String strAltitudeTo
= record
.get(COL_ALTITUDE_TO
);
464 Integer intAltitudeFrom
= intFromString(state
, strAltitudeFrom
, line
, COL_ALTITUDE_FROM
);
465 Integer intAltitudeTo
= intFromString(state
, strAltitudeTo
, line
, COL_ALTITUDE_TO
);
466 if (intAltitudeFrom
!= null){
467 facade
.setAbsoluteElevation(intAltitudeFrom
);
468 if (!intAltitudeFrom
.equals(intAltitudeTo
)){
469 facade
.setAbsoluteElevationMax(intAltitudeTo
);
471 if (!record
.get(COL_ALTITUDE_UNIT
).equals("m")){
472 state
.getResult().addWarning("Altitude unit is not m but " + record
.get(COL_ALTITUDE_UNIT
), "makeLocationFields", line
);
475 checkNoToIfNoFrom(strAltitudeFrom
, strAltitudeTo
, state
, line
, COL_ALTITUDE_TO
);
478 String locality
= record
.get(COL_LOCALITY
);
479 if (locality
!= null){ //should always exist
480 facade
.setLocality(locality
, Language
.SPANISH_CASTILIAN());
484 String strLatitude
= record
.get(COL_LATITUDE
);
485 String strLongitude
= record
.get(COL_LONGITUDE
);
486 String strError
= record
.get(COL_ERROR_DISTANCE
);
487 Double dblLatitude
= doubleFromString(state
, strLatitude
, line
, COL_LATITUDE
);
488 Double dblLongitude
= doubleFromString(state
, strLongitude
, line
, COL_LONGITUDE
);
489 Integer intError
= intFromString(state
, strError
, line
, COL_ERROR_DISTANCE
);
490 ReferenceSystem referenceSystem
= makeReferenceSystem(state
, record
, line
);
492 if (dblLatitude
!= null || dblLongitude
!= null || intError
!= null){ //should always exist
493 Point exactLocation
= Point
.NewInstance(dblLongitude
, dblLatitude
, referenceSystem
, intError
);
494 facade
.setExactLocation(exactLocation
);
498 String strCountry
= record
.get(COL_COUNTRY
);
499 if (strCountry
!= null){
500 if (strCountry
.equals("Colombia")){
501 Country colombia
= Country
.COLOMBIAREPUBLICOF();
502 colombia
.setLabel("Colombia");
503 getTermService().saveOrUpdate(colombia
);
504 facade
.setCountry(colombia
);
506 state
.getResult().addWarning("Country was not Colombia as expected but " + strCountry
,
507 "makeLocationFields", line
);
512 String strStateArea
= record
.get(COL_STATE_AREA
);
513 if (strStateArea
!= null){
514 if (strStateArea
.replaceAll("\\s", "").equalsIgnoreCase("Bogotá,D.C.")){
515 NamedArea bogota
= makeBogota(state
, line
);
516 facade
.addCollectingArea(bogota
);
518 state
.getResult().addWarning(COL_STATE_AREA
+ " was not 'Bogotá, D.C.' as expected but " + strCountry
,
519 "makeLocationFields", line
);
526 * @param strAltitudeFrom
527 * @param strAltitudeTo
530 * @param colAltitudeTo
532 private void checkNoToIfNoFrom(String strFrom
, String strTo
,
533 SimpleExcelSpecimenImportState
<CONFIG
> state
,
534 String line
, String toAttributeName
) {
535 if (isNotBlank(strTo
) && isBlank(strFrom
)){
536 String message
= "A min-max attribute has a max value (%s) but no min value. This is invalid."
537 + " The max value attribute name is %s.";
538 message
= String
.format(message
, strTo
, toAttributeName
);
539 state
.getResult().addWarning(message
, null, line
);
543 private ReferenceSystem defaultGeocodeMethod
;
551 private ReferenceSystem
makeReferenceSystem(SimpleExcelSpecimenImportState
<CONFIG
> state
,
552 Map
<String
, String
> record
, String line
) {
553 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.";
554 String strRefSys
= record
.get(COL_GEO_METHOD
);
555 if (strRefSys
== null){
557 }else if (!strRefSys
.equals(defaultStrRefSys
)){
558 state
.getResult().addError("The expected Geocode Method is not the expected default method. Geocode Method was not added.", null, line
);
560 }else if (defaultGeocodeMethod
!= null){
561 return defaultGeocodeMethod
;
563 String label
= "Point radius method";
564 String description
= defaultStrRefSys
;
565 String labelAbbrev
= "PRM";
566 defaultGeocodeMethod
= getReferenceSystem(state
, uuidDefaultGeocodMethod
,
567 label
, description
, labelAbbrev
, null);
568 return defaultGeocodeMethod
;
572 private NamedArea bogota
;
578 private NamedArea
makeBogota(SimpleExcelSpecimenImportState
<CONFIG
> state
, String line
) {
582 String label
= "Bogotá, D.C.";
583 NamedAreaType areaType
= NamedAreaType
.ADMINISTRATION_AREA();
584 NamedAreaLevel level
= NamedAreaLevel
.STATE();
585 bogota
= getNamedArea(state
, uuidBogota
, label
, label
, null, areaType
,
586 level
, null, null, null);
598 private void makeCollectorFields(DerivedUnitFacade facade
, SimpleExcelSpecimenImportState
<CONFIG
> state
, String line
,
599 Map
<String
, String
> record
) {
602 facade
.setFieldNumber(record
.get(COL_COLLECTOR_NUMBER
));
605 String dateFrom
= unknownToNull((record
.get(COL_COLLECTION_DATE_FROM
)));
606 String dateTo
= unknownToNull(record
.get(COL_COLLECTION_DATE_TO
));
607 checkNoToIfNoFrom(dateFrom
, dateTo
, state
, line
, COL_COLLECTION_DATE_TO
);
609 if (dateFrom
!= null && dateFrom
.equals(dateTo
)){
612 TimePeriod gatheringPeriod
= TimePeriodParser
.parseEnglishDate(dateFrom
, dateTo
);
613 facade
.setGatheringPeriod(gatheringPeriod
);
614 } catch (Exception e
) {
615 state
.getResult().addError("Error creating gathering date", e
, null, line
);
619 String collectorType
= record
.get(COL_COLLECTOR_TYPE
);
620 String collectors
= record
.get(COL_COLLECTORS
);
621 AgentBase
<?
> collector
;
622 if (collectorType
.startsWith("Anonymous")){
623 collector
= getAnonymous();
624 }else if (collectorType
.equals("Brother") || collectorType
.equals("Person")){
625 Person person
= Person
.NewInstance();
626 if (collectorType
.equals("Person")){
627 person
.setFamilyName(record
.get(COL_COLLECTOR_LASTNAME
));
628 String initials
= CdmUtils
.concat("", record
.get(COL_COLLECTOR_FIRSTNAME
), record
.get(COL_COLLECTOR_MIDDLENAME
));
629 initials
= (initials
== null)?
null : initials
.replaceAll("\\s", "");
630 person
.setInitials(initials
);
631 String full
= person
.getTitleCache();
632 if (!full
.equals(collectors
)){
633 person
.setTitleCache(collectors
, true);
634 //TODO use setCollectorTitle in future
637 person
.setTitleCache(collectors
, true);
638 person
.setPrefix("Hno.");
639 person
.setGivenName(collectors
.replace("Hno.", "").trim());
642 }else if (collectorType
.equals("Group")){
643 collector
= Team
.NewTitledInstance(collectors
, collectors
);
644 }else if (collectorType
.equals("Institution")){
645 collector
= Institution
.NewNamedInstance(collectors
);
647 String message
= "Collector type " + collectorType
+ " not yet supported by import. Collector not added.";
648 state
.getResult().addError(message
, null, line
);
651 collector
= getDeduplicationHelper(state
).getExistingAgent(state
, collector
);
652 facade
.setCollector(collector
);
660 private String
unknownToNull(String string
) {
661 if (string
== null || string
.equalsIgnoreCase("unknown")){
668 private Person anonymous
;
669 private Person
getAnonymous() {
670 if (anonymous
!= null){
673 anonymous
= CdmBase
.deproxy(getAgentService().find(uuidAnonymous
), Person
.class);
674 if (anonymous
== null){
675 anonymous
= Person
.NewTitledInstance("Anon.");
676 anonymous
.setUuid(uuidAnonymous
);
677 getAgentService().save(anonymous
);
690 private void makeDetermination(DerivedUnit specimen
, SimpleExcelSpecimenImportState
<CONFIG
> state
, String line
,
691 Map
<String
, String
> record
, TaxonName taxonName
) {
693 DeterminationEvent determination
;
694 determination
= DeterminationEvent
.NewInstance(taxonName
, specimen
);
695 determination
.setPreferredFlag(true);
697 //determiner/identifier
698 TeamOrPersonBase
<?
> determiner
= makeDeterminer(state
, record
, line
);
699 determination
.setDeterminer(determiner
);
702 TimePeriod date
= makeIdentificationDate(state
, record
, line
);
703 determination
.setTimeperiod(date
);
706 DefinedTerm qualifier
= makeDeterminationQualifier(state
, record
, line
);
707 determination
.setModifier(qualifier
);
710 String history
= record
.get(COL_IDENTIFICATION_HISTORY
);
711 if (history
!= null){
712 String label
= "Identification History";
714 ExtensionType detHistoryType
= getExtensionType(state
, uuidExtTypeIdentificationHistory
, label
, text
, null);
715 specimen
.addExtension(history
, detHistoryType
);
726 private TeamOrPersonBase
<?
> makeDeterminer(SimpleExcelSpecimenImportState
<CONFIG
> state
,
727 Map
<String
, String
> record
, String line
) {
728 String identifier
= record
.get(COL_IDENTIFIER
);
729 if (identifier
== null){
731 }else if (identifier
.equals("Anon.")){
732 return getAnonymous();
734 Person person
= Person
.NewInstance();
735 person
.setTitleCache(identifier
, true);
737 String
[] splits
= identifier
.split("\\.");
738 int length
= splits
.length
;
739 if (splits
[length
- 1].equals("")){
740 splits
[length
- 2]= splits
[length
- 2]+".";
743 if (splits
[length
- 1].startsWith("-")){
744 splits
[length
- 2]= splits
[length
- 2]+"." + splits
[length
- 1];
747 String familyName
= splits
[length
- 1];
748 String initials
= null;
749 for (int i
= 0; i
< length
-1;i
++){
750 initials
= CdmUtils
.concat("", initials
, splits
[i
]+".");
752 person
.setFamilyName(familyName
);
753 person
.setInitials(initials
);
754 TeamOrPersonBase
<?
> result
= getDeduplicationHelper(state
).getExistingAuthor(state
, person
);
766 private TimePeriod
makeIdentificationDate(SimpleExcelSpecimenImportState
<CONFIG
> state
,
767 Map
<String
, String
> record
, String line
) {
768 String strDate
= record
.get(COL_IDENTIFICATION_DATE
);
769 if (strDate
== null || strDate
.equals("s.n.")){
772 String
[] splits
= strDate
.split("/");
773 String strYear
= splits
[splits
.length
-1];
774 String strMonth
= splits
.length
< 2?
null:splits
[splits
.length
-2];
775 String strDay
= splits
.length
< 3?
null:splits
[splits
.length
-3];
777 Integer year
= intFromString(state
, strYear
, line
, COL_IDENTIFICATION_DATE
);
778 Integer month
= intFromString(state
, strMonth
, line
, COL_IDENTIFICATION_DATE
);
779 Integer day
= intFromString(state
, strDay
, line
, COL_IDENTIFICATION_DATE
);
780 Partial start
= TimePeriodParser
.makePartialFromDateParts(year
, month
, day
);
781 return TimePeriod
.NewInstance(start
);
791 private DefinedTerm
makeDeterminationQualifier(SimpleExcelSpecimenImportState
<CONFIG
> state
,
792 Map
<String
, String
> record
, String line
) {
793 String qualifier
= record
.get(COL_IDENTIFICATION_QUALIFIER
);
794 if (qualifier
!= null){
796 return DeterminationModifierParser
.parseDeterminationQualifier(qualifier
);
797 } catch (UnknownCdmTypeException e
) {
799 if (qualifier
.equals("vel. aff.")){
801 DefinedTerm velAff
= (DefinedTerm
)getTermService().find(uuidDetQualVelAff
);
803 velAff
= DefinedTerm
.NewModifierInstance(qualifier
, qualifier
, qualifier
);
804 velAff
.setUuid(uuidDetQualVelAff
);
805 getTermService().save(velAff
);
809 state
.getResult().addError("Determination qualifier could not be recognized: " + qualifier
, null, line
);
822 private Taxon
getTaxon(TaxonBase
<?
> taxonBase
) {
823 if (taxonBase
.isInstanceOf(Synonym
.class)){
824 return CdmBase
.deproxy(taxonBase
, Synonym
.class).getAcceptedTaxon();
826 return CdmBase
.deproxy(taxonBase
, Taxon
.class);
838 private TaxonBase
<?
> getTaxonByCdmId(SimpleExcelSpecimenImportState
<CONFIG
> state
, String line
,
839 Map
<String
, String
> record
, String noStr
) {
841 String strUuidTaxon
= record
.get(COL_TAXON_UUID
);
842 if (strUuidTaxon
!= null && ! state
.getConfig().isOnlyNonCdmTaxa()){
845 uuidTaxon
= UUID
.fromString(strUuidTaxon
);
846 } catch (Exception e
) {
847 state
.getResult().addError("Taxon uuid has incorrect format. Taxon could not be loaded. Data not imported.", null, line
);
850 TaxonBase
<?
> result
= getTaxonService().find(uuidTaxon
);
852 state
.getResult().addError("Taxon for uuid "+strUuidTaxon
+" could not be found in database. "
853 + "Taxon could not be loaded. Data not imported.", null, line
);
863 protected IdentifiableSource
makeOriginalSource(SimpleExcelSpecimenImportState
<CONFIG
> state
) {
864 return IdentifiableSource
.NewDataImportInstance(getValue(state
.getOriginalRecord(), COL_VOUCHER_ID
), COL_VOUCHER_ID
, getSourceCitation(state
));
871 protected Reference
getSourceCitation(SimpleExcelSpecimenImportState
<CONFIG
> state
) {
872 Reference source
= state
.getConfig().getSourceReference();
873 if (source
.getId() == 0){
874 Reference persisted
= getReferenceService().find(source
.getUuid());
875 if (persisted
== null){
876 getReferenceService().saveOrUpdate(source
);
878 state
.getConfig().setSourceReference(persisted
);