from __future__ import annotations import csv import requests import time import json import os import re import sqlite3 from bs4 import BeautifulSoup, Tag, NavigableString import copy from typing import List, Optional # Initialize the database connection conn = sqlite3.connect('pokemon_cache.db') cursor = conn.cursor() # Create the cache table if it doesn't exist cursor.execute(''' CREATE TABLE IF NOT EXISTS cache ( key TEXT PRIMARY KEY, value TEXT ) ''') conn.commit() # List of all main series Pokémon games in chronological order, with special games first in each generation all_games = [ "Yellow", "Red", "Blue", "Crystal", "Gold", "Silver", "Emerald", "FireRed", "LeafGreen", "Ruby", "Sapphire", "Platinum", "HeartGold", "SoulSilver", "Diamond", "Pearl", "Black 2", "White 2", "Black", "White", "X", "Y", "Omega Ruby", "Alpha Sapphire", "Ultra Sun", "Ultra Moon", "Sun", "Moon", "Sword", "Shield", "Expansion Pass", "Brilliant Diamond", "Shining Pearl", "Legends: Arceus", "Scarlet", "Violet", "The Teal Mask", "The Hidden Treasure of Area Zero", "Unknown" ] big_pokemon_list = [] cache = {} new_entries_count = 0 def get_cached_data(): global cache cursor.execute("SELECT key, value FROM cache") for key, value in cursor.fetchall(): cache[key] = json.loads(value) def save_cached_data(): global cache, new_entries_count if new_entries_count > 0: for key, value in cache.items(): cursor.execute("INSERT OR REPLACE INTO cache (key, value) VALUES (?, ?)", (key, json.dumps(value))) conn.commit() new_entries_count = 0 def update_cache(key, value): global cache, new_entries_count if key not in cache: cache[key] = value new_entries_count += 1 if new_entries_count >= 1: save_cached_data() time.sleep(1) class Pokemon: def __init__(self, name: str, number: int, form: Optional[str] = None): self.name = name self.number = number self.form = form self.stage: Optional[str] = None self.evolution_chain: Optional[List['EvolutionStage']] = [] self.is_baby = False self.encounter_information: Optional[List['EncounterInformation']] = [] self.earliest_game: Optional['EncounterInformation'] = None def __str__(self): return f"{self.name} {self.form if self.form else ''} (#{self.number})" def add_evolution_chain(self, evolution_chain: List['EvolutionStage']): self.evolution_chain = evolution_chain def add_stage(self, stage: str): self.stage = stage self.is_baby = self.stage is not None and 'Baby' in self.stage def update_encounter_information(self): if not self.encounter_information: return non_catchable_methods = ["trade", "event", "global link", "poké transfer", "time capsule", "unobtainable", "pokémon home"] for encounter in self.encounter_information: for location in encounter.locations: encounter.method = "Catchable" for non_catchable in non_catchable_methods: if non_catchable in location.lower(): encounter.method = None break if encounter.method is None: continue if "first partner" in location.lower(): encounter.method = "Starter" elif "received" in location.lower(): encounter.method = "Gift" elif "evolve" in location.lower(): encounter.method = "Evolve" else: encounter.method = "Catchable" def determine_earliest_game(self): if not self.encounter_information: self.earliest_game = None return self.update_encounter_information() game_methods = {} for encounter in self.encounter_information: if encounter.method: game_methods[encounter.game.lower()] = encounter for game in all_games: if game.lower() in game_methods: self.earliest_game = game_methods[game.lower()] return self.earliest_game = None class EvolutionStage: def __init__(self, pokemon: str, method: Optional[str] = None, stage: Optional[str] = None, form: Optional[str] = None): self.pokemon = pokemon self.method = method self.next_stage: Optional[EvolutionStage] = None self.branches: List[EvolutionStage] = [] self.stage = stage self.is_baby = self.stage is not None and 'Baby' in self.stage self.form = form def __str__(self): return f"{self.pokemon} {self.form if self.form else ''} ({self.method if self.method else 'Base'})" class EncounterInformation: def __init__(self, game: str, locations: List[str]): self.game = game self.method = "Unknown" self.locations = locations def parse_evolution_chain(table: Tag, form: Optional[str] = None) -> List[EvolutionStage]: main_chain = [] current_stage = None pending_method = None tbody = table.find('tbody', recursive=False) if not tbody: return [] rows = tbody.find_all('tr', recursive=False) main_row = rows[0] branch_rows = rows[1:] # Parse main evolution chain for td in main_row.find_all('td', recursive=False): if td.find('table'): # This TD contains Pokemon information pokemon_name = extract_pokemon_name(td) stage = extract_stage_form(td) new_stage = EvolutionStage(pokemon_name, pending_method, stage, form) pending_method = None if current_stage: current_stage.next_stage = new_stage current_stage = new_stage main_chain.append(current_stage) else: # This TD contains evolution method for the next Pokemon pending_method = extract_evolution_method(td) # Parse branching evolutions for row in branch_rows: branch_stage = None branch_method = None for td in row.find_all('td', recursive=False): if td.find('table'): pokemon_name = extract_pokemon_name(td) stage = extract_stage_form(td) new_stage = EvolutionStage(pokemon_name, branch_method, stage, form) branch_method = None if branch_stage: branch_stage.next_stage = new_stage branch_stage = new_stage # Find which main chain Pokemon this branches from for main_stage in main_chain: if td.get('rowspan') and main_stage.pokemon == pokemon_name: main_stage.branches.append(branch_stage) break else: branch_method = extract_evolution_method(td) return main_chain def extract_pokemon_name(td: Tag) -> str: # Extract Pokemon name from the table within the TD name_tag = td.find('table').find('a', class_='selflink') if name_tag: return name_tag.get_text(strip=True) name_tag = td.find('table').find('a', title=True) return name_tag.get_text(strip=True) def extract_evolution_method(td: Tag) -> str: # Extract evolution method from the TD return td.get_text(strip=True) def extract_stage_form(td: Tag) -> Optional[str]: stage_tag = td.find('table').find('small') if stage_tag: return stage_tag.get_text(strip=True) return None def extract_is_baby(td: Tag) -> bool: stage_tag = td.find('table').find('small') if stage_tag: return 'Baby' in stage_tag.get_text(strip=True) return False def read_pokemon_list(filename, limit=50): pokemon_list = [] with open(filename, 'r', newline='', encoding='utf-8') as csvfile: reader = csv.DictReader(csvfile) for i, row in enumerate(reader): if i >= limit: break # Split the name into base name and form match = re.match(r'(.*?)\s*(\(.*\))?$', row['name']) base_name, form = match.groups() if match else (row['name'], None) row['base_name'] = base_name.strip() row['form'] = form.strip('() ') if form else None pokemon_list.append(row) new_pokemon = Pokemon(row['base_name'], row['number'], row['form']) big_pokemon_list.append(new_pokemon) return pokemon_list def sanitize_name_and_form(name, form): adjusted_form = None if form: adjusted_form = form.lower() #Some stupid special cases if name.lower() == 'tauros': if adjusted_form == 'paldean form': adjusted_form = 'paldea combat breed' elif 'blaze' in adjusted_form: adjusted_form = 'paldea blaze breed' elif 'aqua' in adjusted_form: adjusted_form = 'paldea aqua breed' replacements = {'forme': '', 'form': '', 'alolan': 'alola', 'galarian': 'galar', 'hisuian': 'hisui', 'paldean': 'paldea', 'size': '', '10%': '10 power construct', 'hoopa': '', 'style': '', 'core': '', 'color': '', 'blood moon': 'bloodmoon'}; for old, new in replacements.items(): adjusted_form = adjusted_form.replace(old, new).strip() missing_forms = ['burmy', 'shellos', 'gastrodon', 'wormadam', 'unown', "deerling", "sawsbuck", "vivillon", "flabébé", "floette", "florges", "furfrou", "sinistea", "polteageist", "alcremie", "poltchageist", "sinistcha"] if name.lower() in missing_forms: adjusted_form = None if name.lower() == 'wormadam': adjusted_form = adjusted_form.replace('cloak', '').strip() if name.lower() == 'rotom': adjusted_form = adjusted_form.replace('rotom', '').strip() if name.lower() == 'darmanitan': adjusted_form = adjusted_form + ' standard' else: default_forms = {'deoxys': 'normal', 'wormadam': 'plant', 'giratina': 'origin', 'tornadus': 'incarnate', 'shaymin': 'land', 'basculin': 'red-striped', 'darmanitan': 'standard', 'thundurus': 'incarnate', 'landorus': 'incarnate', 'enamorus': 'incarnate', 'keldeo': 'ordinary', 'meloetta': 'aria', 'meowstic': 'male', 'aegislash': 'shield', 'pumpkaboo': 'average', 'gourgeist': 'average', 'minior': 'red-meteor', 'zygarde': '50 power construct', 'oricorio': 'baile', 'lycanroc': 'midday', 'wishiwashi': 'solo', 'mimikyu': 'disguised', 'cramorant': 'gulping', 'toxtricity': 'low-key', 'eiscue': 'ice', 'indeedee': 'male', 'urshifu': 'single-strike', 'morpeko': 'full belly', 'oinkologne': 'male', 'maushold': 'family of three', 'squawkabilly': 'green plumage', 'palafin': 'zero', 'tatsugiri': 'curly', 'dudunsparce': 'two segment', 'basculegion': 'male'} if name.lower() in default_forms: adjusted_form = default_forms[name.lower()] if adjusted_form: api_name = f"{name.lower()}-{adjusted_form}" else: api_name = name.lower() api_name = api_name.replace(' ', '-').replace("'", "").replace(".", "").replace('é', 'e').replace(':', '') #more special cases if api_name == 'oinkologne-male': api_name = '916' return api_name def get_pokemon_data(pokemon_name, form, cache): cache_key = f"pokemon_{pokemon_name}_{form}" if form else f"pokemon_{pokemon_name}" if cache_key in cache: return cache[cache_key] api_name = sanitize_name_and_form(pokemon_name, form) url = f"https://pokeapi.co/api/v2/pokemon/{api_name}" print(f"Fetching Pokémon data for {pokemon_name}: {url}") response = requests.get(url) if response.status_code == 200: data = response.json() update_cache(cache_key, data) return data return None def get_pokemon_data_bulbapedia(pokemon_name, cache): cache_key = f"pokemon_{pokemon_name}_bulbapedia" if cache_key in cache: return cache[cache_key] url = f"https://bulbapedia.bulbagarden.net/wiki/{pokemon_name}_(Pokémon)" print(f"Fetching Pokémon data for {pokemon_name}: {url}") response = requests.get(url) if response.status_code == 200: data = response.text update_cache(cache_key, data) return data def get_pokemon_encounter_data(pokemon_name, form, cache): cache_key = f"pokemon_encounter_{pokemon_name}_{form}" if form else f"pokemon_encounter_{pokemon_name}" if cache_key in cache: return cache[cache_key] api_name = sanitize_name_and_form(pokemon_name, form) url = f"https://pokeapi.co/api/v2/pokemon/{api_name}/encounters" print(f"Fetching encounter data for {pokemon_name}: {url}") response = requests.get(url) if response.status_code == 200: data = response.json() update_cache(cache_key, data) return data else: return None def split_td_contents(td): groups = [] current_group = [] for content in td.contents: if isinstance(content, Tag) and content.name == 'br': if current_group: groups.append(BeautifulSoup('', 'html.parser').new_tag('div')) for item in current_group: groups[-1].append(copy.copy(item)) current_group = [] else: current_group.append(content) if current_group: groups.append(BeautifulSoup('', 'html.parser').new_tag('div')) for item in current_group: groups[-1].append(copy.copy(item)) return groups def parse_form_information(html_content): soup = BeautifulSoup(html_content, 'html.parser') form_info = soup.find('small') if form_info: form_text = form_info.get_text(strip=True) # Remove parentheses form_text = form_text.strip('()') # Split the text into main form and breed (if present) parts = form_text.split('(') main_form = parts[0].strip() breed = parts[1].strip(')') if len(parts) > 1 else None return main_form, breed return None, None def get_evolution_data_from_bulbapedia(pokemon_name, form, cache): page_data = get_pokemon_data_bulbapedia(pokemon_name, cache) if not page_data: return None soup = BeautifulSoup(page_data, 'html.parser') evolution_section = soup.find('span', id='Evolution_data') if not evolution_section: return None evolution_table = None if form: form = form.replace('Form', '').replace('form', '').strip() for tag in evolution_section.parent.find_next_siblings(): if tag.name == 'h4' and form in tag.get_text(strip=True): evolution_table = tag.find_next('table') break if tag.name == 'h3': break else: evolution_table = evolution_section.parent.find_next('table') if not evolution_table: return None evolution_chain = parse_evolution_chain(evolution_table, form) return evolution_chain # This is going to be a little odd. # the first TR contains a full evolution chain # other TRs contain branching evolution chains # any TDs in the first TR with a rowspan are part of the main evolution chain # any other TDS are part of the branching evolution chains # a table in a TD is information about the current Pokémon in that evolution stage # a TD without a table is information on how to trigger the next evolution def get_locations_from_bulbapedia(pokemon_name, form, cache): page_data = get_pokemon_data_bulbapedia(pokemon_name, cache) if not page_data: return None soup = BeautifulSoup(page_data, 'html.parser') locations_section = soup.find('span', id='Game_locations') if not locations_section: return None locations_table = locations_section.find_next('table', class_='roundy') if not locations_table: return None raw_game_locations = {} # Ok so the table is a bit of a mess. It has some nested tables and stuff. # In each row is a nested table with all the games in a generation. # Next is another nexted table, but i can't tell what for. # within that nested table, is another nested table with the games, either the release pair or a single game spanning two columns. # Next to that is another nested table with the locations. generation_tbody = locations_table.find('tbody', recursive=False) generation_rows = generation_tbody.find_all('tr', recursive=False) for generation_row in generation_rows: random_nested_td = generation_row.find('td', recursive=False) if not random_nested_td: continue random_nested_table = random_nested_td.find('table', recursive=False) if not random_nested_table: continue random_nested_tbody = random_nested_table.find('tbody', recursive=False) random_nested_rows = random_nested_tbody.find_all('tr', recursive=False) for nested_row in random_nested_rows: if 'Generation' in nested_row.get_text(strip=True): continue games_container_td = nested_row.find('td', recursive=False) if not games_container_td: continue games_container_table = games_container_td.find('table', recursive=False) if not games_container_table: continue games_container_tbody = games_container_table.find('tbody', recursive=False) games_container_rows = games_container_tbody.find_all('tr', recursive=False) for games_container_row in games_container_rows: games = games_container_row.find_all('th') for game in games: raw_game = game.get_text(strip=True) if raw_game not in all_games: continue locations_container_td = games_container_row.find('td', recursive=False) if not locations_container_td: continue locations_container_table = locations_container_td.find('table', recursive=False) if not locations_container_table: continue locations_container_tbody = locations_container_table.find('tbody', recursive=False) locations = locations_container_tbody.find_all('td') for location in locations: groups = split_td_contents(location) for group in groups: if raw_game not in raw_game_locations: raw_game_locations[raw_game] = [] raw_game_locations[raw_game].append(group) events_section = soup.find('span', id='In_events') event_tables = {} if events_section: event_header = events_section.parent variant = "" for sibling in event_header.find_next_siblings(): if sibling.name == 'h4' or "held" in sibling.getText(strip=True).lower(): break if sibling.name == 'h5': variant = sibling.get_text(strip=True) if sibling.name == 'table': event_tables[variant] = sibling game_locations = {} for raw_game, raw_locations in raw_game_locations.items(): if form is None: for raw_location in raw_locations: locations = raw_location.get_text().split(',') for location in locations: if raw_game not in game_locations: game_locations[raw_game] = [] game_locations[raw_game].append(location.strip()) else: for raw_location in raw_locations: main_form, sub_form = parse_form_information(str(raw_location)) if main_form == form: locations = raw_location.get_text().split(',') for location in locations: if raw_game not in game_locations: game_locations[raw_game] = [] game_locations[raw_game].append(location.strip()) # For Later for variant in event_tables: if (variant == pokemon_name and form is None)or (form and form in variant): games_container_rows = event_tables[variant].find_all('tr') for game_row in games_container_rows: entries = game_row.find_all('td') if len(entries) > 1: games_string = entries[0].find('a').get('title') for game in all_games: if game in games_string: if game not in game_locations: game_locations[game] = [] game_locations[game].append("Event") return game_locations def get_earliest_game(encounter_data, pokemon_name, form): if not encounter_data: return "Unknown", "Unknown" non_catchable_methods = ["trade", "event", "global link", "poké transfer", "time capsule", "unobtainable", "pokémon home"] game_methods = {} for game, locations in encounter_data.items(): for location in locations: method = "Catchable" for non_catchable in non_catchable_methods: if non_catchable in location.lower(): method = None break if method is None: continue if "first partner" in location.lower(): method = "Starter" elif "received" in location.lower(): method = "Gift" elif "evolve" in location.lower(): method = "Evolve" else: method = "Catchable" if method: if game not in game_methods: game_methods[game.lower()] = method else: if method == "Catchable": game_methods[game.lower()] = method for game in all_games: if game.lower() in game_methods: return game, game_methods[game.lower()] return "Unknown", "Unknown" def determine_earliest_games(pokemon_list, cache): for pokemon in big_pokemon_list: print(f"Processing {pokemon}") encounter_data = get_locations_from_bulbapedia(pokemon.name, pokemon.form, cache) for encounter in encounter_data: encounter_information = EncounterInformation(encounter, encounter_data[encounter]) pokemon.encounter_information.append(encounter_information) pokemon.determine_earliest_game() print(f"Processed {pokemon}: {pokemon.earliest_game.game} ({pokemon.earliest_game.method})") for pokemon in pokemon_list: print(f"Processing {pokemon['name']} (#{pokemon['number']})") encounter_data = get_locations_from_bulbapedia(pokemon['base_name'], pokemon['form'], cache) pokemon['earliest_game'], pokemon['obtain_method'] = get_earliest_game(encounter_data, pokemon['base_name'], pokemon['form']) print(f"Processed {pokemon['name']} (#{pokemon['number']}): {pokemon['earliest_game']} ({pokemon['obtain_method']})") #pokemon_data = get_pokemon_data(pokemon['base_name'], pokemon['form'], cache) #encounter_data = get_pokemon_encounter_data(pokemon['base_name'], pokemon['form'], cache) #pokemon['earliest_game'], pokemon['obtain_method'] = get_earliest_game(encounter_data) #print(f"Processed {pokemon['name']} (#{pokemon['number']}): {pokemon['earliest_game']} ({pokemon['obtain_method']})") return pokemon_list def get_species_data(pokemon_name, cache): cache_key = f"species_{pokemon_name}" if cache_key in cache: return cache[cache_key] api_name = sanitize_name_and_form(pokemon_name, None) url = f"https://pokeapi.co/api/v2/pokemon-species/{api_name}/" print(f"Fetching species data for {pokemon_name}: {url}") response = requests.get(url) if response.status_code == 200: data = response.json() update_cache(cache_key, data) return data return None def get_evolution_chain(pokemon_name, cache): species_data = get_species_data(pokemon_name, cache) if not species_data: return None cache_key = f"evolution_{species_data['evolution_chain']['url']}" if cache_key in cache: return cache[cache_key] evolution_response = requests.get(species_data['evolution_chain']['url']) if evolution_response.status_code == 200: evolution_data = evolution_response.json() update_cache(cache_key, evolution_data) return evolution_data return None def get_base_form(evolution_chain:List[EvolutionStage]): if not evolution_chain: return None for stage in evolution_chain: if stage.stage == "Unevolved": return stage.pokemon if stage.is_baby: return stage.next_stage.pokemon return None #current = evolution_chain['chain'] #while current: # species_name = current['species']['name'] # species_data = get_species_data(species_name, cache) # # if species_data and not species_data.get('is_baby', False): # return species_name # # if not current['evolves_to']: # return species_name # # current = current['evolves_to'][0] return None def adjust_for_evolution(pokemon_list, cache): pokemon_dict = {f"{pokemon['base_name']}_{pokemon['form']}".lower(): pokemon for pokemon in pokemon_list} for pokemon in pokemon_list: evolution_chain = get_evolution_data_from_bulbapedia(pokemon['base_name'], pokemon['form'], cache) if evolution_chain: if evolution_chain[0].is_baby: pokemon['obtain_method'] = 'Breed' else: base_form = get_base_form(evolution_chain) base_key = f"{base_form}_{pokemon['form']}".lower() if base_key in pokemon_dict: base_pokemon = pokemon_dict[base_key] if all_games.index(base_pokemon['earliest_game']) <= all_games.index(pokemon['earliest_game']) and base_pokemon['number'] != pokemon['number']: pokemon['earliest_game'] = base_pokemon['earliest_game'] pokemon['obtain_method'] = 'Evolve' #species_data = get_species_data(pokemon['base_name'], cache) #evolution_chain = get_evolution_chain(pokemon['base_name'], cache) #base_form = get_base_form(evolution_chain, cache) # Check if the Pokémon is a baby #if species_data and species_data.get('is_baby', False): # pokemon['obtain_method'] = 'Breed' #elif base_form: # base_key = f"{base_form}_{pokemon['form']}".lower() # if base_key in pokemon_dict: # base_pokemon = pokemon_dict[base_key] # if all_games.index(base_pokemon['earliest_game']) <= all_games.index(pokemon['earliest_game']) and base_pokemon['number'] != pokemon['number']: # pokemon['earliest_game'] = base_pokemon['earliest_game'] # pokemon['obtain_method'] = 'Evolve' print(f"Adjusted {pokemon['name']} (#{pokemon['number']}): {pokemon['earliest_game']} ({pokemon['obtain_method']})") return pokemon_list def save_to_csv(pokemon_list, filename='pokemon_earliest_games.csv'): with open(filename, 'w', newline='', encoding='utf-8') as csvfile: fieldnames = ['number', 'name', 'earliest_game', 'obtain_method', 'encounter_locations'] writer = csv.DictWriter(csvfile, fieldnames=fieldnames) writer.writeheader() for pokemon in pokemon_list: writer.writerow({ 'number': pokemon['number'], 'name': pokemon['name'], 'earliest_game': pokemon['earliest_game'], 'obtain_method': pokemon['obtain_method'], 'encounter_locations': pokemon['encounter_locations'] }) def parse_encounter_locations(encounter_data, game): locations = [] for location_area in encounter_data: for version_detail in location_area['version_details']: if version_detail['version']['name'] == game.lower(): location_name = location_area['location_area']['name'] for encounter_detail in version_detail['encounter_details']: method = encounter_detail['method']['name'] condition = encounter_detail.get('condition', 'Any') time = ', '.join(encounter_detail.get('time', ['Any'])) encounter_info = f"{location_name} ({method}" if condition != 'Any': encounter_info += f", {condition}" if time != 'Any': encounter_info += f", {time}" encounter_info += ")" if encounter_info not in locations: locations.append(encounter_info) return locations def add_encounter_locations(pokemon_list, cache): for pokemon in pokemon_list: if pokemon['obtain_method'] == 'Catchable': encounter_data = get_pokemon_encounter_data(pokemon['base_name'], pokemon['form'], cache) locations = parse_encounter_locations(encounter_data, pokemon['earliest_game']) pokemon['encounter_locations'] = ' | '.join(locations) if locations else 'Unknown' else: pokemon['encounter_locations'] = 'N/A' print(f"Added encounter locations for {pokemon['name']} (#{pokemon['number']}) in {pokemon['earliest_game']}") return pokemon_list def get_marriland_page(pokemon_name, cache): url_name = pokemon_name.lower().replace(' ', '-').replace('(', '').replace(')', '') cache_key = f"marriland_{url_name}" if cache_key in cache: return cache[cache_key] url = f"https://marriland.com/pokedex/{url_name}/" try: response = requests.get(url) response.raise_for_status() # Raise an exception for bad status codes data = response.text update_cache(cache_key, data) return data except requests.RequestException as e: print(f"Error accessing the page for {pokemon_name}: {e}") return None def is_event_pokemon(pokemon_name, cache): page_data = get_marriland_page(pokemon_name, cache) if not page_data: return False soup = BeautifulSoup(page_data, 'html.parser') # Find the "Where to Find" section location_section = soup.find('div', id='locations') if not location_section: print(f"Could not find 'Where to Find' section for {pokemon_name}") return None special_section = soup.find('div', class_='location-special') location_tables = soup.find_all('table', class_='location-table') event_only = "Only available from events or promotions.".lower() if len(location_tables) == 0 and special_section and event_only in special_section.get_text(strip=True).lower(): return True return False def check_alternative_sources(pokemon, cache): # This function will check alternative sources for Pokémon with "Unknown" encounter types species_data = get_species_data(pokemon['base_name'], cache) if species_data: # Check if it's a mythical Pokémon if species_data.get('is_mythical', False): return "Event", "Event" # Check if it's a legendary Pokémon if species_data.get('is_legendary', False): return pokemon['earliest_game'], "Legendary" event_status = is_event_pokemon(pokemon['name'], cache) if event_status: return "Event", "Event" #bulb_locations = get_locations_from_bulbapedia(pokemon['base_name'], pokemon['form'], cache) #if bulb_locations: # return bulb_locations[0], "Bulbapedia" # Check generation introduced #generation = species_data.get('generation', {}).get('name', '') #if generation: # gen_number = int(generation.split('-')[1]) # for game in all_games: # if game != "Unknown" and get_generation(game) == gen_number: # return game, "First appearance" return "Unknown", "Unknown" def handle_unknown_encounters(pokemon_list, cache): for pokemon in pokemon_list: if pokemon['earliest_game'] == "Unknown" or pokemon['obtain_method'] == "Unknown": new_game, new_method = check_alternative_sources(pokemon, cache) if new_game != "Unknown": pokemon['earliest_game'] = new_game pokemon['obtain_method'] = new_method pokemon['encounter_locations'] = 'N/A' print(f"Checked alternative sources for {pokemon['name']} (#{pokemon['number']}): {pokemon['earliest_game']} ({pokemon['obtain_method']})") return pokemon_list # Update the main function if __name__ == "__main__": get_cached_data() pokemon_list = read_pokemon_list('pokemon_home_list.csv', limit=200) pokemon_list_with_games = determine_earliest_games(pokemon_list, cache) pokemon_list_adjusted = adjust_for_evolution(pokemon_list_with_games, cache) pokemon_list_with_locations = add_encounter_locations(pokemon_list_adjusted, cache) pokemon_list_final = handle_unknown_encounters(pokemon_list_with_locations, cache) save_to_csv(pokemon_list_final) save_cached_data() # Save any remaining new entries conn.close() # Close the database connection print(f"Earliest obtainable games and encounter locations determined for {len(pokemon_list)} Pokémon and saved to pokemon_earliest_games.csv")