Add mappings module with pokemon form, location area, encounter method, and version mappings. Auto-download PokeDB JSON exports from CDN on first run, caching in .pokedb_cache/. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
169 lines
5.6 KiB
Python
169 lines
5.6 KiB
Python
"""Load and validate PokeDB JSON export files."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import sys
|
|
import urllib.request
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
_CDN_BASE = "https://cdn.pokedb.org"
|
|
|
|
REQUIRED_FILES: dict[str, str] = {
|
|
"encounters.json": f"{_CDN_BASE}/data_export_encounters_json",
|
|
"locations.json": f"{_CDN_BASE}/data_export_locations_json",
|
|
"location_areas.json": f"{_CDN_BASE}/data_export_location_areas_json",
|
|
"encounter_methods.json": f"{_CDN_BASE}/data_export_encounter_methods_json",
|
|
"versions.json": f"{_CDN_BASE}/data_export_versions_json",
|
|
"pokemon_forms.json": f"{_CDN_BASE}/data_export_pokemon_forms_json",
|
|
}
|
|
|
|
|
|
class PokeDBData:
|
|
"""Container for all loaded PokeDB export data."""
|
|
|
|
def __init__(
|
|
self,
|
|
encounters: list[dict[str, Any]],
|
|
locations: list[dict[str, Any]],
|
|
location_areas: list[dict[str, Any]],
|
|
encounter_methods: list[dict[str, Any]],
|
|
versions: list[dict[str, Any]],
|
|
pokemon_forms: list[dict[str, Any]],
|
|
) -> None:
|
|
self.encounters = encounters
|
|
self.locations = locations
|
|
self.location_areas = location_areas
|
|
self.encounter_methods = encounter_methods
|
|
self.versions = versions
|
|
self.pokemon_forms = pokemon_forms
|
|
|
|
def summary(self) -> str:
|
|
return (
|
|
f"PokeDB data loaded:\n"
|
|
f" encounters: {len(self.encounters):,}\n"
|
|
f" locations: {len(self.locations):,}\n"
|
|
f" location_areas: {len(self.location_areas):,}\n"
|
|
f" encounter_methods: {len(self.encounter_methods):,}\n"
|
|
f" versions: {len(self.versions):,}\n"
|
|
f" pokemon_forms: {len(self.pokemon_forms):,}"
|
|
)
|
|
|
|
|
|
def download_pokedb_data(data_dir: Path) -> None:
|
|
"""Download missing PokeDB JSON export files into data_dir."""
|
|
data_dir.mkdir(parents=True, exist_ok=True)
|
|
missing = {f: url for f, url in REQUIRED_FILES.items() if not (data_dir / f).exists()}
|
|
if not missing:
|
|
return
|
|
|
|
print(f"Downloading {len(missing)} PokeDB file(s) to {data_dir}...")
|
|
for filename, url in missing.items():
|
|
dest = data_dir / filename
|
|
print(f" {filename}...", end="", flush=True)
|
|
try:
|
|
urllib.request.urlretrieve(url, dest)
|
|
size_mb = dest.stat().st_size / (1024 * 1024)
|
|
print(f" {size_mb:.1f} MB")
|
|
except Exception as e:
|
|
print(f" FAILED", file=sys.stderr)
|
|
print(f"Error downloading {url}: {e}", file=sys.stderr)
|
|
sys.exit(1)
|
|
print()
|
|
|
|
|
|
def load_pokedb_data(data_dir: Path) -> PokeDBData:
|
|
"""Load all PokeDB JSON export files from a directory.
|
|
|
|
Downloads any missing files automatically, then validates and loads them.
|
|
"""
|
|
download_pokedb_data(data_dir)
|
|
|
|
missing = [f for f in REQUIRED_FILES if not (data_dir / f).exists()]
|
|
if missing:
|
|
print(f"Error: Still missing files after download: {missing}", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
def _load(filename: str) -> list[dict[str, Any]]:
|
|
path = data_dir / filename
|
|
try:
|
|
with open(path) as f:
|
|
data = json.load(f)
|
|
except json.JSONDecodeError as e:
|
|
print(f"Error: Failed to parse {path}: {e}", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
if not isinstance(data, list):
|
|
print(
|
|
f"Error: Expected a JSON array in {path}, got {type(data).__name__}",
|
|
file=sys.stderr,
|
|
)
|
|
sys.exit(1)
|
|
|
|
return data
|
|
|
|
return PokeDBData(
|
|
encounters=_load("encounters.json"),
|
|
locations=_load("locations.json"),
|
|
location_areas=_load("location_areas.json"),
|
|
encounter_methods=_load("encounter_methods.json"),
|
|
versions=_load("versions.json"),
|
|
pokemon_forms=_load("pokemon_forms.json"),
|
|
)
|
|
|
|
|
|
class SeedConfig:
|
|
"""Container for existing seed configuration files."""
|
|
|
|
def __init__(
|
|
self,
|
|
version_groups: dict[str, Any],
|
|
route_order: dict[str, list[str]],
|
|
special_encounters: dict[str, Any] | None,
|
|
) -> None:
|
|
self.version_groups = version_groups
|
|
self.route_order = route_order
|
|
self.special_encounters = special_encounters
|
|
|
|
|
|
def load_seed_config(seeds_dir: Path) -> SeedConfig:
|
|
"""Load existing seed configuration files (version_groups, route_order, etc.).
|
|
|
|
Exits with an error message if required config files are missing.
|
|
"""
|
|
vg_path = seeds_dir / "version_groups.json"
|
|
if not vg_path.exists():
|
|
print(f"Error: version_groups.json not found at {vg_path}", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
with open(vg_path) as f:
|
|
version_groups = json.load(f)
|
|
|
|
# Load route_order.json and resolve aliases
|
|
ro_path = seeds_dir / "route_order.json"
|
|
if not ro_path.exists():
|
|
print(f"Error: route_order.json not found at {ro_path}", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
with open(ro_path) as f:
|
|
ro_raw = json.load(f)
|
|
|
|
route_order: dict[str, list[str]] = dict(ro_raw.get("routes", {}))
|
|
for alias, target in ro_raw.get("aliases", {}).items():
|
|
if target in route_order:
|
|
route_order[alias] = route_order[target]
|
|
|
|
# Load special_encounters.json (optional)
|
|
se_path = seeds_dir / "special_encounters.json"
|
|
special_encounters = None
|
|
if se_path.exists():
|
|
with open(se_path) as f:
|
|
special_encounters = json.load(f)
|
|
|
|
return SeedConfig(
|
|
version_groups=version_groups,
|
|
route_order=route_order,
|
|
special_encounters=special_encounters,
|
|
)
|