diff --git a/database/db_controller.py b/database/db_controller.py index 2872c73..10b2fff 100644 --- a/database/db_controller.py +++ b/database/db_controller.py @@ -52,7 +52,7 @@ class DBController: ) ''') - def add_pokemon_form(self, pfic, name, form_name, national_dex, generation, sprite_url): + def add_pokemon_form(self, pfic, name, form_name, national_dex, generation, sprite_url, gender_relevant): data = { "name": name, "form_name": form_name, @@ -61,6 +61,7 @@ class DBController: "sprite_url": sprite_url, "is_baby_form": False, "storable_in_home": False, + "gender_relevant": gender_relevant } with self.lock: @@ -70,31 +71,50 @@ class DBController: self.conn.commit() print(f"Added: {pfic}, {name}") + def craft_pokemon_json_query(self, fields_to_include, pfic = None): + query = f"SELECT " + extracts = [] + for field in fields_to_include: + if field == "pfic": + extracts.append("PFIC as pfic") + else: + extracts.append(f"JSON_EXTRACT(data, '$.{field}') AS {field}") + query = query + ", ".join(extracts) + query = query + " FROM pokemon_forms" + + if pfic is not None: + query = query + f" WHERE PFIC = '{pfic}'" + + return query + def get_pokemon_details(self, pfic): - self.cursor.execute(''' - SELECT JSON_EXTRACT(data, '$.name') AS name, - JSON_EXTRACT(data, '$.form_name') AS form_name, - JSON_EXTRACT(data, '$.national_dex') AS national_dex, - JSON_EXTRACT(data, '$.generation') AS generation, - JSON_EXTRACT(data, '$.is_baby_form') AS is_baby_form, - JSON_EXTRACT(data, '$.storable_in_home') AS storable_in_home - FROM pokemon_forms - WHERE PFIC = ? - ''', (pfic,)) + fields = [ + "name", + "form_name", + "national_dex", + "generation", + "is_baby_form", + "storable_in_home", + ] + query = self.craft_pokemon_json_query(fields, pfic) + self.cursor.execute(query) results = self.cursor.fetchone() return dict(results) def get_list_of_pokemon_forms(self): - self.cursor.execute(''' - SELECT JSON_EXTRACT(data, '$.name') AS name, - JSON_EXTRACT(data, '$.form_name') AS form_name, - JSON_EXTRACT(data, '$.national_dex') AS national_dex, - JSON_EXTRACT(data, '$.generation') AS generation, - JSON_EXTRACT(data, '$.is_baby_form') AS is_baby_form, - JSON_EXTRACT(data, '$.storable_in_home') AS storable_in_home, - PFIC as pfic - FROM pokemon_forms - ''',) + fields = [ + "pfic", + "name", + "form_name", + "national_dex", + "generation", + "is_baby_form", + "storable_in_home", + "gender_relevant" + ] + + query = self.craft_pokemon_json_query(fields) + self.cursor.execute(query) results = self.cursor.fetchall() return [dict(row) for row in results] diff --git a/ui/main_window_controller.py b/ui/main_window_controller.py index 7e20b75..bb09c90 100644 --- a/ui/main_window_controller.py +++ b/ui/main_window_controller.py @@ -6,6 +6,7 @@ import os from ui.workers.gather_home_storage_status_worker import GatherHomeStorageStatus from ui.workers.gather_pokemon_forms_worker import GatherPokemonFormsWorker +from utility.functions import get_display_name from db import db class MainWindowController: @@ -21,6 +22,7 @@ class MainWindowController: def initialize_pokemon_list(self, data): self.pokemon_data_cache = data self.view.update_pokemon_forms(data) + self.apply_filters() def filter_pokemon_list(self): self.filter_timer.start() @@ -29,9 +31,12 @@ class MainWindowController: search_text = self.view.search_bar.text().lower() show_only_home_storable = self.view.filter_home_storable.isChecked() show_only_missing_encounters = self.view.highlight_no_encounters.isChecked() + gender_relevant = False filtered_data = [] - for pfic, display_name in self.pokemon_data_cache: + for pokemon in self.pokemon_data_cache: + display_name = get_display_name(pokemon) + pfic = pokemon["pfic"] # Check if the item matches the search text text_match = search_text in display_name.lower() @@ -47,9 +52,13 @@ class MainWindowController: # TODO: reimplement this check. has_encounters = True + include_gender = True + if gender_relevant == False and pokemon["gender_relevant"] == False: + include_gender = not any(item["pfic"][:-2] == pfic[:-2] for item in filtered_data) + # If both conditions are met, add to filtered data - if text_match and home_storable: - filtered_data.append((pfic, display_name)) + if text_match and home_storable and include_gender: + filtered_data.append(pokemon) # Update the view with the filtered data self.view.update_pokemon_forms(filtered_data) @@ -91,7 +100,7 @@ class MainWindowController: # This method will be called in the main thread when the worker finishes # Update the UI with the gathered forms for pokemon in data: - db.add_pokemon_form(pokemon["pfic"], pokemon["name"], pokemon["form_name"], pokemon["national_dex"], pokemon["generation"], pokemon["sprite_url"]) + db.add_pokemon_form(pokemon["pfic"], pokemon["name"], pokemon["form_name"], pokemon["national_dex"], pokemon["generation"], pokemon["sprite_url"], pokemon["gender_relevant"]) self.pokemon_data_cache = data self.view.update_pokemon_forms(data) diff --git a/ui/main_window_view.py b/ui/main_window_view.py index 5862b6f..12601f4 100644 --- a/ui/main_window_view.py +++ b/ui/main_window_view.py @@ -6,6 +6,7 @@ from PyQt6.QtCore import Qt, QSize, QTimer, QMetaObject from PyQt6.QtGui import QPixmap, QFontMetrics, QColor, QAction from .main_window_controller import MainWindowController +from utility.functions import get_display_name from db import db class PokemonUI(QWidget): @@ -217,9 +218,7 @@ class PokemonUI(QWidget): self.pokemon_list.clear() for pokemon in data: - display_name = f"{pokemon["national_dex"]:04d} - {pokemon["name"]}" - if pokemon["form_name"]: - display_name += f" ({pokemon["form_name"]})" + display_name = get_display_name(pokemon, not pokemon["gender_relevant"]) item = QListWidgetItem(display_name) item.setData(Qt.ItemDataRole.UserRole, pokemon["pfic"]) self.pokemon_list.addItem(item) \ No newline at end of file diff --git a/ui/workers/gather_evolutions_worker.py b/ui/workers/gather_evolutions_worker.py new file mode 100644 index 0000000..6953380 --- /dev/null +++ b/ui/workers/gather_evolutions_worker.py @@ -0,0 +1,29 @@ +from PyQt6.QtCore import QObject, pyqtSignal, QRunnable +from bs4 import BeautifulSoup +from cache import cache +from db import db + +class GatherEvolutionsWorkerSignals(QObject): + finished = pyqtSignal(list) + +class GatherHEvolutions(QRunnable): + def __init__(self): + super().__init__() + self.signals = GatherEvolutionsWorkerSignals() + self.base_url = "https://www.serebii.net/pokemonhome/" + + def run(self): + try: + gathered_data = self.gather_evolution_data() + self.signals.finished.emit(gathered_data) + except Exception as e: + print(f"Error gathering Pokémon home storage status: {e}") + + def gather_evolution_data(self): + all_pokemon_forms = db.get_list_of_pokemon_forms() + evolutions = [] + + for pokemon_form in all_pokemon_forms: + pass + + return evolutions \ No newline at end of file diff --git a/ui/workers/gather_pokemon_forms_worker.py b/ui/workers/gather_pokemon_forms_worker.py index 470753e..0a602d0 100644 --- a/ui/workers/gather_pokemon_forms_worker.py +++ b/ui/workers/gather_pokemon_forms_worker.py @@ -108,15 +108,21 @@ class GatherPokemonFormsWorker(QRunnable): form_name = self.extract_form_name(sprite) #logger.info(f'{sprite_url}, {form_name}') + record_male_form = False + record_female_form = False + record_genderless_form = False + gender_relevant = False if form_name != "None": form_index += 1 gender = 0 if form_name.startswith("Male"): form_index -= 1 gender = 1 + gender_relevant = True elif form_name.startswith("Female"): form_index -= 1 gender = 2 + gender_relevant = True dex_page_data = self.get_pokemon_dex_page(url_name) if dex_page_data: @@ -156,15 +162,71 @@ class GatherPokemonFormsWorker(QRunnable): if generation_found: break - pokemon_form = { - "pfic":format_pokemon_id(national_dex_number, generation, form_index, gender), - "name":pokemon_name, - "form_name":form_name if form_name != "None" else None, - "sprite_url":sprite_url, - "national_dex":national_dex_number, - "generation":generation - } - found_forms.append(pokemon_form) + if not gender_relevant: + # see if we can find gender info on the page to see if it has male and female forms anyway. + gender_header = dex_soup.find('th', string="Gender") + if gender_header: + gender_info = gender_header.findNext('td').getText().replace(",", "").split() + skip_next = False + for info in gender_info: + if skip_next: + skip_next = False + continue + if info.lower().startswith("0%"): + skip_next = True + continue + if info.lower() == "male": + record_male_form = True + elif info.lower() == "female": + record_female_form = True + + if not record_female_form and not record_male_form: + record_genderless_form = True + + if gender_relevant or record_genderless_form: + pokemon_form = { + "pfic":format_pokemon_id(national_dex_number, generation, form_index, gender), + "name":pokemon_name, + "form_name":form_name if form_name != "None" else None, + "sprite_url":sprite_url, + "national_dex":national_dex_number, + "generation":generation, + "gender_relevant": gender_relevant + } + found_forms.append(pokemon_form) + else: + if record_male_form: + gendered_form = form_name + if gendered_form == "None": + gendered_form = "Male" + else: + gendered_form = "Male " + gendered_form + pokemon_form = { + "pfic":format_pokemon_id(national_dex_number, generation, form_index, 1), + "name":pokemon_name, + "form_name":gendered_form if gendered_form != "None" else None, + "sprite_url":sprite_url, + "national_dex":national_dex_number, + "generation":generation, + "gender_relevant": gender_relevant + } + found_forms.append(pokemon_form) + if record_female_form: + gendered_form = form_name + if gendered_form == "None": + gendered_form = "Female" + else: + gendered_form = "Female " + gendered_form + pokemon_form = { + "pfic":format_pokemon_id(national_dex_number, generation, form_index, 2), + "name":pokemon_name, + "form_name":gendered_form if gendered_form != "None" else None, + "sprite_url":sprite_url, + "national_dex":national_dex_number, + "generation":generation, + "gender_relevant": gender_relevant + } + found_forms.append(pokemon_form) cache.set(url_name, found_forms) return found_forms \ No newline at end of file diff --git a/utility/functions.py b/utility/functions.py index 840551e..4d6dfe2 100644 --- a/utility/functions.py +++ b/utility/functions.py @@ -75,4 +75,14 @@ def sanitize_filename(filename): return re.sub(r'[<>:"/\\|?*]', '', filename) def get_objects_by_number(array, target_number): - return [obj for obj in array if obj['number'] == target_number] \ No newline at end of file + return [obj for obj in array if obj['number'] == target_number] + +def get_display_name(pokemon, strip_gender = False): + display_name = f"{pokemon["national_dex"]:04d} - {pokemon["name"]}" + if pokemon["form_name"]: + form = pokemon["form_name"] + if strip_gender: + form = form.replace("Female", "").replace("Male", "").strip() + if form != "": + display_name += f" ({form})" + return display_name \ No newline at end of file