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