package org.gbif.checklistbank.nub;
import org.gbif.api.exception.UnparsableException;
import org.gbif.api.model.Constants;
import org.gbif.api.model.checklistbank.ParsedName;
import org.gbif.api.service.checklistbank.NameParser;
import org.gbif.api.vocabulary.Kingdom;
import org.gbif.api.vocabulary.Origin;
import org.gbif.api.vocabulary.Rank;
import org.gbif.api.vocabulary.TaxonomicStatus;
import org.gbif.checklistbank.authorship.AuthorComparator;
import org.gbif.checklistbank.neo.UsageDao;
import org.gbif.checklistbank.nub.model.NubUsage;
import org.gbif.checklistbank.nub.model.NubUsageMatch;
import org.gbif.checklistbank.nub.model.SrcUsage;
import org.gbif.checklistbank.utils.RankUtils;
import org.gbif.nameparser.GBIFNameParser;
import java.util.UUID;
import com.google.common.base.Throwables;
import org.junit.Test;
import org.neo4j.graphdb.Transaction;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class NubDbTest {
final NameParser parser = new GBIFNameParser();
private long counter = 1;
@Test
public void testCountTaxa() throws Exception {
UsageDao dao = UsageDao.temporaryDao(10);
NubDb nub = NubDb.create(dao, AuthorComparator.createWithoutAuthormap());
try (Transaction tx = dao.beginTx()) {
assertEquals(0l, nub.countTaxa());
NubUsage u = new NubUsage();
u.parsedName = new ParsedName();
u.origin = Origin.SOURCE;
u.rank = Rank.SPECIES;
nub.addRoot(u);
assertEquals(1l, nub.countTaxa());
// we add the same nub usage which already has a neo node, nothing changes
nub.addRoot(u);
assertEquals(1l, nub.countTaxa());
u.node = null;
nub.addRoot(u);
assertEquals(2l, nub.countTaxa());
tx.success();
}
}
@Test
public void testFindAcceptedCanonical() throws Exception {
UsageDao dao = UsageDao.temporaryDao(10);
NubDb db = NubDb.create(dao, AuthorComparator.createWithoutAuthormap());
try (Transaction tx = dao.beginTx()) {
final NubUsage plantae = db.addRoot(buildNub(Kingdom.PLANTAE, "Plantae", Rank.KINGDOM, TaxonomicStatus.ACCEPTED));
final NubUsage oenanteP = db.addUsage(plantae, buildNub("Oenanthe Vieillot, 1816", Rank.GENUS, TaxonomicStatus.ACCEPTED));
db.addUsage(oenanteP, buildNub("Oenanthe aquatica Poir.", Rank.SPECIES, TaxonomicStatus.ACCEPTED));
NubUsage acc = db.addUsage(plantae, buildNub("Palma aquatica (Senser.)", Rank.SPECIES, TaxonomicStatus.ACCEPTED));
db.addUsage(acc, buildNub("Oenanthe aquatica Senser.", Rank.SPECIES, TaxonomicStatus.SYNONYM));
db.addUsage(oenanteP, buildNub("Oenanthe carolina (Mill.)", Rank.SPECIES, TaxonomicStatus.DOUBTFUL));
acc = db.addUsage(oenanteP, buildNub("Oenanthe arida", Rank.SPECIES, TaxonomicStatus.ACCEPTED));
db.addUsage(acc, buildNub("Onatus horrida", Rank.SPECIES, TaxonomicStatus.SYNONYM));
db.addUsage(acc, buildNub("Onatus horrida alpina", Rank.SUBSPECIES, TaxonomicStatus.SYNONYM));
final NubUsage animalia = db.addRoot(buildNub(Kingdom.ANIMALIA, "Animalia", Rank.KINGDOM, TaxonomicStatus.ACCEPTED));
final NubUsage oenanteA = db.addUsage(animalia, buildNub("Oenanthe Linnaeus, 1753", Rank.GENUS, TaxonomicStatus.ACCEPTED));
db.addUsage(animalia, buildNub("Geotrupes stercorarius (Linnaeus, 1758)", Rank.SPECIES, TaxonomicStatus.ACCEPTED));
acc = db.addUsage(animalia, buildNub("Geotrupes spiniger (Marsham, 1802)", Rank.SPECIES, TaxonomicStatus.ACCEPTED));
db.addUsage(acc, buildNub("Geotrupes stercorarius Erichson, 1847", Rank.SPECIES, TaxonomicStatus.SYNONYM));
db.addUsage(oenanteA, buildNub("Oenanthe aquatica", Rank.SPECIES, TaxonomicStatus.ACCEPTED));
tx.success();
assertTrue(db.findAcceptedNubUsage(Kingdom.ANIMALIA, "Oenanthe", Rank.GENUS).isMatch());
assertTrue(db.findAcceptedNubUsage(Kingdom.ANIMALIA, "Oenanthe aquatica", Rank.SPECIES).isMatch());
assertFalse(db.findAcceptedNubUsage(Kingdom.ANIMALIA, "Oenanthe aquatica", Rank.SUBSPECIES).isMatch());
assertFalse(db.findAcceptedNubUsage(Kingdom.PLANTAE, "Onatus horrida alpina", Rank.SUBSPECIES).isMatch());
assertFalse(db.findAcceptedNubUsage(Kingdom.PLANTAE, "Onatus horrida", Rank.SPECIES).isMatch());
assertTrue(db.findAcceptedNubUsage(Kingdom.PLANTAE, "Oenanthe", Rank.GENUS).isMatch());
assertTrue(db.findAcceptedNubUsage(Kingdom.PLANTAE, "Oenanthe aquatica", Rank.SPECIES).isMatch());
assertFalse(db.findAcceptedNubUsage(Kingdom.PLANTAE, "Oenanthe aquatica", Rank.SUBSPECIES).isMatch());
}
}
@Test
public void testGetParent() throws Exception {
UsageDao dao = UsageDao.temporaryDao(10);
NubDb db = NubDb.create(dao, AuthorComparator.createWithoutAuthormap());
try (Transaction tx = dao.beginTx()) {
final NubUsage plantae = db.addRoot(buildNub(Kingdom.PLANTAE, "Plantae", Rank.KINGDOM, TaxonomicStatus.ACCEPTED));
final NubUsage oenanteP = db.addUsage(plantae, buildNub("Oenanthe Vieillot, 1816", Rank.GENUS, TaxonomicStatus.ACCEPTED));
db.addUsage(oenanteP, buildNub("Oenanthe aquatica Poir.", Rank.SPECIES, TaxonomicStatus.ACCEPTED));
NubUsage acc = db.addUsage(plantae, buildNub("Palma aquatica (Senser.)", Rank.SPECIES, TaxonomicStatus.ACCEPTED));
db.addUsage(acc, buildNub("Oenanthe aquatica Senser.", Rank.SPECIES, TaxonomicStatus.SYNONYM));
db.addUsage(oenanteP, buildNub("Oenanthe carolina (Mill.)", Rank.SPECIES, TaxonomicStatus.DOUBTFUL));
assertNull(db.parent((NubUsage) null));
assertNull(db.parent(new NubUsage()));
assertNull(db.parent(plantae));
assertNotNull(db.parent(oenanteP));
}
}
@Test
public void testFindTaxa() throws Exception {
UsageDao dao = UsageDao.temporaryDao(25);
NubDb db = NubDb.create(dao, AuthorComparator.createWithoutAuthormap());
try (Transaction tx = dao.beginTx()) {
final NubUsage plantae = db.addRoot(buildNub(Kingdom.PLANTAE, "Plantae", Rank.KINGDOM, TaxonomicStatus.ACCEPTED));
final NubUsage animalia = db.addRoot(buildNub(Kingdom.ANIMALIA, "Animalia", Rank.KINGDOM, TaxonomicStatus.ACCEPTED));
final NubUsage fungi = db.addRoot(buildNub(Kingdom.FUNGI, "Fungi", Rank.KINGDOM, TaxonomicStatus.ACCEPTED));
final NubUsage sagartiidae = addHierarchy(db, animalia, Rank.PHYLUM, "Cnidaria", "Anthozoa", "Actiniaria", "Sagartiidae");
final NubUsage verrillactis = db.addUsage(sagartiidae, buildNub("Verrillactis", Rank.GENUS, TaxonomicStatus.ACCEPTED));
final NubUsage aarum = db.addUsage(sagartiidae, buildNub("Aarum", Rank.GENUS, TaxonomicStatus.ACCEPTED));
final NubUsage barum = db.addUsage(sagartiidae, buildNub("Barum", Rank.GENUS, TaxonomicStatus.ACCEPTED));
final NubUsage carum = db.addUsage(sagartiidae, buildNub("Carum", Rank.GENUS, TaxonomicStatus.ACCEPTED));
final NubUsage darum = db.addUsage(sagartiidae, buildNub("Darum", Rank.GENUS, TaxonomicStatus.ACCEPTED));
final NubUsage vp1870 = db.addUsage(verrillactis, buildNub("Verrillactis paguri (Verrill, 1870)", Rank.SPECIES, TaxonomicStatus.ACCEPTED));
final NubUsage vp = db.addUsage(vp1870, buildNub("Verrillactis paguri (Verrill)", Rank.SPECIES, TaxonomicStatus.SYNONYM));
final NubUsage vp1869 = db.addUsage(vp1870, buildNub("Verrillactis paguri (Verrill, 1869)", Rank.SPECIES, TaxonomicStatus.HETEROTYPIC_SYNONYM));
final NubUsage vp1896 = db.addUsage(vp1870, buildNub("Verrillactis paguri (Verrill, 1896*a*)", Rank.SPECIES, TaxonomicStatus.SYNONYM));
final NubUsage vacc = db.addUsage(verrillactis, buildNub("Verrillactis accepturi (Verrill, 1870)", Rank.SPECIES, TaxonomicStatus.ACCEPTED));
final NubUsage vv = db.addUsage(vacc, buildNub("Verrillactis vagguri (Verrill)", Rank.SPECIES, TaxonomicStatus.SYNONYM));
db.addUsage(vacc, buildNub("Verrillactis vagguri (Verrill, 1869)", Rank.SPECIES, TaxonomicStatus.HETEROTYPIC_SYNONYM));
db.addUsage(vacc, buildNub("Verrillactis vagguri (Verrill, 1896)", Rank.SPECIES, TaxonomicStatus.SYNONYM));
final NubUsage vacc2 = db.addUsage(verrillactis, buildNub("Verrillactis accepturissimo (Verrill, 1870)", Rank.SPECIES, TaxonomicStatus.ACCEPTED));
db.addUsage(vacc, buildNub("Verrillactis laguri (Verrill)", Rank.SPECIES, TaxonomicStatus.SYNONYM));
final NubUsage vl = db.addUsage(vacc2, buildNub("Verrillactis laguri (Verrill, 1869)", Rank.SPECIES, TaxonomicStatus.HETEROTYPIC_SYNONYM));
db.addUsage(vacc2, buildNub("Verrillactis laguri (Verrill, 1896*a*)", Rank.SPECIES, TaxonomicStatus.SYNONYM));
tx.success();
// full name matches
assertMatch(vp1869,
db.findNubUsage(Constants.COL_DATASET_KEY, buildSrc("Verrillactis paguri (Verrill, 1869)", Rank.SPECIES, TaxonomicStatus.ACCEPTED), Kingdom.ANIMALIA, animalia)
);
assertMatch(vv,
db.findNubUsage(Constants.COL_DATASET_KEY, buildSrc("Verrillactis vagguri (Verrill)", Rank.SPECIES, TaxonomicStatus.ACCEPTED), Kingdom.ANIMALIA, animalia)
);
assertMatch(vl,
db.findNubUsage(Constants.COL_DATASET_KEY, buildSrc("Verrillactis laguri (Verrill, 1869)", Rank.SPECIES, TaxonomicStatus.ACCEPTED), Kingdom.ANIMALIA, animalia)
);
// wrong kingdom, but matching authorship
assertMatch(vp1869,
db.findNubUsage(Constants.COL_DATASET_KEY, buildSrc("Verrillactis paguri (Verrill, 1869)", Rank.SPECIES, TaxonomicStatus.ACCEPTED), Kingdom.VIRUSES, animalia)
);
assertMatch(vl,
db.findNubUsage(Constants.COL_DATASET_KEY, buildSrc("Verrillactis laguri (Verrill, 1869)", Rank.SPECIES, TaxonomicStatus.ACCEPTED), Kingdom.VIRUSES, animalia)
);
// wrong rank
assertNoMatch(
db.findNubUsage(Constants.COL_DATASET_KEY, buildSrc("Verrillactis paguri (Verrill, 1869)", Rank.GENUS, TaxonomicStatus.ACCEPTED), Kingdom.ANIMALIA, animalia)
);
// single accepted
assertSnap(vp1870,
db.findNubUsage(Constants.COL_DATASET_KEY, buildSrc("Verrillactis paguri", Rank.SPECIES, TaxonomicStatus.SYNONYM), Kingdom.ANIMALIA, animalia)
);
// single accepted, but wrong kingdom
assertNoMatch(
db.findNubUsage(Constants.COL_DATASET_KEY, buildSrc("Verrillactis paguri", Rank.SPECIES, TaxonomicStatus.ACCEPTED), Kingdom.PLANTAE, animalia)
);
// 3 synonyms, all pointing to the same accepted. Pick first
assertSnap(vv,
db.findNubUsage(Constants.COL_DATASET_KEY, buildSrc("Verrillactis vagguri", Rank.SPECIES, TaxonomicStatus.SYNONYM), Kingdom.ANIMALIA, animalia)
);
// same usage source as the synonyms and authorless accepted
assertNoMatch(
db.findNubUsage(Constants.COL_DATASET_KEY, buildSrc("Verrillactis vagguri", Rank.SPECIES, TaxonomicStatus.ACCEPTED), Kingdom.ANIMALIA, animalia)
);
// different usage source than the synonyms, pick first
assertSnap(vv,
db.findNubUsage(Constants.NUB_NETWORK_KEY, buildSrc("Verrillactis vagguri", Rank.SPECIES, TaxonomicStatus.ACCEPTED), Kingdom.ANIMALIA, animalia)
);
// 2 different accepted parents for these synonyms, ignore
assertIgnoreSource(db, Constants.NUB_NETWORK_KEY, buildSrc("Verrillactis laguri", Rank.SPECIES, TaxonomicStatus.ACCEPTED), Kingdom.ANIMALIA, animalia);
}
}
private void assertMatch(NubUsage expected, NubUsageMatch m) {
assertEquals(expected.node, m.usage.node);
assertFalse(m.ignore);
}
private void assertSnap(NubUsage expected, NubUsageMatch m) {
assertEquals(expected.node, m.usage.node);
assertTrue(m.ignore);
}
private void assertNoMatch(NubUsageMatch m) {
assertNull(m.usage);
}
private void assertIgnoreSource(NubDb db, UUID currSource, SrcUsage u, Kingdom uKingdom, NubUsage currNubParent) {
try {
db.findNubUsage(currSource, u, uKingdom, currNubParent);
fail("IgnoreSourceUsageException expected");
} catch (IgnoreSourceUsageException e) {
return;
}
}
private NubUsage addHierarchy(NubDb db, NubUsage parent, Rank startRank, String... taxa) throws IgnoreSourceUsageException {
Rank rank = startRank;
for (String name : taxa) {
parent = db.addUsage(parent, buildNub(name, rank, TaxonomicStatus.ACCEPTED));
rank = RankUtils.nextLowerLinneanRank(rank);
}
return parent;
}
private NubUsage buildNub(String sciname, Rank rank, TaxonomicStatus status) {
return buildNub(null, sciname, rank, status);
}
private NubUsage buildNub(Kingdom k, String sciname, Rank rank, TaxonomicStatus status) {
NubUsage nu = new NubUsage();
nu.datasetKey = Constants.COL_DATASET_KEY;
try {
nu.parsedName = parser.parse(sciname, rank);
nu.rank = rank;
nu.status = status;
nu.kingdom = k;
} catch (UnparsableException e) {
Throwables.propagate(e);
}
return nu;
}
private SrcUsage buildSrc(String sciname, Rank rank, TaxonomicStatus status) {
SrcUsage u = new SrcUsage();
try {
u.scientificName = sciname;
u.parsedName = parser.parse(sciname, rank);
u.rank = rank;
u.status = status;
} catch (UnparsableException e) {
Throwables.propagate(e);
}
return u;
}
}