.db file as 5 tables: lookalike, sightings, species species_detail and sqlite_sequence. Example .db file added
142 lines
5.1 KiB
Python
142 lines
5.1 KiB
Python
import csv
|
|
import sqlite3
|
|
from pathlib import Path
|
|
|
|
DB_PATH = Path(__file__).parent.parent / "wilddex.db"
|
|
WILDLIFE_CSV = Path(__file__).parent.parent / "wildlife" / "portland_wildlife.csv"
|
|
DEX_ENTRIES_CSV = Path(__file__).parent.parent / "wildlife" / "portland_wildlife_dex_entries.csv"
|
|
|
|
|
|
def create_tables(conn: sqlite3.Connection) -> None:
|
|
conn.executescript("""
|
|
CREATE TABLE IF NOT EXISTS species (
|
|
species_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
inat_taxon_id INTEGER UNIQUE NOT NULL,
|
|
common_name TEXT NOT NULL,
|
|
scientific_name TEXT NOT NULL,
|
|
kingdom TEXT,
|
|
phylum TEXT,
|
|
class TEXT,
|
|
"order" TEXT,
|
|
family TEXT,
|
|
genus TEXT,
|
|
iconic_taxon TEXT,
|
|
conservation_status TEXT,
|
|
pokedex_entry TEXT,
|
|
photo_url_thumb TEXT,
|
|
photo_url_med TEXT,
|
|
lat_min REAL,
|
|
lat_max REAL,
|
|
lng_min REAL,
|
|
lng_max REAL,
|
|
is_unlocked INTEGER NOT NULL DEFAULT 0
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS species_detail (
|
|
detail_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
species_id INTEGER NOT NULL REFERENCES species(species_id),
|
|
size_range TEXT,
|
|
weight_range TEXT,
|
|
lifespan TEXT,
|
|
diet TEXT,
|
|
activity_period TEXT,
|
|
habitat_type TEXT,
|
|
behavior_notes TEXT
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS sighting (
|
|
sighting_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
species_id INTEGER NOT NULL REFERENCES species(species_id),
|
|
observed_at TEXT,
|
|
latitude REAL,
|
|
longitude REAL,
|
|
location_label TEXT,
|
|
user_photo_path TEXT,
|
|
notes TEXT
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS lookalike (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
species_id INTEGER NOT NULL REFERENCES species(species_id),
|
|
lookalike_species_id INTEGER NOT NULL REFERENCES species(species_id),
|
|
distinguishing_tip TEXT
|
|
);
|
|
""")
|
|
conn.commit()
|
|
|
|
|
|
def load_dex_entries(csv_path: Path) -> dict[int, str]:
|
|
"""Return a mapping of taxon_id -> pokedex description."""
|
|
entries = {}
|
|
with open(csv_path, newline="", encoding="utf-8") as f:
|
|
for row in csv.DictReader(f):
|
|
entries[int(row["taxon_id"])] = row["description"]
|
|
return entries
|
|
|
|
|
|
def import_species(conn: sqlite3.Connection) -> None:
|
|
dex_entries = load_dex_entries(DEX_ENTRIES_CSV)
|
|
|
|
with open(WILDLIFE_CSV, newline="", encoding="utf-8") as f:
|
|
rows = list(csv.DictReader(f))
|
|
|
|
inserted = skipped = 0
|
|
for row in rows:
|
|
taxon_id = int(row["taxon_id"])
|
|
try:
|
|
conn.execute(
|
|
"""
|
|
INSERT INTO species (
|
|
inat_taxon_id, common_name, scientific_name,
|
|
kingdom, phylum, class, "order", family, genus,
|
|
iconic_taxon, photo_url_thumb, photo_url_med,
|
|
pokedex_entry
|
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
""",
|
|
(
|
|
taxon_id,
|
|
row["common_name"],
|
|
row["scientific_name"],
|
|
row.get("kingdom"),
|
|
row.get("phylum"),
|
|
row.get("class"),
|
|
row.get("order"),
|
|
row.get("family"),
|
|
row.get("genus"),
|
|
row.get("iconic_taxon"),
|
|
row.get("photo_url_thumb"),
|
|
row.get("photo_url_med"),
|
|
dex_entries.get(taxon_id),
|
|
),
|
|
)
|
|
inserted += 1
|
|
except sqlite3.IntegrityError:
|
|
skipped += 1
|
|
|
|
conn.commit()
|
|
print(f"Species import complete: {inserted} inserted, {skipped} skipped (already exist).")
|
|
|
|
|
|
def verify_db(conn: sqlite3.Connection) -> None:
|
|
tables = ["species", "species_detail", "sighting", "lookalike"]
|
|
for table in tables:
|
|
(count,) = conn.execute(f"SELECT COUNT(*) FROM {table}").fetchone()
|
|
print(f" {table}: {count} rows")
|
|
|
|
print("\nSample species rows:")
|
|
rows = conn.execute(
|
|
"SELECT inat_taxon_id, common_name, scientific_name, iconic_taxon, pokedex_entry IS NOT NULL FROM species LIMIT 5"
|
|
).fetchall()
|
|
for taxon_id, common, sci, iconic, has_dex in rows:
|
|
dex_flag = "has dex entry" if has_dex else "no dex entry"
|
|
print(f" [{taxon_id}] {common} ({sci}) — {iconic} — {dex_flag}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
with sqlite3.connect(DB_PATH) as conn:
|
|
create_tables(conn)
|
|
import_species(conn)
|
|
print("\nVerification:")
|
|
verify_db(conn)
|
|
print(f"\nDatabase ready at {DB_PATH}")
|