diff --git a/.gitignore b/.gitignore index e09be8b3b5..53aec7d6c8 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,6 @@ desktop.ini # Versioning .svn/ .git/ + +smogon/ +scripts/downloads/ diff --git a/scripts/check_missing_sprites.py b/scripts/check_missing_sprites.py new file mode 100644 index 0000000000..9412fbf1b2 --- /dev/null +++ b/scripts/check_missing_sprites.py @@ -0,0 +1,119 @@ +import os +import pandas as pd + +# CONFIGURATION +# Script is in: /Parent/sprites/scripts/ +# Data is in: /Parent/pokeapi/data/v2/csv/ +POKEMON_CSV = "../../pokeapi/data/v2/csv/pokemon.csv" +FORMS_CSV = "../../pokeapi/data/v2/csv/pokemon_forms.csv" +VG_CSV = "../../pokeapi/data/v2/csv/version_groups.csv" + +# Sprite directories relative to this script +BASE_PATH = "../sprites/pokemon" +PATHS = { + "Front": BASE_PATH, + "Front Shiny": os.path.join(BASE_PATH, "shiny"), + "Back": os.path.join(BASE_PATH, "back"), + "Back Shiny": os.path.join(BASE_PATH, "back/shiny"), +} + + +def check_sprites(): + # 1. Validate required files exist + required_files = [POKEMON_CSV, FORMS_CSV, VG_CSV] + for f in required_files: + if not os.path.exists(f): + print(f"❌ Error: Required CSV missing at: {os.path.abspath(f)}") + return + + # 2. Prepare Data (Merge Pokemon + Forms + Version Groups to get Generation) + print("🔍 Loading and merging Pokémon data...") + df_pokemon = pd.read_csv(POKEMON_CSV) + df_forms = pd.read_csv(FORMS_CSV) + df_vg = pd.read_csv(VG_CSV) + + # We use the default form to determine the generation for the Pokemon ID + # This covers both base pokemon and varieties (Megas, Alolan, etc.) + df_forms_subset = df_forms[df_forms["is_default"] == 1][ + ["pokemon_id", "introduced_in_version_group_id"] + ] + + # Merge to get generation_id + df_merged = df_pokemon.merge( + df_forms_subset, left_on="id", right_on="pokemon_id", how="left" + ) + df_merged = df_merged.merge( + df_vg[["id", "generation_id"]], + left_on="introduced_in_version_group_id", + right_on="id", + how="left", + ) + + # Select final columns and rename for clarity + pokemon_entries = ( + df_merged[["id_x", "identifier", "generation_id"]] + .rename(columns={"id_x": "id"}) + .to_dict("records") + ) + + # Dictionary to store missing results + results = {key: [] for key in PATHS.keys()} + + # 3. Check Local Files + print(f"🧪 Comparing {len(pokemon_entries)} entries against local files...") + + for pokemon in pokemon_entries: + p_id = pokemon["id"] + name = pokemon["identifier"] + gen = pokemon["generation_id"] + filename = f"{p_id}.png" + + for label, folder in PATHS.items(): + if os.path.exists(folder): + file_path = os.path.join(folder, filename) + if not os.path.exists(file_path): + results[label].append( + { + "id": p_id, + "identifier": name, + "generation": int(gen) if pd.notnull(gen) else "Unknown", + } + ) + + # 4. Detailed Console Output + print("\n" + "=" * 50) + print(" MISSING ASSETS REPORT") + print("=" * 50) + + for label, missing_list in results.items(): + count = len(missing_list) + icon = "✨" if "Shiny" in label else "👤" + direction = "➡️" if "Front" in label else "⬅️" + + print(f"\n{icon} {direction} {label.upper()} ({count} missing):") + + if missing_list: + # Sort by ID for the print preview + missing_list.sort(key=lambda x: x["id"]) + for item in missing_list[:10]: + print( + f" - Gen {item['generation']} | {item['id']}: {item['identifier']}" + ) + if count > 10: + print(f" ... and {count - 10} others.") + + # Export CSV for this category + file_safe_name = label.lower().replace(" ", "_") + pd.DataFrame(missing_list).to_csv( + f"missing_{file_safe_name}.csv", index=False + ) + else: + print(" ✅ All assets present!") + + print("\n" + "=" * 50) + print("📝 Results with Generation IDs saved to CSV files.") + print("=" * 50) + + +if __name__ == "__main__": + check_sprites() diff --git a/scripts/forms.json b/scripts/forms.json index fcca96c4c6..fc75971a29 100644 --- a/scripts/forms.json +++ b/scripts/forms.json @@ -171,6 +171,13 @@ "25_4": "pikachu-pop-star", "25_5": "pikachu-phd", "25_6": "pikachu-libre", + "25_7": "pikachu-original-cap", + "25_8": "pikachu-hoenn-cap", + "25_9": "pikachu-sinnoh-cap", + "25_10": "pikachu-unova-cap", + "25_11": "pikachu-kalos-cap", + "25_12": "pikachu-alola-cap", + "25_13": "pikachu-partner-cap", "493_17": "arceus-fairy", "722": "rowlet", "723": "dartrix", @@ -377,69 +384,69 @@ "866": "mr-rime", "867": "runerigus", "868": "milcery", - "869_1": "alcremie-vanilla-strawberry", - "869_2": "alcremie-vanilla-berry", - "869_3": "alcremie-vanilla-love", - "869_4": "alcremie-vanilla-star", - "869_5": "alcremie-vanilla-clover", - "869_6": "alcremie-vanilla-flower", - "869_7": "alcremie-vanilla-ribbon", - "869_8": "alcremie-ruby-strawberry", - "869_9": "alcremie-ruby-berry", - "869_10": "alcremie-ruby-love", - "869_11": "alcremie-ruby-star", - "869_12": "alcremie-ruby-clover", - "869_13": "alcremie-ruby-flower", - "869_14": "alcremie-ruby-ribbon", - "869_15": "alcremie-matcha-strawberry", - "869_16": "alcremie-matcha-berry", - "869_17": "alcremie-matcha-love", - "869_18": "alcremie-matcha-star", - "869_19": "alcremie-matcha-clover", - "869_20": "alcremie-matcha-flower", - "869_21": "alcremie-matcha-ribbon", - "869_22": "alcremie-mint-strawberry", - "869_23": "alcremie-mint-berry", - "869_24": "alcremie-mint-love", - "869_25": "alcremie-mint-star", - "869_26": "alcremie-mint-clover", - "869_27": "alcremie-mint-flower", - "869_28": "alcremie-mint-ribbon", - "869_29": "alcremie-lemon-strawberry", - "869_30": "alcremie-lemon-berry", - "869_31": "alcremie-lemon-love", - "869_32": "alcremie-lemon-star", - "869_33": "alcremie-lemon-clover", - "869_34": "alcremie-lemon-flower", - "869_35": "alcremie-lemon-ribbon", - "869_36": "alcremie-salted-strawberry", - "869_37": "alcremie-salted-berry", - "869_38": "alcremie-salted-love", - "869_39": "alcremie-salted-star", - "869_40": "alcremie-salted-clover", - "869_41": "alcremie-salted-flower", - "869_42": "alcremie-salted-ribbon", - "869_43": "alcremie-ruby-swirl-strawberry", - "869_44": "alcremie-ruby-swirl-berry", - "869_45": "alcremie-ruby-swirl-love", - "869_46": "alcremie-ruby-swirl-star", - "869_47": "alcremie-ruby-swirl-clover", - "869_48": "alcremie-ruby-swirl-flower", - "869_49": "alcremie-ruby-swirl-ribbon", - "869_50": "alcremie-caramel-swirl-strawberry", - "869_51": "alcremie-caramel-swirl-berry", - "869_52": "alcremie-caramel-swirl-love", - "869_53": "alcremie-caramel-swirl-star", - "869_54": "alcremie-caramel-swirl-clover", - "869_55": "alcremie-caramel-swirl-flower", - "869_56": "alcremie-caramel-swirl-ribbon", - "869_57": "alcremie-rainbow-swirl-strawberry", - "869_58": "alcremie-rainbow-swirl-berry", - "869_59": "alcremie-rainbow-swirl-love", - "869_60": "alcremie-rainbow-swirl-star", - "869_61": "alcremie-rainbow-swirl-clover", - "869_62": "alcremie-rainbow-swirl-flower", - "869_63": "alcremie-rainbow-swirl-ribbon", + "869_1": "alcremie-vanilla-cream-strawberry-sweet", + "869_2": "alcremie-vanilla-cream-berry-sweet", + "869_3": "alcremie-vanilla-cream-love-sweet", + "869_4": "alcremie-vanilla-cream-star-sweet", + "869_5": "alcremie-vanilla-cream-clover-sweet", + "869_6": "alcremie-vanilla-cream-flower-sweet", + "869_7": "alcremie-vanilla-cream-ribbon-sweet", + "869_8": "alcremie-ruby-cream-strawberry-sweet", + "869_9": "alcremie-ruby-cream-berry-sweet", + "869_10": "alcremie-ruby-cream-love-sweet", + "869_11": "alcremie-ruby-cream-star-sweet", + "869_12": "alcremie-ruby-cream-clover-sweet", + "869_13": "alcremie-ruby-cream-flower-sweet", + "869_14": "alcremie-ruby-cream-ribbon-sweet", + "869_15": "alcremie-matcha-cream-strawberry-sweet", + "869_16": "alcremie-matcha-cream-berry-sweet", + "869_17": "alcremie-matcha-cream-love-sweet", + "869_18": "alcremie-matcha-cream-star-sweet", + "869_19": "alcremie-matcha-cream-clover-sweet", + "869_20": "alcremie-matcha-cream-flower-sweet", + "869_21": "alcremie-matcha-cream-ribbon-sweet", + "869_22": "alcremie-mint-cream-strawberry-sweet", + "869_23": "alcremie-mint-cream-berry-sweet", + "869_24": "alcremie-mint-cream-love-sweet", + "869_25": "alcremie-mint-cream-star-sweet", + "869_26": "alcremie-mint-cream-clover-sweet", + "869_27": "alcremie-mint-cream-flower-sweet", + "869_28": "alcremie-mint-cream-ribbon-sweet", + "869_29": "alcremie-lemon-cream-strawberry-sweet", + "869_30": "alcremie-lemon-cream-berry-sweet", + "869_31": "alcremie-lemon-cream-love-sweet", + "869_32": "alcremie-lemon-cream-star-sweet", + "869_33": "alcremie-lemon-cream-clover-sweet", + "869_34": "alcremie-lemon-cream-flower-sweet", + "869_35": "alcremie-lemon-cream-ribbon-sweet", + "869_36": "alcremie-salted-cream-strawberry-sweet", + "869_37": "alcremie-salted-cream-berry-sweet", + "869_38": "alcremie-salted-cream-love-sweet", + "869_39": "alcremie-salted-cream-star-sweet", + "869_40": "alcremie-salted-cream-clover-sweet", + "869_41": "alcremie-salted-cream-flower-sweet", + "869_42": "alcremie-salted-cream-ribbon-sweet", + "869_43": "alcremie-ruby-cream-swirl-sweet-strawberry", + "869_44": "alcremie-ruby-cream-swirl-sweet-berry", + "869_45": "alcremie-ruby-cream-swirl-sweet-love", + "869_46": "alcremie-ruby-cream-swirl-sweet-star", + "869_47": "alcremie-ruby-cream-swirl-sweet-clover", + "869_48": "alcremie-ruby-cream-swirl-sweet-flower", + "869_49": "alcremie-ruby-cream-swirl-sweet-ribbon", + "869_50": "alcremie-caramel-cream-swirl-sweet-strawberry", + "869_51": "alcremie-caramel-cream-swirl-sweet-berry", + "869_52": "alcremie-caramel-cream-swirl-sweet-love", + "869_53": "alcremie-caramel-cream-swirl-sweet-star", + "869_54": "alcremie-caramel-cream-swirl-sweet-clover", + "869_55": "alcremie-caramel-cream-swirl-sweet-flower", + "869_56": "alcremie-caramel-cream-swirl-sweet-ribbon", + "869_57": "alcremie-rainbow-cream-swirl-sweet-strawberry", + "869_58": "alcremie-rainbow-cream-swirl-sweet-berry", + "869_59": "alcremie-rainbow-cream-swirl-sweet-love", + "869_60": "alcremie-rainbow-cream-swirl-sweet-star", + "869_61": "alcremie-rainbow-cream-swirl-sweet-clover", + "869_62": "alcremie-rainbow-cream-swirl-sweet-flower", + "869_63": "alcremie-rainbow-cream-swirl-sweet-ribbon", "870": "falinks", "871": "pincurchin", "872": "snom", @@ -530,7 +537,7 @@ "869_g": "alcremie-gmax", "879_g": "copperajah-gmax", "884_g": "duraludon-gmax", - "890_g": "eternatus-gmax", + "890_g": "eternatus-eternamax", "892_g2": "urshifu-single-strike-gmax", "892_g1": "urshifu-rapid-strike-gmax", "25_15": "pikachu-world-cap", diff --git a/scripts/generate_report.py b/scripts/generate_report.py index 0ac1a3d56c..c26b52ab96 100644 --- a/scripts/generate_report.py +++ b/scripts/generate_report.py @@ -15,10 +15,7 @@ # Setup caching (24h) # --------------------------------------------------------------------------- requests_cache.install_cache( - "api_cache", - backend="sqlite", - use_cache_dir=True, - expire_after=86400 + "api_cache", backend="sqlite", use_cache_dir=True, expire_after=86400 ) @@ -26,12 +23,8 @@ # Static folder config, extend this map to test more generations/games/folders # --------------------------------------------------------------------------- FOLDER: Dict[str, List[str]] = { - "generation-viii": [ - "brilliant-diamond-shining-pearl" - ], - "generation-ix": [ - "scarlet-violet" - ] + "generation-viii": ["brilliant-diamond-shining-pearl"], + "generation-ix": ["scarlet-violet"], } @@ -42,10 +35,7 @@ # --------------------------------------------------------------------------- # Logging configuration # --------------------------------------------------------------------------- -logging.basicConfig( - level=logging.INFO, - format="[%(levelname)s] %(message)s" -) +logging.basicConfig(level=logging.INFO, format="[%(levelname)s] %(message)s") logger = logging.getLogger(__name__) @@ -67,10 +57,7 @@ def get_pokemon_data(api_url: str, endpoint: str, identifier: str) -> dict: # --------------------------------------------------------------------------- # Local filesystem scan # --------------------------------------------------------------------------- -def get_local_images( - local_path: str, - folder_config: Dict[str, List[str]] -) -> FileInfo: +def get_local_images(local_path: str, folder_config: Dict[str, List[str]]) -> FileInfo: """Scan local directories for image files.""" all_files_by_path: FileInfo = {} @@ -80,8 +67,7 @@ def get_local_images( try: files = { - f for f in os.listdir(path) - if os.path.isfile(os.path.join(path, f)) + f for f in os.listdir(path) if os.path.isfile(os.path.join(path, f)) } logger.info(f"Found {len(files)} files in '{path}'.") @@ -102,7 +88,7 @@ def create_table( generation_key: str, game_key: str, file_set: Set[str], - api_url: str + api_url: str, ) -> str: """Generate the HTML table displaying comparison images.""" table_rows = "" @@ -154,8 +140,7 @@ def create_table( # Extract sprites default_sprite = ( - pokemon_sprites.get("front_default", "") - if pokemon_sprites else "" + pokemon_sprites.get("front_default", "") if pokemon_sprites else "" ) version_sprite = ( @@ -163,12 +148,13 @@ def create_table( .get(generation_key, {}) .get(game_key, {}) .get("front_default", "") - if pokemon_sprites else "" + if pokemon_sprites + else "" ) # Add HTML row table_rows += f""" -