diff --git a/database/db_controller.py b/database/db_controller.py index 3b42f20..e2c0e0c 100644 --- a/database/db_controller.py +++ b/database/db_controller.py @@ -87,6 +87,7 @@ class DBController: name TEXT NOT NULL UNIQUE, alt_names TEXT, generation INTEGER NOT NULL, + mark TEXT NOT NULL, data JSON ) ''') @@ -95,11 +96,12 @@ class DBController: name = game["Name"] alt_names = ", ".join(game["AltNames"]) # Convert list to comma-separated string generation = game["Generation"] + mark = game["Mark"] cursor.execute(''' - INSERT OR IGNORE INTO games (name, alt_names, generation) - VALUES (?, ?, ?) - ''', (name, alt_names, generation)) + INSERT OR IGNORE INTO games (name, alt_names, generation, mark) + VALUES (?, ?, ?, ?) + ''', (name, alt_names, generation, mark)) def add_pokemon_form(self, pfic, name, form_name, national_dex, generation, sprite_url, gender_relevant): data = { @@ -441,7 +443,7 @@ class DBController: self.cursor.execute(''' SELECT * FROM games WHERE id = ? - ''', (id)) + ''', (id,)) # Fetch and print the results result = self.cursor.fetchone() @@ -474,11 +476,15 @@ class DBController: print(f"Added: {pfic}") pass - def get_encounters(self, pfic): - self.cursor.execute(''' + def get_encounters(self, pfic, type=None): + query = ''' SELECT * FROM encounters WHERE PFIC = ? - ''', (pfic)) + ''' + if type: + query += f"AND type = '{type}'" + + self.cursor.execute(query, (pfic,)) # Fetch and print the results results = self.cursor.fetchall() diff --git a/ui/workers/calculate_origin_mark_worker.py b/ui/workers/calculate_origin_mark_worker.py index 4cdaf40..68b1011 100644 --- a/ui/workers/calculate_origin_mark_worker.py +++ b/ui/workers/calculate_origin_mark_worker.py @@ -1,4 +1,5 @@ from PyQt6.QtCore import QObject, pyqtSignal, QRunnable +import json from cache import cache from db import db @@ -35,8 +36,9 @@ class CalculateOriginMarkWorker(QRunnable): #Rule 1 # 1. If a pokemon form has a previous evolution from within the same generation, # use the mark of the previous evolution. This should be recursive within the same generation. + print("Checking Rule 1") chain = db.get_full_evolution_paths(pfic) - if chain: + if chain and (len(chain["predecessors"]) > 0 or len(chain["successors"]) > 0): base_form_in_generation = None last_pfic = pfic current_pfic = pfic @@ -46,7 +48,6 @@ class CalculateOriginMarkWorker(QRunnable): base_form_in_generation = last_pfic break chain_pokemon_data = db.get_pokemon_details(current_pfic) - #chain_pokemon_data = event_system.call_sync('get_pokemon_form_by_pfic', current_pfic[0]) if chain_pokemon_data["generation"] == target_generation: base_form_in_generation = current_pfic else: @@ -58,44 +59,81 @@ class CalculateOriginMarkWorker(QRunnable): print(f"Base form in generation for {get_display_name(form_entry)} is {base_form_in_generation}") mark_id = self.determine_origin_mark(base_form_in_generation, target_generation) if mark_id != None: - #event_system.emit_sync('assign_mark_to_form', (pfic, mark_id)) self.marks[pfic] = mark_id continue elif base_form_in_generation == pfic: mark_id = self.determine_origin_mark(pfic, target_generation) if mark_id != None: - #event_system.emit_sync('assign_mark_to_form', (pfic, mark_id)) self.marks[pfic] = mark_id - continue; + continue + + #Rule 2 + # If a pokemon form has no previous evolution from within the same generation, + # look at the encounters of the pokemon form from this generation and use the mark of the earliest + # game you can encounter that form in from that generation + print("Checking Rule 2") + mark_id = self.determine_origin_mark(pfic, target_generation) + if mark_id != None: + self.marks[pfic] = mark_id + continue + #Rule 3 + # If there are no encounters for the pokemon form from this generation, + # look to see if a previous evolution has an encounter from this generation, and use the mark of the earliest + # game from this generation that the previous evolution is encounterable in. + print("Checking Rule 3") + mark_id = self.test_evolve_encounters(pfic, target_generation) + if mark_id != None: + self.marks[pfic] = mark_id + continue pass def determine_origin_mark(self, pfic, target_generation): shiftable_forms = get_shiftable_forms(pfic) if len(shiftable_forms) > 0: for shiftable_form in shiftable_forms: - mark_id = self.determine_origin_mark(shiftable_form[2], target_generation) + mark_id = self.determine_origin_mark(shiftable_form["to_pfic"], target_generation) return mark_id encounters = db.get_encounters(pfic) if encounters: generation_encounters = [] for encounter in encounters: game_info = db.get_game_by_id(encounter["game_id"]) - game_generation = game_info["generation"] - game_id = game_info["id"] - encounter = encounter + (game_generation, game_id) - if game_generation == target_generation: + encounter["game"] = game_info + if encounter["game"]["generation"] == target_generation: generation_encounters.append(encounter) if len(generation_encounters) > 0: - generation_encounters = sorted(generation_encounters, key=lambda x: x[12]) + generation_encounters = sorted(generation_encounters, key=lambda x: x["game"]["generation"]) form_info = db.get_pokemon_details(pfic) - game_info = db.get_game_by_id(encounter["game_id"]) - mark_id = 1 # TODO: Work this bit out. - mark_id = event_system.call_sync('get_mark_for_game_name', generation_encounters[0][0]) + game_info = generation_encounters[0]["game"] + mark_id = game_info["mark"] if mark_id == None: - self.logger.info(f"No mark found for {form_info[0]} {form_info[1]}") + #self.logger.info(f"No mark found for {form_info[0]} {form_info[1]}") + print(f"No mark found for {get_display_name(form_info)}") else: - mark_details = event_system.call_sync('get_mark_details', mark_id) - self.logger.info(f"Mark for {form_info[0]} {form_info[1]} is {mark_details[0]} - {mark_details[1]}") + #self.logger.info(f"Mark for {form_info[0]} {form_info[1]} is {mark_id}") + print(f"Mark for {get_display_name(form_info)} is {mark_id}") + return mark_id + return None + + def test_evolve_encounters(self, pfic, target_generation): + evolve_encounters = db.get_encounters(pfic, "evolve") + if evolve_encounters: + available_encounters = [] + for encounter in evolve_encounters: + game_info = db.get_game_by_id(encounter["game_id"]) + if game_info["generation"] == target_generation: + available_encounters.append(encounter) + + if len(available_encounters) > 0: + available_encounters = sorted(available_encounters, key=lambda x: x.game_id) + data = json.loads(available_encounters[0]["data"]) + mark_id = self.determine_origin_mark(data["from_pfic"], target_generation) + if mark_id != None: + return mark_id + + mark_id = self.test_evolve_encounters(data["from_pfic"], target_generation) + if mark_id != None: return mark_id + return None diff --git a/ui/workers/gather_encounter_locations.py b/ui/workers/gather_encounter_locations.py index ca2159e..ef6812d 100644 --- a/ui/workers/gather_encounter_locations.py +++ b/ui/workers/gather_encounter_locations.py @@ -9,6 +9,7 @@ from db import db from utility.data import default_forms, regional_descriptors, days, times, rods from utility.functions import is_mainline_game, compare_pokemon_forms, find_match_in_string_array, extract_bracketed_text +from utility.pokemon_word_ninja import PokemonWordNinja class GatherEncountersWorkerSignals(QObject): finished = pyqtSignal(list) @@ -18,6 +19,7 @@ class GatherEncountersWorker(QRunnable): super().__init__() self.signals = GatherEncountersWorkerSignals() self.default_forms_set = set(default_forms) + self.splitter = PokemonWordNinja() self.encounters_to_ignore = [ "trade", "time capsule", @@ -53,6 +55,7 @@ class GatherEncountersWorker(QRunnable): pfic = form_entry["pfic"] print(f'Processing {name}') + self.splitter.add_custom_word(name) if form and name in form: form = form.replace(name, "").strip() @@ -101,7 +104,7 @@ class GatherEncountersWorker(QRunnable): test_location_text = BeautifulSoup(test_location, 'html.parser').get_text().lower() if "evolve" in test_location_text: remaining, details = self.extract_additional_information(location["tag"]) - evolve_info = self.extract_evolve_information(remaining) + evolve_info = self.extract_evolve_information(remaining, form_entry["form_name"]) if evolve_info: #logger.info(f"Evolve Info: {evolve_info}") @@ -521,7 +524,7 @@ class GatherEncountersWorker(QRunnable): else: return full_text, details - def extract_evolve_information(self, s: str): + def extract_evolve_information(self, s: str, search_form): details = {} if s is None or s == "": return details @@ -549,6 +552,11 @@ class GatherEncountersWorker(QRunnable): if compare_pokemon_forms(result["form_name"], form): details["evolve_from"] = result["pfic"] + if results and "evolve_from" not in details: + for result in results: + if compare_pokemon_forms(result["form_name"], search_form if search_form != form else "Male"): + details["evolve_from"] = result["pfic"] + return details def save_evolve_encounter(self, pfic, game, days, times, from_pfic): diff --git a/ui/workers/gather_evolutions_worker.py b/ui/workers/gather_evolutions_worker.py index 9dfbd11..f51ec19 100644 --- a/ui/workers/gather_evolutions_worker.py +++ b/ui/workers/gather_evolutions_worker.py @@ -2,6 +2,7 @@ from typing import Optional from PyQt6.QtCore import QObject, pyqtSignal, QRunnable from bs4 import BeautifulSoup, Tag from fuzzywuzzy import fuzz +from number_parser import parse_ordinal from cache import cache from db import db @@ -140,16 +141,24 @@ class GatherEvolutions(QRunnable): main_row = rows[0] branch_rows = rows[1:] - def create_stage(td, current_stage_number): + def create_stage(td): pokemon_name = self.extract_pokemon_name(td) evolution_form = self.extract_evolution_form(td, pokemon_name) + stage = self.extract_stage_form(td).replace("Evolution", "").replace("evolution", "").strip() + numberical_stage = -1 + if stage == "Unevolved" or stage == "Baby form": + numberical_stage = 0 + elif stage == "Castoff": + numberical_stage = 1 + else: + numberical_stage = parse_ordinal(stage) return { "pokemon": pokemon_name, "form": evolution_form, "requirement": None, "method": None, "evolves_to": [], - "stage": current_stage_number + "stage": numberical_stage } # Parse main evolution chain @@ -157,11 +166,10 @@ class GatherEvolutions(QRunnable): pending_method_form = None root = None current_stage = None - stage_number = 0 for td in main_row.find_all('td', recursive=False): if td.find('table'): - new_stage = create_stage(td, stage_number) + new_stage = create_stage(td) new_stage["method"] = pending_method new_stage["requirement"] = pending_method_form pending_method = None @@ -170,41 +178,36 @@ class GatherEvolutions(QRunnable): if current_stage: current_stage["evolves_to"].append(new_stage) current_stage = new_stage - stage_number += 1 else: pending_method, pending_method_form = self.extract_evolution_method(td) - # reduce by one to account for an accidental increase by the last one in the chain. - stage_number -= 1 - # Parse branching evolutions for row in branch_rows: branch_method = None pending_method_form = None branch_stage = None - branch_stage_number = stage_number for td in row.find_all('td', recursive=False): if td.find('table'): - new_stage = create_stage(td, branch_stage_number) + new_stage = create_stage(td) new_stage["method"] = branch_method new_stage["requirement"] = pending_method_form branch_method = None if branch_stage: branch_stage["evolves_to"].append(new_stage) + else: + # Find which main chain Pokémon this branch evolves from + attached = False + for main_stage in self.find_stages(root): + if self.should_attach_branch(main_stage, new_stage): + main_stage["evolves_to"].append(new_stage) + attached = True + break + + if not attached: + print(f"Warning: Could not find a suitable attachment point for branch {new_stage['pokemon']}") branch_stage = new_stage - - # Find which main chain Pokémon this branch evolves from - attached = False - for main_stage in self.find_stages(root): - if self.should_attach_branch(main_stage, branch_stage): - main_stage["evolves_to"].append(branch_stage) - attached = True - break - - if not attached: - print(f"Warning: Could not find a suitable attachment point for branch {branch_stage['pokemon']}") else: branch_method, pending_method_form = self.extract_evolution_method(td) diff --git a/utility/data.py b/utility/data.py index ed88ba4..db09dd9 100644 --- a/utility/data.py +++ b/utility/data.py @@ -16,223 +16,260 @@ regional_descriptors = ["Kantonian", "Johtonian", "Hoennian", "Sinnohan", "Unova yellow = { "Name": "Yellow", "AltNames": ["Pokemon Yellow", "Pokémon Yellow", "Y"], - "Generation": 1 + "Generation": 1, + "Mark": "Game Boy" } red = { "Name": "Red", "AltNames": ["Pokemon Red", "Pokémon Red", "R"], - "Generation": 1 + "Generation": 1, + "Mark": "Game Boy" } blue = { "Name": "Blue", "AltNames": ["Pokemon Blue", "Pokémon Blue", "B"], - "Generation": 1 + "Generation": 1, + "Mark": "Game Boy" } crystal = { "Name": "Crystal", "AltNames": ["Pokemon Crystal", "Pokémon Crystal", "C"], - "Generation": 2 + "Generation": 2, + "Mark": "Game Boy" } gold = { "Name": "Gold", "AltNames": ["Pokemon Gold", "Pokémon Gold", "G"], - "Generation": 2 + "Generation": 2, + "Mark": "Game Boy" } silver = { "Name": "Silver", "AltNames": ["Pokemon Silver", "Pokémon Silver", "S"], - "Generation": 2 + "Generation": 2, + "Mark": "Game Boy" } emerald = { "Name": "Emerald", "AltNames": ["Pokemon Emerald", "Pokémon Emerald", "E"], - "Generation": 3 + "Generation": 3, + "Mark": "Markless" } fire_red = { "Name": "FireRed", "AltNames": ["Pokemon FireRed", "Pokémon FireRed", "FR"], - "Generation": 3 + "Generation": 3, + "Mark": "Markless" } leaf_green = { "Name": "LeafGreen", "AltNames": ["Pokemon LeafGreen", "Pokémon LeafGreen", "LG"], - "Generation": 3 + "Generation": 3, + "Mark": "Markless" } ruby = { "Name": "Ruby", "AltNames": ["Pokemon Ruby", "Pokémon Ruby", "R"], - "Generation": 3 + "Generation": 3, + "Mark": "Markless" } sapphire = { "Name": "Sapphire", "AltNames": ["Pokemon Sapphire", "Pokémon Sapphire", "S"], - "Generation": 3 + "Generation": 3, + "Mark": "Markless" } platinum = { "Name": "Platinum", "AltNames": ["Pokemon Platinum", "Pokémon Platinum", "Pt"], - "Generation": 4 + "Generation": 4, + "Mark": "Markless" } heart_gold = { "Name": "HeartGold", "AltNames": ["Pokemon HeartGold", "Pokémon HeartGold", "HG"], - "Generation": 4 + "Generation": 4, + "Mark": "Markless" } soul_silver = { "Name": "SoulSilver", "AltNames": ["Pokemon SoulSilver", "Pokémon SoulSilver", "SS"], - "Generation": 4 + "Generation": 4, + "Mark": "Markless" } diamond = { "Name": "Diamond", "AltNames": ["Pokemon Diamond", "Pokémon Diamond", "D"], - "Generation": 4 + "Generation": 4, + "Mark": "Markless" } pearl = { "Name": "Pearl", "AltNames": ["Pokemon Pearl", "Pokémon Pearl", "P"], - "Generation": 4 + "Generation": 4, + "Mark": "Markless" } black = { "Name": "Black", "AltNames": ["Pokemon Black", "Pokémon Black", "B"], - "Generation": 5 + "Generation": 5, + "Mark": "Markless" } white = { "Name": "White", "AltNames": ["Pokemon White", "Pokémon White", "W"], - "Generation": 5 + "Generation": 5, + "Mark": "Markless" } black_2 = { "Name": "Black 2", "AltNames": ["Pokemon Black 2", "Pokémon Black 2", "B2"], - "Generation": 5 + "Generation": 5, + "Mark": "Markless" } white_2 = { "Name": "White 2", "AltNames": ["Pokemon White 2", "Pokémon White 2", "W2"], - "Generation": 5 + "Generation": 5, + "Mark": "Markless" } x = { "Name": "X", "AltNames": ["Pokemon X", "Pokémon X"], - "Generation": 6 + "Generation": 6, + "Mark": "Kalos" } y = { "Name": "Y", "AltNames": ["Pokemon Y", "Pokémon Y"], - "Generation": 6 + "Generation": 6, + "Mark": "Kalos" } omega_ruby = { "Name": "Omega Ruby", "AltNames": ["Pokemon Omega Ruby", "Pokémon Omega Ruby", "OR"], - "Generation": 6 + "Generation": 6, + "Mark": "Kalos" } alpha_sapphire = { "Name": "Alpha Sapphire", "AltNames": ["Pokemon Alpha Sapphire", "Pokémon Alpha Sapphire", "AS"], - "Generation": 6 + "Generation": 6, + "Mark": "Kalos" } sun = { "Name": "Sun", "AltNames": ["Pokemon Sun", "Pokémon Sun"], - "Generation": 7 + "Generation": 7, + "Mark": "Alola" } moon = { "Name": "Moon", "AltNames": ["Pokemon Moon", "Pokémon Moon"], - "Generation": 7 + "Generation": 7, + "Mark": "Alola" } ultra_sun = { "Name": "Ultra Sun", "AltNames": ["Pokemon Ultra Sun", "Pokémon Ultra Sun", "US"], - "Generation": 7 + "Generation": 7, + "Mark": "Alola" } ultra_moon = { "Name": "Ultra Moon", "AltNames": ["Pokemon Ultra Moon", "Pokémon Ultra Moon", "UM"], - "Generation": 7 + "Generation": 7, + "Mark": "Alola" } sword = { "Name": "Sword", "AltNames": ["Pokemon Sword", "Pokémon Sword", "Expansion Pass", "Expansion Pass (Sword)"], - "Generation": 8 + "Generation": 8, + "Mark": "Galar" } shield = { "Name": "Shield", "AltNames": ["Pokemon Shield", "Pokémon Shield", "Expansion Pass", "Expansion Pass (Shield)"], - "Generation": 8 + "Generation": 8, + "Mark": "Galar" } brilliant_diamond = { "Name": "Brilliant Diamond", "AltNames": ["Pokemon Brilliant Diamond", "Pokémon Brilliant Diamond", "BD"], - "Generation": 8 + "Generation": 8, + "Mark": "Sinnoh" } shining_pearl = { "Name": "Shining Pearl", "AltNames": ["Pokemon Shining Pearl", "Pokémon Shining Pearl", "SP"], - "Generation": 8 + "Generation": 8, + "Mark": "Sinnoh" } legends_arceus = { "Name": "Legends: Arceus", "AltNames": ["Pokemon Legends: Arceus", "Pokémon Legends: Arceus", "LA", "Legends Arceus", "Arceus"], - "Generation": 8 + "Generation": 8, + "Mark": "Hisui" } scarlet = { "Name": "Scarlet", "AltNames": ["Pokemon Scarlet", "Pokémon Scarlet", "The Hidden Treasure of Area Zero", "The Hidden Treasure of Area Zero (Scarlet)", "The Teal Mask", "The Teal Mask (Scarlet)"], - "Generation": 9 + "Generation": 9, + "Mark": "Paldea" } violet = { "Name": "Violet", "AltNames": ["Pokemon Violet", "Pokémon Violet", "The Hidden Treasure of Area Zero", "The Hidden Treasure of Area Zero (Violet)", "The Teal Mask", "The Teal Mask (Violet)"], - "Generation": 9 + "Generation": 9, + "Mark": "Paldea" } lets_go_pikachu = { "Name": "Lets Go Pikachu", "AltNames": [], - "Generation": 8 + "Generation": 8, + "Mark": "Let's Go" } lets_go_eevee = { "Name": "Lets Go Eevee", "AltNames": [], - "Generation": 8 + "Generation": 8, + "Mark": "Let's Go" } main_line_games = [