diff --git a/dev_scripts/database_setup.py b/dev_scripts/database_setup.py new file mode 100644 index 0000000..68cf6cd --- /dev/null +++ b/dev_scripts/database_setup.py @@ -0,0 +1,141 @@ +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}") diff --git a/wilddex.db b/wilddex.db new file mode 100644 index 0000000..535ee15 Binary files /dev/null and b/wilddex.db differ