From f7cfa89287b84e3a07677dd30ef3078a8c0bbb81 Mon Sep 17 00:00:00 2001 From: Quildra Date: Wed, 6 Nov 2024 22:00:25 +0000 Subject: [PATCH] WIP new way of extracting the node an edge work for evolutions --- database/db_controller.py | 31 ++++---- ui/workers/gather_evolutions_worker.py | 106 ++++++++++++------------- 2 files changed, 67 insertions(+), 70 deletions(-) diff --git a/database/db_controller.py b/database/db_controller.py index 0eca676..246be21 100644 --- a/database/db_controller.py +++ b/database/db_controller.py @@ -164,10 +164,11 @@ class DBController: self.conn.commit() def update_evolution_graph(self, evolutions): - for evolution in evolutions: - from_pfic = evolution["from_pfic"] - to_pfic = evolution["to_pfic"] - method = evolution["method"] + for key in evolutions: + value = evolutions[key] + from_pfic = value["from_pfic"] + to_pfic = value["to_pfic"] + method = value["method"] # Add nodes if they do not already exist if not self.graph.has_node(from_pfic): @@ -184,15 +185,15 @@ class DBController: def get_evolution_paths(self, start_node): paths = [] - + # Define a recursive function to traverse the graph def traverse(current_node, current_path): - # Add the current node to the path - current_path.append(current_node) - + # Add the current node to the path as a tuple (node, None) + current_path.append((current_node, None)) + # Get successors of the current node successors = list(self.graph.successors(current_node)) - + if not successors: # If there are no successors, add the current path to paths list paths.append(current_path.copy()) @@ -200,17 +201,19 @@ class DBController: # Traverse each successor and add edge metadata for successor in successors: method = self.graph[current_node][successor]["method"] - # Add the edge metadata as a tuple (to_node, method) + # Add the successor node and method as a tuple (successor, method) current_path.append((successor, method)) - + # Recur for the successor traverse(successor, current_path) - + # Backtrack (remove the last node and edge metadata) current_path.pop() - current_path.pop() + + # Remove the initial node tuple when backtracking fully + current_path.pop() # Start traversal from the start_node traverse(start_node, []) - + return paths diff --git a/ui/workers/gather_evolutions_worker.py b/ui/workers/gather_evolutions_worker.py index 712e424..5743064 100644 --- a/ui/workers/gather_evolutions_worker.py +++ b/ui/workers/gather_evolutions_worker.py @@ -11,7 +11,7 @@ from utility.functions import get_form_name, get_display_name, parse_pfic from utility.data import non_evolution_forms class GatherEvolutionsWorkerSignals(QObject): - finished = pyqtSignal(list) + finished = pyqtSignal(dict) class GatherEvolutions(QRunnable): def __init__(self): @@ -19,6 +19,8 @@ class GatherEvolutions(QRunnable): self.signals = GatherEvolutionsWorkerSignals() self.base_url = "https://bulbapedia.bulbagarden.net/wiki/" + self.evolution_methods = set() + def run(self): try: gathered_data = self.gather_evolution_data() @@ -28,7 +30,6 @@ class GatherEvolutions(QRunnable): def gather_evolution_data(self, force_refresh = True): all_pokemon_forms = db.get_list_of_pokemon_forms() - #evolutions = [] evolutions = {} for pokemon_form in all_pokemon_forms: @@ -90,55 +91,15 @@ class GatherEvolutions(QRunnable): evolution_tree = None if pokemon_name == "Eevee": evolution_tree = self.parse_eevee_evolution_chain(evolution_table, pokemon_form) - #evolutions.append(evolution_chain) else: evolution_tree = self.parse_evolution_chain(evolution_table, pokemon_form) - #evolutions.append(evolution_chain) if evolution_tree: self.traverse_and_store(evolution_tree, evolutions, gender) - chain = [] - for pokemon in evolution_chain: - from_pfic = self.get_pokemon_form_by_name(pokemon["pokemon"], pokemon["form"], gender=gender) - if not from_pfic: - #logger.warning(f"Could not find PFIC for {stage.pokemon} {stage.form}") - continue - - stage = pokemon["next_stage"] - if stage: - to_pfic = self.get_pokemon_form_by_name(stage["pokemon"], stage["form"], gender=gender) - if to_pfic: - evolution_info = { - "from_pfic": from_pfic, - "to_pfic": to_pfic, - "method": stage["method"] - } - evolutions[pokemon_form["pfic"]] = evolution_info - chain.append(evolution_info) - - #insert_evolution_info(evolution_info) - - #if "breed" in stage["next_stage"]["method"].lower(): - # update_pokemon_baby_status(from_pfic, True) - - for branch in pokemon["branches"]: - to_pfic = self.get_pokemon_form_by_name(branch["pokemon"], branch["form"], gender=gender) - if to_pfic: - evolution_info = { - "from_pfic": from_pfic, - "to_pfic": to_pfic, - "method": branch["method"] - } - evolutions[pokemon_form["pfic"]] = evolution_info - chain.append(evolution_info) - #EvolutionInfo(from_pfic, to_pfic, branch.method) - #insert_evolution_info(evolution_info) - - #if "breed" in branch.method.lower(): - # update_pokemon_baby_status(from_pfic, True) - cache.set(cache_record_name, chain) + #cache.set(cache_record_name, chain) + print(self.evolution_methods) return evolutions def traverse_and_store(self, node, evolutions, gender): @@ -159,7 +120,7 @@ class GatherEvolutions(QRunnable): evolutions[composite_key] = (evolution_info) self.traverse_and_store(next_stage, evolutions, gender) - def parse_evolution_chain(self, table, pokemon_form, force_refresh = False): + def parse_evolution_chain(self, table, pokemon_form, force_refresh = True): cache_record_name = f"evo_{pokemon_form['pfic']}" if force_refresh: cache.purge(cache_record_name) @@ -178,59 +139,85 @@ class GatherEvolutions(QRunnable): main_row = rows[0] branch_rows = rows[1:] - def create_stage(td): + def create_stage(td, current_stage_number): pokemon_name = self.extract_pokemon_name(td) evolution_form = self.extract_evolution_form(td, pokemon_name) return { "pokemon": pokemon_name, "form": evolution_form, + "requirement": None, "method": None, - "evolves_to": [] + "evolves_to": [], + "stage": current_stage_number } # Parse main evolution chain pending_method = None + 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) + new_stage = create_stage(td, stage_number) new_stage["method"] = pending_method + new_stage["requirement"] = pending_method_form pending_method = None if root is None: root = new_stage # Assign the root node if current_stage: current_stage["evolves_to"].append(new_stage) current_stage = new_stage + stage_number += 1 else: - pending_method = self.extract_evolution_method(td) + 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) + new_stage = create_stage(td, branch_stage_number) new_stage["method"] = branch_method + new_stage["requirement"] = pending_method_form branch_method = None + if branch_stage: branch_stage["evolves_to"].append(new_stage) branch_stage = new_stage - # Find which main chain Pokémon this branches from + # Find which main chain Pokémon this branch evolves from + attached = False for main_stage in self.find_stages(root): - if td.get('rowspan') and main_stage["pokemon"] == new_stage["pokemon"]: + 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 = self.extract_evolution_method(td) + branch_method, pending_method_form = self.extract_evolution_method(td) cache.set(cache_record_name, root) return root + def should_attach_branch(self, main_stage, branch_stage): + # Ensure the main_stage is a valid node to attach a branch + if main_stage["stage"] == branch_stage["stage"] - 1: + return True + # You can add more logic to determine if branch_stage should connect to main_stage + # For instance, check if they are forms of the same evolution or based on other criteria + return False + def find_stages(self, node): """Helper function to find all stages in the evolution chain recursively.""" stages = [node] @@ -269,7 +256,14 @@ class GatherEvolutions(QRunnable): def extract_evolution_method(self, td: Tag) -> str: # Extract evolution method from the TD - return td.get_text(strip=True) + text = td.get_text() + form = None + if text and "(male)" in text.lower(): + form = "male" + elif text and "(female)" in text.lower(): + form = "female" + + return td.get_text(strip=True), form def parse_eevee_evolution_chain(self, table, pokemon_form): tbody = table.find('tbody', recursive=False) @@ -376,8 +370,8 @@ class GatherEvolutions(QRunnable): return entry["pfic"] # Some times we get a form for a pokemon that doesn't really have one. - if len(results) > 1 and form != None: - return results[0]["pfic"] + #if len(results) > 1 and form != None and gender and threshold != 100: + # return results[0]["pfic"] return None