Skip to content

Commit 5757221

Browse files
committed
chg: use Duckdb Python API for GBIF backbone
1 parent 5dda3c2 commit 5757221

4 files changed

Lines changed: 44 additions & 44 deletions

File tree

src/datasync/gbif_backbone.py

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,16 @@
1-
from importlib.resources import files
2-
3-
import duckdb
41
import fsspec
52
import typer
3+
from duckdb import (
4+
DuckDBPyConnection,
5+
connect,
6+
)
67

7-
from .libs.helpers import pa_read_tsv
8+
from .libs.helpers import DuckDBAtomicTransaction
89
from .settings import (
910
env,
1011
log,
1112
)
1213

13-
resources = files(__package__).joinpath("gbif_backbone")
14-
taxon_sql = resources.joinpath("taxon.sql").read_text()
15-
vernacularname_sql = resources.joinpath("vernacularname.sql").read_text()
16-
17-
1814
log.debug("Importing GBIF Backbone settings")
1915

2016

@@ -29,23 +25,41 @@
2925
app = typer.Typer(help="export GBIF Backbone data to DuckDB database")
3026

3127

32-
def import_taxon(conn, archive):
28+
def import_taxon(conn: DuckDBPyConnection, archive):
3329
log.debug("Importing Taxon.tsv")
34-
pa_taxon = pa_read_tsv(archive, "Taxon.tsv") # noqa: F841
35-
conn.execute(taxon_sql)
30+
conn.sql("DROP TABLE IF EXISTS taxon")
31+
conn.from_csv_auto(archive.open("Taxon.tsv")).to_table("taxon")
32+
conn.sql("CREATE INDEX taxon_taxonid ON taxon (taxonid)")
33+
conn.sql("""
34+
PRAGMA create_fts_index(
35+
"taxon", "taxonID", "canonicalName", overwrite=TRUE
36+
)
37+
""")
3638

3739

38-
def import_vernacular_name(conn, archive):
40+
def import_vernacular_name(conn: DuckDBPyConnection, archive):
3941
log.debug("Importing VernacularName.tsv")
40-
pa_vernacular_names = pa_read_tsv(archive, "VernacularName.tsv") # noqa: F841
41-
conn.execute(vernacularname_sql)
42+
conn.sql("DROP TABLE IF EXISTS vernacular_name")
43+
vernacular_names = conn.from_csv_auto(archive.open("VernacularName.tsv")) # noqa: F841
44+
conn.sql("""
45+
SELECT *, CONCAT_WS('|', taxonID, language, vernacularName) AS vernacularID
46+
FROM vernacular_names
47+
""").to_table("vernacular_name")
48+
conn.sql("CREATE INDEX vernacular_name_taxonid ON vernacular_name (taxonid)")
49+
conn.sql("CREATE INDEX vernacular_name_language ON vernacular_name (language)")
50+
conn.sql("""
51+
PRAGMA create_fts_index(
52+
"vernacular_name", "vernacularID", "vernacularName", overwrite=TRUE
53+
)
54+
""")
4255

4356

4457
@app.command()
4558
def import_all():
4659
"""Import GBIF Backbone data into a DuckDB database."""
4760
archive = fsspec.filesystem("zip", fo=GBIF_BACKBONE_URL.geturl(), mode="r")
48-
with duckdb.connect(GBIF_BACKBONE_DUCKDB_NAME) as conn:
49-
import_taxon(conn, archive)
50-
import_vernacular_name(conn, archive)
61+
with connect(GBIF_BACKBONE_DUCKDB_NAME) as conn:
62+
with DuckDBAtomicTransaction(conn):
63+
import_taxon(conn, archive)
64+
import_vernacular_name(conn, archive)
5165
log.info("GBIF Backbone data imported successfully")

src/datasync/gbif_backbone/taxon.sql

Lines changed: 0 additions & 6 deletions
This file was deleted.

src/datasync/gbif_backbone/vernacularname.sql

Lines changed: 0 additions & 11 deletions
This file was deleted.

src/datasync/libs/helpers.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import re
22

3+
from duckdb import DuckDBPyConnection
34
from lxml import etree
45
from lxml.etree import _Element
5-
from pyarrow import csv
66

77
PARSER: etree.XMLParser = etree.XMLParser(resolve_entities=False)
88

@@ -32,13 +32,16 @@ def get_anytext(bag: str | _Element | list[str]) -> str:
3232
raise TypeError("xpath result was not a list of strings")
3333

3434

35-
TSV_PARSE_OPTIONS = csv.ParseOptions(
36-
delimiter="\t", quote_char=False, double_quote=False
37-
)
35+
class DuckDBAtomicTransaction:
36+
def __init__(self, conn: DuckDBPyConnection):
37+
self.conn = conn
3838

39+
def __enter__(self):
40+
self.conn.begin()
41+
return self.conn
3942

40-
def pa_read_tsv(archive, filename):
41-
return csv.read_csv(
42-
archive.open(filename),
43-
parse_options=TSV_PARSE_OPTIONS,
44-
)
43+
def __exit__(self, exc_type, exc_value, traceback):
44+
if exc_type is None:
45+
self.conn.commit()
46+
else:
47+
self.conn.rollback()

0 commit comments

Comments
 (0)