Files
WildDex/dev_scripts/database_setup.py
T
Suppai_Neko 8978130b7c Created boilerplate setup for wilddex.db file.
.db file as 5 tables: lookalike, sightings, species
species_detail and sqlite_sequence.
Example .db file added
2026-03-26 15:41:51 -07:00

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}")