188 lines
5.5 KiB
Python
188 lines
5.5 KiB
Python
#!/usr/bin/env python3
|
|
"""Fetch moves and abilities from PokeAPI and save as seed data JSON files.
|
|
|
|
Usage:
|
|
cd backend && uv run python scripts/fetch_moves_abilities.py
|
|
|
|
This script fetches all moves and abilities from PokeAPI, extracts their names
|
|
and introduced generation, and saves them to the seed data directory.
|
|
"""
|
|
|
|
import asyncio
|
|
import json
|
|
import re
|
|
from pathlib import Path
|
|
|
|
import httpx
|
|
|
|
DATA_DIR = Path(__file__).parent.parent / "src" / "app" / "seeds" / "data"
|
|
POKEAPI_BASE = "https://pokeapi.co/api/v2"
|
|
|
|
# Map generation names to numbers
|
|
GEN_MAP = {
|
|
"generation-i": 1,
|
|
"generation-ii": 2,
|
|
"generation-iii": 3,
|
|
"generation-iv": 4,
|
|
"generation-v": 5,
|
|
"generation-vi": 6,
|
|
"generation-vii": 7,
|
|
"generation-viii": 8,
|
|
"generation-ix": 9,
|
|
}
|
|
|
|
|
|
def title_case_name(name: str) -> str:
|
|
"""Convert a hyphenated PokeAPI name to title case.
|
|
|
|
Examples:
|
|
'thunder-punch' -> 'Thunder Punch'
|
|
'self-destruct' -> 'Self-Destruct'
|
|
"""
|
|
return " ".join(word.capitalize() for word in name.split("-"))
|
|
|
|
|
|
async def fetch_all_moves(client: httpx.AsyncClient) -> list[dict]:
|
|
"""Fetch all moves from PokeAPI."""
|
|
moves = []
|
|
|
|
# First, get the list of all moves
|
|
print("Fetching move list...")
|
|
url = f"{POKEAPI_BASE}/move?limit=10000"
|
|
resp = await client.get(url)
|
|
resp.raise_for_status()
|
|
data = resp.json()
|
|
|
|
move_urls = [m["url"] for m in data["results"]]
|
|
print(f"Found {len(move_urls)} moves")
|
|
|
|
# Fetch each move's details in batches
|
|
batch_size = 50
|
|
for i in range(0, len(move_urls), batch_size):
|
|
batch = move_urls[i : i + batch_size]
|
|
print(f"Fetching moves {i + 1}-{min(i + batch_size, len(move_urls))}...")
|
|
|
|
tasks = [client.get(url) for url in batch]
|
|
responses = await asyncio.gather(*tasks, return_exceptions=True)
|
|
|
|
for resp in responses:
|
|
if isinstance(resp, Exception):
|
|
print(f" Error fetching move: {resp}")
|
|
continue
|
|
if resp.status_code != 200:
|
|
print(f" HTTP {resp.status_code} for {resp.url}")
|
|
continue
|
|
|
|
move_data = resp.json()
|
|
gen_name = move_data["generation"]["name"]
|
|
introduced_gen = GEN_MAP.get(gen_name)
|
|
|
|
if introduced_gen is None:
|
|
print(f" Unknown generation: {gen_name} for move {move_data['name']}")
|
|
continue
|
|
|
|
# Get type if available
|
|
move_type = None
|
|
if move_data.get("type"):
|
|
move_type = move_data["type"]["name"]
|
|
|
|
moves.append(
|
|
{
|
|
"name": title_case_name(move_data["name"]),
|
|
"introduced_gen": introduced_gen,
|
|
"type": move_type,
|
|
}
|
|
)
|
|
|
|
# Sort by name for consistent ordering
|
|
moves.sort(key=lambda m: m["name"])
|
|
return moves
|
|
|
|
|
|
async def fetch_all_abilities(client: httpx.AsyncClient) -> list[dict]:
|
|
"""Fetch all abilities from PokeAPI."""
|
|
abilities = []
|
|
|
|
# First, get the list of all abilities
|
|
print("Fetching ability list...")
|
|
url = f"{POKEAPI_BASE}/ability?limit=10000"
|
|
resp = await client.get(url)
|
|
resp.raise_for_status()
|
|
data = resp.json()
|
|
|
|
ability_urls = [a["url"] for a in data["results"]]
|
|
print(f"Found {len(ability_urls)} abilities")
|
|
|
|
# Fetch each ability's details in batches
|
|
batch_size = 50
|
|
for i in range(0, len(ability_urls), batch_size):
|
|
batch = ability_urls[i : i + batch_size]
|
|
print(f"Fetching abilities {i + 1}-{min(i + batch_size, len(ability_urls))}...")
|
|
|
|
tasks = [client.get(url) for url in batch]
|
|
responses = await asyncio.gather(*tasks, return_exceptions=True)
|
|
|
|
for resp in responses:
|
|
if isinstance(resp, Exception):
|
|
print(f" Error fetching ability: {resp}")
|
|
continue
|
|
if resp.status_code != 200:
|
|
print(f" HTTP {resp.status_code} for {resp.url}")
|
|
continue
|
|
|
|
ability_data = resp.json()
|
|
gen_name = ability_data["generation"]["name"]
|
|
introduced_gen = GEN_MAP.get(gen_name)
|
|
|
|
if introduced_gen is None:
|
|
print(
|
|
f" Unknown generation: {gen_name} for ability {ability_data['name']}"
|
|
)
|
|
continue
|
|
|
|
abilities.append(
|
|
{
|
|
"name": title_case_name(ability_data["name"]),
|
|
"introduced_gen": introduced_gen,
|
|
}
|
|
)
|
|
|
|
# Sort by name for consistent ordering
|
|
abilities.sort(key=lambda a: a["name"])
|
|
return abilities
|
|
|
|
|
|
async def main():
|
|
print("Fetching moves and abilities from PokeAPI...")
|
|
print()
|
|
|
|
async with httpx.AsyncClient(timeout=30.0) as client:
|
|
# Fetch moves
|
|
moves = await fetch_all_moves(client)
|
|
print()
|
|
|
|
# Fetch abilities
|
|
abilities = await fetch_all_abilities(client)
|
|
print()
|
|
|
|
# Write moves to JSON
|
|
moves_path = DATA_DIR / "moves.json"
|
|
with open(moves_path, "w") as f:
|
|
json.dump(moves, f, indent=2)
|
|
f.write("\n")
|
|
print(f"Wrote {len(moves)} moves to {moves_path}")
|
|
|
|
# Write abilities to JSON
|
|
abilities_path = DATA_DIR / "abilities.json"
|
|
with open(abilities_path, "w") as f:
|
|
json.dump(abilities, f, indent=2)
|
|
f.write("\n")
|
|
print(f"Wrote {len(abilities)} abilities to {abilities_path}")
|
|
|
|
print()
|
|
print("Done!")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
asyncio.run(main())
|