fix AccessionNumber->Locality bug in Specimen Excel Import
[cdmlib.git] / cdmlib-io / src / main / java / eu / etaxonomy / cdm / io / specimen / excel / in / SpecimenCdmExcelImport.java
index ebb0a26cb96c65088180c61ed129a4260b2113c6..b43d33e9b8f34e162b0986e0bef67605a054044c 100644 (file)
@@ -19,7 +19,6 @@ import org.apache.log4j.Logger;
 import org.springframework.stereotype.Component;
 
 import eu.etaxonomy.cdm.api.facade.DerivedUnitFacade;
-import eu.etaxonomy.cdm.api.facade.DerivedUnitFacade.DerivedUnitType;
 import eu.etaxonomy.cdm.api.service.config.MatchingTaxonConfigurator;
 import eu.etaxonomy.cdm.common.CdmUtils;
 import eu.etaxonomy.cdm.io.common.ICdmIO;
@@ -33,6 +32,7 @@ import eu.etaxonomy.cdm.model.agent.Team;
 import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;
 import eu.etaxonomy.cdm.model.common.Annotation;
 import eu.etaxonomy.cdm.model.common.AnnotationType;
+import eu.etaxonomy.cdm.model.common.CdmBase;
 import eu.etaxonomy.cdm.model.common.IdentifiableSource;
 import eu.etaxonomy.cdm.model.common.Language;
 import eu.etaxonomy.cdm.model.common.TimePeriod;
@@ -43,22 +43,29 @@ import eu.etaxonomy.cdm.model.location.NamedArea;
 import eu.etaxonomy.cdm.model.location.NamedAreaLevel;
 import eu.etaxonomy.cdm.model.location.NamedAreaType;
 import eu.etaxonomy.cdm.model.location.ReferenceSystem;
-import eu.etaxonomy.cdm.model.location.WaterbodyOrCountry;
+import eu.etaxonomy.cdm.model.location.Country;
+import eu.etaxonomy.cdm.model.name.BotanicalName;
 import eu.etaxonomy.cdm.model.name.NomenclaturalCode;
 import eu.etaxonomy.cdm.model.name.NonViralName;
 import eu.etaxonomy.cdm.model.name.Rank;
 import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignation;
 import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignationStatus;
 import eu.etaxonomy.cdm.model.name.TaxonNameBase;
+import eu.etaxonomy.cdm.model.name.ZoologicalName;
 import eu.etaxonomy.cdm.model.occurrence.Collection;
-import eu.etaxonomy.cdm.model.occurrence.DerivedUnitBase;
+import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
 import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent;
+import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationType;
 import eu.etaxonomy.cdm.model.reference.Reference;
 import eu.etaxonomy.cdm.model.reference.ReferenceFactory;
 import eu.etaxonomy.cdm.model.taxon.Taxon;
+import eu.etaxonomy.cdm.model.taxon.TaxonBase;
 import eu.etaxonomy.cdm.persistence.query.MatchMode;
+import eu.etaxonomy.cdm.strategy.exceptions.StringNotParsableException;
 import eu.etaxonomy.cdm.strategy.exceptions.UnknownCdmTypeException;
+import eu.etaxonomy.cdm.strategy.parser.INonViralNameParser;
 import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImpl;
+import eu.etaxonomy.cdm.strategy.parser.TimePeriodParser;
 
 /**
  * @author a.mueller
@@ -81,6 +88,8 @@ public class SpecimenCdmExcelImport  extends ExcelTaxonOrSpecimenImportBase<Spec
        private static final String COLLECTION_DATE_COLUMN = "(?i)(CollectionDate)";
        private static final String COLLECTION_DATE_END_COLUMN = "(?i)(CollectionDateEnd)";
        private static final String COLLECTOR_COLUMN = "(?i)(Collector)";
+       private static final String COLLECTORS_COLUMN = "(?i)(Collectors)";
+       private static final String PRIMARY_COLLECTOR_COLUMN = "(?i)(PrimaryCollector)";
        private static final String LONGITUDE_COLUMN = "(?i)(Longitude)";
        private static final String LATITUDE_COLUMN = "(?i)(Latitude)";
        private static final String REFERENCE_SYSTEM_COLUMN = "(?i)(ReferenceSystem)";
@@ -112,12 +121,10 @@ public class SpecimenCdmExcelImport  extends ExcelTaxonOrSpecimenImportBase<Spec
        private static final String DETERMINATION_AUTHOR_COLUMN = "(?i)(Author)";
        private static final String DETERMINATION_MODIFIER_COLUMN = "(?i)(DeterminationModifier)";
        private static final String DETERMINED_BY_COLUMN = "(?i)(DeterminationBy)";
-       private static final String DETERMINED_WHEN_COLUMN = "(?i)(DeterminationWhen)";
+       private static final String DETERMINED_WHEN_COLUMN = "(?i)(Det(ermination)?When)";
        private static final String DETERMINATION_NOTES_COLUMN = "(?i)(DeterminationNote)";
        private static final String EXTENSION_COLUMN = "(?i)(Ext(ension)?)";
        
-       private static final String IGNORE_COLUMN = "(?i)(Ignore|Not)";
-       
 
        public SpecimenCdmExcelImport() {
                super();
@@ -127,9 +134,8 @@ public class SpecimenCdmExcelImport  extends ExcelTaxonOrSpecimenImportBase<Spec
 
 
        @Override
-       protected boolean analyzeSingleValue(KeyValue keyValue, SpecimenCdmExcelImportState state) {
+       protected void analyzeSingleValue(KeyValue keyValue, SpecimenCdmExcelImportState state) {
                SpecimenRow row = state.getCurrentRow();
-               boolean success = true;
                String value = keyValue.value;
                if(keyValue.key.matches(BASIS_OF_RECORD_COLUMN)) {
                        row.setBasisOfRecord(value);
@@ -147,6 +153,8 @@ public class SpecimenCdmExcelImport  extends ExcelTaxonOrSpecimenImportBase<Spec
                        row.setAltitudeMax(value);              
                } else if(keyValue.key.matches(COLLECTOR_COLUMN)) {
                        row.putCollector(keyValue.index, value);                
+               } else if(keyValue.key.matches(PRIMARY_COLLECTOR_COLUMN)) {
+                       row.setPrimaryCollector(value);         
                } else if(keyValue.key.matches(ECOLOGY_COLUMN)) {
                        row.setEcology(value);
                } else if(keyValue.key.matches(PLANT_DESCRIPTION_COLUMN)) {
@@ -157,6 +165,8 @@ public class SpecimenCdmExcelImport  extends ExcelTaxonOrSpecimenImportBase<Spec
                        row.setCollectingDate(value);           
                } else if(keyValue.key.matches(COLLECTION_DATE_END_COLUMN)) {
                        row.setCollectingDateEnd(value);                
+               } else if(keyValue.key.matches(COLLECTORS_COLUMN)) {
+                       row.setCollectors(value);       
                } else if(keyValue.key.matches(COLLECTOR_COLUMN)) {
                        row.putCollector(keyValue.index, value);        
                } else if(keyValue.key.matches(COLLECTORS_NUMBER_COLUMN)) {
@@ -175,11 +185,12 @@ public class SpecimenCdmExcelImport  extends ExcelTaxonOrSpecimenImportBase<Spec
                        }else{
                                logger.warn("Not yet implemented");
                        }
-               
+               } else if(keyValue.key.matches(LANGUAGE)) {
+                       row.setLanguage(value);         
                        
                        
                } else if(keyValue.key.matches(ACCESSION_NUMBER_COLUMN)) {
-                       row.setLocality(value);         
+                       row.setAccessionNumber(value);          
                } else if(keyValue.key.matches(BARCODE_COLUMN)) {
                        row.setBarcode(value);          
                } else if(keyValue.key.matches(UNIT_NOTES_COLUMN)) {
@@ -196,6 +207,8 @@ public class SpecimenCdmExcelImport  extends ExcelTaxonOrSpecimenImportBase<Spec
                        row.putDeterminationInfraSpeciesEpi(keyValue.index, value);                     
                } else if(keyValue.key.matches(RANK_COLUMN)) {
                        row.putDeterminationRank(keyValue.index, value);                        
+               } else if(keyValue.key.matches(TAXON_UUID_COLUMN)) {
+                       row.putDeterminationTaxonUuid(keyValue.index, value);                   
                } else if(keyValue.key.matches(FULL_NAME_COLUMN)) {
                        row.putDeterminationFullName(keyValue.index, value);                    
                } else if(keyValue.key.matches(DETERMINATION_AUTHOR_COLUMN)) {
@@ -221,7 +234,7 @@ public class SpecimenCdmExcelImport  extends ExcelTaxonOrSpecimenImportBase<Spec
                
                
                } else if(keyValue.key.matches(SOURCE_COLUMN)) {
-                       row.putSourceReference(keyValue.index, getOrMakeReference(state, value));       
+                       row.putSourceReference(keyValue.index, getOrMakeReference(state, value) );      
                } else if(keyValue.key.matches(ID_IN_SOURCE_COLUMN)) {
                        row.putIdInSource(keyValue.index, value);               
                } else if(keyValue.key.matches(EXTENSION_COLUMN)) {
@@ -231,31 +244,41 @@ public class SpecimenCdmExcelImport  extends ExcelTaxonOrSpecimenImportBase<Spec
                                logger.warn("Extension without postfix not yet implemented");
                        }
                        
-               } else if(keyValue.key.matches(IGNORE_COLUMN)) {
-                       logger.debug("Ignored column" + keyValue.originalKey);          
                }else {
-                       success = false;
+                       state.setUnsuccessfull();
                        logger.error("Unexpected column header " + keyValue.originalKey);
                }
 
-       return success;
+       return;
        }
 
 
        @Override
-       protected boolean firstPass(SpecimenCdmExcelImportState state) {
+       protected void firstPass(SpecimenCdmExcelImportState state) {
                SpecimenRow row = state.getCurrentRow();
                
                //basis of record
-               DerivedUnitType type = DerivedUnitType.valueOf2(row.getBasisOfRecord());
+               SpecimenOrObservationType type = SpecimenOrObservationType.valueOf2(row.getBasisOfRecord());
                if (type == null){
-                       String message = "%s is not a valid BasisOfRecord. 'Unknown' is used instead.";
-                       message = String.format(message, row.getBasisOfRecord());
+                       String message = "%s is not a valid BasisOfRecord. 'Unknown' is used instead in line %d.";
+                       message = String.format(message, row.getBasisOfRecord(), state.getCurrentLine());
                        logger.warn(message);
-                       type = DerivedUnitType.DerivedUnit;
+                       type = SpecimenOrObservationType.DerivedUnit;
                }
                DerivedUnitFacade facade = DerivedUnitFacade.NewInstance(type);
                
+               
+               Language lang = Language.DEFAULT();
+               if (StringUtils.isNotBlank(row.getLanguage())){
+                       Language langIso = getTermService().getLanguageByIso(row.getLanguage());
+                       if (langIso == null){
+                               String message = "Language could not be recognized: %s. Use default language instead. Line %d.";
+                               message = String.format(message, langIso, state.getCurrentLine());
+                       }else{
+                               lang = langIso;
+                       }
+               }
+               
                //country
                handleCountry(facade, row, state);
                handleAreas(facade,row, state);
@@ -264,14 +287,14 @@ public class SpecimenCdmExcelImport  extends ExcelTaxonOrSpecimenImportBase<Spec
                facade.setLocality(row.getLocality());
                facade.setFieldNotes(row.getFieldNotes());
                facade.setFieldNumber(row.getCollectorsNumber());
-               facade.setEcology(row.getEcology());
-               facade.setPlantDescription(row.getPlantDescription());
+               facade.setEcology(row.getEcology(), lang);
+               facade.setPlantDescription(row.getPlantDescription(), lang);
 //             facade.setSex(row.get)
                handleExactLocation(facade, row, state);
                facade.setCollector(getOrMakeAgent(state, row.getCollectors()));
+               facade.setPrimaryCollector(getOrMakePrimaryCollector(facade, row.getPrimaryCollector(), state));
                handleAbsoluteElevation(facade, row, state);
                
-               
                //derivedUnit
                facade.setBarcode(row.getBarcode());
                facade.setAccessionNumber(row.getAccessionNumber());
@@ -280,7 +303,7 @@ public class SpecimenCdmExcelImport  extends ExcelTaxonOrSpecimenImportBase<Spec
                        facade.addSource(source);
                }
                for (SpecimenTypeDesignation designation : row.getTypeDesignations()){
-                       //FIXME
+                       logger.warn("FIXME"); //FIXME
 //                     facade.innerDerivedUnit().addSpecimenTypeDesignation(designation);
                }
                handleDeterminations(state, row, facade);
@@ -292,7 +315,7 @@ public class SpecimenCdmExcelImport  extends ExcelTaxonOrSpecimenImportBase<Spec
                
                //save
                getOccurrenceService().save(facade.innerDerivedUnit());
-               return true;
+               return;
        }
 
        private void handleAbsoluteElevation(DerivedUnitFacade facade, SpecimenRow row, SpecimenCdmExcelImportState state) {
@@ -309,7 +332,7 @@ public class SpecimenCdmExcelImport  extends ExcelTaxonOrSpecimenImportBase<Spec
                        int value = Integer.valueOf(altitude);
                        facade.setAbsoluteElevation(value);
                } catch (NumberFormatException e) {
-                       String message = "Absolute elevation / Altitude '%s' is not an integer number in line %d";
+                       String message = "Absolute elevation / altitude '%s' is not an integer number in line %d";
                        message = String.format(message, row.getAltitude(), state.getCurrentLine());
                        logger.warn(message);
                        return;
@@ -379,20 +402,52 @@ public class SpecimenCdmExcelImport  extends ExcelTaxonOrSpecimenImportBase<Spec
         */
        private void handleDeterminations(SpecimenCdmExcelImportState state,SpecimenRow row, DerivedUnitFacade facade) {
                boolean isFirstDetermination = true;
+               DeterminationLight commonDetermination = row.getCommonDetermination();
+               Taxon commonTaxon = null;
+               TaxonNameBase<?,?> commonName = null;
+               
+               boolean hasCommonTaxonInfo = (commonDetermination == null) ? false : commonDetermination.hasTaxonInformation();
+               if (hasCommonTaxonInfo && commonDetermination != null){
+                       TaxonBase<?> taxonBase = null;
+                       if (StringUtils.isNotBlank(commonDetermination.taxonUuid)){
+                               UUID taxonUuid = UUID.fromString(commonDetermination.taxonUuid);
+                               taxonBase = getTaxonService().find(taxonUuid);
+                               if (taxonBase == null){
+                                       String message = "Taxon for uuid %s not found in line %d.";
+                                       message = String.format(message, taxonUuid.toString(), state.getCurrentLine());
+                                       logger.warn(message);
+                               }
+                       }else{
+                               taxonBase = findBestMatchingTaxon(state, commonDetermination, state.getConfig().isCreateTaxonIfNotExists());
+                       }
+                       commonTaxon = getAcceptedTaxon(taxonBase);
+                       if (taxonBase != null){
+                               commonName = taxonBase.getName();
+                       }else{
+                               commonTaxon = createTaxonFromDetermination(state, commonDetermination);
+                               commonName = commonTaxon.getName();
+                       }
+               }
+               
+               
                for (DeterminationLight determinationLight : row.getDetermination()){
-                       Taxon taxon = findBestMatchingTaxon(state, determinationLight, state.getConfig().isCreateTaxonIfNotExists());
-                       TaxonNameBase<?,?> name = findBestMatchingName(state, determinationLight);
+                       Taxon taxon;
+                       if (! hasCommonTaxonInfo){
+                               taxon = findBestMatchingTaxon(state, determinationLight, state.getConfig().isCreateTaxonIfNotExists());
+                       }else{
+                               taxon = commonTaxon;
+                       }
                        if (taxon != null){
                                getTaxonService().saveOrUpdate(taxon);
                                if (state.getConfig().isMakeIndividualAssociations() && taxon != null){
                                        IndividualsAssociation indivAssociciation = IndividualsAssociation.NewInstance();
-                                       DerivedUnitBase<?> du = facade.innerDerivedUnit();
+                                       DerivedUnit du = facade.innerDerivedUnit();
                                        indivAssociciation.setAssociatedSpecimenOrObservation(du);
                                        getTaxonDescription(taxon).addElement(indivAssociciation);
                                        Feature feature = Feature.INDIVIDUALS_ASSOCIATION();
-                                       if (facade.getType().equals(DerivedUnitType.Specimen)){
+                                       if (facade.getType().isPreservedSpecimen()){
                                                feature = Feature.SPECIMEN();
-                                       }else if (facade.getType().equals(DerivedUnitType.Observation)){
+                                       }else if (facade.getType().isFeatureObservation()){
                                                feature = Feature.OBSERVATION();
                                        }
                                        if (state.getConfig().isUseMaterialsExaminedForIndividualsAssociations()){
@@ -407,8 +462,19 @@ public class SpecimenCdmExcelImport  extends ExcelTaxonOrSpecimenImportBase<Spec
                                        facade.addDetermination(detEvent);
                                }
                        }
-                       if (name != null){
-                               if (isFirstDetermination && state.getConfig().isFirstDeterminationIsStoredUnder()){
+                       
+                       if (isFirstDetermination && state.getConfig().isFirstDeterminationIsStoredUnder()){
+                               TaxonNameBase<?,?> name;
+                               
+                               if (!hasCommonTaxonInfo){
+                                       name = findBestMatchingName(state, determinationLight);
+                               }else{
+                                       if (commonName == null){
+                                               commonName = findBestMatchingName(state, commonDetermination);
+                                       }
+                                       name = commonName;
+                               }
+                               if (name != null){
                                        facade.setStoredUnder(name);
                                }
                        }
@@ -416,6 +482,104 @@ public class SpecimenCdmExcelImport  extends ExcelTaxonOrSpecimenImportBase<Spec
                }
        }
 
+       private Taxon createTaxonFromDetermination( SpecimenCdmExcelImportState state, DeterminationLight commonDetermination) {
+               
+               //rank
+               Rank rank;
+               try {
+                       rank = StringUtils.isBlank(commonDetermination.rank) ? null : Rank.getRankByNameOrIdInVoc(commonDetermination.rank, true);
+               } catch (UnknownCdmTypeException e) {
+                       rank = null;
+               }
+
+               //name
+               NonViralName<?> name;
+               INonViralNameParser<NonViralName> parser = NonViralNameParserImpl.NewInstance();
+               NomenclaturalCode nc = state.getConfig().getNomenclaturalCode();
+               if (StringUtils.isNotBlank(commonDetermination.fullName)){
+                       name = parser.parseFullName(commonDetermination.fullName, nc, rank);
+                       if (StringUtils.isBlank(name.getAuthorshipCache()) && StringUtils.isNotBlank(commonDetermination.author)){
+                               setAuthorship(name, commonDetermination.author, parser);
+                       }
+               }else{
+                       if (nc != null){
+                               name = (NonViralName)nc.getNewTaxonNameInstance(rank);
+                       }else{
+                               name = NonViralName.NewInstance(rank);
+                       }
+                       if (StringUtils.isNotBlank(commonDetermination.genus)){
+                               name.setGenusOrUninomial(commonDetermination.genus);
+                       }
+                       if (StringUtils.isNotBlank(commonDetermination.speciesEpi)){
+                               name.setSpecificEpithet(commonDetermination.speciesEpi);
+                       }
+                       if (StringUtils.isNotBlank(commonDetermination.infraSpeciesEpi)){
+                               name.setInfraSpecificEpithet(commonDetermination.infraSpeciesEpi);
+                       }
+                       if (StringUtils.isNotBlank(commonDetermination.author)){
+                               setAuthorship(name, commonDetermination.author, parser);
+                       }
+                       //guess rank if null
+                       if (name.getRank() == null){
+                               if (name.getInfraGenericEpithet() != null && name.getSpecificEpithet() == null){
+                                       name.setRank(Rank.INFRAGENERICTAXON());
+                               }else if (name.getSpecificEpithet() != null && name.getInfraSpecificEpithet() == null){
+                                       name.setRank(Rank.SPECIES());
+                               }else if (name.getInfraSpecificEpithet() != null){
+                                       name.setRank(Rank.INFRASPECIFICTAXON());
+                               }
+                               
+                       }
+                       
+               }
+               //sec
+               Reference<?> sec = null;
+               if (StringUtils.isNotBlank(commonDetermination.determinedBy)){
+                       sec = ReferenceFactory.newGeneric();
+                       TeamOrPersonBase<?> determinedBy;
+                       BotanicalName dummyName = BotanicalName.NewInstance(Rank.SPECIES());
+                       try {
+                               parser.parseAuthors(dummyName, commonDetermination.determinedBy);
+                               determinedBy = (TeamOrPersonBase<?>)dummyName.getCombinationAuthorTeam();
+                       } catch (StringNotParsableException e) {
+                               determinedBy = Team.NewTitledInstance(commonDetermination.determinedBy, commonDetermination.determinedBy);
+                       }
+                       sec.setAuthorTeam(determinedBy);
+               }
+               
+               //taxon
+               Taxon taxon = Taxon.NewInstance(name, sec);
+
+               if (StringUtils.isNotBlank(commonDetermination.family)){
+                       if (name.getRank() == null || name.getRank().isLower(Rank.FAMILY()) ){
+                               logger.warn("Family taxon could not be created");
+                       }
+               }
+               
+               //return
+               return taxon;
+               
+       }
+
+
+
+
+       private void setAuthorship(NonViralName<?> name, String author, INonViralNameParser<NonViralName> parser) {
+               if (name.isInstanceOf(BotanicalName.class) || name.isInstanceOf(ZoologicalName.class)){
+                       try {
+                               parser.parseAuthors(name, author);
+                       } catch (StringNotParsableException e) {
+                               name.setAuthorshipCache(author);
+                       }               
+               }else{
+                       name.setAuthorshipCache(author);
+               }
+               
+       }
+
+
+
+
        /**
         * This method tries to find the best matching taxon depending on the import configuration,
         * the taxon name information and the concept information available.
@@ -463,7 +627,7 @@ public class SpecimenCdmExcelImport  extends ExcelTaxonOrSpecimenImportBase<Spec
         * @return
         */
        private String makeSearchNameTitleCache(SpecimenCdmExcelImportState state, DeterminationLight determinationLight, 
-                               NonViralName name) {
+                               NonViralName<?> name) {
                String titleCache = determinationLight.fullName;
                if (! state.getConfig().isPreferNameCache() || StringUtils.isBlank(titleCache) ){
                        String computedTitleCache = name.getTitleCache();
@@ -480,9 +644,12 @@ public class SpecimenCdmExcelImport  extends ExcelTaxonOrSpecimenImportBase<Spec
         * @param determinationLight
         * @return
         */
-       private NonViralName makeTaxonName(SpecimenCdmExcelImportState state, DeterminationLight determinationLight) {
-               //TODO correct type by config.nc
-               NonViralName name =NonViralName.NewInstance(null);
+       private NonViralName<?> makeTaxonName(SpecimenCdmExcelImportState state, DeterminationLight determinationLight) {
+               NonViralName<?> name = NonViralName.NewInstance(null);
+               NomenclaturalCode nc = state.getConfig().getNomenclaturalCode();
+               if (nc != null){
+                       name = (NonViralName<?>)nc.getNewTaxonNameInstance(null);
+               }
                name.setGenusOrUninomial(determinationLight.genus);
                name.setSpecificEpithet(determinationLight.speciesEpi);
                name.setInfraSpecificEpithet(determinationLight.infraSpeciesEpi);
@@ -492,13 +659,12 @@ public class SpecimenCdmExcelImport  extends ExcelTaxonOrSpecimenImportBase<Spec
                if (StringUtils.isNotBlank(determinationLight.author)){
                        authors.add(determinationLight.author);
                }
-               TeamOrPersonBase agent = (TeamOrPersonBase)getOrMakeAgent(state, authors);
+               TeamOrPersonBase<?> agent = (TeamOrPersonBase)getOrMakeAgent(state, authors);
                name.setCombinationAuthorTeam(agent);
                
-               NomenclaturalCode nc = state.getConfig().getNomenclaturalCode();
                try {
                        if (StringUtils.isNotBlank(determinationLight.rank) ){
-                               name.setRank(Rank.getRankByNameOrAbbreviation(determinationLight.rank, nc, true));
+                               name.setRank(Rank.getRankByNameOrIdInVoc(determinationLight.rank, nc, true));
                        }
                } catch (UnknownCdmTypeException e) {
                        String message = "Rank not found: %s: ";
@@ -520,7 +686,7 @@ public class SpecimenCdmExcelImport  extends ExcelTaxonOrSpecimenImportBase<Spec
 
        private TaxonNameBase findBestMatchingName(SpecimenCdmExcelImportState state, DeterminationLight determinationLight) {
                
-               NonViralName name = makeTaxonName(state, determinationLight);
+               NonViralName<?> name = makeTaxonName(state, determinationLight);
                String titleCache = makeSearchNameTitleCache(state, determinationLight, name);
                
                //TODO
@@ -543,7 +709,7 @@ public class SpecimenCdmExcelImport  extends ExcelTaxonOrSpecimenImportBase<Spec
                event.setTaxon(taxon);
                
                //date
-               TimePeriod date = TimePeriod.parseString(determination.determinedWhen);
+               TimePeriod date = TimePeriodParser.parseString(determination.determinedWhen);
                event.setTimeperiod(date);
                //by
                //FIXME bracketAuthors and teams not yet implemented!!!
@@ -551,7 +717,12 @@ public class SpecimenCdmExcelImport  extends ExcelTaxonOrSpecimenImportBase<Spec
                if (StringUtils.isNotBlank(determination.determinedBy)){
                        authors.add(determination.determinedBy);
                }
-               AgentBase actor = getOrMakeAgent(state, authors);
+               TeamOrPersonBase<?> actor = getOrMakeAgent(state, authors);
+               TeamOrPersonBase<?> secAuthor = taxon.getSec() == null ? null : taxon.getSec().getAuthorTeam();
+               if (actor != null && secAuthor != null && secAuthor.getTitleCache().equals(actor.getTitleCache()) && secAuthor.getNomenclaturalTitle().equals(actor.getNomenclaturalTitle())) {
+                       actor = secAuthor;
+               }
+               
                event.setActor(actor);
                
                //TODO
@@ -561,8 +732,10 @@ public class SpecimenCdmExcelImport  extends ExcelTaxonOrSpecimenImportBase<Spec
 //             DeterminationModifier modifier = DeterminationModifier.NewInstance(term, label, labelAbbrev);
 //             determination.modifier;
                //notes
-               Annotation annotation = Annotation.NewInstance(determination.notes, AnnotationType.EDITORIAL(), Language.DEFAULT());
-               event.addAnnotation(annotation);
+               if (StringUtils.isNotEmpty(determination.notes)){
+                       Annotation annotation = Annotation.NewInstance(determination.notes, AnnotationType.EDITORIAL(), Language.DEFAULT());
+                       event.addAnnotation(annotation);
+               }
 
                return event;
        }
@@ -572,7 +745,7 @@ public class SpecimenCdmExcelImport  extends ExcelTaxonOrSpecimenImportBase<Spec
                return desc;
        }
 
-       private AgentBase<?> getOrMakeAgent(SpecimenCdmExcelImportState state, List<String> agents) {
+       private TeamOrPersonBase<?> getOrMakeAgent(SpecimenCdmExcelImportState state, List<String> agents) {
                if (agents.size() == 0){
                        return null;
                }else if (agents.size() == 1){
@@ -581,6 +754,34 @@ public class SpecimenCdmExcelImport  extends ExcelTaxonOrSpecimenImportBase<Spec
                        return getOrMakeTeam(state, agents);
                }
        }
+       
+       private Person getOrMakePrimaryCollector(DerivedUnitFacade facade, String primaryCollector, SpecimenCdmExcelImportState state) {
+               if (StringUtils.isBlank(primaryCollector)){
+                       return null;
+               }
+               AgentBase<?> collector = facade.getCollector();
+               List<Person> collectors = new ArrayList<Person>();
+               if (collector.isInstanceOf(Team.class) ){
+                       Team team = CdmBase.deproxy(collector, Team.class);
+                       collectors.addAll(team.getTeamMembers());
+               }else if (collector.isInstanceOf(Person.class)){
+                       collectors.add(CdmBase.deproxy(collector, Person.class));
+               }else{
+                       throw new IllegalStateException("Unknown subclass of agentbase: " + collector.getClass().getName() );
+               }
+               for (Person person :collectors){
+                       if (primaryCollector.equalsIgnoreCase(person.getTitleCache())){
+                               return person;
+                       }
+                       if (primaryCollector.equalsIgnoreCase(person.getNomenclaturalTitle())){
+                               return person;
+                       }
+               }
+               String message = "Primary Agent '%s' could not be determined in collector(s) in line %d";
+               message = String.format(message, primaryCollector, state.getCurrentLine());
+               logger.warn(message);
+               return null;
+       }
 
        private Team getOrMakeTeam(SpecimenCdmExcelImportState state, List<String> agents) {
                String key = CdmUtils.concat("_", agents.toArray(new String[0]));
@@ -682,6 +883,7 @@ public class SpecimenCdmExcelImport  extends ExcelTaxonOrSpecimenImportBase<Spec
                        try {
                                refSys = state.getTransformer().getReferenceSystemByKey(strRefSys);
                                if (refSys == null){
+                                       //TODO we still need user defined Reference Systems here
                                        refUuid = state.getTransformer().getReferenceSystemUuid(strRefSys);
                                        if (refUuid == null){
                                                String message = "Unknown reference system %s in line %d";
@@ -745,7 +947,7 @@ public class SpecimenCdmExcelImport  extends ExcelTaxonOrSpecimenImportBase<Spec
                        }
                }
                if (StringUtils.isNotBlank(row.getCountry())){
-                       List<WaterbodyOrCountry> countries = getOccurrenceService().getWaterbodyOrCountryByName(row.getCountry());
+                       List<Country> countries = getOccurrenceService().getCountryByName(row.getCountry());
                        if (countries.size() >0){
                                facade.setCountry(countries.get(0));
                        }else{
@@ -762,9 +964,9 @@ public class SpecimenCdmExcelImport  extends ExcelTaxonOrSpecimenImportBase<Spec
        }
 
        @Override
-       protected boolean secondPass(SpecimenCdmExcelImportState state) {
+       protected void secondPass(SpecimenCdmExcelImportState state) {
                //no second path defined yet
-               return true;
+               return;
        }