Browse Source

- Rewrite of the tool now I know a little more about python. UI setup but not all working

master
Dan 1 year ago
parent
commit
01382b47cf
  1. 2
      .gitignore
  2. 16
      .vscode/launch.json
  3. 7
      .vscode/settings.json
  4. 2
      cache.py
  5. 0
      data_gathering/__init__.py
  6. 23
      main.py
  7. 0
      ui/__init__.py
  8. 120
      ui/main_window_controller.py
  9. 229
      ui/main_window_view.py
  10. 170
      ui/workers.py
  11. 0
      utility/__init__.py
  12. 52
      utility/cache_manager.py
  13. 250
      utility/data.py
  14. 48
      utility/functions.py

2
.gitignore

@ -138,3 +138,5 @@ dmypy.json
# Cython debug symbols
cython_debug/
# diskcache folder
cache/

16
.vscode/launch.json

@ -0,0 +1,16 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Run OriginDex App",
"type": "debugpy",
"request": "launch",
"program": "${workspaceFolder}/main.py",
"console": "integratedTerminal",
"justMyCode": false
}
]
}

7
.vscode/settings.json

@ -0,0 +1,7 @@
{
"python.languageServer": "Pylance",
"python.analysis.diagnosticSeverityOverrides": {
"reportMissingModuleSource": "none",
"reportShadowedImports": "none"
}
}

2
cache.py

@ -0,0 +1,2 @@
from utility.cache_manager import CacheManager
cache = CacheManager()

0
data_gathering/__init__.py

23
main.py

@ -0,0 +1,23 @@
import sys
from pathlib import Path
sys.path.append(str(Path(__file__).resolve().parent))
from PyQt6 import QtWidgets
from ui.main_window_view import PokemonUI
from cache import cache
def main():
import sys
app = QtWidgets.QApplication(sys.argv)
ui = PokemonUI()
ui.show()
sys.exit(app.exec())
if __name__ == "__main__":
try:
main()
finally:
# Ensure the cache is closed at the end of the application
cache.close()

0
ui/__init__.py

120
ui/main_window_controller.py

@ -0,0 +1,120 @@
from PyQt6.QtCore import Qt, QTimer, QThreadPool
from PyQt6.QtWidgets import QMenu
from PyQt6.QtGui import QAction
from ui.workers import GatherPokemonFormsWorker
class MainWindowController:
def __init__(self, view):
self.view = view
self.pokemon_data_cache = []
self.filter_timer = QTimer()
self.filter_timer.setInterval(300) # 300 ms delay to wait for user to stop typing
self.filter_timer.setSingleShot(True)
self.filter_timer.timeout.connect(self.apply_filters)
self.thread_pool = QThreadPool()
def initialize_pokemon_list(self, data):
self.pokemon_data_cache = data
self.view.update_pokemon_list(data)
def filter_pokemon_list(self):
self.filter_timer.start()
def apply_filters(self):
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()
filtered_data = []
for pfic, display_name in self.pokemon_data_cache:
# Check if the item matches the search text
text_match = search_text in display_name.lower()
# Check if the item is storable in Home (if the filter is active)
home_storable = True
if show_only_home_storable:
# TODO: update the call to correctly filter the data, or better yet update the data at the source to include this info.
home_storable = True #event_system.call_sync('get_home_storable', pfic)
# Check to see if the pokemon has encounters
has_encounters = True
if show_only_missing_encounters:
# TODO: reimplement this check.
has_encounters = True
# If both conditions are met, add to filtered data
if text_match and home_storable:
filtered_data.append((pfic, display_name))
# Update the view with the filtered data
self.view.update_pokemon_list(filtered_data)
def show_pokemon_context_menu(self, position):
item = self.view.pokemon_list.itemAt(position)
if item is not None:
context_menu = QMenu(self)
refresh_action = QAction("Refresh Encounters", self)
refresh_action.triggered.connect(lambda: self.refresh_pokemon_encounters(item))
context_menu.addAction(refresh_action)
context_menu.exec(self.pokemon_list.viewport().mapToGlobal(position))
def on_pokemon_selected(self, item):
pfic = item.data(Qt.ItemDataRole.UserRole)
self.refresh_pokemon_details_panel(pfic)
def edit_encounter(self):
pass
def add_new_encounter(self):
pass
def add_new_evolution(self):
pass
def save_changes(self):
pass
def export_database(self):
pass
def gather_pokemon_forms(self):
worker = GatherPokemonFormsWorker()
worker.signals.finished.connect(self.on_forms_gathered)
self.thread_pool.start(worker)
def on_forms_gathered(self, data):
# This method will be called in the main thread when the worker finishes
# Update the UI with the gathered forms
self.view.update_pokemon_forms(data)
print("Work's Done!", data)
def gather_home_storage_info(self):
pass
def gather_evolution_info(self):
pass
def reinitialize_database(self):
pass
def gather_encounter_info(self):
pass
def gather_marks_info(self):
pass
def load_shiftable_forms(self):
pass
def on_exclusive_set_selected(self):
pass
def add_new_exclusive_set(self):
pass
def show_encounter_context_menu(self):
pass
def add_encounter_to_set(self):
pass

229
ui/main_window_view.py

@ -0,0 +1,229 @@
from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QListWidget, QLineEdit,
QLabel, QCheckBox, QPushButton, QFormLayout, QListWidgetItem, QSplitter, QTreeWidget,
QTreeWidgetItem, QDialog, QDialogButtonBox, QComboBox, QMessageBox, QSpinBox, QMenu, QTabWidget,
QTextEdit)
from PyQt6.QtCore import Qt, QSize, QTimer, QMetaObject
from PyQt6.QtGui import QPixmap, QFontMetrics, QColor, QAction
from .main_window_controller import MainWindowController
class PokemonUI(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.controller = MainWindowController(self)
self.setup_ui()
def setup_ui(self):
main_layout = QVBoxLayout(self)
self.tab_widget = QTabWidget()
main_layout.addWidget(self.tab_widget)
self.setup_main_tab()
self.setup_db_operations_tab()
self.setup_manage_encounters_tab()
self.save_button = QPushButton("Save Changes")
self.save_button.clicked.connect(self.controller.save_changes)
main_layout.addWidget(self.save_button)
self.export_button = QPushButton("Export Database")
self.export_button.clicked.connect(self.controller.export_database)
main_layout.addWidget(self.export_button)
def setup_main_tab(self):
main_tab = QWidget()
main_tab_layout = QHBoxLayout(main_tab)
self.tab_widget.addTab(main_tab, "Main")
self.create_main_left_panel(main_tab_layout)
self.create_main_right_panel(main_tab_layout)
def create_main_left_panel(self, main_tab_layout):
left_layout = QVBoxLayout()
search_layout = QHBoxLayout()
self.search_bar = QLineEdit()
self.search_bar.setPlaceholderText("Search Pokémon...")
self.search_bar.textChanged.connect(self.controller.filter_pokemon_list)
search_layout.addWidget(self.search_bar)
left_layout.addLayout(search_layout)
self.pokemon_list = QListWidget()
self.pokemon_list.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
self.pokemon_list.customContextMenuRequested.connect(self.controller.show_pokemon_context_menu)
self.pokemon_list.currentItemChanged.connect(self.controller.on_pokemon_selected)
left_layout.addWidget(self.pokemon_list)
self.highlight_no_encounters = QCheckBox("Highlight Pokémon without encounters")
self.highlight_no_encounters.stateChanged.connect(self.controller.filter_pokemon_list)
left_layout.addWidget(self.highlight_no_encounters)
self.filter_home_storable = QCheckBox("Show only Home-storable Pokémon")
self.filter_home_storable.stateChanged.connect(self.controller.filter_pokemon_list)
left_layout.addWidget(self.filter_home_storable)
main_tab_layout.addLayout(left_layout, 1)
def create_main_right_panel(self, main_tab_layout):
right_layout = QVBoxLayout()
# Left side of right panel: Text information
info_layout = QHBoxLayout()
text_layout = QVBoxLayout()
self.edit_form = QFormLayout()
self.name_label = QLabel()
self.form_name_label = QLabel()
self.national_dex_label = QLabel()
self.generation_label = QLabel()
self.home_checkbox = QCheckBox("Available in Home")
self.is_baby_form_checkbox = QCheckBox("Is Baby Form")
self.edit_form.addRow("Name:", self.name_label)
self.edit_form.addRow("Form:", self.form_name_label)
self.edit_form.addRow("National Dex:", self.national_dex_label)
self.edit_form.addRow("Generation:", self.generation_label)
self.edit_form.addRow(self.home_checkbox)
self.edit_form.addRow(self.is_baby_form_checkbox)
text_layout.addLayout(self.edit_form)
# Right side of right panel: Image
image_layout = QVBoxLayout()
self.image_label = QLabel()
self.image_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.image_label.setFixedSize(150, 150)
image_layout.addWidget(self.image_label)
image_layout.addStretch(1)
info_layout.addLayout(text_layout)
info_layout.addLayout(image_layout)
second_half_layout = QVBoxLayout()
# Evolution chain tree
self.evolution_tree = QTreeWidget()
self.evolution_tree.setHeaderLabels(["Pokémon", "Evolution Method"])
self.evolution_tree.setColumnWidth(0, 200)
second_half_layout.addWidget(self.evolution_tree)
# Add Locations tree
self.locations_tree = QTreeWidget()
self.locations_tree.setHeaderLabels(["Game/Location", "Details"])
self.locations_tree.setColumnWidth(0, 200)
self.locations_tree.itemDoubleClicked.connect(self.controller.edit_encounter)
second_half_layout.addWidget(QLabel("Locations:"))
second_half_layout.addWidget(self.locations_tree)
# Add New Encounter button
self.add_encounter_button = QPushButton("Add New Encounter")
self.add_encounter_button.clicked.connect(self.controller.add_new_encounter)
second_half_layout.addWidget(self.add_encounter_button)
# Move buttons to the bottom
second_half_layout.addStretch(1)
# Add New Evolution button
self.add_evolution_button = QPushButton("Add New Evolution")
self.add_evolution_button.clicked.connect(self.controller.add_new_evolution)
second_half_layout.addWidget(self.add_evolution_button)
right_layout.addLayout(info_layout)
right_layout.addLayout(second_half_layout)
main_tab_layout.addLayout(right_layout, 1)
def setup_db_operations_tab(self):
db_tab = QWidget()
db_tab_layout = QVBoxLayout(db_tab)
self.tab_widget.addTab(db_tab, "Database Operations")
gather_forms_btn = QPushButton("Gather Pokémon Forms")
gather_forms_btn.clicked.connect(self.controller.gather_pokemon_forms)
db_tab_layout.addWidget(gather_forms_btn)
gather_home_btn = QPushButton("Gather Home Storage Info")
gather_home_btn.clicked.connect(self.controller.gather_home_storage_info)
db_tab_layout.addWidget(gather_home_btn)
gather_evolutions_btn = QPushButton("Gather Evolution Information")
gather_evolutions_btn.clicked.connect(self.controller.gather_evolution_info)
db_tab_layout.addWidget(gather_evolutions_btn)
gather_encounters_btn = QPushButton("Gather Encounter Information")
gather_encounters_btn.clicked.connect(self.controller.gather_encounter_info)
db_tab_layout.addWidget(gather_encounters_btn)
gather_marks_btn = QPushButton("Gather Marks Information")
gather_marks_btn.clicked.connect(self.controller.gather_marks_info)
db_tab_layout.addWidget(gather_marks_btn)
load_shiftable_forms_btn = QPushButton("Load Shiftable Forms")
load_shiftable_forms_btn.clicked.connect(self.controller.load_shiftable_forms)
db_tab_layout.addWidget(load_shiftable_forms_btn)
self.progress_text = QTextEdit()
self.progress_text.setReadOnly(True)
self.progress_text.setMinimumHeight(200) # Set a minimum height
db_tab_layout.addWidget(self.progress_text)
db_tab_layout.addStretch(1)
reinit_db_btn = QPushButton("Clear and Reinitialize Database")
reinit_db_btn.clicked.connect(self.controller.reinitialize_database)
db_tab_layout.addWidget(reinit_db_btn)
def setup_manage_encounters_tab(self):
manage_encounters = QWidget()
self.manage_encounters_tab = QHBoxLayout(manage_encounters)
self.tab_widget.addTab(manage_encounters, "Manage Encounters")
left_layout = QVBoxLayout()
self.exclusive_set_list = QListWidget()
self.exclusive_set_list.currentItemChanged.connect(self.controller.on_exclusive_set_selected)
left_layout.addWidget(QLabel("Exclusive Encounter Sets:"))
left_layout.addWidget(self.exclusive_set_list)
add_set_button = QPushButton("Add New Set")
add_set_button.clicked.connect(self.controller.add_new_exclusive_set)
left_layout.addWidget(add_set_button)
# Right side: Set details and encounters
right_layout = QVBoxLayout()
self.set_name_label = QLabel()
self.set_description_label = QLabel()
self.set_game_label = QLabel()
right_layout.addWidget(self.set_name_label)
right_layout.addWidget(self.set_description_label)
right_layout.addWidget(self.set_game_label)
self.encounter_list = QListWidget()
self.encounter_list.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
self.encounter_list.customContextMenuRequested.connect(self.controller.show_encounter_context_menu)
right_layout.addWidget(QLabel("Encounters in Set:"))
right_layout.addWidget(self.encounter_list)
add_encounter_button = QPushButton("Add Encounter to Set")
add_encounter_button.clicked.connect(self.controller.add_encounter_to_set)
right_layout.addWidget(add_encounter_button)
self.manage_encounters_tab.addLayout(left_layout, 1)
self.manage_encounters_tab.addLayout(right_layout, 2)
#self.load_exclusive_sets()
def update_pokemon_list(self, data):
self.pokemon_list.clear()
for pfic, display_name in data:
item = QListWidgetItem(display_name)
item.setData(Qt.ItemDataRole.UserRole, pfic)
self.pokemon_list.addItem(item)
def update_pokemon_forms(self, data):
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"]})"
item = QListWidgetItem(display_name)
item.setData(Qt.ItemDataRole.UserRole, pokemon["pfic"])
self.pokemon_list.addItem(item)

170
ui/workers.py

@ -0,0 +1,170 @@
from PyQt6.QtCore import QObject, pyqtSignal, QRunnable
from bs4 import BeautifulSoup
import re
from cache import cache
from utility.functions import get_generation_from_national_dex, sanitise_pokemon_name_for_url, remove_accents, compare_pokemon_forms, find_game_generation, format_pokemon_id
class GatherPokemonFormsWorkerSignals(QObject):
finished = pyqtSignal(list)
class GatherPokemonFormsWorker(QRunnable):
def __init__(self):
super().__init__()
self.signals = GatherPokemonFormsWorkerSignals()
def run(self):
try:
gathered_data = self.gather_forms_data()
self.signals.finished.emit(gathered_data)
except Exception as e:
print(f"Error gathering Pokémon forms: {e}")
def gather_forms_data(self):
# Get the sprites page from pokemondb.
# This gives us every pokemon in its default form.
url = "https://pokemondb.net/sprites"
page_data = cache.fetch_url(url)
if not page_data:
return None
soup = BeautifulSoup(page_data, 'html.parser')
pokemon = soup.find_all('a', class_='infocard')
# Loop through each card for the pokemon so we can extract out more information
pokemon_forms = []
for index, mon in enumerate(pokemon):
new_forms = self.process_pokemon_entry(index+1, mon)
if new_forms:
pokemon_forms.extend(new_forms)
return pokemon_forms
def get_pokemon_sprites_page_data(self, pokemon_name: str):
url = f"https://pokemondb.net/sprites/{pokemon_name}"
return cache.fetch_url(url)
def get_pokemon_dex_page(self, pokemon_name: str):
url = f"https://pokemondb.net/pokedex/{pokemon_name}"
return cache.fetch_url(url)
def extract_form_name(self, soup):
if soup.find('small'):
smalls = soup.find_all('small')
form_name = ""
for small in smalls:
form_name += small.get_text(strip=True) + " "
form_name = form_name.strip()
return form_name
return "None"
def process_pokemon_entry(self, national_dex_number, pokemon_soup, force_refresh = True):
found_forms = []
generation = get_generation_from_national_dex(national_dex_number)
pokemon_name = pokemon_soup.get_text(strip=True)
print(f"Processing {pokemon_name}")
url_name = sanitise_pokemon_name_for_url(pokemon_name)
if force_refresh:
cache.purge(url_name)
cached_entry = cache.get(url_name)
if cached_entry != None:
return cached_entry
sprites_page_data = self.get_pokemon_sprites_page_data(url_name)
if not sprites_page_data:
return None
form_pattern = re.compile(r'a(?:n)? (\w+) Form(?:,)? introduced in (?:the )?([\w\s:]+)(?:\/([\w\s:]+))?', re.IGNORECASE)
update_pattern = re.compile(r'a(?:n)? (\w+) form(?:,)? available in the latest update to ([\w\s:]+)(?:& ([\w\s:]+))?', re.IGNORECASE)
multiple_forms_pattern = re.compile(r'has (?:\w+) new (\w+) Form(?:s)?(?:,)? available in (?:the )?([\w\s:]+)(?:& ([\w\s:]+))?', re.IGNORECASE)
expansion_pass_pattern = re.compile(r'a(?:n)? (\w+) form(?:,)? introduced in the Crown Tundra Expansion Pass to ([\w\s:]+)(?:& ([\w\s:]+))?', re.IGNORECASE)
patterns = [form_pattern, update_pattern, multiple_forms_pattern, expansion_pass_pattern]
sprites_soup = BeautifulSoup(sprites_page_data, 'html.parser')
generation_8_table = sprites_soup.find('h2', string='Generation 8')
if generation_8_table:
generation_8_table = generation_8_table.find_next('table')
if generation_8_table:
generation_8_rows = generation_8_table.select('tbody > tr')
generation_8_rows = [row for row in generation_8_rows if "Home" in row.get_text(strip=True)]
for row in generation_8_rows:
sprites = row.find_all('span', class_='sprites-table-card')
if not sprites:
continue
form_index = 0
for sprite in sprites:
sprite_img = sprite.find('img')
sprite_url = "missing"
if sprite_img:
sprite_url = sprite_img.get('src')
if "shiny" in sprite_url:
continue
form_name = self.extract_form_name(sprite)
#logger.info(f'{sprite_url}, {form_name}')
if form_name != "None":
form_index += 1
gender = 0
if form_name.startswith("Male"):
form_index -= 1
gender = 1
elif form_name.startswith("Female"):
form_index -= 1
gender = 2
dex_page_data = self.get_pokemon_dex_page(url_name)
if dex_page_data:
dex_soup = BeautifulSoup(dex_page_data, 'html.parser')
#Find a heading that has the pokemon name in it
dex_header = dex_soup.find('h1', string=pokemon_name)
if dex_header:
#The next <p> tag contains the generation number, in the format "{pokemon name} is a {type}(/{2nd_type}) type Pokémon introduced in Generation {generation number}."
generation_tag = dex_header.find_next('p')
dex_text = generation_tag.get_text()
pattern = r'^(.+?) is a (\w+)(?:/(\w+))? type Pokémon introduced in Generation (\d+)\.$'
match = re.match(pattern, dex_text)
if match:
name, type1, type2, gen = match.groups()
generation = int(gen)
if form_name != "None":
next_tag = generation_tag.find_next('p')
if next_tag:
extra_text = next_tag.get_text()
extra_text = remove_accents(extra_text)
test_form = form_name.replace(pokemon_name, "").replace("Male", "").replace("Female", "").strip()
if pokemon_name == "Tauros" and (form_name == "Aqua Breed" or form_name == "Blaze Breed" or form_name == "Combat Breed"):
test_form = "Paldean"
for pattern in patterns:
matches = re.findall(pattern, extra_text)
generation_found = False
for i, (regional, game1, game2) in enumerate(matches, 1):
if compare_pokemon_forms(test_form, regional):
target_game = game1.replace("Pokemon", "").strip()
result = find_game_generation(target_game)
if result:
generation = result
generation_found = True
break
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)
cache.set(url_name, found_forms)
return found_forms

0
utility/__init__.py

52
utility/cache_manager.py

@ -0,0 +1,52 @@
import time
import requests
from typing import Any, Optional, Dict, List
import diskcache as dc
from threading import Lock
class CacheManager:
def __init__(self, cache_dir='cache', max_connections: int = 10):
# Initialize the disk cache
self.cache = dc.Cache(cache_dir)
self.fetch_url_lock = Lock()
def get(self, key: str) -> Optional[Any]:
# Fetch the value from the cache
return self.cache.get(key)
def set(self, key: str, value: Any, expire: int = None):
# Store the value in the cache with optional expiry
self.cache.set(key, value, expire=expire)
def purge(self, key: str):
self.cache.delete(key)
def bulk_get(self, keys: List[str]) -> Dict[str, Any]:
# Use a dictionary comprehension to fetch multiple values
return {key: self.cache.get(key) for key in keys if key in self.cache}
def fetch_url(self, url: str, force_refresh: bool = False, expiry: int = 86400*30) -> Optional[str]:
cache_key = f"url_{url}"
if not force_refresh:
cached_data = self.get(cache_key)
if cached_data:
cached_time = cached_data['timestamp']
if time.time() - cached_time < expiry:
return cached_data['content']
# Fetch the URL if not in cache or if a refresh is requested
with self.fetch_url_lock:
print(f"Fetching URL: {url}")
response = requests.get(url)
if response.status_code == 200:
content = response.text
self.set(cache_key, {
'content': content,
'timestamp': time.time()
}, expire=expiry)
time.sleep(0.25) # Throttle requests to avoid being blocked
return content
return None
def close(self):
self.cache.close()

250
utility/data.py

@ -0,0 +1,250 @@
pokemon_generations = {
1: {"min": 1, "max": 151},
2: {"min": 152, "max": 251},
3: {"min": 252, "max": 386},
4: {"min": 387, "max": 493},
5: {"min": 494, "max": 649},
6: {"min": 650, "max": 721},
7: {"min": 722, "max": 809},
8: {"min": 810, "max": 905},
9: {"min": 906, "max": 1025},
}
regional_descriptors = ["kantonian", "johtonian", "hoennian", "sinnohan", "unovan", "kalosian", "alolan", "galarian", "hisuian", "paldean"]
yellow = {
"Name": "Yellow",
"AltNames": ["Pokemon Yellow", "Pokémon Yellow", "Y"],
"Generation": 1
}
red = {
"Name": "Red",
"AltNames": ["Pokemon Red", "Pokémon Red", "R"],
"Generation": 1
}
blue = {
"Name": "Blue",
"AltNames": ["Pokemon Blue", "Pokémon Blue", "B"],
"Generation": 1
}
crystal = {
"Name": "Crystal",
"AltNames": ["Pokemon Crystal", "Pokémon Crystal", "C"],
"Generation": 2
}
gold = {
"Name": "Gold",
"AltNames": ["Pokemon Gold", "Pokémon Gold", "G"],
"Generation": 2
}
silver = {
"Name": "Silver",
"AltNames": ["Pokemon Silver", "Pokémon Silver", "S"],
"Generation": 2
}
emerald = {
"Name": "Emerald",
"AltNames": ["Pokemon Emerald", "Pokémon Emerald", "E"],
"Generation": 3
}
fire_red = {
"Name": "FireRed",
"AltNames": ["Pokemon FireRed", "Pokémon FireRed", "FR"],
"Generation": 3
}
leaf_green = {
"Name": "LeafGreen",
"AltNames": ["Pokemon LeafGreen", "Pokémon LeafGreen", "LG"],
"Generation": 3
}
ruby = {
"Name": "Ruby",
"AltNames": ["Pokemon Ruby", "Pokémon Ruby", "R"],
"Generation": 3
}
sapphire = {
"Name": "Sapphire",
"AltNames": ["Pokemon Sapphire", "Pokémon Sapphire", "S"],
"Generation": 3
}
platinum = {
"Name": "Platinum",
"AltNames": ["Pokemon Platinum", "Pokémon Platinum", "Pt"],
"Generation": 4
}
heart_gold = {
"Name": "HeartGold",
"AltNames": ["Pokemon HeartGold", "Pokémon HeartGold", "HG"],
"Generation": 4
}
soul_silver = {
"Name": "SoulSilver",
"AltNames": ["Pokemon SoulSilver", "Pokémon SoulSilver", "SS"],
"Generation": 4
}
diamond = {
"Name": "Diamond",
"AltNames": ["Pokemon Diamond", "Pokémon Diamond", "D"],
"Generation": 4
}
pearl = {
"Name": "Pearl",
"AltNames": ["Pokemon Pearl", "Pokémon Pearl", "P"],
"Generation": 4
}
black = {
"Name": "Black",
"AltNames": ["Pokemon Black", "Pokémon Black", "B"],
"Generation": 5
}
white = {
"Name": "White",
"AltNames": ["Pokemon White", "Pokémon White", "W"],
"Generation": 5
}
black_2 = {
"Name": "Black 2",
"AltNames": ["Pokemon Black 2", "Pokémon Black 2", "B2"],
"Generation": 5
}
white_2 = {
"Name": "White 2",
"AltNames": ["Pokemon White 2", "Pokémon White 2", "W2"],
"Generation": 5
}
x = {
"Name": "X",
"AltNames": ["Pokemon X", "Pokémon X"],
"Generation": 6
}
y = {
"Name": "Y",
"AltNames": ["Pokemon Y", "Pokémon Y"],
"Generation": 6
}
omega_ruby = {
"Name": "Omega Ruby",
"AltNames": ["Pokemon Omega Ruby", "Pokémon Omega Ruby", "OR"],
"Generation": 6
}
alpha_sapphire = {
"Name": "Alpha Sapphire",
"AltNames": ["Pokemon Alpha Sapphire", "Pokémon Alpha Sapphire", "AS"],
"Generation": 6
}
sun = {
"Name": "Sun",
"AltNames": ["Pokemon Sun", "Pokémon Sun"],
"Generation": 7
}
moon = {
"Name": "Moon",
"AltNames": ["Pokemon Moon", "Pokémon Moon"],
"Generation": 7
}
ultra_sun = {
"Name": "Ultra Sun",
"AltNames": ["Pokemon Ultra Sun", "Pokémon Ultra Sun", "US"],
"Generation": 7
}
ultra_moon = {
"Name": "Ultra Moon",
"AltNames": ["Pokemon Ultra Moon", "Pokémon Ultra Moon", "UM"],
"Generation": 7
}
sword = {
"Name": "Sword",
"AltNames": ["Pokemon Sword", "Pokémon Sword", "Expansion Pass", "Expansion Pass (Sword)"],
"Generation": 8
}
shield = {
"Name": "Shield",
"AltNames": ["Pokemon Shield", "Pokémon Shield", "Expansion Pass", "Expansion Pass (Shield)"],
"Generation": 8
}
brilliant_diamond = {
"Name": "Brilliant Diamond",
"AltNames": ["Pokemon Brilliant Diamond", "Pokémon Brilliant Diamond", "BD"],
"Generation": 8
}
shining_pearl = {
"Name": "Shining Pearl",
"AltNames": ["Pokemon Shining Pearl", "Pokémon Shining Pearl", "SP"],
"Generation": 8
}
legends_arceus = {
"Name": "Legends: Arceus",
"AltNames": ["Pokemon Legends: Arceus", "Pokémon Legends: Arceus", "LA", "Legends Arceus", "Arceus"],
"Generation": 8
}
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
}
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
}
lets_go_pikachu = {
"Name": "Lets Go Pikachu",
"AltNames": [],
"Generation": 8
}
lets_go_eevee = {
"Name": "Lets Go Eevee",
"AltNames": [],
"Generation": 8
}
main_line_games = [
yellow, red, blue,
crystal, gold, silver,
emerald, fire_red, leaf_green, ruby, sapphire,
platinum, heart_gold, soul_silver, diamond, pearl,
black_2, white_2, black, white,
x, y, omega_ruby, alpha_sapphire,
ultra_sun, ultra_moon, sun, moon, lets_go_pikachu, lets_go_eevee,
sword, shield,
brilliant_diamond, shining_pearl,
legends_arceus,
scarlet, violet,
]

48
utility/functions.py

@ -0,0 +1,48 @@
from .data import pokemon_generations, main_line_games
import unicodedata
def format_pokemon_id(national_dex: int, region_code: int, form_index: int, gender_code: int) -> str:
return f"{national_dex:04d}-{region_code:02d}-{form_index:03d}-{gender_code}"
def compare_pokemon_forms(a, b):
if a == None or b == None:
return False
if a == b:
return True
temp_a = a.lower().replace("forme", "").replace("form", "").replace("é", "e").strip()
temp_b = b.lower().replace("forme", "").replace("form", "").replace("é", "e").strip()
# Common spelling mistakes
temp_a = temp_a.replace("deputante", "debutante").replace("p'au", "pa'u").replace("blood moon", "bloodmoon")
temp_b = temp_b.replace("deputante", "debutante").replace("p'au", "pa'u").replace("blood moon", "bloodmoon")
if temp_a == temp_b:
return True
return False
def get_generation_from_national_dex(national_dex_number):
generation = 1
for gen in pokemon_generations:
if pokemon_generations[gen]["min"] <= national_dex_number <= pokemon_generations[gen]["max"]:
generation = gen
break
return generation
def sanitise_pokemon_name_for_url(pokemon_name):
pokemon_url_name = pokemon_name.replace("", "-f").replace("", "-m").replace("'", "").replace(".", "").replace('é', 'e').replace(':', '')
pokemon_url_name = pokemon_url_name.replace(" ", "-")
return pokemon_url_name
def remove_accents(input_str):
nfkd_form = unicodedata.normalize('NFKD', input_str)
return u"".join([c for c in nfkd_form if not unicodedata.combining(c)])
def find_game_generation(game_name: str) -> int:
game_name = game_name.lower()
for game in main_line_games:
if game_name == game["Name"].lower() or game_name in (name.lower() for name in game["AltNames"]):
return game["Generation"]
return None
Loading…
Cancel
Save