diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml
index 570b611d7..354c4c99d 100644
--- a/.github/workflows/code-quality.yml
+++ b/.github/workflows/code-quality.yml
@@ -18,9 +18,7 @@ on:
jobs:
ruff:
name: Ruff (lint)
- runs-on: ubuntu-latest
- # TODO: remove once the codebase is fully lint-clean
- continue-on-error: true
+ runs-on: ubuntu-26.04-arm
steps:
- name: Checkout
uses: actions/checkout@v6
@@ -29,27 +27,11 @@ jobs:
- name: Install dependencies
run: make install
- name: Lint
- run: make lint-check || true
- - name: Summary
- if: always()
- run: |
- {
- echo "## Ruff lint results"
- echo
- echo "Expand to see all errors (backlog while the codebase is being linted incrementally)
"
- echo
- echo '```'
- uv run ruff check --output-format=concise . || true
- echo '```'
- echo
- echo " "
- } >> "$GITHUB_STEP_SUMMARY"
+ run: make lint-check
ty:
name: ty (type-check)
- runs-on: ubuntu-latest
- # TODO: remove once the codebase is fully typed
- continue-on-error: true
+ runs-on: ubuntu-26.04-arm
steps:
- name: Checkout
uses: actions/checkout@v6
@@ -58,18 +40,4 @@ jobs:
- name: Install dependencies
run: make install
- name: Type-check
- run: make typecheck || true
- - name: Summary
- if: always()
- run: |
- {
- echo "## ty type-check results"
- echo
- echo "Expand to see all errors (backlog while the codebase is being typed incrementally)
"
- echo
- echo '```'
- uv run ty check --output-format=concise || true
- echo '```'
- echo
- echo " "
- } >> "$GITHUB_STEP_SUMMARY"
+ run: make typecheck
diff --git a/.github/workflows/database.yml b/.github/workflows/database.yml
index fa4a46198..4ba3256c2 100644
--- a/.github/workflows/database.yml
+++ b/.github/workflows/database.yml
@@ -34,7 +34,7 @@ jobs:
./csvlint -lazyquotes "$filename" # TODO: remove lazyquotes when https://github.com/Clever/csvlint/issues/45 will be addressed
done
openapi:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-26.04-arm
steps:
- name: Checkout
uses: actions/checkout@v6
@@ -45,7 +45,7 @@ jobs:
make install-base
make openapi-generate
sqlite:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-26.04-arm
strategy:
fail-fast: false
matrix:
@@ -71,7 +71,7 @@ jobs:
- name: Test data
run: curl -Ss http://localhost:8000/api/v2/pokemon/1/ | grep -q 'bulbasaur'
postgres:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-26.04-arm
steps:
- name: Checkout
uses: actions/checkout@v6
diff --git a/.github/workflows/docker-build-and-push.yml b/.github/workflows/docker-build-and-push.yml
index 285d55582..c2caa84dd 100644
--- a/.github/workflows/docker-build-and-push.yml
+++ b/.github/workflows/docker-build-and-push.yml
@@ -17,7 +17,7 @@ on:
jobs:
docker:
if: ${{ !github.event.repository.fork }}
- runs-on: ubuntu-latest
+ runs-on: ubuntu-26.04-arm
steps:
- name: Checkout
uses: actions/checkout@v6
diff --git a/.github/workflows/docker-k8s.yml b/.github/workflows/docker-k8s.yml
index 183d6e2e9..6760031cc 100644
--- a/.github/workflows/docker-k8s.yml
+++ b/.github/workflows/docker-k8s.yml
@@ -16,7 +16,7 @@ on:
jobs:
docker:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-26.04-arm
steps:
- name: Checkout
uses: actions/checkout@v6
@@ -45,7 +45,7 @@ jobs:
- name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }}
k8s:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-26.04-arm
steps:
- name: Checkout
uses: actions/checkout@v6
@@ -112,7 +112,7 @@ jobs:
- name: Get GQL output
run: kubectl logs jobs/load-graphql
compose:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-26.04-arm
steps:
- name: Checkout
uses: actions/checkout@v6
diff --git a/.github/workflows/nix-environment.yml b/.github/workflows/nix-environment.yml
index 798b1c497..894b94c66 100644
--- a/.github/workflows/nix-environment.yml
+++ b/.github/workflows/nix-environment.yml
@@ -16,7 +16,7 @@ on:
jobs:
nix-test:
- runs-on: ubuntu-latest
+ runs-on: ubuntu-26.04-arm
steps:
- name: Checkout
uses: actions/checkout@v6
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 6b49f55c9..54f898f05 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -18,7 +18,7 @@ on:
jobs:
release:
if: ${{ !github.event.repository.fork }}
- runs-on: ubuntu-latest
+ runs-on: ubuntu-26.04-arm
steps:
- name: Checkout
uses: actions/checkout@v6
diff --git a/data/v2/__init__.py b/data/v2/__init__.py
index 39496a2f6..9212439dc 100644
--- a/data/v2/__init__.py
+++ b/data/v2/__init__.py
@@ -1 +1,3 @@
-from .build import *
+from .build import build_all
+
+__all__: tuple[str, ...] = ("build_all",)
diff --git a/data/v2/build.py b/data/v2/build.py
index e959d8118..5ccbcd920 100644
--- a/data/v2/build.py
+++ b/data/v2/build.py
@@ -11,618 +11,191 @@
# wipe it and rewrite each row using the data found in data/v2/csv.
-import contextlib
-import csv
-import gzip
-import json
-import os
-import os.path
-import pathlib
import re
-import urllib.error
-import urllib.request
-from collections.abc import Callable
+from collections.abc import Iterator
+from contextlib import closing
from copy import deepcopy
-from email.utils import parsedate_to_datetime
-from typing import Any
-from django.db import connection
-
-from pokemon_v2.models import *
-
-# why this way? how about use `__file__`
-DATA_LOCATION = "data/v2/csv/"
-DATA_LOCATION2 = os.path.join(os.path.dirname(__file__), "csv")
-GROUP_RGX = r"\[(.*?)\]\{(.*?)\}"
-SUB_RGX = r"\[.*?\]\{.*?\}"
-
-DB_CURSOR = connection.cursor()
-DB_VENDOR = connection.vendor
-
-
-MEDIA_DIR = "{prefix}{{file_name}}".format(
- prefix=os.environ.get(
- "POKEAPI_SPRITES_PREFIX",
- "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/",
- )
+from data.v2.utils.helpers import BATCH_SIZE, build_generic, load_data
+from data.v2.utils.media import (
+ POKEMON_SPRITE_CONFIG,
+ TYPE_SPRITE_GAME_MAP,
+ file_path_or_none,
+ pokemon_form_sprite_lookup,
+ pokemon_sprite_lookup,
+ resolve_sprite_config,
)
-SOUND_DIR = "{prefix}{{file_name}}".format(
- prefix=os.environ.get(
- "POKEAPI_CRIES_PREFIX",
- "https://raw.githubusercontent.com/PokeAPI/cries/main/cries/",
- )
+from pokemon_v2.models import (
+ Ability,
+ AbilityChange,
+ AbilityChangeEffectText,
+ AbilityEffectText,
+ AbilityFlavorText,
+ AbilityName,
+ Berry,
+ BerryFirmness,
+ BerryFirmnessName,
+ BerryFlavor,
+ BerryFlavorMap,
+ BerryFlavorName,
+ Characteristic,
+ CharacteristicDescription,
+ ContestCombo,
+ ContestEffect,
+ ContestEffectEffectText,
+ ContestEffectFlavorText,
+ ContestType,
+ ContestTypeName,
+ Currency,
+ CurrencyName,
+ EggGroup,
+ EggGroupName,
+ Encounter,
+ EncounterCondition,
+ EncounterConditionName,
+ EncounterConditionValue,
+ EncounterConditionValueMap,
+ EncounterConditionValueName,
+ EncounterMethod,
+ EncounterMethodName,
+ EncounterPokemonDetail,
+ EncounterSlot,
+ EvolutionChain,
+ EvolutionTrigger,
+ EvolutionTriggerName,
+ EvolutionVariable,
+ EvolutionVariableDescription,
+ EvolutionVariableName,
+ Experience,
+ Gender,
+ Generation,
+ GenerationName,
+ GrowthRate,
+ GrowthRateDescription,
+ Item,
+ ItemAttribute,
+ ItemAttributeDescription,
+ ItemAttributeMap,
+ ItemAttributeName,
+ ItemCategory,
+ ItemCategoryName,
+ ItemEffectText,
+ ItemFlavorText,
+ ItemFlingEffect,
+ ItemFlingEffectEffectText,
+ ItemGameIndex,
+ ItemName,
+ ItemPocket,
+ ItemPocketName,
+ ItemPrice,
+ ItemSprites,
+ Language,
+ LanguageName,
+ Location,
+ LocationArea,
+ LocationAreaEncounterRate,
+ LocationAreaName,
+ LocationGameIndex,
+ LocationName,
+ Machine,
+ Move,
+ MoveAttribute,
+ MoveAttributeDescription,
+ MoveAttributeMap,
+ MoveAttributeName,
+ MoveBattleStyle,
+ MoveBattleStyleName,
+ MoveChange,
+ MoveDamageClass,
+ MoveDamageClassDescription,
+ MoveDamageClassName,
+ MoveEffect,
+ MoveEffectChange,
+ MoveEffectChangeEffectText,
+ MoveEffectEffectText,
+ MoveFlavorText,
+ MoveLearnMethod,
+ MoveLearnMethodDescription,
+ MoveLearnMethodName,
+ MoveMeta,
+ MoveMetaAilment,
+ MoveMetaAilmentName,
+ MoveMetaCategory,
+ MoveMetaCategoryDescription,
+ MoveMetaStatChange,
+ MoveName,
+ MoveTarget,
+ MoveTargetDescription,
+ MoveTargetName,
+ Nature,
+ NatureBattleStylePreference,
+ NatureName,
+ NaturePokeathlonStat,
+ PalPark,
+ PalParkArea,
+ PalParkAreaName,
+ PokeathlonStat,
+ PokeathlonStatName,
+ Pokedex,
+ PokedexDescription,
+ PokedexName,
+ PokedexVersionGroup,
+ Pokemon,
+ PokemonAbility,
+ PokemonAbilityPast,
+ PokemonColor,
+ PokemonColorName,
+ PokemonCries,
+ PokemonDexNumber,
+ PokemonEggGroup,
+ PokemonEvolution,
+ PokemonForm,
+ PokemonFormCondition,
+ PokemonFormFlavorText,
+ PokemonFormGeneration,
+ PokemonFormName,
+ PokemonFormSprites,
+ PokemonFormTrigger,
+ PokemonFormType,
+ PokemonGameIndex,
+ PokemonHabitat,
+ PokemonHabitatName,
+ PokemonItem,
+ PokemonMove,
+ PokemonShape,
+ PokemonShapeName,
+ PokemonSpecies,
+ PokemonSpeciesDescription,
+ PokemonSpeciesFlavorText,
+ PokemonSpeciesName,
+ PokemonSprites,
+ PokemonStat,
+ PokemonStatPast,
+ PokemonType,
+ PokemonTypePast,
+ Region,
+ RegionName,
+ Stat,
+ StatName,
+ SuperContestCombo,
+ SuperContestEffect,
+ SuperContestEffectFlavorText,
+ Type,
+ TypeEfficacy,
+ TypeEfficacyPast,
+ TypeGameIndex,
+ TypeName,
+ TypeSprites,
+ Version,
+ VersionGroup,
+ VersionGroupMoveLearnMethod,
+ VersionGroupRegion,
+ VersionName,
)
-IMAGE_DIR = os.getcwd() + "/data/v2/sprites/sprites/"
-CRIES_DIR = os.getcwd() + "/data/v2/cries/cries/"
-
-
-def _load_manifest_from_path(path: str) -> set[str] | None:
- try:
- with gzip.open(path, "rt", encoding="utf-8") as f:
- return set(json.load(f))
- except (OSError, json.JSONDecodeError) as e:
- print(f"Warning: Failed to read local sprite manifest at {path}: {e}")
- return None
-
-
-def _fetch_manifest(url: str, cache_file: str) -> set[str] | None:
- try:
- # Check freshness if cache_file exists
- if os.path.isfile(cache_file):
- try:
- head_req = urllib.request.Request(url, method="HEAD")
- with urllib.request.urlopen(head_req, timeout=10) as head_resp:
- last_mod_header = head_resp.headers.get("Last-Modified")
- if last_mod_header:
- remote_mtime = parsedate_to_datetime(last_mod_header).timestamp()
- if os.path.getmtime(cache_file) >= remote_mtime:
- return _load_manifest_from_path(cache_file)
- except (OSError, urllib.error.URLError, ValueError):
- pass
-
- # Update manifest
- with urllib.request.urlopen(url, timeout=30) as resp:
- raw_bytes: bytes = resp.read()
- data = json.loads(gzip.decompress(raw_bytes).decode("utf-8"))
-
- with contextlib.suppress(OSError):
- pathlib.Path(cache_file).write_bytes(raw_bytes)
- last_mod = resp.headers.get("Last-Modified")
- if last_mod:
- mtime = parsedate_to_datetime(last_mod).timestamp()
- os.utime(cache_file, (mtime, mtime))
-
- return set(data)
- except (OSError, urllib.error.URLError, json.JSONDecodeError, gzip.BadGzipFile) as e:
- print(f"Warning: Failed to fetch sprite manifest from {url}: {e}")
- return None
-
-
-def _load_local_sprites() -> set[str]:
- """Scan the local cloned sprites submodule directory."""
- if not (os.path.isdir(IMAGE_DIR) and os.listdir(IMAGE_DIR)):
- return set()
-
- images: set[str] = set()
- for root, _dirs, files in os.walk(IMAGE_DIR):
- for file in files:
- image_path = os.path.join(root.replace(IMAGE_DIR, ""), file)
- images.add(image_path.replace("\\", "/"))
- return images
-
-
-def _load_resource_images() -> set[str]:
- use_local = os.environ.get("POKEAPI_USE_LOCAL_SPRITES", "").lower() in ("1", "true", "yes")
- if use_local and (local_images := _load_local_sprites()):
- return local_images
-
- cache_file = os.path.join(os.path.dirname(__file__), "manifest.json.gz")
- manifest_urls: list[str] = list(
- filter(
- None,
- [
- os.environ.get("POKEAPI_SPRITES_MANIFEST_URL", ""),
- "https://pokeapi.github.io/sprites/manifest.json.gz",
- "https://github.com/PokeAPI/sprites/releases/latest/download/manifest.json.gz",
- ],
- )
- )
- for url in manifest_urls:
- if (data := _fetch_manifest(url, cache_file)) is not None:
- return data
-
- # If offline / all remote requests failed, use cached manifest if available
- if os.path.isfile(cache_file) and (cached := _load_manifest_from_path(cache_file)) is not None:
- return cached
-
- # Fallback to local files if manifest is completely unavailable
- if local_images := _load_local_sprites():
- return local_images
-
- print("Warning: Could not load sprite resources from local files or remote manifest. Sprites will be empty.")
- return set()
-
-
-RESOURCE_IMAGES: set[str] = _load_resource_images()
-RESOURCE_CRIES: set[str] = set()
-
-for root, _dirs, files in os.walk(CRIES_DIR):
- for file in files:
- cry_path = os.path.join(root.replace(CRIES_DIR, ""), file)
- cry_path = cry_path.replace("\\", "/") # convert Windows-style path to Unix
- RESOURCE_CRIES.add(cry_path)
-
-
-POKEMON_SPRITE_CONFIG: dict[str, Any] = {
- "front_default": ("pokemon/", "png"),
- "front_female": ("pokemon/female/", "png"),
- "front_shiny": ("pokemon/shiny/", "png"),
- "front_shiny_female": ("pokemon/shiny/female/", "png"),
- "back_default": ("pokemon/back/", "png"),
- "back_female": ("pokemon/back/female/", "png"),
- "back_shiny": ("pokemon/back/shiny/", "png"),
- "back_shiny_female": ("pokemon/back/shiny/female/", "png"),
- "other": {
- "dream_world": {
- "front_default": ("pokemon/other/dream-world/", "svg"),
- "front_female": ("pokemon/other/dream-world/female/", "svg"),
- },
- "home": {
- "front_default": ("pokemon/other/home/", "png"),
- "front_female": ("pokemon/other/home/female/", "png"),
- "front_shiny": ("pokemon/other/home/shiny/", "png"),
- "front_shiny_female": ("pokemon/other/home/shiny/female/", "png"),
- },
- "official-artwork": {
- "front_default": ("pokemon/other/official-artwork/", "png"),
- "front_shiny": ("pokemon/other/official-artwork/shiny/", "png"),
- "versions": {
- "generation-i": {
- "red-and-blue": {
- "front_default": ("pokemon/other/official-artwork/versions/generation-i/red-and-blue/", "png")
- },
- "red-and-green": {
- "front_default": ("pokemon/other/official-artwork/versions/generation-i/red-and-green/", "png")
- },
- },
- "generation-ii": {
- "gold-and-silver": {
- "front_default": (
- "pokemon/other/official-artwork/versions/generation-ii/gold-and-silver/",
- "png",
- )
- }
- },
- },
- },
- "showdown": {
- "front_default": ("pokemon/other/showdown/", "gif"),
- "front_female": ("pokemon/other/showdown/female/", "gif"),
- "front_shiny": ("pokemon/other/showdown/shiny/", "gif"),
- "front_shiny_female": ("pokemon/other/showdown/shiny/female/", "gif"),
- "back_default": ("pokemon/other/showdown/back/", "gif"),
- "back_female": ("pokemon/other/showdown/back/female/", "gif"),
- "back_shiny": ("pokemon/other/showdown/back/shiny/", "gif"),
- "back_shiny_female": ("pokemon/other/showdown/back/shiny/female/", "gif"),
- },
- },
- "versions": {
- "generation-i": {
- "red-blue": {
- "front_default": ("pokemon/versions/generation-i/red-blue/", "png"),
- "front_gray": ("pokemon/versions/generation-i/red-blue/gray/", "png"),
- "front_transparent": ("pokemon/versions/generation-i/red-blue/transparent/", "png"),
- "front_transparent_gray": ("pokemon/versions/generation-i/red-blue/transparent/gray/", "png"),
- "back_default": ("pokemon/versions/generation-i/red-blue/back/", "png"),
- "back_gray": ("pokemon/versions/generation-i/red-blue/back/gray/", "png"),
- "back_transparent": ("pokemon/versions/generation-i/red-blue/transparent/back/", "png"),
- "back_transparent_gray": ("pokemon/versions/generation-i/red-blue/transparent/back/gray/", "png"),
- },
- "red-green-japan": {
- "front_default": ("pokemon/versions/generation-i/red-green-japan/", "png"),
- "front_gray": ("pokemon/versions/generation-i/red-green-japan/gray/", "png"),
- "back_default": ("pokemon/versions/generation-i/red-green-japan/back/", "png"),
- "back_gray": ("pokemon/versions/generation-i/red-green-japan/back/gray/", "png"),
- },
- "yellow": {
- "front_default": ("pokemon/versions/generation-i/yellow/", "png"),
- "front_gbc": ("pokemon/versions/generation-i/yellow/gbc/", "png"),
- "front_gray": ("pokemon/versions/generation-i/yellow/gray/", "png"),
- "front_transparent": ("pokemon/versions/generation-i/yellow/transparent/", "png"),
- "front_transparent_gray": ("pokemon/versions/generation-i/yellow/transparent/gray/", "png"),
- "back_default": ("pokemon/versions/generation-i/yellow/back/", "png"),
- "back_gbc": ("pokemon/versions/generation-i/yellow/back/gbc/", "png"),
- "back_gray": ("pokemon/versions/generation-i/yellow/back/gray/", "png"),
- "back_transparent": ("pokemon/versions/generation-i/yellow/transparent/back/", "png"),
- "back_transparent_gray": ("pokemon/versions/generation-i/yellow/transparent/back/gray/", "png"),
- },
- },
- "generation-ii": {
- "crystal": {
- "front_default": ("pokemon/versions/generation-ii/crystal/", "png"),
- "front_shiny": ("pokemon/versions/generation-ii/crystal/shiny/", "png"),
- "front_transparent": ("pokemon/versions/generation-ii/crystal/transparent/", "png"),
- "front_shiny_transparent": ("pokemon/versions/generation-ii/crystal/transparent/shiny/", "png"),
- "back_default": ("pokemon/versions/generation-ii/crystal/back/", "png"),
- "back_shiny": ("pokemon/versions/generation-ii/crystal/back/shiny/", "png"),
- "back_transparent": ("pokemon/versions/generation-ii/crystal/transparent/back/", "png"),
- "back_shiny_transparent": ("pokemon/versions/generation-ii/crystal/transparent/back/shiny/", "png"),
- "animated": {
- "front_default": ("pokemon/versions/generation-ii/crystal/animated/", "gif"),
- "front_shiny": ("pokemon/versions/generation-ii/crystal/animated/shiny/", "gif"),
- },
- },
- "gold": {
- "front_default": ("pokemon/versions/generation-ii/gold/", "png"),
- "front_shiny": ("pokemon/versions/generation-ii/gold/shiny/", "png"),
- "front_transparent": ("pokemon/versions/generation-ii/gold/transparent/", "png"),
- "front_shiny_transparent": ("pokemon/versions/generation-ii/gold/transparent/shiny/", "png"),
- "back_default": ("pokemon/versions/generation-ii/gold/back/", "png"),
- "back_shiny": ("pokemon/versions/generation-ii/gold/back/shiny/", "png"),
- "back_transparent": ("pokemon/versions/generation-ii/gold/transparent/back/", "png"),
- "back_shiny_transparent": ("pokemon/versions/generation-ii/gold/transparent/back/shiny/", "png"),
- },
- "silver": {
- "front_default": ("pokemon/versions/generation-ii/silver/", "png"),
- "front_shiny": ("pokemon/versions/generation-ii/silver/shiny/", "png"),
- "front_transparent": ("pokemon/versions/generation-ii/silver/transparent/", "png"),
- "front_shiny_transparent": ("pokemon/versions/generation-ii/silver/transparent/shiny/", "png"),
- "back_default": ("pokemon/versions/generation-ii/silver/back/", "png"),
- "back_shiny": ("pokemon/versions/generation-ii/silver/back/shiny/", "png"),
- "back_transparent": ("pokemon/versions/generation-ii/silver/transparent/back/", "png"),
- "back_shiny_transparent": ("pokemon/versions/generation-ii/silver/transparent/back/shiny/", "png"),
- },
- },
- "generation-iii": {
- "emerald": {
- "front_default": ("pokemon/versions/generation-iii/emerald/", "png"),
- "front_shiny": ("pokemon/versions/generation-iii/emerald/shiny/", "png"),
- "back_default": ("pokemon/versions/generation-iii/emerald/back/", "png"),
- "back_shiny": ("pokemon/versions/generation-iii/emerald/back/shiny/", "png"),
- "animated": {
- "front_default": ("pokemon/versions/generation-iii/emerald/animated/", "png"),
- "front_shiny": ("pokemon/versions/generation-iii/emerald/animated/shiny/", "png"),
- "back_default": ("pokemon/versions/generation-iii/emerald/animated/back/", "png"),
- "back_shiny": ("pokemon/versions/generation-iii/emerald/animated/back/shiny/", "png"),
- },
- },
- "firered-leafgreen": {
- "front_default": ("pokemon/versions/generation-iii/firered-leafgreen/", "png"),
- "front_shiny": ("pokemon/versions/generation-iii/firered-leafgreen/shiny/", "png"),
- "back_default": ("pokemon/versions/generation-iii/firered-leafgreen/back/", "png"),
- "back_shiny": ("pokemon/versions/generation-iii/firered-leafgreen/back/shiny/", "png"),
- },
- "icons": {
- "front_default": ("pokemon/versions/generation-iii/icons/", "png"),
- },
- "ruby-sapphire": {
- "front_default": ("pokemon/versions/generation-iii/ruby-sapphire/", "png"),
- "front_shiny": ("pokemon/versions/generation-iii/ruby-sapphire/shiny/", "png"),
- "back_default": ("pokemon/versions/generation-iii/ruby-sapphire/back/", "png"),
- "back_shiny": ("pokemon/versions/generation-iii/ruby-sapphire/back/shiny/", "png"),
- },
- },
- "generation-iv": {
- "diamond-pearl": {
- "front_default": ("pokemon/versions/generation-iv/diamond-pearl/", "png"),
- "front_female": ("pokemon/versions/generation-iv/diamond-pearl/female/", "png"),
- "front_shiny": ("pokemon/versions/generation-iv/diamond-pearl/shiny/", "png"),
- "front_shiny_female": ("pokemon/versions/generation-iv/diamond-pearl/shiny/female/", "png"),
- "back_default": ("pokemon/versions/generation-iv/diamond-pearl/back/", "png"),
- "back_female": ("pokemon/versions/generation-iv/diamond-pearl/back/female/", "png"),
- "back_shiny": ("pokemon/versions/generation-iv/diamond-pearl/back/shiny/", "png"),
- "back_shiny_female": ("pokemon/versions/generation-iv/diamond-pearl/back/shiny/female/", "png"),
- "animated": {
- "front_default": ("pokemon/versions/generation-iv/diamond-pearl/animated/", "png"),
- "front_female": ("pokemon/versions/generation-iv/diamond-pearl/animated/female/", "png"),
- "front_shiny": ("pokemon/versions/generation-iv/diamond-pearl/animated/shiny/", "png"),
- "front_shiny_female": (
- "pokemon/versions/generation-iv/diamond-pearl/animated/shiny/female/",
- "png",
- ),
- },
- },
- "heartgold-soulsilver": {
- "front_default": ("pokemon/versions/generation-iv/heartgold-soulsilver/", "png"),
- "front_female": ("pokemon/versions/generation-iv/heartgold-soulsilver/female/", "png"),
- "front_shiny": ("pokemon/versions/generation-iv/heartgold-soulsilver/shiny/", "png"),
- "front_shiny_female": ("pokemon/versions/generation-iv/heartgold-soulsilver/shiny/female/", "png"),
- "back_default": ("pokemon/versions/generation-iv/heartgold-soulsilver/back/", "png"),
- "back_female": ("pokemon/versions/generation-iv/heartgold-soulsilver/back/female/", "png"),
- "back_shiny": ("pokemon/versions/generation-iv/heartgold-soulsilver/back/shiny/", "png"),
- "back_shiny_female": ("pokemon/versions/generation-iv/heartgold-soulsilver/back/shiny/female/", "png"),
- "animated": {
- "front_default": ("pokemon/versions/generation-iv/heartgold-soulsilver/animated/", "png"),
- "front_female": ("pokemon/versions/generation-iv/heartgold-soulsilver/animated/female/", "png"),
- "front_shiny": ("pokemon/versions/generation-iv/heartgold-soulsilver/animated/shiny/", "png"),
- "front_shiny_female": (
- "pokemon/versions/generation-iv/heartgold-soulsilver/animated/shiny/female/",
- "png",
- ),
- },
- },
- "icons": {
- "front_default": ("pokemon/versions/generation-iv/icons/", "png"),
- },
- "platinum": {
- "front_default": ("pokemon/versions/generation-iv/platinum/", "png"),
- "front_female": ("pokemon/versions/generation-iv/platinum/female/", "png"),
- "front_shiny": ("pokemon/versions/generation-iv/platinum/shiny/", "png"),
- "front_shiny_female": ("pokemon/versions/generation-iv/platinum/shiny/female/", "png"),
- "back_default": ("pokemon/versions/generation-iv/platinum/back/", "png"),
- "back_female": ("pokemon/versions/generation-iv/platinum/back/female/", "png"),
- "back_shiny": ("pokemon/versions/generation-iv/platinum/back/shiny/", "png"),
- "back_shiny_female": ("pokemon/versions/generation-iv/platinum/back/shiny/female/", "png"),
- "animated": {
- "front_default": ("pokemon/versions/generation-iv/platinum/animated/", "png"),
- "front_female": ("pokemon/versions/generation-iv/platinum/animated/female/", "png"),
- "front_shiny": ("pokemon/versions/generation-iv/platinum/animated/shiny/", "png"),
- "front_shiny_female": ("pokemon/versions/generation-iv/platinum/animated/shiny/female/", "png"),
- },
- },
- },
- "generation-v": {
- "black-white": {
- "front_default": ("pokemon/versions/generation-v/black-white/", "png"),
- "front_female": ("pokemon/versions/generation-v/black-white/female/", "png"),
- "front_shiny": ("pokemon/versions/generation-v/black-white/shiny/", "png"),
- "front_shiny_female": ("pokemon/versions/generation-v/black-white/shiny/female/", "png"),
- "back_default": ("pokemon/versions/generation-v/black-white/back/", "png"),
- "back_female": ("pokemon/versions/generation-v/black-white/back/female/", "png"),
- "back_shiny": ("pokemon/versions/generation-v/black-white/back/shiny/", "png"),
- "back_shiny_female": ("pokemon/versions/generation-v/black-white/back/shiny/female/", "png"),
- "animated": {
- "front_default": ("pokemon/versions/generation-v/black-white/animated/", "gif"),
- "front_female": ("pokemon/versions/generation-v/black-white/animated/female/", "gif"),
- "front_shiny": ("pokemon/versions/generation-v/black-white/animated/shiny/", "gif"),
- "front_shiny_female": ("pokemon/versions/generation-v/black-white/animated/shiny/female/", "gif"),
- "back_default": ("pokemon/versions/generation-v/black-white/animated/back/", "gif"),
- "back_female": ("pokemon/versions/generation-v/black-white/animated/back/female/", "gif"),
- "back_shiny": ("pokemon/versions/generation-v/black-white/animated/back/shiny/", "gif"),
- "back_shiny_female": (
- "pokemon/versions/generation-v/black-white/animated/back/shiny/female/",
- "gif",
- ),
- },
- },
- "icons": {
- "front_default": ("pokemon/versions/generation-v/icons/", "png"),
- "animated": {
- "front_default": ("pokemon/versions/generation-v/icons/animated/", "png"),
- },
- },
- },
- "generation-vi": {
- "icons": {
- "front_default": ("pokemon/versions/generation-vi/icons/", "png"),
- "front_female": ("pokemon/versions/generation-vi/icons/female/", "png"),
- },
- "omega-ruby-alpha-sapphire": {
- "front_default": ("pokemon/versions/generation-vi/omega-ruby-alpha-sapphire/", "png"),
- "front_female": ("pokemon/versions/generation-vi/omega-ruby-alpha-sapphire/female/", "png"),
- "front_shiny": ("pokemon/versions/generation-vi/omega-ruby-alpha-sapphire/shiny/", "png"),
- "front_shiny_female": ("pokemon/versions/generation-vi/omega-ruby-alpha-sapphire/shiny/female/", "png"),
- "back_default": ("pokemon/versions/generation-vi/omega-ruby-alpha-sapphire/back/", "png"),
- "back_female": ("pokemon/versions/generation-vi/omega-ruby-alpha-sapphire/back/female/", "png"),
- "back_shiny": ("pokemon/versions/generation-vi/omega-ruby-alpha-sapphire/back/shiny/", "png"),
- "back_shiny_female": (
- "pokemon/versions/generation-vi/omega-ruby-alpha-sapphire/back/shiny/female/",
- "png",
- ),
- },
- "x-y": {
- "front_default": ("pokemon/versions/generation-vi/x-y/", "png"),
- "front_female": ("pokemon/versions/generation-vi/x-y/female/", "png"),
- "front_shiny": ("pokemon/versions/generation-vi/x-y/shiny/", "png"),
- "front_shiny_female": ("pokemon/versions/generation-vi/x-y/shiny/female/", "png"),
- "back_default": ("pokemon/versions/generation-vi/x-y/back/", "png"),
- "back_female": ("pokemon/versions/generation-vi/x-y/back/female/", "png"),
- "back_shiny": ("pokemon/versions/generation-vi/x-y/back/shiny/", "png"),
- "back_shiny_female": ("pokemon/versions/generation-vi/x-y/back/shiny/female/", "png"),
- },
- },
- "generation-vii": {
- "icons": {
- "front_default": ("pokemon/versions/generation-vii/icons/", "png"),
- "front_female": ("pokemon/versions/generation-vii/icons/female/", "png"),
- },
- "lets-go-pikachu-lets-go-eevee": {
- "front_default": ("pokemon/versions/generation-vii/lets-go-pikachu-lets-go-eevee/", "gif"),
- "front_female": ("pokemon/versions/generation-vii/lets-go-pikachu-lets-go-eevee/female/", "gif"),
- "front_shiny": ("pokemon/versions/generation-vii/lets-go-pikachu-lets-go-eevee/shiny/", "gif"),
- "front_shiny_female": (
- "pokemon/versions/generation-vii/lets-go-pikachu-lets-go-eevee/shiny/female/",
- "gif",
- ),
- "back_default": ("pokemon/versions/generation-vii/lets-go-pikachu-lets-go-eevee/back/", "gif"),
- "back_female": ("pokemon/versions/generation-vii/lets-go-pikachu-lets-go-eevee/back/female/", "gif"),
- "back_shiny": ("pokemon/versions/generation-vii/lets-go-pikachu-lets-go-eevee/back/shiny/", "gif"),
- "back_shiny_female": (
- "pokemon/versions/generation-vii/lets-go-pikachu-lets-go-eevee/back/shiny/female/",
- "gif",
- ),
- "icons": {
- "front_default": ("pokemon/versions/generation-vii/lets-go-pikachu-lets-go-eevee/icons/", "png")
- },
- },
- "ultra-sun-ultra-moon": {
- "front_default": ("pokemon/versions/generation-vii/ultra-sun-ultra-moon/", "gif"),
- "front_female": ("pokemon/versions/generation-vii/ultra-sun-ultra-moon/female/", "gif"),
- "front_shiny": ("pokemon/versions/generation-vii/ultra-sun-ultra-moon/shiny/", "gif"),
- "front_shiny_female": ("pokemon/versions/generation-vii/ultra-sun-ultra-moon/shiny/female/", "gif"),
- "back_default": ("pokemon/versions/generation-vii/ultra-sun-ultra-moon/back/", "gif"),
- "back_female": ("pokemon/versions/generation-vii/ultra-sun-ultra-moon/back/female/", "gif"),
- "back_shiny": ("pokemon/versions/generation-vii/ultra-sun-ultra-moon/back/shiny/", "gif"),
- "back_shiny_female": ("pokemon/versions/generation-vii/ultra-sun-ultra-moon/back/shiny/female/", "gif"),
- },
- },
- "generation-viii": {
- "brilliant-diamond-shining-pearl": {
- "front_default": ("pokemon/versions/generation-viii/brilliant-diamond-shining-pearl/", "webp"),
- "front_female": ("pokemon/versions/generation-viii/brilliant-diamond-shining-pearl/female/", "webp"),
- "front_shiny": ("pokemon/versions/generation-viii/brilliant-diamond-shining-pearl/shiny/", "webp"),
- "front_shiny_female": (
- "pokemon/versions/generation-viii/brilliant-diamond-shining-pearl/shiny/female/",
- "webp",
- ),
- "back_default": ("pokemon/versions/generation-viii/brilliant-diamond-shining-pearl/back/", "webp"),
- "back_female": (
- "pokemon/versions/generation-viii/brilliant-diamond-shining-pearl/back/female/",
- "webp",
- ),
- "back_shiny": ("pokemon/versions/generation-viii/brilliant-diamond-shining-pearl/back/shiny/", "webp"),
- "back_shiny_female": (
- "pokemon/versions/generation-viii/brilliant-diamond-shining-pearl/back/shiny/female/",
- "webp",
- ),
- "icons": {
- "front_default": ("pokemon/versions/generation-viii/brilliant-diamond-shining-pearl/icons/", "png"),
- },
- },
- "legends-arceus": {
- "front_default": ("pokemon/versions/generation-viii/legends-arceus/", "webp"),
- "front_female": ("pokemon/versions/generation-viii/legends-arceus/female/", "webp"),
- "front_shiny": ("pokemon/versions/generation-viii/legends-arceus/shiny/", "webp"),
- "front_shiny_female": ("pokemon/versions/generation-viii/legends-arceus/shiny/female/", "webp"),
- "back_default": ("pokemon/versions/generation-viii/legends-arceus/back/", "webp"),
- "back_female": ("pokemon/versions/generation-viii/legends-arceus/back/female/", "webp"),
- "back_shiny": ("pokemon/versions/generation-viii/legends-arceus/back/shiny/", "webp"),
- "back_shiny_female": ("pokemon/versions/generation-viii/legends-arceus/back/shiny/female/", "webp"),
- "icons": {
- "front_default": ("pokemon/versions/generation-viii/legends-arceus/icons/", "png"),
- "front_female": ("pokemon/versions/generation-viii/legends-arceus/icons/female/", "png"),
- "front_shiny": ("pokemon/versions/generation-viii/legends-arceus/icons/shiny/", "png"),
- "front_shiny_female": (
- "pokemon/versions/generation-viii/legends-arceus/icons/shiny/female/",
- "png",
- ),
- },
- },
- "sword-shield": {
- "front_default": ("pokemon/versions/generation-viii/sword-shield/", "webp"),
- "front_female": ("pokemon/versions/generation-viii/sword-shield/female/", "webp"),
- "front_shiny": ("pokemon/versions/generation-viii/sword-shield/shiny/", "webp"),
- "front_shiny_female": ("pokemon/versions/generation-viii/sword-shield/shiny/female/", "webp"),
- "back_default": ("pokemon/versions/generation-viii/sword-shield/back/", "webp"),
- "back_female": ("pokemon/versions/generation-viii/sword-shield/back/female/", "webp"),
- "back_shiny": ("pokemon/versions/generation-viii/sword-shield/back/shiny/", "webp"),
- "back_shiny_female": ("pokemon/versions/generation-viii/sword-shield/back/shiny/female/", "webp"),
- "icons": {
- "front_default": ("pokemon/versions/generation-viii/sword-shield/icons/", "png"),
- "front_female": ("pokemon/versions/generation-viii/sword-shield/icons/female/", "png"),
- },
- },
- },
- "generation-ix": {
- "champions": {
- "front_default": ("pokemon/versions/generation-ix/champions/", "webp"),
- "front_female": ("pokemon/versions/generation-ix/champions/female/", "webp"),
- "front_shiny": ("pokemon/versions/generation-ix/champions/shiny/", "webp"),
- "front_shiny_female": ("pokemon/versions/generation-ix/champions/shiny/female/", "webp"),
- "back_default": ("pokemon/versions/generation-ix/champions/back/", "webp"),
- "back_female": ("pokemon/versions/generation-ix/champions/back/female/", "webp"),
- "back_shiny": ("pokemon/versions/generation-ix/champions/back/shiny/", "webp"),
- "back_shiny_female": ("pokemon/versions/generation-ix/champions/back/shiny/female/", "webp"),
- "icons": {
- "front_default": ("pokemon/versions/generation-ix/champions/icons/", "png"),
- "front_shiny": ("pokemon/versions/generation-ix/champions/icons/shiny/", "png"),
- },
- },
- "legends-za": {
- "front_default": ("pokemon/versions/generation-ix/legends-za/", "webp"),
- "front_female": ("pokemon/versions/generation-ix/legends-za/female/", "webp"),
- "front_shiny": ("pokemon/versions/generation-ix/legends-za/shiny/", "webp"),
- "front_shiny_female": ("pokemon/versions/generation-ix/legends-za/shiny/female/", "webp"),
- "back_default": ("pokemon/versions/generation-ix/legends-za/back/", "webp"),
- "back_female": ("pokemon/versions/generation-ix/legends-za/back/female/", "webp"),
- "back_shiny": ("pokemon/versions/generation-ix/legends-za/back/shiny/", "webp"),
- "back_shiny_female": ("pokemon/versions/generation-ix/legends-za/back/shiny/female/", "webp"),
- "icons": {
- "front_default": ("pokemon/versions/generation-ix/legends-za/icons/", "png"),
- "front_female": ("pokemon/versions/generation-ix/legends-za/icons/female/", "png"),
- "front_shiny": ("pokemon/versions/generation-ix/legends-za/icons/shiny/", "png"),
- "front_shiny_female": ("pokemon/versions/generation-ix/legends-za/icons/shiny/female/", "png"),
- },
- },
- "scarlet-violet": {
- "front_default": ("pokemon/versions/generation-ix/scarlet-violet/", "webp"),
- "front_female": ("pokemon/versions/generation-ix/scarlet-violet/female/", "webp"),
- "front_shiny": ("pokemon/versions/generation-ix/scarlet-violet/shiny/", "webp"),
- "front_shiny_female": ("pokemon/versions/generation-ix/scarlet-violet/shiny/female/", "webp"),
- "back_default": ("pokemon/versions/generation-ix/scarlet-violet/back/", "webp"),
- "back_female": ("pokemon/versions/generation-ix/scarlet-violet/back/female/", "webp"),
- "back_shiny": ("pokemon/versions/generation-ix/scarlet-violet/back/shiny/", "webp"),
- "back_shiny_female": ("pokemon/versions/generation-ix/scarlet-violet/back/shiny/female/", "webp"),
- "icons": {
- "front_default": ("pokemon/versions/generation-ix/scarlet-violet/icons/", "png"),
- "front_female": ("pokemon/versions/generation-ix/scarlet-violet/icons/female/", "png"),
- },
- },
- },
- },
-}
-
-
-def file_path_or_none(file_name, image_file=True):
- if not image_file:
- return SOUND_DIR.format(file_name=file_name) if file_name in RESOURCE_CRIES else None
- return MEDIA_DIR.format(file_name=file_name) if file_name in RESOURCE_IMAGES else None
-
-
-def with_iter(context, iterable=None):
- if iterable is None:
- iterable = context
- with context:
- yield from iterable
-
-
-def load_data(file_name):
- # with_iter closes the file when it has finished
- return csv.reader(with_iter(open(DATA_LOCATION + file_name, encoding="utf8")), delimiter=",")
-
-
-def clear_table(model):
- table_name = model._meta.db_table
- model.objects.all().delete()
- print("building " + table_name)
- # Reset DB auto increments to start at 1
- if DB_VENDOR == "sqlite":
- DB_CURSOR.execute("DELETE FROM sqlite_sequence WHERE name = " + "'" + table_name + "'")
- else:
- DB_CURSOR.execute("SELECT setval(pg_get_serial_sequence(" + "'" + table_name + "'" + ",'id'), 1, false);")
-
-
-def build_generic(model_classes, file_name, csv_record_to_objects):
- batches = {}
- for model_class in model_classes:
- clear_table(model_class)
- batches[model_class] = [] # one batch per model class
-
- csv_data = load_data(file_name)
- next(csv_data, None) # skip header
-
- for csv_record in csv_data:
- for obj in csv_record_to_objects(csv_record):
- model_class = type(obj)
- batches[model_class].append(obj)
-
- # Limit the batch size
- if len(batches[model_class]) > 200:
- model_class.objects.bulk_create(batches[model_class])
- batches[model_class] = []
-
- for model_class, batch in batches.items():
- model_class.objects.bulk_create(batch)
-
-
-##############
-# LANGUAGE #
-##############
-
-
-def _build_languages():
- def csv_record_to_objects(info):
+
+def _build_languages() -> None:
+ def languages_from_rows(info: list[str]) -> Iterator[Language]:
yield Language(
id=int(info[0]),
iso639=info[1],
@@ -632,55 +205,40 @@ def csv_record_to_objects(info):
order=info[5],
)
- build_generic((Language,), "languages.csv", csv_record_to_objects)
+ build_generic((Language,), "languages.csv", languages_from_rows)
- def csv_record_to_objects(info):
+ def language_names_from_rows(info: list[str]) -> Iterator[LanguageName]:
yield LanguageName(language_id=int(info[0]), local_language_id=int(info[1]), name=info[2])
- build_generic((LanguageName,), "language_names.csv", csv_record_to_objects)
+ build_generic((LanguageName,), "language_names.csv", language_names_from_rows)
-############
-# REGION #
-############
-
-
-def _build_regions():
- def csv_record_to_objects(info):
+def _build_regions() -> None:
+ def regions_from_rows(info: list[str]) -> Iterator[Region]:
yield Region(id=int(info[0]), name=info[1])
- build_generic((Region,), "regions.csv", csv_record_to_objects)
+ build_generic((Region,), "regions.csv", regions_from_rows)
- def csv_record_to_objects(info):
+ def region_names_from_rows(info: list[str]) -> Iterator[RegionName]:
yield RegionName(region_id=int(info[0]), language_id=int(info[1]), name=info[2])
- build_generic((RegionName,), "region_names.csv", csv_record_to_objects)
-
+ build_generic((RegionName,), "region_names.csv", region_names_from_rows)
-################
-# GENERATION #
-################
-
-def _build_generations():
- def csv_record_to_objects(info):
+def _build_generations() -> None:
+ def generations_from_rows(info: list[str]) -> Iterator[Generation]:
yield Generation(id=int(info[0]), region_id=int(info[1]), name=info[2])
- build_generic((Generation,), "generations.csv", csv_record_to_objects)
+ build_generic((Generation,), "generations.csv", generations_from_rows)
- def csv_record_to_objects(info):
+ def generation_names_from_rows(info: list[str]) -> Iterator[GenerationName]:
yield GenerationName(generation_id=int(info[0]), language_id=int(info[1]), name=info[2])
- build_generic((GenerationName,), "generation_names.csv", csv_record_to_objects)
-
+ build_generic((GenerationName,), "generation_names.csv", generation_names_from_rows)
-#############
-# VERSION #
-#############
-
-def _build_versions():
- def csv_record_to_objects(info):
+def _build_versions() -> None:
+ def version_groups_from_rows(info: list[str]) -> Iterator[VersionGroup]:
yield VersionGroup(
id=int(info[0]),
name=info[1],
@@ -688,36 +246,33 @@ def csv_record_to_objects(info):
order=int(info[3]),
)
- build_generic((VersionGroup,), "version_groups.csv", csv_record_to_objects)
+ build_generic((VersionGroup,), "version_groups.csv", version_groups_from_rows)
- def csv_record_to_objects(info):
+ def version_group_regions_from_rows(info: list[str]) -> Iterator[VersionGroupRegion]:
yield VersionGroupRegion(version_group_id=int(info[0]), region_id=int(info[1]))
- build_generic((VersionGroupRegion,), "version_group_regions.csv", csv_record_to_objects)
+ build_generic((VersionGroupRegion,), "version_group_regions.csv", version_group_regions_from_rows)
- def csv_record_to_objects(info):
+ def versions_from_rows(info: list[str]) -> Iterator[Version]:
yield Version(id=int(info[0]), version_group_id=int(info[1]), name=info[2])
- build_generic((Version,), "versions.csv", csv_record_to_objects)
+ build_generic((Version,), "versions.csv", versions_from_rows)
- def csv_record_to_objects(info):
+ def version_names_from_rows(info: list[str]) -> Iterator[VersionName]:
yield VersionName(version_id=int(info[0]), language_id=int(info[1]), name=info[2])
- build_generic((VersionName,), "version_names.csv", csv_record_to_objects)
-
-
-##################
-# DAMAGE CLASS #
-##################
+ build_generic((VersionName,), "version_names.csv", version_names_from_rows)
-def _build_damage_classes():
- def csv_record_to_objects(info):
+def _build_damage_classes() -> None:
+ def move_damage_classes_from_rows(info: list[str]) -> Iterator[MoveDamageClass]:
yield MoveDamageClass(id=int(info[0]), name=info[1])
- build_generic((MoveDamageClass,), "move_damage_classes.csv", csv_record_to_objects)
+ build_generic((MoveDamageClass,), "move_damage_classes.csv", move_damage_classes_from_rows)
- def csv_record_to_objects(info):
+ def move_damage_class_prose_from_rows(
+ info: list[str],
+ ) -> Iterator[MoveDamageClassName | MoveDamageClassDescription]:
yield MoveDamageClassName(move_damage_class_id=int(info[0]), language_id=int(info[1]), name=info[2])
yield MoveDamageClassDescription(
move_damage_class_id=int(info[0]),
@@ -728,17 +283,12 @@ def csv_record_to_objects(info):
build_generic(
(MoveDamageClassName, MoveDamageClassDescription),
"move_damage_class_prose.csv",
- csv_record_to_objects,
+ move_damage_class_prose_from_rows,
)
-###########
-# STATS #
-###########
-
-
-def _build_stats():
- def csv_record_to_objects(info):
+def _build_stats() -> None:
+ def stats_from_rows(info: list[str]) -> Iterator[Stat]:
yield Stat(
id=int(info[0]),
move_damage_class_id=int(info[1]) if info[1] != "" else None,
@@ -747,31 +297,26 @@ def csv_record_to_objects(info):
game_index=int(info[4]) if info[4] else 0,
)
- build_generic((Stat,), "stats.csv", csv_record_to_objects)
+ build_generic((Stat,), "stats.csv", stats_from_rows)
- def csv_record_to_objects(info):
+ def stat_names_from_rows(info: list[str]) -> Iterator[StatName]:
yield StatName(stat_id=int(info[0]), language_id=int(info[1]), name=info[2])
- build_generic((StatName,), "stat_names.csv", csv_record_to_objects)
+ build_generic((StatName,), "stat_names.csv", stat_names_from_rows)
- def csv_record_to_objects(info):
+ def pokeathlon_stats_from_rows(info: list[str]) -> Iterator[PokeathlonStat]:
yield PokeathlonStat(id=int(info[0]), name=info[1])
- build_generic((PokeathlonStat,), "pokeathlon_stats.csv", csv_record_to_objects)
+ build_generic((PokeathlonStat,), "pokeathlon_stats.csv", pokeathlon_stats_from_rows)
- def csv_record_to_objects(info):
+ def pokeathlon_stat_names_from_rows(info: list[str]) -> Iterator[PokeathlonStatName]:
yield PokeathlonStatName(pokeathlon_stat_id=int(info[0]), language_id=int(info[1]), name=info[2])
- build_generic((PokeathlonStatName,), "pokeathlon_stat_names.csv", csv_record_to_objects)
+ build_generic((PokeathlonStatName,), "pokeathlon_stat_names.csv", pokeathlon_stat_names_from_rows)
-# ###############
-# # ABILITIES #
-# ###############
-
-
-def _build_abilities():
- def csv_record_to_objects(info):
+def _build_abilities() -> None:
+ def abilities_from_rows(info: list[str]) -> Iterator[Ability]:
yield Ability(
id=int(info[0]),
name=info[1],
@@ -779,19 +324,19 @@ def csv_record_to_objects(info):
is_main_series=bool(int(info[3])),
)
- build_generic((Ability,), "abilities.csv", csv_record_to_objects)
+ build_generic((Ability,), "abilities.csv", abilities_from_rows)
- def csv_record_to_objects(info):
+ def ability_names_from_rows(info: list[str]) -> Iterator[AbilityName]:
yield AbilityName(ability_id=int(info[0]), language_id=int(info[1]), name=info[2])
- build_generic((AbilityName,), "ability_names.csv", csv_record_to_objects)
+ build_generic((AbilityName,), "ability_names.csv", ability_names_from_rows)
- def csv_record_to_objects(info):
+ def ability_changes_from_rows(info: list[str]) -> Iterator[AbilityChange]:
yield AbilityChange(id=int(info[0]), ability_id=int(info[1]), version_group_id=int(info[2]))
- build_generic((AbilityChange,), "ability_changelog.csv", csv_record_to_objects)
+ build_generic((AbilityChange,), "ability_changelog.csv", ability_changes_from_rows)
- def csv_record_to_objects(info):
+ def ability_effect_texts_from_rows(info: list[str]) -> Iterator[AbilityEffectText]:
yield AbilityEffectText(
ability_id=int(info[0]),
language_id=int(info[1]),
@@ -799,18 +344,18 @@ def csv_record_to_objects(info):
effect=info[3],
)
- build_generic((AbilityEffectText,), "ability_prose.csv", csv_record_to_objects)
+ build_generic((AbilityEffectText,), "ability_prose.csv", ability_effect_texts_from_rows)
- def csv_record_to_objects(info):
+ def ability_change_effect_texts_from_rows(info: list[str]) -> Iterator[AbilityChangeEffectText]:
yield AbilityChangeEffectText(
ability_change_id=int(info[0]),
language_id=int(info[1]),
effect=info[2],
)
- build_generic((AbilityChangeEffectText,), "ability_changelog_prose.csv", csv_record_to_objects)
+ build_generic((AbilityChangeEffectText,), "ability_changelog_prose.csv", ability_change_effect_texts_from_rows)
- def csv_record_to_objects(info):
+ def ability_flavor_texts_from_rows(info: list[str]) -> Iterator[AbilityFlavorText]:
yield AbilityFlavorText(
ability_id=int(info[0]),
version_group_id=int(info[1]),
@@ -818,96 +363,76 @@ def csv_record_to_objects(info):
flavor_text=info[3],
)
- build_generic((AbilityFlavorText,), "ability_flavor_text.csv", csv_record_to_objects)
-
+ build_generic((AbilityFlavorText,), "ability_flavor_text.csv", ability_flavor_texts_from_rows)
-####################
-# CHARACTERISTIC #
-####################
-
-def _build_characteristics():
- def csv_record_to_objects(info):
+def _build_characteristics() -> None:
+ def characteristics_from_rows(info: list[str]) -> Iterator[Characteristic]:
yield Characteristic(id=int(info[0]), stat_id=int(info[1]), gene_mod_5=int(info[2]))
- build_generic((Characteristic,), "characteristics.csv", csv_record_to_objects)
+ build_generic((Characteristic,), "characteristics.csv", characteristics_from_rows)
- def csv_record_to_objects(info):
+ def characteristic_descriptions_from_rows(info: list[str]) -> Iterator[CharacteristicDescription]:
yield CharacteristicDescription(
characteristic_id=int(info[0]),
language_id=int(info[1]),
description=info[2],
)
- build_generic((CharacteristicDescription,), "characteristic_text.csv", csv_record_to_objects)
-
+ build_generic((CharacteristicDescription,), "characteristic_text.csv", characteristic_descriptions_from_rows)
-###############
-# EGG GROUP #
-###############
-
-def _build_egg_groups():
- def csv_record_to_objects(info):
+def _build_egg_groups() -> None:
+ def egg_groups_from_rows(info: list[str]) -> Iterator[EggGroup]:
yield EggGroup(id=int(info[0]), name=info[1])
- build_generic((EggGroup,), "egg_groups.csv", csv_record_to_objects)
+ build_generic((EggGroup,), "egg_groups.csv", egg_groups_from_rows)
- def csv_record_to_objects(info):
+ def egg_group_names_from_rows(info: list[str]) -> Iterator[EggGroupName]:
yield EggGroupName(egg_group_id=int(info[0]), language_id=int(info[1]), name=info[2])
- build_generic((EggGroupName,), "egg_group_prose.csv", csv_record_to_objects)
-
-
-#################
-# GROWTH RATE #
-#################
+ build_generic((EggGroupName,), "egg_group_prose.csv", egg_group_names_from_rows)
-def _build_growth_rates():
- def csv_record_to_objects(info):
+def _build_growth_rates() -> None:
+ def growth_rates_from_rows(info: list[str]) -> Iterator[GrowthRate]:
yield GrowthRate(id=int(info[0]), name=info[1], formula=info[2])
- build_generic((GrowthRate,), "growth_rates.csv", csv_record_to_objects)
+ build_generic((GrowthRate,), "growth_rates.csv", growth_rates_from_rows)
- def csv_record_to_objects(info):
+ def growth_rate_descriptions_from_rows(info: list[str]) -> Iterator[GrowthRateDescription]:
yield GrowthRateDescription(growth_rate_id=int(info[0]), language_id=int(info[1]), description=info[2])
- build_generic((GrowthRateDescription,), "growth_rate_prose.csv", csv_record_to_objects)
-
-
-# ###########
-# # ITEMS #
-# ###########
+ build_generic((GrowthRateDescription,), "growth_rate_prose.csv", growth_rate_descriptions_from_rows)
-def _build_items():
- def csv_record_to_objects(info):
+def _build_items() -> None:
+ def currencies_from_rows(info: list[str]) -> Iterator[Currency]:
yield Currency(id=int(info[0]), name=info[1])
- build_generic((Currency,), "currencies.csv", csv_record_to_objects)
+ build_generic((Currency,), "currencies.csv", currencies_from_rows)
- def csv_record_to_objects(info):
+ def currency_names_from_rows(info: list[str]) -> Iterator[CurrencyName]:
yield CurrencyName(currency_id=int(info[0]), language_id=int(info[1]), name=info[2])
- build_generic((CurrencyName,), "currency_names.csv", csv_record_to_objects)
+ build_generic((CurrencyName,), "currency_names.csv", currency_names_from_rows)
- def csv_record_to_objects(info):
+ def item_pockets_from_rows(info: list[str]) -> Iterator[ItemPocket]:
yield ItemPocket(id=int(info[0]), name=info[1])
- build_generic((ItemPocket,), "item_pockets.csv", csv_record_to_objects)
+ build_generic((ItemPocket,), "item_pockets.csv", item_pockets_from_rows)
- def csv_record_to_objects(info):
+ def item_pocket_names_from_rows(info: list[str]) -> Iterator[ItemPocketName]:
yield ItemPocketName(item_pocket_id=int(info[0]), language_id=int(info[1]), name=info[2])
- build_generic((ItemPocketName,), "item_pocket_names.csv", csv_record_to_objects)
+ build_generic((ItemPocketName,), "item_pocket_names.csv", item_pocket_names_from_rows)
- def csv_record_to_objects(info):
+ def item_fling_effects_from_rows(info: list[str]) -> Iterator[ItemFlingEffect]:
yield ItemFlingEffect(id=int(info[0]), name=info[1])
- build_generic((ItemFlingEffect,), "item_fling_effects.csv", csv_record_to_objects)
+ build_generic((ItemFlingEffect,), "item_fling_effects.csv", item_fling_effects_from_rows)
- def csv_record_to_objects(info):
+ def item_fling_effect_effect_texts_from_rows(info: list[str]) -> Iterator[ItemFlingEffectEffectText]:
yield ItemFlingEffectEffectText(
item_fling_effect_id=int(info[0]),
language_id=int(info[1]),
@@ -917,20 +442,20 @@ def csv_record_to_objects(info):
build_generic(
(ItemFlingEffectEffectText,),
"item_fling_effect_prose.csv",
- csv_record_to_objects,
+ item_fling_effect_effect_texts_from_rows,
)
- def csv_record_to_objects(info):
+ def item_categories_from_rows(info: list[str]) -> Iterator[ItemCategory]:
yield ItemCategory(id=int(info[0]), item_pocket_id=int(info[1]), name=info[2])
- build_generic((ItemCategory,), "item_categories.csv", csv_record_to_objects)
+ build_generic((ItemCategory,), "item_categories.csv", item_categories_from_rows)
- def csv_record_to_objects(info):
+ def item_category_names_from_rows(info: list[str]) -> Iterator[ItemCategoryName]:
yield ItemCategoryName(item_category_id=int(info[0]), language_id=int(info[1]), name=info[2])
- build_generic((ItemCategoryName,), "item_category_prose.csv", csv_record_to_objects)
+ build_generic((ItemCategoryName,), "item_category_prose.csv", item_category_names_from_rows)
- def csv_record_to_objects(info):
+ def items_from_rows(info: list[str]) -> Iterator[Item]:
yield Item(
id=int(info[0]),
name=info[1],
@@ -939,9 +464,9 @@ def csv_record_to_objects(info):
item_fling_effect_id=int(info[5]) if info[5] != "" else None,
)
- build_generic((Item,), "items.csv", csv_record_to_objects)
+ build_generic((Item,), "items.csv", items_from_rows)
- def csv_record_to_objects(info):
+ def item_sprites_from_rows(info: list[str]) -> Iterator[ItemSprites]:
if re.search(r"^data-card", info[1]):
file_name = "data-card.png"
elif re.search(r"^tm[0-9]", info[1]):
@@ -955,14 +480,14 @@ def csv_record_to_objects(info):
sprites = {"default": file_path_or_none(item_sprites.format(file_name))}
yield ItemSprites(id=int(info[0]), item_id=int(info[0]), sprites=sprites)
- build_generic((ItemSprites,), "items.csv", csv_record_to_objects)
+ build_generic((ItemSprites,), "items.csv", item_sprites_from_rows)
- def csv_record_to_objects(info):
+ def item_names_from_rows(info: list[str]) -> Iterator[ItemName]:
yield ItemName(item_id=int(info[0]), language_id=int(info[1]), name=info[2])
- build_generic((ItemName,), "item_names.csv", csv_record_to_objects)
+ build_generic((ItemName,), "item_names.csv", item_names_from_rows)
- def csv_record_to_objects(info):
+ def item_effect_texts_from_rows(info: list[str]) -> Iterator[ItemEffectText]:
yield ItemEffectText(
item_id=int(info[0]),
language_id=int(info[1]),
@@ -970,14 +495,14 @@ def csv_record_to_objects(info):
effect=info[3],
)
- build_generic((ItemEffectText,), "item_prose.csv", csv_record_to_objects)
+ build_generic((ItemEffectText,), "item_prose.csv", item_effect_texts_from_rows)
- def csv_record_to_objects(info):
+ def item_game_indexes_from_rows(info: list[str]) -> Iterator[ItemGameIndex]:
yield ItemGameIndex(item_id=int(info[0]), generation_id=int(info[1]), game_index=int(info[2]))
- build_generic((ItemGameIndex,), "item_game_indices.csv", csv_record_to_objects)
+ build_generic((ItemGameIndex,), "item_game_indices.csv", item_game_indexes_from_rows)
- def csv_record_to_objects(info):
+ def item_prices_from_rows(info: list[str]) -> Iterator[ItemPrice]:
# Keep backward compatibility with 4-column files (no currency_id).
has_currency_id = len(info) >= 5
purchase_price_index = 3 if has_currency_id else 2
@@ -991,9 +516,9 @@ def csv_record_to_objects(info):
sell_price=int(info[sell_price_index]) if info[sell_price_index] else None,
)
- build_generic((ItemPrice,), "item_prices.csv", csv_record_to_objects)
+ build_generic((ItemPrice,), "item_prices.csv", item_prices_from_rows)
- def csv_record_to_objects(info):
+ def item_flavor_texts_from_rows(info: list[str]) -> Iterator[ItemFlavorText]:
yield ItemFlavorText(
item_id=int(info[0]),
version_group_id=int(info[1]),
@@ -1001,14 +526,14 @@ def csv_record_to_objects(info):
flavor_text=info[3],
)
- build_generic((ItemFlavorText,), "item_flavor_text.csv", csv_record_to_objects)
+ build_generic((ItemFlavorText,), "item_flavor_text.csv", item_flavor_texts_from_rows)
- def csv_record_to_objects(info):
+ def item_attributes_from_rows(info: list[str]) -> Iterator[ItemAttribute]:
yield ItemAttribute(id=int(info[0]), name=info[1])
- build_generic((ItemAttribute,), "item_flags.csv", csv_record_to_objects)
+ build_generic((ItemAttribute,), "item_flags.csv", item_attributes_from_rows)
- def csv_record_to_objects(info):
+ def item_flag_prose_from_rows(info: list[str]) -> Iterator[ItemAttributeName | ItemAttributeDescription]:
yield ItemAttributeName(item_attribute_id=int(info[0]), language_id=int(info[1]), name=info[2])
yield ItemAttributeDescription(
item_attribute_id=int(info[0]),
@@ -1019,22 +544,17 @@ def csv_record_to_objects(info):
build_generic(
(ItemAttributeName, ItemAttributeDescription),
"item_flag_prose.csv",
- csv_record_to_objects,
+ item_flag_prose_from_rows,
)
- def csv_record_to_objects(info):
+ def item_attribute_maps_from_rows(info: list[str]) -> Iterator[ItemAttributeMap]:
yield ItemAttributeMap(item_id=int(info[0]), item_attribute_id=int(info[1]))
- build_generic((ItemAttributeMap,), "item_flag_map.csv", csv_record_to_objects)
+ build_generic((ItemAttributeMap,), "item_flag_map.csv", item_attribute_maps_from_rows)
-###########
-# TYPES #
-###########
-
-
-def _build_types():
- def csv_record_to_objects(info):
+def _build_types() -> None:
+ def types_from_rows(info: list[str]) -> Iterator[Type]:
yield Type(
id=int(info[0]),
name=info[1],
@@ -1042,28 +562,28 @@ def csv_record_to_objects(info):
move_damage_class_id=int(info[3]) if info[3] != "" else None,
)
- build_generic((Type,), "types.csv", csv_record_to_objects)
+ build_generic((Type,), "types.csv", types_from_rows)
- def csv_record_to_objects(info):
+ def type_names_from_rows(info: list[str]) -> Iterator[TypeName]:
yield TypeName(type_id=int(info[0]), language_id=int(info[1]), name=info[2])
- build_generic((TypeName,), "type_names.csv", csv_record_to_objects)
+ build_generic((TypeName,), "type_names.csv", type_names_from_rows)
- def csv_record_to_objects(info):
+ def type_game_indexes_from_rows(info: list[str]) -> Iterator[TypeGameIndex]:
yield TypeGameIndex(type_id=int(info[0]), generation_id=int(info[1]), game_index=int(info[2]))
- build_generic((TypeGameIndex,), "type_game_indices.csv", csv_record_to_objects)
+ build_generic((TypeGameIndex,), "type_game_indices.csv", type_game_indexes_from_rows)
- def csv_record_to_objects(info):
+ def type_efficacies_from_rows(info: list[str]) -> Iterator[TypeEfficacy]:
yield TypeEfficacy(
damage_type_id=int(info[0]),
target_type_id=int(info[1]),
damage_factor=int(info[2]),
)
- build_generic((TypeEfficacy,), "type_efficacy.csv", csv_record_to_objects)
+ build_generic((TypeEfficacy,), "type_efficacy.csv", type_efficacies_from_rows)
- def csv_record_to_objects(info):
+ def type_efficacies_past_from_rows(info: list[str]) -> Iterator[TypeEfficacyPast]:
yield TypeEfficacyPast(
damage_type_id=int(info[0]),
target_type_id=int(info[1]),
@@ -1071,34 +591,11 @@ def csv_record_to_objects(info):
generation_id=int(info[3]),
)
- build_generic((TypeEfficacyPast,), "type_efficacy_past.csv", csv_record_to_objects)
-
- def csv_record_to_objects(info):
- game_map = {
- "generation-iii": [
- "colosseum",
- "emerald",
- "firered-leafgreen",
- "ruby-sapphire",
- "xd",
- ],
- "generation-iv": ["diamond-pearl", "heartgold-soulsilver", "platinum"],
- "generation-v": ["black-2-white-2", "black-white"],
- "generation-vi": ["omega-ruby-alpha-sapphire", "x-y"],
- "generation-vii": [
- "lets-go-pikachu-lets-go-eevee",
- "sun-moon",
- "ultra-sun-ultra-moon",
- ],
- "generation-viii": [
- "brilliant-diamond-shining-pearl",
- "legends-arceus",
- "sword-shield",
- ],
- "generation-ix": ["scarlet-violet"],
- }
+ build_generic((TypeEfficacyPast,), "type_efficacy_past.csv", type_efficacies_past_from_rows)
+
+ def type_sprites_from_rows(info: list[str]) -> Iterator[TypeSprites]:
sprites = {}
- for generation, games in game_map.items():
+ for generation, games in TYPE_SPRITE_GAME_MAP.items():
for game in games:
if generation not in sprites:
sprites[generation] = {}
@@ -1109,21 +606,16 @@ def csv_record_to_objects(info):
yield TypeSprites(type_id=int(info[0]), sprites=sprites)
- build_generic((TypeSprites,), "types.csv", csv_record_to_objects)
-
+ build_generic((TypeSprites,), "types.csv", type_sprites_from_rows)
-#############
-# CONTEST #
-#############
-
-def _build_contests():
- def csv_record_to_objects(info):
+def _build_contests() -> None:
+ def contest_types_from_rows(info: list[str]) -> Iterator[ContestType]:
yield ContestType(id=int(info[0]), name=info[1])
- build_generic((ContestType,), "contest_types.csv", csv_record_to_objects)
+ build_generic((ContestType,), "contest_types.csv", contest_types_from_rows)
- def csv_record_to_objects(info):
+ def contest_type_names_from_rows(info: list[str]) -> Iterator[ContestTypeName]:
yield ContestTypeName(
contest_type_id=int(info[0]),
language_id=int(info[1]),
@@ -1132,14 +624,14 @@ def csv_record_to_objects(info):
color=info[4],
)
- build_generic((ContestTypeName,), "contest_type_names.csv", csv_record_to_objects)
+ build_generic((ContestTypeName,), "contest_type_names.csv", contest_type_names_from_rows)
- def csv_record_to_objects(info):
+ def contest_effects_from_rows(info: list[str]) -> Iterator[ContestEffect]:
yield ContestEffect(id=int(info[0]), appeal=int(info[1]), jam=int(info[2]))
- build_generic((ContestEffect,), "contest_effects.csv", csv_record_to_objects)
+ build_generic((ContestEffect,), "contest_effects.csv", contest_effects_from_rows)
- def csv_record_to_objects(info):
+ def contest_effect_prose_from_rows(info: list[str]) -> Iterator[ContestEffectEffectText | ContestEffectFlavorText]:
yield ContestEffectEffectText(contest_effect_id=int(info[0]), language_id=int(info[1]), effect=info[3])
yield ContestEffectFlavorText(
contest_effect_id=int(info[0]),
@@ -1150,15 +642,15 @@ def csv_record_to_objects(info):
build_generic(
(ContestEffectEffectText, ContestEffectFlavorText),
"contest_effect_prose.csv",
- csv_record_to_objects,
+ contest_effect_prose_from_rows,
)
- def csv_record_to_objects(info):
+ def super_contest_effects_from_rows(info: list[str]) -> Iterator[SuperContestEffect]:
yield SuperContestEffect(id=int(info[0]), appeal=int(info[1]))
- build_generic((SuperContestEffect,), "super_contest_effects.csv", csv_record_to_objects)
+ build_generic((SuperContestEffect,), "super_contest_effects.csv", super_contest_effects_from_rows)
- def csv_record_to_objects(info):
+ def super_contest_effect_flavor_texts_from_rows(info: list[str]) -> Iterator[SuperContestEffectFlavorText]:
yield SuperContestEffectFlavorText(
super_contest_effect_id=int(info[0]),
language_id=int(info[1]),
@@ -1168,22 +660,17 @@ def csv_record_to_objects(info):
build_generic(
(SuperContestEffectFlavorText,),
"super_contest_effect_prose.csv",
- csv_record_to_objects,
+ super_contest_effect_flavor_texts_from_rows,
)
-###########
-# MOVES #
-###########
-
-
-def _build_moves():
- def csv_record_to_objects(info):
+def _build_moves() -> None:
+ def move_effects_from_rows(info: list[str]) -> Iterator[MoveEffect]:
yield MoveEffect(id=int(info[0]))
- build_generic((MoveEffect,), "move_effects.csv", csv_record_to_objects)
+ build_generic((MoveEffect,), "move_effects.csv", move_effects_from_rows)
- def csv_record_to_objects(info):
+ def move_effect_effect_texts_from_rows(info: list[str]) -> Iterator[MoveEffectEffectText]:
yield MoveEffectEffectText(
move_effect_id=int(info[0]),
language_id=int(info[1]),
@@ -1191,14 +678,14 @@ def csv_record_to_objects(info):
effect=info[3],
)
- build_generic((MoveEffectEffectText,), "move_effect_prose.csv", csv_record_to_objects)
+ build_generic((MoveEffectEffectText,), "move_effect_prose.csv", move_effect_effect_texts_from_rows)
- def csv_record_to_objects(info):
+ def move_effect_changes_from_rows(info: list[str]) -> Iterator[MoveEffectChange]:
yield MoveEffectChange(id=int(info[0]), move_effect_id=int(info[1]), version_group_id=int(info[2]))
- build_generic((MoveEffectChange,), "move_effect_changelog.csv", csv_record_to_objects)
+ build_generic((MoveEffectChange,), "move_effect_changelog.csv", move_effect_changes_from_rows)
- def csv_record_to_objects(info):
+ def move_effect_change_effect_texts_from_rows(info: list[str]) -> Iterator[MoveEffectChangeEffectText]:
yield MoveEffectChangeEffectText(
move_effect_change_id=int(info[0]),
language_id=int(info[1]),
@@ -1208,24 +695,26 @@ def csv_record_to_objects(info):
build_generic(
(MoveEffectChangeEffectText,),
"move_effect_changelog_prose.csv",
- csv_record_to_objects,
+ move_effect_change_effect_texts_from_rows,
)
- def csv_record_to_objects(info):
+ def move_learn_methods_from_rows(info: list[str]) -> Iterator[MoveLearnMethod]:
yield MoveLearnMethod(id=int(info[0]), name=info[1])
- build_generic((MoveLearnMethod,), "pokemon_move_methods.csv", csv_record_to_objects)
+ build_generic((MoveLearnMethod,), "pokemon_move_methods.csv", move_learn_methods_from_rows)
- def csv_record_to_objects(info):
+ def version_group_move_learn_methods_from_rows(info: list[str]) -> Iterator[VersionGroupMoveLearnMethod]:
yield VersionGroupMoveLearnMethod(version_group_id=int(info[0]), move_learn_method_id=int(info[1]))
build_generic(
(VersionGroupMoveLearnMethod,),
"version_group_pokemon_move_methods.csv",
- csv_record_to_objects,
+ version_group_move_learn_methods_from_rows,
)
- def csv_record_to_objects(info):
+ def pokemon_move_method_prose_from_rows(
+ info: list[str],
+ ) -> Iterator[MoveLearnMethodName | MoveLearnMethodDescription]:
yield MoveLearnMethodName(move_learn_method_id=int(info[0]), language_id=int(info[1]), name=info[2])
yield MoveLearnMethodDescription(
move_learn_method_id=int(info[0]),
@@ -1236,25 +725,25 @@ def csv_record_to_objects(info):
build_generic(
(MoveLearnMethodName, MoveLearnMethodDescription),
"pokemon_move_method_prose.csv",
- csv_record_to_objects,
+ pokemon_move_method_prose_from_rows,
)
- def csv_record_to_objects(info):
+ def move_targets_from_rows(info: list[str]) -> Iterator[MoveTarget]:
yield MoveTarget(id=int(info[0]), name=info[1])
- build_generic((MoveTarget,), "move_targets.csv", csv_record_to_objects)
+ build_generic((MoveTarget,), "move_targets.csv", move_targets_from_rows)
- def csv_record_to_objects(info):
+ def move_target_prose_from_rows(info: list[str]) -> Iterator[MoveTargetName | MoveTargetDescription]:
yield MoveTargetName(move_target_id=int(info[0]), language_id=int(info[1]), name=info[2])
yield MoveTargetDescription(move_target_id=int(info[0]), language_id=int(info[1]), description=info[3])
build_generic(
(MoveTargetName, MoveTargetDescription),
"move_target_prose.csv",
- csv_record_to_objects,
+ move_target_prose_from_rows,
)
- def csv_record_to_objects(info):
+ def moves_from_rows(info: list[str]) -> Iterator[Move]:
yield Move(
id=int(info[0]),
name=info[1],
@@ -1273,14 +762,14 @@ def csv_record_to_objects(info):
super_contest_effect_id=int(info[14]) if info[14] != "" else None,
)
- build_generic((Move,), "moves.csv", csv_record_to_objects)
+ build_generic((Move,), "moves.csv", moves_from_rows)
- def csv_record_to_objects(info):
+ def move_names_from_rows(info: list[str]) -> Iterator[MoveName]:
yield MoveName(move_id=int(info[0]), language_id=int(info[1]), name=info[2])
- build_generic((MoveName,), "move_names.csv", csv_record_to_objects)
+ build_generic((MoveName,), "move_names.csv", move_names_from_rows)
- def csv_record_to_objects(info):
+ def move_flavor_texts_from_rows(info: list[str]) -> Iterator[MoveFlavorText]:
yield MoveFlavorText(
move_id=int(info[0]),
version_group_id=int(info[1]),
@@ -1288,11 +777,11 @@ def csv_record_to_objects(info):
flavor_text=info[3],
)
- build_generic((MoveFlavorText,), "move_flavor_text.csv", csv_record_to_objects)
+ build_generic((MoveFlavorText,), "move_flavor_text.csv", move_flavor_texts_from_rows)
existing_effect_ids = set(MoveEffect.objects.values_list("pk", flat=True))
- def csv_record_to_objects(info):
+ def move_changes_from_rows(info: list[str]) -> Iterator[MoveChange]:
effect_id = int(info[8]) if info[8] != "" else None
if effect_id not in existing_effect_ids:
effect_id = None
@@ -1308,29 +797,29 @@ def csv_record_to_objects(info):
move_effect_chance=int(info[9]) if info[9] != "" else None,
)
- build_generic((MoveChange,), "move_changelog.csv", csv_record_to_objects)
+ build_generic((MoveChange,), "move_changelog.csv", move_changes_from_rows)
- def csv_record_to_objects(info):
+ def move_battle_styles_from_rows(info: list[str]) -> Iterator[MoveBattleStyle]:
yield MoveBattleStyle(id=int(info[0]), name=info[1])
- build_generic((MoveBattleStyle,), "move_battle_styles.csv", csv_record_to_objects)
+ build_generic((MoveBattleStyle,), "move_battle_styles.csv", move_battle_styles_from_rows)
- def csv_record_to_objects(info):
+ def move_battle_style_names_from_rows(info: list[str]) -> Iterator[MoveBattleStyleName]:
yield MoveBattleStyleName(move_battle_style_id=int(info[0]), language_id=int(info[1]), name=info[2])
- build_generic((MoveBattleStyleName,), "move_battle_style_prose.csv", csv_record_to_objects)
+ build_generic((MoveBattleStyleName,), "move_battle_style_prose.csv", move_battle_style_names_from_rows)
- def csv_record_to_objects(info):
+ def move_attributes_from_rows(info: list[str]) -> Iterator[MoveAttribute]:
yield MoveAttribute(id=int(info[0]), name=info[1])
- build_generic((MoveAttribute,), "move_flags.csv", csv_record_to_objects)
+ build_generic((MoveAttribute,), "move_flags.csv", move_attributes_from_rows)
- def csv_record_to_objects(info):
+ def move_attribute_maps_from_rows(info: list[str]) -> Iterator[MoveAttributeMap]:
yield MoveAttributeMap(move_id=int(info[0]), move_attribute_id=int(info[1]))
- build_generic((MoveAttributeMap,), "move_flag_map.csv", csv_record_to_objects)
+ build_generic((MoveAttributeMap,), "move_flag_map.csv", move_attribute_maps_from_rows)
- def csv_record_to_objects(info):
+ def move_flag_prose_from_rows(info: list[str]) -> Iterator[MoveAttributeName | MoveAttributeDescription]:
yield MoveAttributeName(move_attribute_id=int(info[0]), language_id=int(info[1]), name=info[2])
yield MoveAttributeDescription(
move_attribute_id=int(info[0]),
@@ -1341,25 +830,25 @@ def csv_record_to_objects(info):
build_generic(
(MoveAttributeName, MoveAttributeDescription),
"move_flag_prose.csv",
- csv_record_to_objects,
+ move_flag_prose_from_rows,
)
- def csv_record_to_objects(info):
+ def move_meta_ailments_from_rows(info: list[str]) -> Iterator[MoveMetaAilment]:
yield MoveMetaAilment(id=int(info[0]), name=info[1])
- build_generic((MoveMetaAilment,), "move_meta_ailments.csv", csv_record_to_objects)
+ build_generic((MoveMetaAilment,), "move_meta_ailments.csv", move_meta_ailments_from_rows)
- def csv_record_to_objects(info):
+ def move_meta_ailment_names_from_rows(info: list[str]) -> Iterator[MoveMetaAilmentName]:
yield MoveMetaAilmentName(move_meta_ailment_id=int(info[0]), language_id=int(info[1]), name=info[2])
- build_generic((MoveMetaAilmentName,), "move_meta_ailment_names.csv", csv_record_to_objects)
+ build_generic((MoveMetaAilmentName,), "move_meta_ailment_names.csv", move_meta_ailment_names_from_rows)
- def csv_record_to_objects(info):
+ def move_meta_categories_from_rows(info: list[str]) -> Iterator[MoveMetaCategory]:
yield MoveMetaCategory(id=int(info[0]), name=info[1])
- build_generic((MoveMetaCategory,), "move_meta_categories.csv", csv_record_to_objects)
+ build_generic((MoveMetaCategory,), "move_meta_categories.csv", move_meta_categories_from_rows)
- def csv_record_to_objects(info):
+ def move_meta_category_descriptions_from_rows(info: list[str]) -> Iterator[MoveMetaCategoryDescription]:
yield MoveMetaCategoryDescription(
move_meta_category_id=int(info[0]),
language_id=int(info[1]),
@@ -1369,10 +858,10 @@ def csv_record_to_objects(info):
build_generic(
(MoveMetaCategoryDescription,),
"move_meta_category_prose.csv",
- csv_record_to_objects,
+ move_meta_category_descriptions_from_rows,
)
- def csv_record_to_objects(info):
+ def move_metadata_from_rows(info: list[str]) -> Iterator[MoveMeta]:
yield MoveMeta(
move_id=int(info[0]),
move_meta_category_id=int(info[1]),
@@ -1389,46 +878,44 @@ def csv_record_to_objects(info):
stat_chance=int(info[12]) if info[12] != "" else None,
)
- build_generic((MoveMeta,), "move_meta.csv", csv_record_to_objects)
+ build_generic((MoveMeta,), "move_meta.csv", move_metadata_from_rows)
- def csv_record_to_objects(info):
+ def move_meta_stat_changes_from_rows(info: list[str]) -> Iterator[MoveMetaStatChange]:
yield MoveMetaStatChange(move_id=int(info[0]), stat_id=int(info[1]), change=int(info[2]))
- build_generic((MoveMetaStatChange,), "move_meta_stat_changes.csv", csv_record_to_objects)
+ build_generic((MoveMetaStatChange,), "move_meta_stat_changes.csv", move_meta_stat_changes_from_rows)
- def csv_record_to_objects(info):
+ def contest_combos_from_rows(info: list[str]) -> Iterator[ContestCombo]:
yield ContestCombo(first_move_id=int(info[0]), second_move_id=int(info[1]))
- build_generic((ContestCombo,), "contest_combos.csv", csv_record_to_objects)
+ build_generic((ContestCombo,), "contest_combos.csv", contest_combos_from_rows)
- def csv_record_to_objects(info):
+ def super_contest_combos_from_rows(info: list[str]) -> Iterator[SuperContestCombo]:
yield SuperContestCombo(first_move_id=int(info[0]), second_move_id=int(info[1]))
- build_generic((SuperContestCombo,), "super_contest_combos.csv", csv_record_to_objects)
-
+ build_generic((SuperContestCombo,), "super_contest_combos.csv", super_contest_combos_from_rows)
-#############
-# BERRIES #
-#############
-
-def _build_berries():
- def csv_record_to_objects(info):
+def _build_berries() -> None:
+ def berry_firmnesses_from_rows(info: list[str]) -> Iterator[BerryFirmness]:
yield BerryFirmness(id=int(info[0]), name=info[1])
- build_generic((BerryFirmness,), "berry_firmness.csv", csv_record_to_objects)
+ build_generic((BerryFirmness,), "berry_firmness.csv", berry_firmnesses_from_rows)
- def csv_record_to_objects(info):
+ def berry_firmness_names_from_rows(info: list[str]) -> Iterator[BerryFirmnessName]:
yield BerryFirmnessName(berry_firmness_id=int(info[0]), language_id=int(info[1]), name=info[2])
- build_generic((BerryFirmnessName,), "berry_firmness_names.csv", csv_record_to_objects)
+ build_generic((BerryFirmnessName,), "berry_firmness_names.csv", berry_firmness_names_from_rows)
+
+ item_names = dict(Item.objects.values_list("pk", "name"))
- def csv_record_to_objects(info):
- item = Item.objects.get(pk=int(info[1]))
+ def berries_from_rows(info: list[str]) -> Iterator[Berry]:
+ item_id = int(info[1])
+ item_name = item_names[item_id] if item_id in item_names else Item.objects.get(pk=item_id).name
yield Berry(
id=int(info[0]),
- item_id=int(info[1]),
- name=item.name.split("-berry")[0],
+ item_id=item_id,
+ name=item_name.split("-berry")[0],
berry_firmness_id=int(info[2]) if info[2] else None,
natural_gift_power=int(info[3]) if info[3] else None,
natural_gift_type_id=int(info[4]) if info[4] else None,
@@ -1439,76 +926,92 @@ def csv_record_to_objects(info):
smoothness=int(info[9]) if info[9] else None,
)
- build_generic((Berry,), "berries.csv", csv_record_to_objects)
+ build_generic((Berry,), "berries.csv", berries_from_rows)
+
+ contest_type_ids = set(ContestType.objects.values_list("pk", flat=True))
+ contest_flavors: dict[int, list[str]] = {}
+ english_flavors = ContestTypeName.objects.filter(language_id=9).values_list("contest_type_id", "flavor")
+ for contest_type_id, flavor in english_flavors:
+ contest_flavors.setdefault(contest_type_id, []).append(flavor)
- def csv_record_to_objects(info):
- # Get the english name for this contest type
- contest_type_name = ContestTypeName.objects.get(contest_type_id=int(info[0]), language_id=9)
+ def berry_flavors_from_rows(info: list[str]) -> Iterator[BerryFlavor]:
+ contest_type_id = int(info[0])
+ flavors = contest_flavors.get(contest_type_id, [])
+ if len(flavors) == 1:
+ flavor = flavors[0]
+ else:
+ # Preserve Django's missing and duplicate reference errors.
+ flavor = ContestTypeName.objects.get(contest_type_id=contest_type_id, language_id=9).flavor
+ if contest_type_id not in contest_type_ids:
+ ContestType.objects.get(pk=contest_type_id)
yield BerryFlavor(
- id=int(info[0]),
- name=contest_type_name.flavor.lower(),
- contest_type=ContestType.objects.get(pk=int(info[0])),
+ id=contest_type_id,
+ name=flavor.lower(),
+ contest_type_id=contest_type_id,
)
- # This is not an error
- build_generic((BerryFlavor,), "contest_types.csv", csv_record_to_objects)
+ build_generic((BerryFlavor,), "contest_types.csv", berry_flavors_from_rows)
- def csv_record_to_objects(info):
+ def berry_flavor_names_from_rows(info: list[str]) -> Iterator[BerryFlavorName]:
yield BerryFlavorName(berry_flavor_id=int(info[0]), language_id=int(info[1]), name=info[3])
- # This is not an error
- build_generic((BerryFlavorName,), "contest_type_names.csv", csv_record_to_objects)
+ build_generic((BerryFlavorName,), "contest_type_names.csv", berry_flavor_names_from_rows)
- def csv_record_to_objects(info):
+ def berry_flavor_maps_from_rows(info: list[str]) -> Iterator[BerryFlavorMap]:
yield BerryFlavorMap(berry_id=int(info[0]), berry_flavor_id=int(info[1]), potency=int(info[2]))
- # This is not an error
- build_generic((BerryFlavorMap,), "berry_flavors.csv", csv_record_to_objects)
+ build_generic((BerryFlavorMap,), "berry_flavors.csv", berry_flavor_maps_from_rows)
-############
-# NATURE #
-############
+def _build_natures() -> None:
+ stat_ids = set(Stat.objects.values_list("pk", flat=True))
+ flavor_ids = set(BerryFlavor.objects.values_list("pk", flat=True))
-
-def _build_natures():
- def csv_record_to_objects(info):
- decreased_stat = None
- increased_stat = None
- hates_flavor = None
- likes_flavor = None
+ def natures_from_rows(info: list[str]) -> Iterator[Nature]:
+ decreased_stat_id = None
+ increased_stat_id = None
+ hates_flavor_id = None
+ likes_flavor_id = None
if info[2] != info[3]:
- decreased_stat = Stat.objects.get(pk=int(info[2]))
- increased_stat = Stat.objects.get(pk=int(info[3]))
+ decreased_stat_id = int(info[2])
+ if decreased_stat_id not in stat_ids:
+ Stat.objects.get(pk=decreased_stat_id)
+ increased_stat_id = int(info[3])
+ if increased_stat_id not in stat_ids:
+ Stat.objects.get(pk=increased_stat_id)
if info[4] != info[5]:
- hates_flavor = BerryFlavor.objects.get(pk=int(info[4]))
- likes_flavor = BerryFlavor.objects.get(pk=int(info[5]))
+ hates_flavor_id = int(info[4])
+ if hates_flavor_id not in flavor_ids:
+ BerryFlavor.objects.get(pk=hates_flavor_id)
+ likes_flavor_id = int(info[5])
+ if likes_flavor_id not in flavor_ids:
+ BerryFlavor.objects.get(pk=likes_flavor_id)
yield Nature(
id=int(info[0]),
name=info[1],
- decreased_stat=decreased_stat,
- increased_stat=increased_stat,
- hates_flavor=hates_flavor,
- likes_flavor=likes_flavor,
+ decreased_stat_id=decreased_stat_id,
+ increased_stat_id=increased_stat_id,
+ hates_flavor_id=hates_flavor_id,
+ likes_flavor_id=likes_flavor_id,
game_index=info[6],
)
- build_generic((Nature,), "natures.csv", csv_record_to_objects)
+ build_generic((Nature,), "natures.csv", natures_from_rows)
- def csv_record_to_objects(info):
+ def nature_names_from_rows(info: list[str]) -> Iterator[NatureName]:
yield NatureName(nature_id=int(info[0]), language_id=int(info[1]), name=info[2])
- build_generic((NatureName,), "nature_names.csv", csv_record_to_objects)
+ build_generic((NatureName,), "nature_names.csv", nature_names_from_rows)
- def csv_record_to_objects(info):
+ def nature_pokeathlon_stats_from_rows(info: list[str]) -> Iterator[NaturePokeathlonStat]:
yield NaturePokeathlonStat(nature_id=(info[0]), pokeathlon_stat_id=(info[1]), max_change=info[2])
- build_generic((NaturePokeathlonStat,), "nature_pokeathlon_stats.csv", csv_record_to_objects)
+ build_generic((NaturePokeathlonStat,), "nature_pokeathlon_stats.csv", nature_pokeathlon_stats_from_rows)
- def csv_record_to_objects(info):
+ def nature_battle_style_preferences_from_rows(info: list[str]) -> Iterator[NatureBattleStylePreference]:
yield NatureBattleStylePreference(
nature_id=int(info[0]),
move_battle_style_id=int(info[1]),
@@ -1519,41 +1022,26 @@ def csv_record_to_objects(info):
build_generic(
(NatureBattleStylePreference,),
"nature_battle_style_preferences.csv",
- csv_record_to_objects,
+ nature_battle_style_preferences_from_rows,
)
-###########
-# GENDER #
-###########
-
-
-def _build_genders():
- def csv_record_to_objects(info):
+def _build_genders() -> None:
+ def genders_from_rows(info: list[str]) -> Iterator[Gender]:
yield Gender(id=int(info[0]), name=info[1])
- build_generic((Gender,), "genders.csv", csv_record_to_objects)
-
-
-################
-# EXPERIENCE #
-################
+ build_generic((Gender,), "genders.csv", genders_from_rows)
-def _build_experiences():
- def csv_record_to_objects(info):
+def _build_experiences() -> None:
+ def experiences_from_rows(info: list[str]) -> Iterator[Experience]:
yield Experience(growth_rate_id=int(info[0]), level=int(info[1]), experience=int(info[2]))
- build_generic((Experience,), "experience.csv", csv_record_to_objects)
+ build_generic((Experience,), "experience.csv", experiences_from_rows)
-##############
-# MACHINES #
-##############
-
-
-def _build_machines():
- def csv_record_to_objects(info):
+def _build_machines() -> None:
+ def machines_from_rows(info: list[str]) -> Iterator[Machine]:
yield Machine(
machine_number=int(info[0]),
version_group_id=int(info[1]),
@@ -1561,34 +1049,29 @@ def csv_record_to_objects(info):
move_id=int(info[3]),
)
- build_generic((Machine,), "machines.csv", csv_record_to_objects)
-
+ build_generic((Machine,), "machines.csv", machines_from_rows)
-###############
-# EVOLUTION #
-###############
-
-def _build_evolutions():
- def csv_record_to_objects(info):
+def _build_evolutions() -> None:
+ def evolution_chains_from_rows(info: list[str]) -> Iterator[EvolutionChain]:
yield EvolutionChain(
id=int(info[0]),
baby_trigger_item_id=int(info[1]) if info[1] != "" else None,
)
- build_generic((EvolutionChain,), "evolution_chains.csv", csv_record_to_objects)
+ build_generic((EvolutionChain,), "evolution_chains.csv", evolution_chains_from_rows)
- def csv_record_to_objects(info):
+ def evolution_triggers_from_rows(info: list[str]) -> Iterator[EvolutionTrigger]:
yield EvolutionTrigger(id=int(info[0]), name=info[1])
- build_generic((EvolutionTrigger,), "evolution_triggers.csv", csv_record_to_objects)
+ build_generic((EvolutionTrigger,), "evolution_triggers.csv", evolution_triggers_from_rows)
- def csv_record_to_objects(info):
+ def evolution_trigger_names_from_rows(info: list[str]) -> Iterator[EvolutionTriggerName]:
yield EvolutionTriggerName(evolution_trigger_id=int(info[0]), language_id=int(info[1]), name=info[2])
- build_generic((EvolutionTriggerName,), "evolution_trigger_prose.csv", csv_record_to_objects)
+ build_generic((EvolutionTriggerName,), "evolution_trigger_prose.csv", evolution_trigger_names_from_rows)
- def csv_record_to_objects(info):
+ def evolution_variables_from_rows(info: list[str]) -> Iterator[EvolutionVariable]:
yield EvolutionVariable(
id=int(info[0]),
name=info[1],
@@ -1598,9 +1081,11 @@ def csv_record_to_objects(info):
source=info[5],
)
- build_generic((EvolutionVariable,), "evolution_variables.csv", csv_record_to_objects)
+ build_generic((EvolutionVariable,), "evolution_variables.csv", evolution_variables_from_rows)
- def csv_record_to_objects(info):
+ def evolution_variable_prose_from_rows(
+ info: list[str],
+ ) -> Iterator[EvolutionVariableName | EvolutionVariableDescription]:
yield EvolutionVariableName(
evolution_variable_id=int(info[0]),
language_id=int(info[1]),
@@ -1615,17 +1100,12 @@ def csv_record_to_objects(info):
build_generic(
(EvolutionVariableName, EvolutionVariableDescription),
"evolution_variable_prose.csv",
- csv_record_to_objects,
+ evolution_variable_prose_from_rows,
)
-#############
-# POKEDEX #
-#############
-
-
-def _build_pokedexes():
- def csv_record_to_objects(info):
+def _build_pokedexes() -> None:
+ def pokedexes_from_rows(info: list[str]) -> Iterator[Pokedex]:
yield Pokedex(
id=int(info[0]),
region_id=int(info[1]) if info[1] != "" else None,
@@ -1633,136 +1113,108 @@ def csv_record_to_objects(info):
is_main_series=bool(int(info[3])),
)
- build_generic((Pokedex,), "pokedexes.csv", csv_record_to_objects)
+ build_generic((Pokedex,), "pokedexes.csv", pokedexes_from_rows)
- def csv_record_to_objects(info):
+ def pokedex_prose_from_rows(info: list[str]) -> Iterator[PokedexName | PokedexDescription]:
yield PokedexName(pokedex_id=int(info[0]), language_id=int(info[1]), name=info[2])
yield PokedexDescription(pokedex_id=int(info[0]), language_id=int(info[1]), description=info[3])
- build_generic((PokedexName, PokedexDescription), "pokedex_prose.csv", csv_record_to_objects)
+ build_generic((PokedexName, PokedexDescription), "pokedex_prose.csv", pokedex_prose_from_rows)
- def csv_record_to_objects(info):
+ def pokedex_version_groups_from_rows(info: list[str]) -> Iterator[PokedexVersionGroup]:
yield PokedexVersionGroup(pokedex_id=int(info[0]), version_group_id=int(info[1]))
- build_generic((PokedexVersionGroup,), "pokedex_version_groups.csv", csv_record_to_objects)
-
-
-##############
-# LOCATION #
-##############
+ build_generic((PokedexVersionGroup,), "pokedex_version_groups.csv", pokedex_version_groups_from_rows)
-def _build_locations():
- def csv_record_to_objects(info):
+def _build_locations() -> None:
+ def locations_from_rows(info: list[str]) -> Iterator[Location]:
yield Location(
id=int(info[0]),
region_id=int(info[1]) if info[1] != "" else None,
name=info[2],
)
- build_generic((Location,), "locations.csv", csv_record_to_objects)
+ build_generic((Location,), "locations.csv", locations_from_rows)
- def csv_record_to_objects(info):
+ def location_names_from_rows(info: list[str]) -> Iterator[LocationName]:
yield LocationName(location_id=int(info[0]), language_id=int(info[1]), name=info[2])
- build_generic((LocationName,), "location_names.csv", csv_record_to_objects)
+ build_generic((LocationName,), "location_names.csv", location_names_from_rows)
- def csv_record_to_objects(info):
+ def location_game_indexes_from_rows(info: list[str]) -> Iterator[LocationGameIndex]:
yield LocationGameIndex(
location_id=int(info[0]),
generation_id=int(info[1]),
game_index=int(info[2]),
)
- build_generic((LocationGameIndex,), "location_game_indices.csv", csv_record_to_objects)
+ build_generic((LocationGameIndex,), "location_game_indices.csv", location_game_indexes_from_rows)
- def csv_record_to_objects(info):
- location = Location.objects.get(pk=int(info[1]))
+ location_names = dict(Location.objects.values_list("pk", "name"))
+
+ def location_areas_from_rows(info: list[str]) -> Iterator[LocationArea]:
+ location_id = int(info[1])
+ location_name = (
+ location_names[location_id] if location_id in location_names else Location.objects.get(pk=location_id).name
+ )
yield LocationArea(
id=int(info[0]),
- location_id=int(info[1]),
+ location_id=location_id,
game_index=int(info[2]),
- name=(f"{location.name}-{info[3]}" if info[3] else "{}-{}".format(location.name, "area")),
+ name=(f"{location_name}-{info[3]}" if info[3] else "{}-{}".format(location_name, "area")),
)
- build_generic((LocationArea,), "location_areas.csv", csv_record_to_objects)
+ build_generic((LocationArea,), "location_areas.csv", location_areas_from_rows)
- def csv_record_to_objects(info):
+ def location_area_names_from_rows(info: list[str]) -> Iterator[LocationAreaName]:
yield LocationAreaName(location_area_id=int(info[0]), language_id=int(info[1]), name=info[2])
- build_generic((LocationAreaName,), "location_area_prose.csv", csv_record_to_objects)
+ build_generic((LocationAreaName,), "location_area_prose.csv", location_area_names_from_rows)
-#############
-# POKEMON #
-#############
+def _link_species_evolutions() -> None:
+ species_ids = set(PokemonSpecies.objects.values_list("pk", flat=True))
+ def species_id_from_csv(value: str) -> int:
+ species_id = int(value)
+ if species_id not in species_ids:
+ PokemonSpecies.objects.get(pk=species_id)
+ return species_id
-def _resolve_sprite_config(config: dict[str, Any], lookup: Callable[[str, str], str | None]) -> dict[str, Any]:
- resolved: dict[str, Any] = {}
- for name, value in config.items():
- if isinstance(value, dict):
- resolved[name] = _resolve_sprite_config(value, lookup)
- else:
- path, extension = value
- resolved[name] = lookup(path, extension)
- return resolved
-
-
-def _pokemon_sprite_lookup(info: list[str]) -> Callable[[str, str], str | None]:
- pokemon_id, identifier, species_id = info[:3]
- file_names = [pokemon_id]
- if "-" in identifier:
- file_names.append(f"{species_id}-{identifier.split('-', 1)[1]}")
-
- def lookup(path: str, extension: str) -> str | None:
- for file_name in file_names:
- sprite = file_path_or_none(f"{path}{file_name}.{extension}")
- if sprite:
- return sprite
- return None
-
- return lookup
-
-
-def _pokemon_form_sprite_lookup(info: list[str]) -> Callable[[str, str], str | None]:
- form_identifier = info[2]
- pokemon_id = int(info[3])
- is_default = info[5] == "1"
-
- file_names: list[str] = []
- if form_identifier:
- file_names.append(f"{pokemon_id}-{form_identifier}")
- if is_default or not form_identifier:
- file_names.append(str(pokemon_id))
-
- def lookup(path: str, extension: str) -> str | None:
- for file_name in file_names:
- sprite = file_path_or_none(f"{path}{file_name}.{extension}")
- if sprite:
- return sprite
- return None
-
- return lookup
+ species_links: list[PokemonSpecies] = []
+ with closing(load_data("pokemon_species.csv")) as data:
+ next(data, None)
+ for info in data:
+ if info[3] == "":
+ continue
+ parent_id = species_id_from_csv(info[3])
+ species_id = species_id_from_csv(info[0])
+ species_links.append(PokemonSpecies(pk=species_id, evolves_from_species_id=parent_id))
+ if len(species_links) >= BATCH_SIZE:
+ PokemonSpecies.objects.bulk_update(species_links, ["evolves_from_species"], batch_size=BATCH_SIZE)
+ species_links = []
+ if species_links:
+ PokemonSpecies.objects.bulk_update(species_links, ["evolves_from_species"], batch_size=BATCH_SIZE)
-def _build_pokemons():
- def csv_record_to_objects(info):
+def _build_pokemons() -> None:
+ def pokemon_colors_from_rows(info: list[str]) -> Iterator[PokemonColor]:
yield PokemonColor(id=int(info[0]), name=info[1])
- build_generic((PokemonColor,), "pokemon_colors.csv", csv_record_to_objects)
+ build_generic((PokemonColor,), "pokemon_colors.csv", pokemon_colors_from_rows)
- def csv_record_to_objects(info):
+ def pokemon_color_names_from_rows(info: list[str]) -> Iterator[PokemonColorName]:
yield PokemonColorName(pokemon_color_id=int(info[0]), language_id=int(info[1]), name=info[2])
- build_generic((PokemonColorName,), "pokemon_color_names.csv", csv_record_to_objects)
+ build_generic((PokemonColorName,), "pokemon_color_names.csv", pokemon_color_names_from_rows)
- def csv_record_to_objects(info):
+ def pokemon_shapes_from_rows(info: list[str]) -> Iterator[PokemonShape]:
yield PokemonShape(id=int(info[0]), name=info[1])
- build_generic((PokemonShape,), "pokemon_shapes.csv", csv_record_to_objects)
+ build_generic((PokemonShape,), "pokemon_shapes.csv", pokemon_shapes_from_rows)
- def csv_record_to_objects(info):
+ def pokemon_shape_names_from_rows(info: list[str]) -> Iterator[PokemonShapeName]:
yield PokemonShapeName(
pokemon_shape_id=int(info[0]),
language_id=int(info[1]),
@@ -1770,14 +1222,14 @@ def csv_record_to_objects(info):
awesome_name=info[3],
)
- build_generic((PokemonShapeName,), "pokemon_shape_prose.csv", csv_record_to_objects)
+ build_generic((PokemonShapeName,), "pokemon_shape_prose.csv", pokemon_shape_names_from_rows)
- def csv_record_to_objects(info):
+ def pokemon_habitats_from_rows(info: list[str]) -> Iterator[PokemonHabitat]:
yield PokemonHabitat(id=int(info[0]), name=info[1])
- build_generic((PokemonHabitat,), "pokemon_habitats.csv", csv_record_to_objects)
+ build_generic((PokemonHabitat,), "pokemon_habitats.csv", pokemon_habitats_from_rows)
- def csv_record_to_objects(info):
+ def pokemon_species_from_rows(info: list[str]) -> Iterator[PokemonSpecies]:
yield PokemonSpecies(
id=int(info[0]),
name=info[1],
@@ -1800,19 +1252,12 @@ def csv_record_to_objects(info):
order=int(info[18]) if info[18] != "" else None,
)
- build_generic((PokemonSpecies,), "pokemon_species.csv", csv_record_to_objects)
+ build_generic((PokemonSpecies,), "pokemon_species.csv", pokemon_species_from_rows)
# PokemonSpecies.evolves_from_species can't be set until all the species are created
- data = load_data("pokemon_species.csv")
- for index, info in enumerate(data):
- if index > 0:
- evolves = PokemonSpecies.objects.get(pk=int(info[3])) if info[3] != "" else None
- if evolves:
- species = PokemonSpecies.objects.get(pk=int(info[0]))
- species.evolves_from_species = evolves
- species.save()
-
- def csv_record_to_objects(info):
+ _link_species_evolutions()
+
+ def pokemon_species_names_from_rows(info: list[str]) -> Iterator[PokemonSpeciesName]:
yield PokemonSpeciesName(
pokemon_species_id=int(info[0]),
language_id=int(info[1]),
@@ -1820,18 +1265,18 @@ def csv_record_to_objects(info):
genus=info[3],
)
- build_generic((PokemonSpeciesName,), "pokemon_species_names.csv", csv_record_to_objects)
+ build_generic((PokemonSpeciesName,), "pokemon_species_names.csv", pokemon_species_names_from_rows)
- def csv_record_to_objects(info):
+ def pokemon_species_descriptions_from_rows(info: list[str]) -> Iterator[PokemonSpeciesDescription]:
yield PokemonSpeciesDescription(
pokemon_species_id=int(info[0]),
language_id=int(info[1]),
description=info[2],
)
- build_generic((PokemonSpeciesDescription,), "pokemon_species_prose.csv", csv_record_to_objects)
+ build_generic((PokemonSpeciesDescription,), "pokemon_species_prose.csv", pokemon_species_descriptions_from_rows)
- def csv_record_to_objects(info):
+ def pokemon_species_flavor_texts_from_rows(info: list[str]) -> Iterator[PokemonSpeciesFlavorText]:
yield PokemonSpeciesFlavorText(
pokemon_species_id=int(info[0]),
version_id=int(info[1]),
@@ -1842,10 +1287,10 @@ def csv_record_to_objects(info):
build_generic(
(PokemonSpeciesFlavorText,),
"pokemon_species_flavor_text.csv",
- csv_record_to_objects,
+ pokemon_species_flavor_texts_from_rows,
)
- def csv_record_to_objects(info):
+ def pokemon_from_rows(info: list[str]) -> Iterator[Pokemon]:
yield Pokemon(
id=int(info[0]),
name=info[1],
@@ -1857,23 +1302,28 @@ def csv_record_to_objects(info):
is_default=bool(int(info[7])) if info[7] != "" else None,
)
- build_generic((Pokemon,), "pokemon.csv", csv_record_to_objects)
+ build_generic((Pokemon,), "pokemon.csv", pokemon_from_rows)
+
+ pokemon_ids = set(Pokemon.objects.values_list("pk", flat=True))
- def csv_record_to_objects(info):
- sprites = _resolve_sprite_config(POKEMON_SPRITE_CONFIG, _pokemon_sprite_lookup(info))
+ def pokemon_sprites_from_rows(info: list[str]) -> Iterator[PokemonSprites]:
+ sprites = resolve_sprite_config(POKEMON_SPRITE_CONFIG, pokemon_sprite_lookup(info))
+ pokemon_id = int(info[0])
+ if pokemon_id not in pokemon_ids:
+ Pokemon.objects.get(pk=pokemon_id)
yield PokemonSprites(
- id=int(info[0]),
- pokemon=Pokemon.objects.get(pk=int(info[0])),
+ id=pokemon_id,
+ pokemon_id=pokemon_id,
sprites=sprites,
)
- build_generic((PokemonSprites,), "pokemon.csv", csv_record_to_objects)
+ build_generic((PokemonSprites,), "pokemon.csv", pokemon_sprites_from_rows)
- def try_cry_names(path, info, extension):
+ def try_cry_names(path: str, info: list[str], extension: str) -> str | None:
file_name = f"{info[0]}.{extension}"
return file_path_or_none(path + file_name, image_file=False)
- def csv_record_to_objects(info):
+ def pokemon_cries_from_rows(info: list[str]) -> Iterator[PokemonCries]:
poke_cries = "pokemon"
latest = f"{poke_cries}/latest/"
legacy = f"{poke_cries}/legacy/"
@@ -1881,15 +1331,18 @@ def csv_record_to_objects(info):
"latest": try_cry_names(latest, info, "ogg"),
"legacy": try_cry_names(legacy, info, "ogg"),
}
+ pokemon_id = int(info[0])
+ if pokemon_id not in pokemon_ids:
+ Pokemon.objects.get(pk=pokemon_id)
yield PokemonCries(
- id=int(info[0]),
- pokemon=Pokemon.objects.get(pk=int(info[0])),
+ id=pokemon_id,
+ pokemon_id=pokemon_id,
cries=cries,
)
- build_generic((PokemonCries,), "pokemon.csv", csv_record_to_objects)
+ build_generic((PokemonCries,), "pokemon.csv", pokemon_cries_from_rows)
- def csv_record_to_objects(info):
+ def pokemon_abilities_from_rows(info: list[str]) -> Iterator[PokemonAbility]:
yield PokemonAbility(
pokemon_id=int(info[0]),
ability_id=int(info[1]),
@@ -1897,9 +1350,9 @@ def csv_record_to_objects(info):
slot=int(info[3]),
)
- build_generic((PokemonAbility,), "pokemon_abilities.csv", csv_record_to_objects)
+ build_generic((PokemonAbility,), "pokemon_abilities.csv", pokemon_abilities_from_rows)
- def csv_record_to_objects(info):
+ def pokemon_abilities_past_from_rows(info: list[str]) -> Iterator[PokemonAbilityPast]:
yield PokemonAbilityPast(
pokemon_id=int(info[0]),
generation_id=int(info[1]),
@@ -1908,23 +1361,23 @@ def csv_record_to_objects(info):
slot=int(info[4]),
)
- build_generic((PokemonAbilityPast,), "pokemon_abilities_past.csv", csv_record_to_objects)
+ build_generic((PokemonAbilityPast,), "pokemon_abilities_past.csv", pokemon_abilities_past_from_rows)
- def csv_record_to_objects(info):
+ def pokemon_dex_numbers_from_rows(info: list[str]) -> Iterator[PokemonDexNumber]:
yield PokemonDexNumber(
pokemon_species_id=int(info[0]),
pokedex_id=int(info[1]),
pokedex_number=int(info[2]),
)
- build_generic((PokemonDexNumber,), "pokemon_dex_numbers.csv", csv_record_to_objects)
+ build_generic((PokemonDexNumber,), "pokemon_dex_numbers.csv", pokemon_dex_numbers_from_rows)
- def csv_record_to_objects(info):
+ def pokemon_egg_groups_from_rows(info: list[str]) -> Iterator[PokemonEggGroup]:
yield PokemonEggGroup(pokemon_species_id=int(info[0]), egg_group_id=int(info[1]))
- build_generic((PokemonEggGroup,), "pokemon_egg_groups.csv", csv_record_to_objects)
+ build_generic((PokemonEggGroup,), "pokemon_egg_groups.csv", pokemon_egg_groups_from_rows)
- def csv_record_to_objects(info):
+ def pokemon_forms_from_rows(info: list[str]) -> Iterator[PokemonForm]:
yield PokemonForm(
id=int(info[0]),
name=info[1],
@@ -1938,9 +1391,9 @@ def csv_record_to_objects(info):
order=int(info[9]),
)
- build_generic((PokemonForm,), "pokemon_forms.csv", csv_record_to_objects)
+ build_generic((PokemonForm,), "pokemon_forms.csv", pokemon_forms_from_rows)
- def csv_record_to_objects(info):
+ def pokemon_evolutions_from_rows(info: list[str]) -> Iterator[PokemonEvolution]:
yield PokemonEvolution(
id=int(info[0]),
evolved_species_id=int(info[1]),
@@ -1978,50 +1431,59 @@ def csv_record_to_objects(info):
percentage_chance=(float(info[33]) if len(info) > 33 and info[33] != "" else None),
)
- build_generic((PokemonEvolution,), "pokemon_evolution.csv", csv_record_to_objects)
+ build_generic((PokemonEvolution,), "pokemon_evolution.csv", pokemon_evolutions_from_rows)
pokemon_sprites_by_id = dict(PokemonSprites.objects.values_list("pokemon_id", "sprites"))
- def csv_record_to_objects(info):
+ def pokemon_form_sprites_from_rows(info: list[str]) -> Iterator[PokemonFormSprites]:
if info[2]:
- lookup = _pokemon_form_sprite_lookup(info)
- sprites = _resolve_sprite_config(POKEMON_SPRITE_CONFIG, lookup)
+ lookup = pokemon_form_sprite_lookup(info)
+ sprites = resolve_sprite_config(POKEMON_SPRITE_CONFIG, lookup)
else:
sprites = deepcopy(pokemon_sprites_by_id.get(int(info[3]), {}))
yield PokemonFormSprites(id=int(info[0]), pokemon_form_id=int(info[0]), sprites=sprites)
- build_generic((PokemonFormSprites,), "pokemon_forms.csv", csv_record_to_objects)
+ build_generic((PokemonFormSprites,), "pokemon_forms.csv", pokemon_form_sprites_from_rows)
+
+ form_ids = set(PokemonForm.objects.values_list("pk", flat=True))
+ language_ids = set(Language.objects.values_list("pk", flat=True))
- def csv_record_to_objects(info):
+ def pokemon_form_names_from_rows(info: list[str]) -> Iterator[PokemonFormName]:
+ form_id = int(info[0])
+ if form_id not in form_ids:
+ PokemonForm.objects.get(pk=form_id)
+ language_id = int(info[1])
+ if language_id not in language_ids:
+ Language.objects.get(pk=language_id)
yield PokemonFormName(
- pokemon_form=PokemonForm.objects.get(pk=int(info[0])),
- language=Language.objects.get(pk=int(info[1])),
+ pokemon_form_id=form_id,
+ language_id=language_id,
name=info[2],
pokemon_name=info[3],
)
- build_generic((PokemonFormName,), "pokemon_form_names.csv", csv_record_to_objects)
+ build_generic((PokemonFormName,), "pokemon_form_names.csv", pokemon_form_names_from_rows)
- def csv_record_to_objects(info):
+ def pokemon_form_generations_from_rows(info: list[str]) -> Iterator[PokemonFormGeneration]:
yield PokemonFormGeneration(
pokemon_form_id=int(info[0]),
generation_id=int(info[1]),
game_index=int(info[2]),
)
- build_generic((PokemonFormGeneration,), "pokemon_form_generations.csv", csv_record_to_objects)
+ build_generic((PokemonFormGeneration,), "pokemon_form_generations.csv", pokemon_form_generations_from_rows)
- def csv_record_to_objects(info):
+ def pokemon_form_types_from_rows(info: list[str]) -> Iterator[PokemonFormType]:
yield PokemonFormType(pokemon_form_id=int(info[0]), type_id=int(info[1]), slot=int(info[2]))
- build_generic((PokemonFormType,), "pokemon_form_types.csv", csv_record_to_objects)
+ build_generic((PokemonFormType,), "pokemon_form_types.csv", pokemon_form_types_from_rows)
- def csv_record_to_objects(info):
+ def pokemon_form_triggers_from_rows(info: list[str]) -> Iterator[PokemonFormTrigger]:
yield PokemonFormTrigger(id=int(info[0]), name=info[1])
- build_generic((PokemonFormTrigger,), "pokemon_form_triggers.csv", csv_record_to_objects)
+ build_generic((PokemonFormTrigger,), "pokemon_form_triggers.csv", pokemon_form_triggers_from_rows)
- def csv_record_to_objects(info):
+ def pokemon_form_conditions_from_rows(info: list[str]) -> Iterator[PokemonFormCondition]:
yield PokemonFormCondition(
pokemon_form_id=int(info[0]),
form_trigger_id=int(info[1]),
@@ -2031,9 +1493,9 @@ def csv_record_to_objects(info):
base_form_id=int(info[5]) if info[5] != "" else None,
)
- build_generic((PokemonFormCondition,), "pokemon_form_conditions.csv", csv_record_to_objects)
+ build_generic((PokemonFormCondition,), "pokemon_form_conditions.csv", pokemon_form_conditions_from_rows)
- def csv_record_to_objects(info):
+ def pokemon_form_flavor_texts_from_rows(info: list[str]) -> Iterator[PokemonFormFlavorText]:
yield PokemonFormFlavorText(
pokemon_form_id=int(info[0]),
version_id=int(info[1]),
@@ -2044,20 +1506,20 @@ def csv_record_to_objects(info):
build_generic(
(PokemonFormFlavorText,),
"pokemon_form_flavor_text.csv",
- csv_record_to_objects,
+ pokemon_form_flavor_texts_from_rows,
)
- def csv_record_to_objects(info):
+ def pokemon_game_indexes_from_rows(info: list[str]) -> Iterator[PokemonGameIndex]:
yield PokemonGameIndex(pokemon_id=int(info[0]), version_id=int(info[1]), game_index=int(info[2]))
- build_generic((PokemonGameIndex,), "pokemon_game_indices.csv", csv_record_to_objects)
+ build_generic((PokemonGameIndex,), "pokemon_game_indices.csv", pokemon_game_indexes_from_rows)
- def csv_record_to_objects(info):
+ def pokemon_habitat_names_from_rows(info: list[str]) -> Iterator[PokemonHabitatName]:
yield PokemonHabitatName(pokemon_habitat_id=int(info[0]), language_id=int(info[1]), name=info[2])
- build_generic((PokemonHabitatName,), "pokemon_habitat_names.csv", csv_record_to_objects)
+ build_generic((PokemonHabitatName,), "pokemon_habitat_names.csv", pokemon_habitat_names_from_rows)
- def csv_record_to_objects(info):
+ def pokemon_items_from_rows(info: list[str]) -> Iterator[PokemonItem]:
yield PokemonItem(
pokemon_id=int(info[0]),
version_id=int(info[1]),
@@ -2065,9 +1527,9 @@ def csv_record_to_objects(info):
rarity=int(info[3]),
)
- build_generic((PokemonItem,), "pokemon_items.csv", csv_record_to_objects)
+ build_generic((PokemonItem,), "pokemon_items.csv", pokemon_items_from_rows)
- def csv_record_to_objects(info):
+ def pokemon_moves_from_rows(info: list[str]) -> Iterator[PokemonMove]:
yield PokemonMove(
pokemon_id=int(info[0]),
version_group_id=int(info[1]),
@@ -2077,9 +1539,9 @@ def csv_record_to_objects(info):
order=int(info[5]) if info[5] != "" else None,
)
- build_generic((PokemonMove,), "pokemon_moves.csv", csv_record_to_objects)
+ build_generic((PokemonMove,), "pokemon_moves.csv", pokemon_moves_from_rows, batch_size=4_000)
- def csv_record_to_objects(info):
+ def pokemon_stats_from_rows(info: list[str]) -> Iterator[PokemonStat]:
yield PokemonStat(
pokemon_id=int(info[0]),
stat_id=int(info[1]),
@@ -2087,9 +1549,9 @@ def csv_record_to_objects(info):
effort=int(info[3]),
)
- build_generic((PokemonStat,), "pokemon_stats.csv", csv_record_to_objects)
+ build_generic((PokemonStat,), "pokemon_stats.csv", pokemon_stats_from_rows)
- def csv_record_to_objects(info):
+ def pokemon_stats_past_from_rows(info: list[str]) -> Iterator[PokemonStatPast]:
yield PokemonStatPast(
pokemon_id=int(info[0]),
generation_id=int(info[1]),
@@ -2098,14 +1560,14 @@ def csv_record_to_objects(info):
effort=int(info[4]),
)
- build_generic((PokemonStatPast,), "pokemon_stats_past.csv", csv_record_to_objects)
+ build_generic((PokemonStatPast,), "pokemon_stats_past.csv", pokemon_stats_past_from_rows)
- def csv_record_to_objects(info):
+ def pokemon_types_from_rows(info: list[str]) -> Iterator[PokemonType]:
yield PokemonType(pokemon_id=int(info[0]), type_id=int(info[1]), slot=int(info[2]))
- build_generic((PokemonType,), "pokemon_types.csv", csv_record_to_objects)
+ build_generic((PokemonType,), "pokemon_types.csv", pokemon_types_from_rows)
- def csv_record_to_objects(info):
+ def pokemon_types_past_from_rows(info: list[str]) -> Iterator[PokemonTypePast]:
yield PokemonTypePast(
pokemon_id=int(info[0]),
generation_id=int(info[1]),
@@ -2113,21 +1575,16 @@ def csv_record_to_objects(info):
slot=int(info[3]),
)
- build_generic((PokemonTypePast,), "pokemon_types_past.csv", csv_record_to_objects)
+ build_generic((PokemonTypePast,), "pokemon_types_past.csv", pokemon_types_past_from_rows)
-###############
-# ENCOUNTER #
-###############
-
-
-def _build_encounters():
- def csv_record_to_objects(info):
+def _build_encounters() -> None:
+ def encounter_methods_from_rows(info: list[str]) -> Iterator[EncounterMethod]:
yield EncounterMethod(id=int(info[0]), name=info[1], order=int(info[2]))
- build_generic((EncounterMethod,), "encounter_methods.csv", csv_record_to_objects)
+ build_generic((EncounterMethod,), "encounter_methods.csv", encounter_methods_from_rows)
- def csv_record_to_objects(info):
+ def location_area_encounter_rates_from_rows(info: list[str]) -> Iterator[LocationAreaEncounterRate]:
yield LocationAreaEncounterRate(
location_area_id=int(info[0]),
encounter_method_id=int(info[1]),
@@ -2138,29 +1595,29 @@ def csv_record_to_objects(info):
build_generic(
(LocationAreaEncounterRate,),
"location_area_encounter_rates.csv",
- csv_record_to_objects,
+ location_area_encounter_rates_from_rows,
)
- def csv_record_to_objects(info):
+ def encounter_method_names_from_rows(info: list[str]) -> Iterator[EncounterMethodName]:
yield EncounterMethodName(encounter_method_id=int(info[0]), language_id=int(info[1]), name=info[2])
- build_generic((EncounterMethodName,), "encounter_method_prose.csv", csv_record_to_objects)
+ build_generic((EncounterMethodName,), "encounter_method_prose.csv", encounter_method_names_from_rows)
- def csv_record_to_objects(info):
+ def encounter_conditions_from_rows(info: list[str]) -> Iterator[EncounterCondition]:
yield EncounterCondition(id=int(info[0]), name=info[1])
- build_generic((EncounterCondition,), "encounter_conditions.csv", csv_record_to_objects)
+ build_generic((EncounterCondition,), "encounter_conditions.csv", encounter_conditions_from_rows)
- def csv_record_to_objects(info):
+ def encounter_condition_names_from_rows(info: list[str]) -> Iterator[EncounterConditionName]:
yield EncounterConditionName(encounter_condition_id=int(info[0]), language_id=int(info[1]), name=info[2])
build_generic(
(EncounterConditionName,),
"encounter_condition_prose.csv",
- csv_record_to_objects,
+ encounter_condition_names_from_rows,
)
- def csv_record_to_objects(info):
+ def encounter_slots_from_rows(info: list[str]) -> Iterator[EncounterSlot]:
yield EncounterSlot(
id=int(info[0]),
version_group_id=int(info[1]),
@@ -2169,9 +1626,9 @@ def csv_record_to_objects(info):
rarity=int(info[4]),
)
- build_generic((EncounterSlot,), "encounter_slots.csv", csv_record_to_objects)
+ build_generic((EncounterSlot,), "encounter_slots.csv", encounter_slots_from_rows)
- def csv_record_to_objects(info):
+ def encounters_from_rows(info: list[str]) -> Iterator[Encounter]:
yield Encounter(
id=int(info[0]),
version_id=int(info[1]),
@@ -2182,9 +1639,9 @@ def csv_record_to_objects(info):
max_level=int(info[6]),
)
- build_generic((Encounter,), "encounters.csv", csv_record_to_objects)
+ build_generic((Encounter,), "encounters.csv", encounters_from_rows, batch_size=2_000)
- def csv_record_to_objects(info):
+ def encounter_condition_values_from_rows(info: list[str]) -> Iterator[EncounterConditionValue]:
yield EncounterConditionValue(
id=int(info[0]),
encounter_condition_id=int(info[1]),
@@ -2195,10 +1652,10 @@ def csv_record_to_objects(info):
build_generic(
(EncounterConditionValue,),
"encounter_condition_values.csv",
- csv_record_to_objects,
+ encounter_condition_values_from_rows,
)
- def csv_record_to_objects(info):
+ def encounter_condition_value_names_from_rows(info: list[str]) -> Iterator[EncounterConditionValueName]:
yield EncounterConditionValueName(
encounter_condition_value_id=int(info[0]),
language_id=int(info[1]),
@@ -2208,19 +1665,20 @@ def csv_record_to_objects(info):
build_generic(
(EncounterConditionValueName,),
"encounter_condition_value_prose.csv",
- csv_record_to_objects,
+ encounter_condition_value_names_from_rows,
)
- def csv_record_to_objects(info):
+ def encounter_condition_value_maps_from_rows(info: list[str]) -> Iterator[EncounterConditionValueMap]:
yield EncounterConditionValueMap(encounter_id=int(info[0]), encounter_condition_value_id=int(info[1]))
build_generic(
(EncounterConditionValueMap,),
"encounter_condition_value_map.csv",
- csv_record_to_objects,
+ encounter_condition_value_maps_from_rows,
+ batch_size=2_000,
)
- def csv_record_to_objects(info):
+ def encounter_pokemon_details_from_rows(info: list[str]) -> Iterator[EncounterPokemonDetail]:
yield EncounterPokemonDetail(
encounter_id=int(info[0]),
min_perfect_ivs=int(info[1]) if info[1] != "" else None,
@@ -2232,27 +1690,22 @@ def csv_record_to_objects(info):
build_generic(
(EncounterPokemonDetail,),
"encounter_pokemon_details.csv",
- csv_record_to_objects,
+ encounter_pokemon_details_from_rows,
)
-##############
-# PAL PARK #
-##############
-
-
-def _build_pal_parks():
- def csv_record_to_objects(info):
+def _build_pal_parks() -> None:
+ def pal_park_areas_from_rows(info: list[str]) -> Iterator[PalParkArea]:
yield PalParkArea(id=int(info[0]), name=info[1])
- build_generic((PalParkArea,), "pal_park_areas.csv", csv_record_to_objects)
+ build_generic((PalParkArea,), "pal_park_areas.csv", pal_park_areas_from_rows)
- def csv_record_to_objects(info):
+ def pal_park_area_names_from_rows(info: list[str]) -> Iterator[PalParkAreaName]:
yield PalParkAreaName(pal_park_area_id=int(info[0]), language_id=int(info[1]), name=info[2])
- build_generic((PalParkAreaName,), "pal_park_area_names.csv", csv_record_to_objects)
+ build_generic((PalParkAreaName,), "pal_park_area_names.csv", pal_park_area_names_from_rows)
- def csv_record_to_objects(info):
+ def pal_parks_from_rows(info: list[str]) -> Iterator[PalPark]:
yield PalPark(
pokemon_species_id=int(info[0]),
pal_park_area_id=int(info[1]),
@@ -2260,10 +1713,10 @@ def csv_record_to_objects(info):
rate=int(info[3]),
)
- build_generic((PalPark,), "pal_park.csv", csv_record_to_objects)
+ build_generic((PalPark,), "pal_park.csv", pal_parks_from_rows)
-def build_all():
+def build_all() -> None:
_build_languages()
_build_regions()
_build_generations()
diff --git a/data/v2/utils/__init__.py b/data/v2/utils/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/data/v2/utils/helpers.py b/data/v2/utils/helpers.py
new file mode 100644
index 000000000..2b979d112
--- /dev/null
+++ b/data/v2/utils/helpers.py
@@ -0,0 +1,67 @@
+import csv
+from collections.abc import Callable, Generator, Iterable
+from contextlib import closing
+from pathlib import Path
+from time import perf_counter
+
+from django.db import connection
+from django.db.models import Model
+
+DATA_LOCATION = Path(__file__).resolve().parents[1] / "csv"
+BATCH_SIZE: int = 1_000
+
+
+def load_data(file_name: str) -> Generator[list[str], None, None]:
+ with open(DATA_LOCATION / file_name, encoding="utf8") as file:
+ yield from csv.reader(file, delimiter=",")
+
+
+def clear_table(model: type[Model]) -> float:
+ table_name = model._meta.db_table
+ print("building " + table_name, flush=True)
+ started_at = perf_counter()
+ model.objects.all().delete()
+ with connection.cursor() as cursor:
+ if connection.vendor == "sqlite":
+ cursor.execute("DELETE FROM sqlite_sequence WHERE name = " + "'" + table_name + "'")
+ else:
+ # Reset DB auto increments to start at 1
+ cursor.execute("SELECT setval(pg_get_serial_sequence(" + "'" + table_name + "'" + ",'id'), 1, false);")
+ return started_at
+
+
+def build_generic(
+ model_classes: tuple[type[Model], ...],
+ file_name: str,
+ csv_record_to_objects: Callable[[list[str]], Iterable[Model]],
+ *,
+ batch_size: int = BATCH_SIZE,
+) -> None:
+ if batch_size < 1:
+ raise ValueError("batch_size must be positive")
+
+ batches: dict[type[Model], list[Model]] = {}
+ build_started_at: dict[type[Model], float] = {}
+
+ for model_class in model_classes:
+ build_started_at[model_class] = clear_table(model_class)
+ batches[model_class] = []
+
+ with closing(load_data(file_name)) as csv_data:
+ next(csv_data, None) # skip header
+
+ for csv_record in csv_data:
+ for obj in csv_record_to_objects(csv_record):
+ model_class = type(obj)
+ batch = batches[model_class]
+ batch.append(obj)
+
+ if len(batch) >= batch_size:
+ model_class.objects.bulk_create(batch)
+ batches[model_class] = []
+
+ for model_class, batch in batches.items():
+ if batch:
+ model_class.objects.bulk_create(batch)
+ elapsed = perf_counter() - build_started_at[model_class]
+ print(f"built {model_class._meta.db_table} in {elapsed:.2f}s", flush=True)
diff --git a/data/v2/utils/media.py b/data/v2/utils/media.py
new file mode 100644
index 000000000..e909efb0c
--- /dev/null
+++ b/data/v2/utils/media.py
@@ -0,0 +1,609 @@
+import contextlib
+import gzip
+import json
+import os
+import urllib.error
+import urllib.request
+from collections.abc import Callable
+from email.utils import parsedate_to_datetime
+from pathlib import Path
+from typing import Any
+
+MEDIA_DIR = "{prefix}{{file_name}}".format(
+ prefix=os.environ.get(
+ "POKEAPI_SPRITES_PREFIX",
+ "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/",
+ )
+)
+SOUND_DIR = "{prefix}{{file_name}}".format(
+ prefix=os.environ.get(
+ "POKEAPI_CRIES_PREFIX",
+ "https://raw.githubusercontent.com/PokeAPI/cries/main/cries/",
+ )
+)
+DATA_DIR = Path(__file__).resolve().parents[1]
+IMAGE_DIR = DATA_DIR / "sprites" / "sprites"
+CRIES_DIR = DATA_DIR / "cries" / "cries"
+
+
+def _load_manifest_from_path(path: Path) -> set[str] | None:
+ try:
+ with gzip.open(path, "rt", encoding="utf-8") as f:
+ return set(json.load(f))
+ except (OSError, json.JSONDecodeError) as e:
+ print(f"Warning: Failed to read local sprite manifest at {path}: {e}")
+ return None
+
+
+def _fetch_manifest(url: str, cache_file: Path) -> set[str] | None:
+ try:
+ if cache_file.is_file():
+ try:
+ head_req = urllib.request.Request(url, method="HEAD")
+ with urllib.request.urlopen(head_req, timeout=10) as head_resp:
+ last_mod_header = head_resp.headers.get("Last-Modified")
+ if last_mod_header:
+ remote_mtime = parsedate_to_datetime(last_mod_header).timestamp()
+ if cache_file.stat().st_mtime >= remote_mtime:
+ return _load_manifest_from_path(cache_file)
+ except (OSError, urllib.error.URLError, ValueError):
+ pass
+
+ with urllib.request.urlopen(url, timeout=30) as resp:
+ raw_bytes: bytes = resp.read()
+ data = json.loads(gzip.decompress(raw_bytes).decode("utf-8"))
+
+ with contextlib.suppress(OSError):
+ cache_file.write_bytes(raw_bytes)
+ last_mod = resp.headers.get("Last-Modified")
+ if last_mod:
+ mtime = parsedate_to_datetime(last_mod).timestamp()
+ os.utime(cache_file, (mtime, mtime))
+
+ return set(data)
+ except (OSError, urllib.error.URLError, json.JSONDecodeError, gzip.BadGzipFile) as e:
+ print(f"Warning: Failed to fetch sprite manifest from {url}: {e}")
+ return None
+
+
+def _load_local_sprites() -> set[str]:
+ """Scan the local cloned sprites submodule directory."""
+ if not IMAGE_DIR.is_dir():
+ return set()
+
+ images: set[str] = set()
+ for image_path in IMAGE_DIR.rglob("*"):
+ if not image_path.is_dir():
+ images.add(image_path.relative_to(IMAGE_DIR).as_posix())
+ return images
+
+
+def _load_resource_images() -> set[str]:
+ use_local = os.environ.get("POKEAPI_USE_LOCAL_SPRITES", "").lower() in ("1", "true", "yes")
+ if use_local and (local_images := _load_local_sprites()):
+ return local_images
+
+ cache_file = DATA_DIR / "manifest.json.gz"
+ manifest_urls: list[str] = list(
+ filter(
+ None,
+ [
+ os.environ.get("POKEAPI_SPRITES_MANIFEST_URL", ""),
+ "https://pokeapi.github.io/sprites/manifest.json.gz",
+ "https://github.com/PokeAPI/sprites/releases/latest/download/manifest.json.gz",
+ ],
+ )
+ )
+
+ for url in manifest_urls:
+ if (data := _fetch_manifest(url, cache_file)) is not None:
+ return data
+
+ # If offline / all remote requests failed, use cached manifest if available
+ if cache_file.is_file() and (cached := _load_manifest_from_path(cache_file)) is not None:
+ return cached
+
+ # Fallback to local files if manifest is completely unavailable
+ if local_images := _load_local_sprites():
+ return local_images
+
+ print("Warning: Could not load sprite resources from local files or remote manifest. Sprites will be empty.")
+ return set()
+
+
+RESOURCE_IMAGES: set[str] = _load_resource_images()
+RESOURCE_CRIES: set[str] = set()
+
+for cry_path in CRIES_DIR.rglob("*"):
+ if not cry_path.is_dir():
+ cry_key = cry_path.relative_to(CRIES_DIR).as_posix()
+ RESOURCE_CRIES.add(cry_key)
+
+
+TYPE_SPRITE_GAME_MAP: dict[str, list[str]] = {
+ "generation-iii": [
+ "colosseum",
+ "emerald",
+ "firered-leafgreen",
+ "ruby-sapphire",
+ "xd",
+ ],
+ "generation-iv": ["diamond-pearl", "heartgold-soulsilver", "platinum"],
+ "generation-v": ["black-2-white-2", "black-white"],
+ "generation-vi": ["omega-ruby-alpha-sapphire", "x-y"],
+ "generation-vii": [
+ "lets-go-pikachu-lets-go-eevee",
+ "sun-moon",
+ "ultra-sun-ultra-moon",
+ ],
+ "generation-viii": [
+ "brilliant-diamond-shining-pearl",
+ "legends-arceus",
+ "sword-shield",
+ ],
+ "generation-ix": ["scarlet-violet", "champions"],
+}
+
+
+POKEMON_SPRITE_CONFIG: dict[str, Any] = {
+ "front_default": ("pokemon/", "png"),
+ "front_female": ("pokemon/female/", "png"),
+ "front_shiny": ("pokemon/shiny/", "png"),
+ "front_shiny_female": ("pokemon/shiny/female/", "png"),
+ "back_default": ("pokemon/back/", "png"),
+ "back_female": ("pokemon/back/female/", "png"),
+ "back_shiny": ("pokemon/back/shiny/", "png"),
+ "back_shiny_female": ("pokemon/back/shiny/female/", "png"),
+ "other": {
+ "dream_world": {
+ "front_default": ("pokemon/other/dream-world/", "svg"),
+ "front_female": ("pokemon/other/dream-world/female/", "svg"),
+ },
+ "home": {
+ "front_default": ("pokemon/other/home/", "png"),
+ "front_female": ("pokemon/other/home/female/", "png"),
+ "front_shiny": ("pokemon/other/home/shiny/", "png"),
+ "front_shiny_female": ("pokemon/other/home/shiny/female/", "png"),
+ },
+ "official-artwork": {
+ "front_default": ("pokemon/other/official-artwork/", "png"),
+ "front_shiny": ("pokemon/other/official-artwork/shiny/", "png"),
+ "versions": {
+ "generation-i": {
+ "red-and-blue": {
+ "front_default": ("pokemon/other/official-artwork/versions/generation-i/red-and-blue/", "png")
+ },
+ "red-and-green": {
+ "front_default": ("pokemon/other/official-artwork/versions/generation-i/red-and-green/", "png")
+ },
+ },
+ "generation-ii": {
+ "gold-and-silver": {
+ "front_default": (
+ "pokemon/other/official-artwork/versions/generation-ii/gold-and-silver/",
+ "png",
+ )
+ }
+ },
+ },
+ },
+ "showdown": {
+ "front_default": ("pokemon/other/showdown/", "gif"),
+ "front_female": ("pokemon/other/showdown/female/", "gif"),
+ "front_shiny": ("pokemon/other/showdown/shiny/", "gif"),
+ "front_shiny_female": ("pokemon/other/showdown/shiny/female/", "gif"),
+ "back_default": ("pokemon/other/showdown/back/", "gif"),
+ "back_female": ("pokemon/other/showdown/back/female/", "gif"),
+ "back_shiny": ("pokemon/other/showdown/back/shiny/", "gif"),
+ "back_shiny_female": ("pokemon/other/showdown/back/shiny/female/", "gif"),
+ },
+ },
+ "versions": {
+ "generation-i": {
+ "red-blue": {
+ "front_default": ("pokemon/versions/generation-i/red-blue/", "png"),
+ "front_gray": ("pokemon/versions/generation-i/red-blue/gray/", "png"),
+ "front_transparent": ("pokemon/versions/generation-i/red-blue/transparent/", "png"),
+ "front_transparent_gray": ("pokemon/versions/generation-i/red-blue/transparent/gray/", "png"),
+ "back_default": ("pokemon/versions/generation-i/red-blue/back/", "png"),
+ "back_gray": ("pokemon/versions/generation-i/red-blue/back/gray/", "png"),
+ "back_transparent": ("pokemon/versions/generation-i/red-blue/transparent/back/", "png"),
+ "back_transparent_gray": ("pokemon/versions/generation-i/red-blue/transparent/back/gray/", "png"),
+ },
+ "red-green-japan": {
+ "front_default": ("pokemon/versions/generation-i/red-green-japan/", "png"),
+ "front_gray": ("pokemon/versions/generation-i/red-green-japan/gray/", "png"),
+ "back_default": ("pokemon/versions/generation-i/red-green-japan/back/", "png"),
+ "back_gray": ("pokemon/versions/generation-i/red-green-japan/back/gray/", "png"),
+ },
+ "yellow": {
+ "front_default": ("pokemon/versions/generation-i/yellow/", "png"),
+ "front_gbc": ("pokemon/versions/generation-i/yellow/gbc/", "png"),
+ "front_gray": ("pokemon/versions/generation-i/yellow/gray/", "png"),
+ "front_transparent": ("pokemon/versions/generation-i/yellow/transparent/", "png"),
+ "front_transparent_gray": ("pokemon/versions/generation-i/yellow/transparent/gray/", "png"),
+ "back_default": ("pokemon/versions/generation-i/yellow/back/", "png"),
+ "back_gbc": ("pokemon/versions/generation-i/yellow/back/gbc/", "png"),
+ "back_gray": ("pokemon/versions/generation-i/yellow/back/gray/", "png"),
+ "back_transparent": ("pokemon/versions/generation-i/yellow/transparent/back/", "png"),
+ "back_transparent_gray": ("pokemon/versions/generation-i/yellow/transparent/back/gray/", "png"),
+ },
+ },
+ "generation-ii": {
+ "crystal": {
+ "front_default": ("pokemon/versions/generation-ii/crystal/", "png"),
+ "front_shiny": ("pokemon/versions/generation-ii/crystal/shiny/", "png"),
+ "front_transparent": ("pokemon/versions/generation-ii/crystal/transparent/", "png"),
+ "front_shiny_transparent": ("pokemon/versions/generation-ii/crystal/transparent/shiny/", "png"),
+ "back_default": ("pokemon/versions/generation-ii/crystal/back/", "png"),
+ "back_shiny": ("pokemon/versions/generation-ii/crystal/back/shiny/", "png"),
+ "back_transparent": ("pokemon/versions/generation-ii/crystal/transparent/back/", "png"),
+ "back_shiny_transparent": ("pokemon/versions/generation-ii/crystal/transparent/back/shiny/", "png"),
+ "animated": {
+ "front_default": ("pokemon/versions/generation-ii/crystal/animated/", "gif"),
+ "front_shiny": ("pokemon/versions/generation-ii/crystal/animated/shiny/", "gif"),
+ },
+ },
+ "gold": {
+ "front_default": ("pokemon/versions/generation-ii/gold/", "png"),
+ "front_shiny": ("pokemon/versions/generation-ii/gold/shiny/", "png"),
+ "front_transparent": ("pokemon/versions/generation-ii/gold/transparent/", "png"),
+ "front_shiny_transparent": ("pokemon/versions/generation-ii/gold/transparent/shiny/", "png"),
+ "back_default": ("pokemon/versions/generation-ii/gold/back/", "png"),
+ "back_shiny": ("pokemon/versions/generation-ii/gold/back/shiny/", "png"),
+ "back_transparent": ("pokemon/versions/generation-ii/gold/transparent/back/", "png"),
+ "back_shiny_transparent": ("pokemon/versions/generation-ii/gold/transparent/back/shiny/", "png"),
+ },
+ "silver": {
+ "front_default": ("pokemon/versions/generation-ii/silver/", "png"),
+ "front_shiny": ("pokemon/versions/generation-ii/silver/shiny/", "png"),
+ "front_transparent": ("pokemon/versions/generation-ii/silver/transparent/", "png"),
+ "front_shiny_transparent": ("pokemon/versions/generation-ii/silver/transparent/shiny/", "png"),
+ "back_default": ("pokemon/versions/generation-ii/silver/back/", "png"),
+ "back_shiny": ("pokemon/versions/generation-ii/silver/back/shiny/", "png"),
+ "back_transparent": ("pokemon/versions/generation-ii/silver/transparent/back/", "png"),
+ "back_shiny_transparent": ("pokemon/versions/generation-ii/silver/transparent/back/shiny/", "png"),
+ },
+ },
+ "generation-iii": {
+ "emerald": {
+ "front_default": ("pokemon/versions/generation-iii/emerald/", "png"),
+ "front_shiny": ("pokemon/versions/generation-iii/emerald/shiny/", "png"),
+ "back_default": ("pokemon/versions/generation-iii/emerald/back/", "png"),
+ "back_shiny": ("pokemon/versions/generation-iii/emerald/back/shiny/", "png"),
+ "animated": {
+ "front_default": ("pokemon/versions/generation-iii/emerald/animated/", "png"),
+ "front_shiny": ("pokemon/versions/generation-iii/emerald/animated/shiny/", "png"),
+ "back_default": ("pokemon/versions/generation-iii/emerald/animated/back/", "png"),
+ "back_shiny": ("pokemon/versions/generation-iii/emerald/animated/back/shiny/", "png"),
+ },
+ },
+ "firered-leafgreen": {
+ "front_default": ("pokemon/versions/generation-iii/firered-leafgreen/", "png"),
+ "front_shiny": ("pokemon/versions/generation-iii/firered-leafgreen/shiny/", "png"),
+ "back_default": ("pokemon/versions/generation-iii/firered-leafgreen/back/", "png"),
+ "back_shiny": ("pokemon/versions/generation-iii/firered-leafgreen/back/shiny/", "png"),
+ },
+ "icons": {
+ "front_default": ("pokemon/versions/generation-iii/icons/", "png"),
+ },
+ "ruby-sapphire": {
+ "front_default": ("pokemon/versions/generation-iii/ruby-sapphire/", "png"),
+ "front_shiny": ("pokemon/versions/generation-iii/ruby-sapphire/shiny/", "png"),
+ "back_default": ("pokemon/versions/generation-iii/ruby-sapphire/back/", "png"),
+ "back_shiny": ("pokemon/versions/generation-iii/ruby-sapphire/back/shiny/", "png"),
+ },
+ },
+ "generation-iv": {
+ "diamond-pearl": {
+ "front_default": ("pokemon/versions/generation-iv/diamond-pearl/", "png"),
+ "front_female": ("pokemon/versions/generation-iv/diamond-pearl/female/", "png"),
+ "front_shiny": ("pokemon/versions/generation-iv/diamond-pearl/shiny/", "png"),
+ "front_shiny_female": ("pokemon/versions/generation-iv/diamond-pearl/shiny/female/", "png"),
+ "back_default": ("pokemon/versions/generation-iv/diamond-pearl/back/", "png"),
+ "back_female": ("pokemon/versions/generation-iv/diamond-pearl/back/female/", "png"),
+ "back_shiny": ("pokemon/versions/generation-iv/diamond-pearl/back/shiny/", "png"),
+ "back_shiny_female": ("pokemon/versions/generation-iv/diamond-pearl/back/shiny/female/", "png"),
+ "animated": {
+ "front_default": ("pokemon/versions/generation-iv/diamond-pearl/animated/", "png"),
+ "front_female": ("pokemon/versions/generation-iv/diamond-pearl/animated/female/", "png"),
+ "front_shiny": ("pokemon/versions/generation-iv/diamond-pearl/animated/shiny/", "png"),
+ "front_shiny_female": (
+ "pokemon/versions/generation-iv/diamond-pearl/animated/shiny/female/",
+ "png",
+ ),
+ },
+ },
+ "heartgold-soulsilver": {
+ "front_default": ("pokemon/versions/generation-iv/heartgold-soulsilver/", "png"),
+ "front_female": ("pokemon/versions/generation-iv/heartgold-soulsilver/female/", "png"),
+ "front_shiny": ("pokemon/versions/generation-iv/heartgold-soulsilver/shiny/", "png"),
+ "front_shiny_female": ("pokemon/versions/generation-iv/heartgold-soulsilver/shiny/female/", "png"),
+ "back_default": ("pokemon/versions/generation-iv/heartgold-soulsilver/back/", "png"),
+ "back_female": ("pokemon/versions/generation-iv/heartgold-soulsilver/back/female/", "png"),
+ "back_shiny": ("pokemon/versions/generation-iv/heartgold-soulsilver/back/shiny/", "png"),
+ "back_shiny_female": ("pokemon/versions/generation-iv/heartgold-soulsilver/back/shiny/female/", "png"),
+ "animated": {
+ "front_default": ("pokemon/versions/generation-iv/heartgold-soulsilver/animated/", "png"),
+ "front_female": ("pokemon/versions/generation-iv/heartgold-soulsilver/animated/female/", "png"),
+ "front_shiny": ("pokemon/versions/generation-iv/heartgold-soulsilver/animated/shiny/", "png"),
+ "front_shiny_female": (
+ "pokemon/versions/generation-iv/heartgold-soulsilver/animated/shiny/female/",
+ "png",
+ ),
+ },
+ },
+ "icons": {
+ "front_default": ("pokemon/versions/generation-iv/icons/", "png"),
+ },
+ "platinum": {
+ "front_default": ("pokemon/versions/generation-iv/platinum/", "png"),
+ "front_female": ("pokemon/versions/generation-iv/platinum/female/", "png"),
+ "front_shiny": ("pokemon/versions/generation-iv/platinum/shiny/", "png"),
+ "front_shiny_female": ("pokemon/versions/generation-iv/platinum/shiny/female/", "png"),
+ "back_default": ("pokemon/versions/generation-iv/platinum/back/", "png"),
+ "back_female": ("pokemon/versions/generation-iv/platinum/back/female/", "png"),
+ "back_shiny": ("pokemon/versions/generation-iv/platinum/back/shiny/", "png"),
+ "back_shiny_female": ("pokemon/versions/generation-iv/platinum/back/shiny/female/", "png"),
+ "animated": {
+ "front_default": ("pokemon/versions/generation-iv/platinum/animated/", "png"),
+ "front_female": ("pokemon/versions/generation-iv/platinum/animated/female/", "png"),
+ "front_shiny": ("pokemon/versions/generation-iv/platinum/animated/shiny/", "png"),
+ "front_shiny_female": ("pokemon/versions/generation-iv/platinum/animated/shiny/female/", "png"),
+ },
+ },
+ },
+ "generation-v": {
+ "black-white": {
+ "front_default": ("pokemon/versions/generation-v/black-white/", "png"),
+ "front_female": ("pokemon/versions/generation-v/black-white/female/", "png"),
+ "front_shiny": ("pokemon/versions/generation-v/black-white/shiny/", "png"),
+ "front_shiny_female": ("pokemon/versions/generation-v/black-white/shiny/female/", "png"),
+ "back_default": ("pokemon/versions/generation-v/black-white/back/", "png"),
+ "back_female": ("pokemon/versions/generation-v/black-white/back/female/", "png"),
+ "back_shiny": ("pokemon/versions/generation-v/black-white/back/shiny/", "png"),
+ "back_shiny_female": ("pokemon/versions/generation-v/black-white/back/shiny/female/", "png"),
+ "animated": {
+ "front_default": ("pokemon/versions/generation-v/black-white/animated/", "gif"),
+ "front_female": ("pokemon/versions/generation-v/black-white/animated/female/", "gif"),
+ "front_shiny": ("pokemon/versions/generation-v/black-white/animated/shiny/", "gif"),
+ "front_shiny_female": ("pokemon/versions/generation-v/black-white/animated/shiny/female/", "gif"),
+ "back_default": ("pokemon/versions/generation-v/black-white/animated/back/", "gif"),
+ "back_female": ("pokemon/versions/generation-v/black-white/animated/back/female/", "gif"),
+ "back_shiny": ("pokemon/versions/generation-v/black-white/animated/back/shiny/", "gif"),
+ "back_shiny_female": (
+ "pokemon/versions/generation-v/black-white/animated/back/shiny/female/",
+ "gif",
+ ),
+ },
+ },
+ "icons": {
+ "front_default": ("pokemon/versions/generation-v/icons/", "png"),
+ "animated": {
+ "front_default": ("pokemon/versions/generation-v/icons/animated/", "png"),
+ },
+ },
+ },
+ "generation-vi": {
+ "icons": {
+ "front_default": ("pokemon/versions/generation-vi/icons/", "png"),
+ "front_female": ("pokemon/versions/generation-vi/icons/female/", "png"),
+ },
+ "omega-ruby-alpha-sapphire": {
+ "front_default": ("pokemon/versions/generation-vi/omega-ruby-alpha-sapphire/", "png"),
+ "front_female": ("pokemon/versions/generation-vi/omega-ruby-alpha-sapphire/female/", "png"),
+ "front_shiny": ("pokemon/versions/generation-vi/omega-ruby-alpha-sapphire/shiny/", "png"),
+ "front_shiny_female": ("pokemon/versions/generation-vi/omega-ruby-alpha-sapphire/shiny/female/", "png"),
+ "back_default": ("pokemon/versions/generation-vi/omega-ruby-alpha-sapphire/back/", "png"),
+ "back_female": ("pokemon/versions/generation-vi/omega-ruby-alpha-sapphire/back/female/", "png"),
+ "back_shiny": ("pokemon/versions/generation-vi/omega-ruby-alpha-sapphire/back/shiny/", "png"),
+ "back_shiny_female": (
+ "pokemon/versions/generation-vi/omega-ruby-alpha-sapphire/back/shiny/female/",
+ "png",
+ ),
+ },
+ "x-y": {
+ "front_default": ("pokemon/versions/generation-vi/x-y/", "png"),
+ "front_female": ("pokemon/versions/generation-vi/x-y/female/", "png"),
+ "front_shiny": ("pokemon/versions/generation-vi/x-y/shiny/", "png"),
+ "front_shiny_female": ("pokemon/versions/generation-vi/x-y/shiny/female/", "png"),
+ "back_default": ("pokemon/versions/generation-vi/x-y/back/", "png"),
+ "back_female": ("pokemon/versions/generation-vi/x-y/back/female/", "png"),
+ "back_shiny": ("pokemon/versions/generation-vi/x-y/back/shiny/", "png"),
+ "back_shiny_female": ("pokemon/versions/generation-vi/x-y/back/shiny/female/", "png"),
+ },
+ },
+ "generation-vii": {
+ "icons": {
+ "front_default": ("pokemon/versions/generation-vii/icons/", "png"),
+ "front_female": ("pokemon/versions/generation-vii/icons/female/", "png"),
+ },
+ "lets-go-pikachu-lets-go-eevee": {
+ "front_default": ("pokemon/versions/generation-vii/lets-go-pikachu-lets-go-eevee/", "gif"),
+ "front_female": ("pokemon/versions/generation-vii/lets-go-pikachu-lets-go-eevee/female/", "gif"),
+ "front_shiny": ("pokemon/versions/generation-vii/lets-go-pikachu-lets-go-eevee/shiny/", "gif"),
+ "front_shiny_female": (
+ "pokemon/versions/generation-vii/lets-go-pikachu-lets-go-eevee/shiny/female/",
+ "gif",
+ ),
+ "back_default": ("pokemon/versions/generation-vii/lets-go-pikachu-lets-go-eevee/back/", "gif"),
+ "back_female": ("pokemon/versions/generation-vii/lets-go-pikachu-lets-go-eevee/back/female/", "gif"),
+ "back_shiny": ("pokemon/versions/generation-vii/lets-go-pikachu-lets-go-eevee/back/shiny/", "gif"),
+ "back_shiny_female": (
+ "pokemon/versions/generation-vii/lets-go-pikachu-lets-go-eevee/back/shiny/female/",
+ "gif",
+ ),
+ "icons": {
+ "front_default": ("pokemon/versions/generation-vii/lets-go-pikachu-lets-go-eevee/icons/", "png")
+ },
+ },
+ "ultra-sun-ultra-moon": {
+ "front_default": ("pokemon/versions/generation-vii/ultra-sun-ultra-moon/", "gif"),
+ "front_female": ("pokemon/versions/generation-vii/ultra-sun-ultra-moon/female/", "gif"),
+ "front_shiny": ("pokemon/versions/generation-vii/ultra-sun-ultra-moon/shiny/", "gif"),
+ "front_shiny_female": ("pokemon/versions/generation-vii/ultra-sun-ultra-moon/shiny/female/", "gif"),
+ "back_default": ("pokemon/versions/generation-vii/ultra-sun-ultra-moon/back/", "gif"),
+ "back_female": ("pokemon/versions/generation-vii/ultra-sun-ultra-moon/back/female/", "gif"),
+ "back_shiny": ("pokemon/versions/generation-vii/ultra-sun-ultra-moon/back/shiny/", "gif"),
+ "back_shiny_female": ("pokemon/versions/generation-vii/ultra-sun-ultra-moon/back/shiny/female/", "gif"),
+ },
+ },
+ "generation-viii": {
+ "brilliant-diamond-shining-pearl": {
+ "front_default": ("pokemon/versions/generation-viii/brilliant-diamond-shining-pearl/", "webp"),
+ "front_female": ("pokemon/versions/generation-viii/brilliant-diamond-shining-pearl/female/", "webp"),
+ "front_shiny": ("pokemon/versions/generation-viii/brilliant-diamond-shining-pearl/shiny/", "webp"),
+ "front_shiny_female": (
+ "pokemon/versions/generation-viii/brilliant-diamond-shining-pearl/shiny/female/",
+ "webp",
+ ),
+ "back_default": ("pokemon/versions/generation-viii/brilliant-diamond-shining-pearl/back/", "webp"),
+ "back_female": (
+ "pokemon/versions/generation-viii/brilliant-diamond-shining-pearl/back/female/",
+ "webp",
+ ),
+ "back_shiny": ("pokemon/versions/generation-viii/brilliant-diamond-shining-pearl/back/shiny/", "webp"),
+ "back_shiny_female": (
+ "pokemon/versions/generation-viii/brilliant-diamond-shining-pearl/back/shiny/female/",
+ "webp",
+ ),
+ "icons": {
+ "front_default": ("pokemon/versions/generation-viii/brilliant-diamond-shining-pearl/icons/", "png"),
+ },
+ },
+ "legends-arceus": {
+ "front_default": ("pokemon/versions/generation-viii/legends-arceus/", "webp"),
+ "front_female": ("pokemon/versions/generation-viii/legends-arceus/female/", "webp"),
+ "front_shiny": ("pokemon/versions/generation-viii/legends-arceus/shiny/", "webp"),
+ "front_shiny_female": ("pokemon/versions/generation-viii/legends-arceus/shiny/female/", "webp"),
+ "back_default": ("pokemon/versions/generation-viii/legends-arceus/back/", "webp"),
+ "back_female": ("pokemon/versions/generation-viii/legends-arceus/back/female/", "webp"),
+ "back_shiny": ("pokemon/versions/generation-viii/legends-arceus/back/shiny/", "webp"),
+ "back_shiny_female": ("pokemon/versions/generation-viii/legends-arceus/back/shiny/female/", "webp"),
+ "icons": {
+ "front_default": ("pokemon/versions/generation-viii/legends-arceus/icons/", "png"),
+ "front_female": ("pokemon/versions/generation-viii/legends-arceus/icons/female/", "png"),
+ "front_shiny": ("pokemon/versions/generation-viii/legends-arceus/icons/shiny/", "png"),
+ "front_shiny_female": (
+ "pokemon/versions/generation-viii/legends-arceus/icons/shiny/female/",
+ "png",
+ ),
+ },
+ },
+ "sword-shield": {
+ "front_default": ("pokemon/versions/generation-viii/sword-shield/", "webp"),
+ "front_female": ("pokemon/versions/generation-viii/sword-shield/female/", "webp"),
+ "front_shiny": ("pokemon/versions/generation-viii/sword-shield/shiny/", "webp"),
+ "front_shiny_female": ("pokemon/versions/generation-viii/sword-shield/shiny/female/", "webp"),
+ "back_default": ("pokemon/versions/generation-viii/sword-shield/back/", "webp"),
+ "back_female": ("pokemon/versions/generation-viii/sword-shield/back/female/", "webp"),
+ "back_shiny": ("pokemon/versions/generation-viii/sword-shield/back/shiny/", "webp"),
+ "back_shiny_female": ("pokemon/versions/generation-viii/sword-shield/back/shiny/female/", "webp"),
+ "icons": {
+ "front_default": ("pokemon/versions/generation-viii/sword-shield/icons/", "png"),
+ "front_female": ("pokemon/versions/generation-viii/sword-shield/icons/female/", "png"),
+ },
+ },
+ },
+ "generation-ix": {
+ "champions": {
+ "front_default": ("pokemon/versions/generation-ix/champions/", "webp"),
+ "front_female": ("pokemon/versions/generation-ix/champions/female/", "webp"),
+ "front_shiny": ("pokemon/versions/generation-ix/champions/shiny/", "webp"),
+ "front_shiny_female": ("pokemon/versions/generation-ix/champions/shiny/female/", "webp"),
+ "back_default": ("pokemon/versions/generation-ix/champions/back/", "webp"),
+ "back_female": ("pokemon/versions/generation-ix/champions/back/female/", "webp"),
+ "back_shiny": ("pokemon/versions/generation-ix/champions/back/shiny/", "webp"),
+ "back_shiny_female": ("pokemon/versions/generation-ix/champions/back/shiny/female/", "webp"),
+ "icons": {
+ "front_default": ("pokemon/versions/generation-ix/champions/icons/", "png"),
+ "front_shiny": ("pokemon/versions/generation-ix/champions/icons/shiny/", "png"),
+ },
+ },
+ "legends-za": {
+ "front_default": ("pokemon/versions/generation-ix/legends-za/", "webp"),
+ "front_female": ("pokemon/versions/generation-ix/legends-za/female/", "webp"),
+ "front_shiny": ("pokemon/versions/generation-ix/legends-za/shiny/", "webp"),
+ "front_shiny_female": ("pokemon/versions/generation-ix/legends-za/shiny/female/", "webp"),
+ "back_default": ("pokemon/versions/generation-ix/legends-za/back/", "webp"),
+ "back_female": ("pokemon/versions/generation-ix/legends-za/back/female/", "webp"),
+ "back_shiny": ("pokemon/versions/generation-ix/legends-za/back/shiny/", "webp"),
+ "back_shiny_female": ("pokemon/versions/generation-ix/legends-za/back/shiny/female/", "webp"),
+ "icons": {
+ "front_default": ("pokemon/versions/generation-ix/legends-za/icons/", "png"),
+ "front_female": ("pokemon/versions/generation-ix/legends-za/icons/female/", "png"),
+ "front_shiny": ("pokemon/versions/generation-ix/legends-za/icons/shiny/", "png"),
+ "front_shiny_female": ("pokemon/versions/generation-ix/legends-za/icons/shiny/female/", "png"),
+ },
+ },
+ "scarlet-violet": {
+ "front_default": ("pokemon/versions/generation-ix/scarlet-violet/", "webp"),
+ "front_female": ("pokemon/versions/generation-ix/scarlet-violet/female/", "webp"),
+ "front_shiny": ("pokemon/versions/generation-ix/scarlet-violet/shiny/", "webp"),
+ "front_shiny_female": ("pokemon/versions/generation-ix/scarlet-violet/shiny/female/", "webp"),
+ "back_default": ("pokemon/versions/generation-ix/scarlet-violet/back/", "webp"),
+ "back_female": ("pokemon/versions/generation-ix/scarlet-violet/back/female/", "webp"),
+ "back_shiny": ("pokemon/versions/generation-ix/scarlet-violet/back/shiny/", "webp"),
+ "back_shiny_female": ("pokemon/versions/generation-ix/scarlet-violet/back/shiny/female/", "webp"),
+ "icons": {
+ "front_default": ("pokemon/versions/generation-ix/scarlet-violet/icons/", "png"),
+ "front_female": ("pokemon/versions/generation-ix/scarlet-violet/icons/female/", "png"),
+ },
+ },
+ },
+ },
+}
+
+
+def file_path_or_none(file_name: str, image_file: bool = True) -> str | None:
+ if not image_file:
+ return SOUND_DIR.format(file_name=file_name) if file_name in RESOURCE_CRIES else None
+ return MEDIA_DIR.format(file_name=file_name) if file_name in RESOURCE_IMAGES else None
+
+
+def resolve_sprite_config(config: dict[str, Any], lookup: Callable[[str, str], str | None]) -> dict[str, Any]:
+ resolved: dict[str, Any] = {}
+ for name, value in config.items():
+ if isinstance(value, dict):
+ resolved[name] = resolve_sprite_config(value, lookup) # pyright: ignore[reportUnknownArgumentType]
+ else:
+ path, extension = value
+ resolved[name] = lookup(path, extension)
+ return resolved
+
+
+def pokemon_sprite_lookup(info: list[str]) -> Callable[[str, str], str | None]:
+ pokemon_id, identifier, species_id = info[:3]
+ file_names = [pokemon_id]
+ if "-" in identifier:
+ file_names.append(f"{species_id}-{identifier.split('-', 1)[1]}")
+
+ def lookup(path: str, extension: str) -> str | None:
+ for file_name in file_names:
+ sprite = file_path_or_none(f"{path}{file_name}.{extension}")
+ if sprite:
+ return sprite
+ return None
+
+ return lookup
+
+
+def pokemon_form_sprite_lookup(info: list[str]) -> Callable[[str, str], str | None]:
+ form_identifier = info[2]
+ pokemon_id = int(info[3])
+ is_default = info[5] == "1"
+
+ file_names: list[str] = []
+ if form_identifier:
+ file_names.append(f"{pokemon_id}-{form_identifier}")
+ if is_default or not form_identifier:
+ file_names.append(str(pokemon_id))
+
+ def lookup(path: str, extension: str) -> str | None:
+ for file_name in file_names:
+ sprite = file_path_or_none(f"{path}{file_name}.{extension}")
+ if sprite:
+ return sprite
+ return None
+
+ return lookup
diff --git a/pokemon_v2/test_models.py b/pokemon_v2/test_models.py
index 9a54394d4..06036713b 100644
--- a/pokemon_v2/test_models.py
+++ b/pokemon_v2/test_models.py
@@ -7,8 +7,8 @@
from django.test import TestCase
from typing_extensions import override
-from data.v2 import build
-from data.v2.build import POKEMON_SPRITE_CONFIG, _pokemon_form_sprite_lookup
+from data.v2.utils import media
+from data.v2.utils.media import POKEMON_SPRITE_CONFIG, pokemon_form_sprite_lookup
from pokemon_v2.models import *
@@ -255,14 +255,14 @@ def test_form_sprite_lookup_falls_back_to_pokemon_id_for_default_forms(self):
unown_b = ["10001", "unown-b", "b", "201", "3", "0"]
images = {"pokemon/201.png", "pokemon/back/201-a.png", "pokemon/back/201.png"}
- with mock.patch.object(build, "RESOURCE_IMAGES", images):
- unown_a_lookup = _pokemon_form_sprite_lookup(unown_a)
- unown_b_lookup = _pokemon_form_sprite_lookup(unown_b)
+ with mock.patch.object(media, "RESOURCE_IMAGES", images):
+ unown_a_lookup = pokemon_form_sprite_lookup(unown_a)
+ unown_b_lookup = pokemon_form_sprite_lookup(unown_b)
- self.assertEqual(unown_a_lookup("pokemon/", "png"), build.MEDIA_DIR.format(file_name="pokemon/201.png"))
+ self.assertEqual(unown_a_lookup("pokemon/", "png"), media.MEDIA_DIR.format(file_name="pokemon/201.png"))
# a form-specific sprite still takes precedence over the fallback
self.assertEqual(
- unown_a_lookup("pokemon/back/", "png"), build.MEDIA_DIR.format(file_name="pokemon/back/201-a.png")
+ unown_a_lookup("pokemon/back/", "png"), media.MEDIA_DIR.format(file_name="pokemon/back/201-a.png")
)
# non-default forms never borrow the base pokemon's sprite
self.assertIsNone(unown_b_lookup("pokemon/", "png"))
diff --git a/ruff.toml b/ruff.toml
index d32503079..40e593363 100644
--- a/ruff.toml
+++ b/ruff.toml
@@ -34,8 +34,5 @@ preview = true
# TODO: Test files
"pokemon_v2/tests.py" = ["ANN", "UP032", "F403", "F405"]
"pokemon_v2/test_models.py" = ["ANN", "F403", "F405"]
-# TODO: Data loading scripts
-"data/v2/build.py" = ["ANN", "ERA001", "F403", "F405"]
-"data/v2/__init__.py" = ["F403", "F405"]
# Django migration generated code
"pokemon_v2/migrations/*.py" = ["RUF012", "ERA001", "E501"]