From 107422679b1a257ff25be0a7a1f9e8d7273e8f5e Mon Sep 17 00:00:00 2001 From: Dan Date: Thu, 7 Nov 2024 14:07:43 +0000 Subject: [PATCH] - A good place for evolutions now. They are tracking in a node graph, and displayed correctly. --- database/db_controller.py | 110 +++++++++++++++++++++++-- ui/main_window_controller.py | 3 +- ui/main_window_view.py | 93 ++++++++++++++------- ui/workers/gather_evolutions_worker.py | 21 ++--- 4 files changed, 179 insertions(+), 48 deletions(-) diff --git a/database/db_controller.py b/database/db_controller.py index 246be21..62c4d32 100644 --- a/database/db_controller.py +++ b/database/db_controller.py @@ -181,15 +181,34 @@ class DBController: self.graph.add_edge(from_pfic, to_pfic, method=method) def get_evolution_graph(self, pfic): + if self.graph.has_node(pfic) == False: + return [] + return list(self.graph.successors(pfic)) + def get_previous_evolution(self, pfic): + if self.graph.has_node(pfic) == False: + return None, None + + predecessor = next(self.graph.predecessors(pfic), None) + + if predecessor: + method = self.graph[predecessor][pfic]["method"] + return predecessor, method + else: + return None, None + def get_evolution_paths(self, start_node): paths = [] + if self.graph.has_node(start_node) == False: + return paths + # Define a recursive function to traverse the graph - def traverse(current_node, current_path): - # Add the current node to the path as a tuple (node, None) - current_path.append((current_node, None)) + def traverse(current_node, current_path, is_root=False): + if is_root: + # 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)) @@ -211,9 +230,90 @@ class DBController: current_path.pop() # Remove the initial node tuple when backtracking fully - current_path.pop() + if is_root: + current_path.pop() # Start traversal from the start_node - traverse(start_node, []) + traverse(start_node, [], True) return paths + + def get_full_evolution_paths(self, start_node): + """ + Get all evolution paths starting from a given node, including predecessors and successors. + :param start_node: The starting node (e.g., a specific Pokemon form). + :return: A dictionary containing predecessors and successors paths. + """ + full_paths = { + "predecessors": [], + "successors": [] + } + + if self.graph.has_node(start_node) == False: + return full_paths + + # Traverse predecessors + def traverse_predecessors(current_node, current_path, is_root=False): + #if not is_root: + # Add the current node to the path + #current_path.append(current_node) + + # Get predecessors of the current node + predecessors = list(self.graph.predecessors(current_node)) + + if not predecessors: + # If there are no predecessors, add the current path to the list + full_paths["predecessors"].append(current_path.copy()) + else: + # Traverse each predecessor + for predecessor in predecessors: + method = self.graph[predecessor][current_node]["method"] + # Add the edge metadata as a tuple (predecessor, method) + current_path.append((predecessor, method)) + + # Recur for the predecessor + traverse_predecessors(predecessor, current_path) + + # Backtrack (remove the last node and edge metadata) + current_path.pop() + #current_path.pop() + + # Traverse successors + def traverse_successors(current_node, current_path, is_root=False): + if is_root: + # Add the current node to the path as a tuple (node, None) + predecessor = next(self.graph.predecessors(current_node), None) + if predecessor: + method = self.graph[predecessor][current_node]["method"] + current_path.append((current_node, method)) + else: + 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 + full_paths["successors"].append(current_path.copy()) + else: + # Traverse each successor and add edge metadata + for successor in successors: + method = self.graph[current_node][successor]["method"] + # Add the successor node and method as a tuple (successor, method) + current_path.append((successor, method)) + + # Recur for the successor + traverse_successors(successor, current_path) + + # Backtrack (remove the last node and edge metadata) + current_path.pop() + + if is_root: + # Remove the initial node tuple when backtracking fully + current_path.pop() + + # Start traversal from the start_node for both predecessors and successors + traverse_predecessors(start_node, [], True) + traverse_successors(start_node, [], True) + + return full_paths \ No newline at end of file diff --git a/ui/main_window_controller.py b/ui/main_window_controller.py index 8723d36..33099fb 100644 --- a/ui/main_window_controller.py +++ b/ui/main_window_controller.py @@ -174,5 +174,6 @@ class MainWindowController: self.current_pfic = pfic def load_evolution_chain(self, pfic): - chain = db.get_evolution_paths(pfic) + #chain = db.get_evolution_paths(pfic) + chain = db.get_full_evolution_paths(pfic) self.view.update_evolution_tree(chain, pfic) diff --git a/ui/main_window_view.py b/ui/main_window_view.py index fafdc6c..760f1ad 100644 --- a/ui/main_window_view.py +++ b/ui/main_window_view.py @@ -223,41 +223,70 @@ class PokemonUI(QWidget): item.setData(Qt.ItemDataRole.UserRole, pokemon["pfic"]) self.pokemon_list.addItem(item) - def update_evolution_tree(self, evolution_chain, selected_pfic): + def update_evolution_tree(self, evolution_chains, selected_pfic): + self.evolution_tree.clear() tree_items = {} - #for item in evolution_chain: - # print(item) - for pfic in evolution_chain: - pokemon_details = db.get_pokemon_details(pfic) - display_name = get_display_name(pokemon_details, not pokemon_details["gender_relevant"]) - item = QTreeWidgetItem([display_name, method if method else ""]) - item.setData(0, Qt.ItemDataRole.UserRole, current_pfic) - tree_items[current_pfic] = item - - if current_pfic == selected_pfic: - item.setBackground(0, QColor(255, 255, 0, 100)) # Highlight selected Pokémon - - # Second pass: build the tree structure - root = None - for current_pfic, name, form_name, method in evolution_chain: - item = tree_items[current_pfic] - - # Find the parent of this item - #parent_pfic = event_system.call_sync('get_evolution_parent', data=current_pfic) - parent_pfic = None - - if parent_pfic: - parent_item = tree_items.get(parent_pfic[0]) - if parent_item: - parent_item.addChild(item) - elif not root: - root = item - self.evolution_tree.addTopLevelItem(root) + + for chains in evolution_chains["predecessors"]: + for pfic, method in chains: + pokemon_details = db.get_pokemon_details(pfic) + display_name = get_display_name(pokemon_details, not pokemon_details["gender_relevant"]) + item = QTreeWidgetItem([display_name, method if method else ""]) + item.setData(0, Qt.ItemDataRole.UserRole, pfic) + tree_items[pfic] = item + + if pfic == selected_pfic: + item.setBackground(0, QColor(255, 255, 0, 100)) # Highlight selected Pokémon + + # Second pass: build the tree structure + root = None + for pfic, method in chains: + item = tree_items[pfic] + + # Find the parent of this item + parent_pfic, method = db.get_previous_evolution(pfic) + + if parent_pfic: + parent_item = tree_items.get(parent_pfic) + if parent_item: + parent_item.addChild(item) + elif not root: + root = item + self.evolution_tree.addTopLevelItem(root) + + for chains in evolution_chains["successors"]: + for pfic, method in chains: + pokemon_details = db.get_pokemon_details(pfic) + display_name = get_display_name(pokemon_details, not pokemon_details["gender_relevant"]) + item = QTreeWidgetItem([display_name, method if method else ""]) + item.setData(0, Qt.ItemDataRole.UserRole, pfic) + tree_items[pfic] = item + + if pfic == selected_pfic: + item.setBackground(0, QColor(255, 255, 0, 100)) # Highlight selected Pokémon + + # Second pass: build the tree structure + root = None + for pfic, method in chains: + item = tree_items[pfic] + + # Find the parent of this item + parent_pfic, method = db.get_previous_evolution(pfic) + + if parent_pfic: + parent_item = tree_items.get(parent_pfic) + if parent_item: + parent_item.addChild(item) + elif not root: + root = item + self.evolution_tree.addTopLevelItem(root) + # Expand the entire tree self.evolution_tree.expandAll() # Scroll to and select the current Pokémon - current_item = tree_items[selected_pfic] - self.evolution_tree.scrollToItem(current_item) - self.evolution_tree.setCurrentItem(current_item) \ No newline at end of file + if selected_pfic in tree_items: + current_item = tree_items[selected_pfic] + self.evolution_tree.scrollToItem(current_item) + self.evolution_tree.setCurrentItem(current_item) \ No newline at end of file diff --git a/ui/workers/gather_evolutions_worker.py b/ui/workers/gather_evolutions_worker.py index 5743064..22d9c05 100644 --- a/ui/workers/gather_evolutions_worker.py +++ b/ui/workers/gather_evolutions_worker.py @@ -28,7 +28,7 @@ class GatherEvolutions(QRunnable): except Exception as e: print(f"Error gathering Pokémon home storage status: {e}") - def gather_evolution_data(self, force_refresh = True): + def gather_evolution_data(self, force_refresh = False): all_pokemon_forms = db.get_list_of_pokemon_forms() evolutions = {} @@ -44,10 +44,10 @@ class GatherEvolutions(QRunnable): if force_refresh: cache.purge(cache_record_name) - #cached_entry = cache.get(cache_record_name) - #if cached_entry != None: - # evolutions[pokemon_form["pfic"]] = cached_entry - # continue + cached_entry = cache.get(cache_record_name) + if cached_entry != None: + evolutions = evolutions | cached_entry + continue #form = get_form_name(pokemon_form, not pokemon_form["gender_relevant"]) search_form = form @@ -85,19 +85,20 @@ class GatherEvolutions(QRunnable): if tag.name == 'h3': break if not evolution_table: - continue + continue - evolution_chain = [] evolution_tree = None if pokemon_name == "Eevee": evolution_tree = self.parse_eevee_evolution_chain(evolution_table, pokemon_form) else: evolution_tree = self.parse_evolution_chain(evolution_table, pokemon_form) + cacheable_container = {} if evolution_tree: - self.traverse_and_store(evolution_tree, evolutions, gender) + self.traverse_and_store(evolution_tree, cacheable_container, gender) - #cache.set(cache_record_name, chain) + cache.set(cache_record_name, cacheable_container) + evolutions = evolutions | cacheable_container print(self.evolution_methods) return evolutions @@ -120,7 +121,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 = True): + def parse_evolution_chain(self, table, pokemon_form, force_refresh = False): cache_record_name = f"evo_{pokemon_form['pfic']}" if force_refresh: cache.purge(cache_record_name)