25 changed files with 2936 additions and 8390 deletions
@ -0,0 +1,382 @@ |
|||
import sqlite3 |
|||
import sys |
|||
import os |
|||
|
|||
from event_system import event_system |
|||
|
|||
def parse_pfic(pfic): |
|||
parts = pfic.split('-') |
|||
return tuple(int(part) if part.isdigit() else part for part in parts) |
|||
|
|||
class DBController: |
|||
def __init__(self): |
|||
self.conn = sqlite3.connect(':memory:', check_same_thread=False) # Use in-memory database for runtime |
|||
self.cursor = self.conn.cursor() |
|||
|
|||
event_system.add_listener('get_pokemon_data', self.load_pokemon_details) |
|||
event_system.add_listener('get_pokemon_list', self.send_pokemon_list) |
|||
event_system.add_listener('get_home_storable', self.get_home_storable) |
|||
event_system.add_listener('get_evolution_chain', self.get_evolution_chain) |
|||
event_system.add_listener('get_evolution_parent', self.get_evolution_parent) |
|||
event_system.add_listener('get_encounter_locations', self.get_encounter_locations) |
|||
event_system.add_listener('get_game_generation', self.get_game_generation) |
|||
event_system.add_listener('get_pokemon_with_encounters', self.get_pokemon_with_encounters) |
|||
event_system.add_listener('refresh_in_memory_db', self.refresh_in_memory_db) |
|||
event_system.add_listener('clear_encounters_for_pokemon', self.clear_encounters_for_pokemon) |
|||
event_system.add_listener('get_game_id_for_name', self.get_game_id_for_name) |
|||
|
|||
def init_database(self): |
|||
disk_conn = sqlite3.connect('pokemon_forms.db') |
|||
disk_cursor = disk_conn.cursor() |
|||
|
|||
# Create tables in the file-based database |
|||
self.create_games_table(disk_cursor) |
|||
self.create_pokemon_forms_table(disk_cursor) |
|||
self.create_pokemon_storage_table(disk_cursor) |
|||
self.create_evolution_chains_table(disk_cursor) |
|||
self.create_exclusive_encounter_groups_table(disk_cursor) |
|||
self.create_encounters_table(disk_cursor) |
|||
self.create_mark_table(disk_cursor) |
|||
|
|||
# Commit changes to the file-based database |
|||
disk_conn.commit() |
|||
|
|||
# Copy the file-based database to the in-memory database |
|||
disk_conn.backup(self.conn) |
|||
|
|||
# Close the file-based database connection |
|||
disk_conn.close() |
|||
|
|||
def create_pokemon_forms_table(self, cursor): |
|||
cursor.execute(''' |
|||
CREATE TABLE IF NOT EXISTS pokemon_forms ( |
|||
PFIC TEXT PRIMARY KEY, |
|||
name TEXT NOT NULL, |
|||
form_name TEXT, |
|||
national_dex INTEGER NOT NULL, |
|||
generation INTEGER NOT NULL, |
|||
is_baby_form BOOLEAN |
|||
) |
|||
''') |
|||
|
|||
def create_pokemon_storage_table(self, cursor): |
|||
cursor.execute(''' |
|||
CREATE TABLE IF NOT EXISTS pokemon_storage ( |
|||
PFIC TEXT PRIMARY KEY, |
|||
storable_in_home BOOLEAN NOT NULL, |
|||
FOREIGN KEY (PFIC) REFERENCES pokemon_forms (PFIC) |
|||
) |
|||
''') |
|||
|
|||
def create_evolution_chains_table(self, cursor): |
|||
cursor.execute(''' |
|||
CREATE TABLE IF NOT EXISTS evolution_chains ( |
|||
from_pfic TEXT, |
|||
to_pfic TEXT, |
|||
method TEXT, |
|||
PRIMARY KEY (from_pfic, to_pfic), |
|||
FOREIGN KEY (from_pfic) REFERENCES pokemon_forms (PFIC), |
|||
FOREIGN KEY (to_pfic) REFERENCES pokemon_forms (PFIC) |
|||
) |
|||
''') |
|||
|
|||
def create_exclusive_encounter_groups_table(self, cursor): |
|||
cursor.execute(''' |
|||
CREATE TABLE IF NOT EXISTS exclusive_encounter_groups ( |
|||
id INTEGER PRIMARY KEY AUTOINCREMENT, |
|||
group_name TEXT NOT NULL, |
|||
description TEXT, |
|||
game_id INTEGER, |
|||
FOREIGN KEY (game_id) REFERENCES games (id) |
|||
) |
|||
''') |
|||
|
|||
def create_encounter_exclusive_group_table(self, cursor): |
|||
cursor.execute(''' |
|||
CREATE TABLE IF NOT EXISTS encounter_exclusive_groups ( |
|||
encounter_id INTEGER, |
|||
group_id INTEGER, |
|||
FOREIGN KEY (encounter_id) REFERENCES encounters (id), |
|||
FOREIGN KEY (group_id) REFERENCES exclusive_encounter_groups (id), |
|||
PRIMARY KEY (encounter_id, group_id) |
|||
) |
|||
''') |
|||
|
|||
def create_encounters_table(self, cursor): |
|||
cursor.execute(''' |
|||
CREATE TABLE IF NOT EXISTS encounters ( |
|||
id INTEGER PRIMARY KEY AUTOINCREMENT, |
|||
pfic TEXT, |
|||
game_id INTEGER, |
|||
location TEXT, |
|||
day TEXT, |
|||
time TEXT, |
|||
dual_slot TEXT, |
|||
static_encounter BOOLEAN, |
|||
static_encounter_count INTEGER, |
|||
extra_text TEXT, |
|||
stars TEXT, |
|||
fishing BOOLEAN, |
|||
rods TEXT, |
|||
starter BOOLEAN, |
|||
FOREIGN KEY (pfic) REFERENCES pokemon_forms (PFIC), |
|||
FOREIGN KEY (game_id) REFERENCES games (id) |
|||
) |
|||
''') |
|||
|
|||
def create_mark_table(self, cursor): |
|||
cursor.execute(''' |
|||
CREATE TABLE IF NOT EXISTS marks ( |
|||
id INTEGER PRIMARY KEY, |
|||
name TEXT NOT NULL UNIQUE, |
|||
icon_path TEXT NOT NULL |
|||
) |
|||
''') |
|||
|
|||
cursor.execute(''' |
|||
CREATE TABLE IF NOT EXISTS mark_game_associations ( |
|||
mark_id INTEGER, |
|||
game_id INTEGER, |
|||
FOREIGN KEY (mark_id) REFERENCES marks (id), |
|||
FOREIGN KEY (game_id) REFERENCES games (id), |
|||
PRIMARY KEY (mark_id, game_id) |
|||
) |
|||
''') |
|||
|
|||
marks = [ |
|||
("Game Boy", "images/marks/GB_icon_HOME.png", ["Red", "Blue", "Yellow", "Gold", "Silver", "Crystal"]), |
|||
("Kalos", "images/marks/Blue_pentagon_HOME.png", ["X", "Y", "Omega Ruby", "Alpha Sapphire"]), |
|||
("Alola", "images/marks/Black_clover_HOME.png", ["Sun", "Moon", "Ultra Sun", "Ultra Moon"]), |
|||
("Let's Go", "images/marks/Let's_Go_icon_HOME.png", ["Let's Go Pikachu", "Let's Go Eevee"]), |
|||
("Galar", "images/marks/Galar_symbol_HOME.png", ["Sword", "Shield"]), |
|||
("Sinnoh", "images/marks/BDSP_icon_HOME.png", ["Brilliant Diamond", "Shining Pearl"]), |
|||
("Hisui", "images/marks/Arceus_mark_HOME.png", ["Legends Arceus"]), |
|||
("Paldea", "images/marks/Paldea_icon_HOME.png", ["Scarlet", "Violet"]), |
|||
] |
|||
|
|||
# Check if marks already exist |
|||
cursor.execute('SELECT COUNT(*) FROM marks') |
|||
marks_count = cursor.fetchone()[0] |
|||
|
|||
if marks_count == 0: |
|||
for mark in marks: |
|||
cursor.execute(''' |
|||
INSERT OR IGNORE INTO marks (name, icon_path) |
|||
VALUES (?, ?) |
|||
''', (mark[0], mark[1])) |
|||
|
|||
mark_id = cursor.lastrowid |
|||
|
|||
for game_name in mark[2]: |
|||
cursor.execute(''' |
|||
INSERT OR IGNORE INTO mark_game_associations (mark_id, game_id) |
|||
SELECT ?, id FROM games WHERE name = ? |
|||
''', (mark_id, game_name)) |
|||
|
|||
def create_games_table(self, cursor): |
|||
|
|||
cursor.execute(''' |
|||
CREATE TABLE IF NOT EXISTS games ( |
|||
id INTEGER PRIMARY KEY, |
|||
name TEXT NOT NULL, |
|||
generation INTEGER NOT NULL |
|||
) |
|||
''') |
|||
|
|||
cursor.execute(''' |
|||
CREATE TABLE IF NOT EXISTS alternate_game_names ( |
|||
id INTEGER PRIMARY KEY, |
|||
game_id INTEGER NOT NULL, |
|||
alternate_name TEXT NOT NULL, |
|||
FOREIGN KEY (game_id) REFERENCES games (id), |
|||
UNIQUE (alternate_name COLLATE NOCASE) |
|||
) |
|||
''') |
|||
|
|||
games = [ |
|||
("Red", 1, ["Red Version"]), |
|||
("Blue", 1, ["Blue Version"]), |
|||
("Yellow", 1, ["Yellow Version"]), |
|||
("Gold", 2, ["Gold Version"]), |
|||
("Silver", 2, ["Silver Version"]), |
|||
("Crystal", 2, ["Crystal Version"]), |
|||
("Ruby", 3, ["Ruby Version"]), |
|||
("Sapphire", 3, ["Sapphire Version"]), |
|||
("Emerald", 3, ["Emerald Version"]), |
|||
("FireRed", 3, ["Fire Red", "Fire-Red"]), |
|||
("LeafGreen", 3, ["Leaf Green", "Leaf-Green"]), |
|||
("Diamond", 4, ["Diamond Version"]), |
|||
("Pearl", 4, ["Pearl Version"]), |
|||
("Platinum", 4, ["Platinum Version"]), |
|||
("HeartGold", 4, ["Heart Gold", "Heart-Gold"]), |
|||
("SoulSilver", 4, ["Soul Silver", "Soul-Silver"]), |
|||
("Black", 5, ["Black Version"]), |
|||
("White", 5, ["White Version"]), |
|||
("Black 2", 5, ["Black Version 2", "Black-2"]), |
|||
("White 2", 5, ["White Version 2", "White-2"]), |
|||
("X", 6, ["X Version"]), |
|||
("Y", 6, ["Y Version"]), |
|||
("Omega Ruby", 6, ["Omega Ruby Version", "Omega-Ruby"]), |
|||
("Alpha Sapphire", 6, ["Alpha Sapphire Version", "Alpha-Sapphire"]), |
|||
("Sun", 7, ["Sun Version"]), |
|||
("Moon", 7, ["Moon Version"]), |
|||
("Ultra Sun", 7, ["Ultra Sun Version", "Ultra-Sun"]), |
|||
("Ultra Moon", 7, ["Ultra Moon Version", "Ultra-Moon"]), |
|||
("Let's Go Pikachu", 7, ["Let's Go, Pikachu!", "Lets Go Pikachu"]), |
|||
("Let's Go Eevee", 7, ["Let's Go, Eevee!", "Lets Go Eevee"]), |
|||
("Sword", 8, ["Sword Version"]), |
|||
("Shield", 8, ["Shield Version"]), |
|||
("Expansion Pass", 8, ["Expansion Pass (Sword)", "Expansion Pass (Shield)"]), |
|||
("Brilliant Diamond", 8, ["Brilliant Diamond Version", "Brilliant-Diamond"]), |
|||
("Shining Pearl", 8, ["Shining Pearl Version", "Shining-Pearl"]), |
|||
("Legends Arceus", 8, ["Legends: Arceus", "Legends-Arceus"]), |
|||
("Scarlet", 9, ["Scarlet Version"]), |
|||
("Violet", 9, ["Violet Version"]), |
|||
("The Teal Mask", 9, ["The Teal Mask Version", "The Teal Mask (Scarlet)", "The Teal Mask (Violet)"]), |
|||
("The Hidden Treasure of Area Zero", 9, ["The Hidden Treasure of Area Zero Version", "The Hidden Treasure of Area Zero (Scarlet)", "The Hidden Treasure of Area Zero (Violet)"]), |
|||
|
|||
("Pokémon Home", 98, ["Pokémon HOME"]), |
|||
("Pokémon Go", 99, ["Pokémon GO"]), |
|||
] |
|||
|
|||
for game in games: |
|||
cursor.execute(''' |
|||
INSERT OR IGNORE INTO games (name, generation) |
|||
VALUES (?, ?) |
|||
''', (game[0], game[1])) |
|||
|
|||
game_id = cursor.lastrowid |
|||
|
|||
# Insert alternate names |
|||
for alt_name in game[2]: |
|||
cursor.execute(''' |
|||
INSERT OR IGNORE INTO alternate_game_names (game_id, alternate_name) |
|||
VALUES (?, ?) |
|||
''', (game_id, alt_name)) |
|||
|
|||
def load_pokemon_details(self, pfic): |
|||
self.cursor.execute(''' |
|||
SELECT pf.name, pf.form_name, pf.national_dex, pf.generation, ps.storable_in_home, pf.is_baby_form |
|||
FROM pokemon_forms pf |
|||
LEFT JOIN pokemon_storage ps ON pf.PFIC = ps.PFIC |
|||
WHERE pf.PFIC = ? |
|||
''', (pfic,)) |
|||
|
|||
return self.cursor.fetchone() |
|||
|
|||
def send_pokemon_list(self, data): |
|||
# Fetch pokemon list from database |
|||
self.cursor.execute(''' |
|||
SELECT pf.PFIC, pf.name, pf.form_name, pf.national_dex |
|||
FROM pokemon_forms pf |
|||
''') |
|||
pokemon_data = self.cursor.fetchall() |
|||
|
|||
# Sort the pokemon_data based on PFIC |
|||
pokemon_data.sort(key=lambda x: parse_pfic(x[0])) |
|||
return pokemon_data |
|||
|
|||
def get_home_storable(self, pfic): |
|||
self.cursor.execute('SELECT storable_in_home FROM pokemon_storage WHERE PFIC = ?', (pfic,)) |
|||
result = self.cursor.fetchone() |
|||
home_storable = result[0] if result else False |
|||
return home_storable |
|||
|
|||
def get_evolution_chain(self, pfic): |
|||
def follow_chain(start_pfic, visited=None): |
|||
if visited is None: |
|||
visited = set() |
|||
|
|||
chain = [] |
|||
stack = [(start_pfic, None)] |
|||
|
|||
while stack: |
|||
current_pfic, method = stack.pop() |
|||
if current_pfic in visited: |
|||
continue |
|||
|
|||
visited.add(current_pfic) |
|||
self.cursor.execute('SELECT name, form_name FROM pokemon_forms WHERE PFIC = ?', (current_pfic,)) |
|||
name, form_name = self.cursor.fetchone() |
|||
|
|||
chain.append((current_pfic, name, form_name, method)) |
|||
|
|||
# Get previous evolutions |
|||
self.cursor.execute('SELECT from_pfic, method FROM evolution_chains WHERE to_pfic = ?', (current_pfic,)) |
|||
prev_evolutions = self.cursor.fetchall() |
|||
for prev_pfic, prev_method in prev_evolutions: |
|||
if prev_pfic not in visited: |
|||
stack.append((prev_pfic, prev_method)) |
|||
|
|||
# Get next evolutions |
|||
self.cursor.execute('SELECT to_pfic, method FROM evolution_chains WHERE from_pfic = ?', (current_pfic,)) |
|||
next_evolutions = self.cursor.fetchall() |
|||
for next_pfic, next_method in next_evolutions: |
|||
if next_pfic not in visited: |
|||
stack.append((next_pfic, next_method)) |
|||
|
|||
return chain |
|||
|
|||
return follow_chain(pfic) |
|||
|
|||
def get_evolution_parent(self, pfic): |
|||
self.cursor.execute('SELECT from_pfic FROM evolution_chains WHERE to_pfic = ?', (pfic,)) |
|||
return self.cursor.fetchone() |
|||
|
|||
def get_encounter_locations(self, pfic): |
|||
self.cursor.execute(''' |
|||
SELECT g.name, e.location, e.day, e.time, e.dual_slot, e.static_encounter_count, e.static_encounter, e.extra_text, e.stars, e.rods, e.fishing |
|||
FROM encounters e |
|||
JOIN games g ON e.game_id = g.id |
|||
WHERE e.pfic = ? |
|||
ORDER BY g.name, e.location |
|||
''', (pfic,)) |
|||
return self.cursor.fetchall() |
|||
|
|||
def get_game_generation(self, game): |
|||
self.cursor.execute(''' |
|||
SELECT g.generation |
|||
FROM games g |
|||
LEFT JOIN alternate_game_names agn ON g.id = agn.game_id |
|||
WHERE g.name = ? OR agn.alternate_name = ? |
|||
LIMIT 1 |
|||
''', (game, game)) |
|||
result = self.cursor.fetchone() |
|||
return result[0] if result else None |
|||
|
|||
def get_game_id_for_name(self, game): |
|||
# First, try to find the game in the main games table |
|||
self.cursor.execute(''' |
|||
SELECT id |
|||
FROM games |
|||
WHERE name = ? |
|||
''', (game,)) |
|||
result = self.cursor.fetchone() |
|||
|
|||
if result: |
|||
return result[0] |
|||
|
|||
# If not found, check the alternate_game_names table |
|||
self.cursor.execute(''' |
|||
SELECT game_id |
|||
FROM alternate_game_names |
|||
WHERE alternate_name = ? |
|||
''', (game,)) |
|||
result = self.cursor.fetchone() |
|||
|
|||
return result[0] if result else None |
|||
|
|||
def get_pokemon_with_encounters(self, data): |
|||
self.cursor.execute(''' |
|||
SELECT DISTINCT pfic |
|||
FROM encounters |
|||
''') |
|||
pokemon_with_encounters = set(row[0] for row in self.cursor.fetchall()) |
|||
return pokemon_with_encounters |
|||
|
|||
def refresh_in_memory_db(self, temp_conn): |
|||
temp_conn.backup(self.conn) |
|||
|
|||
def clear_encounters_for_pokemon(self, pfic): |
|||
self.cursor.execute('DELETE FROM encounters WHERE pfic = ?', (pfic,)) |
|||
self.conn.commit() |
|||
@ -0,0 +1,269 @@ |
|||
import sqlite3 |
|||
from collections import defaultdict |
|||
|
|||
class OriginDexPlanner: |
|||
def __init__(self, db_path): |
|||
self.conn = sqlite3.connect(db_path) |
|||
self.conn.row_factory = sqlite3.Row |
|||
|
|||
def get_game_data(self): |
|||
"""Fetch game and pokemon information from the database.""" |
|||
cursor = self.conn.cursor() |
|||
cursor.execute("SELECT * FROM games") |
|||
games = cursor.fetchall() |
|||
|
|||
cursor.execute("SELECT * FROM pokemon_forms WHERE name IN ('Elekid', 'Electabuzz', 'Electivire')") |
|||
pokemon_forms = cursor.fetchall() |
|||
|
|||
cursor.execute("SELECT * FROM encounters") |
|||
encounters = cursor.fetchall() |
|||
|
|||
exclusive_encounters = [] |
|||
cursor.execute("SELECT * FROM evolution_chains WHERE from_pfic IN (SELECT PFIC FROM pokemon_forms WHERE name IN ('Elekid', 'Electabuzz', 'Electivire'))") |
|||
evolutions = cursor.fetchall() |
|||
|
|||
return games, pokemon_forms, encounters, exclusive_encounters, evolutions |
|||
|
|||
def build_pokemon_data(self): |
|||
"""Construct data dictionaries to store relationships between the Pokemon and encounters.""" |
|||
games, pokemon_forms, encounters, exclusive_encounters, evolutions = self.get_game_data() |
|||
|
|||
pokemon_by_gen = defaultdict(list) |
|||
game_by_gen = defaultdict(list) |
|||
encounter_data = defaultdict(list) |
|||
|
|||
for game in games: |
|||
game_by_gen[game['generation']].append(game) |
|||
|
|||
for pokemon in pokemon_forms: |
|||
pokemon_by_gen[pokemon['generation']].append(pokemon) |
|||
|
|||
# Build a dict of encounters, mapping game_id to Pokemon encountered in it |
|||
for encounter in encounters: |
|||
encounter_data[encounter['game_id']].append(encounter) |
|||
|
|||
evolution_map = defaultdict(list) |
|||
for evolution in evolutions: |
|||
from_pfic = evolution['from_pfic'] |
|||
to_pfic = evolution['to_pfic'] |
|||
# Lookup generation from pokemon_forms table |
|||
cursor = self.conn.cursor() |
|||
cursor.execute("SELECT generation FROM pokemon_forms WHERE PFIC = ?", (to_pfic,)) |
|||
to_generation = cursor.fetchone()['generation'] |
|||
evolution_map[from_pfic].append((to_pfic, to_generation)) |
|||
|
|||
exclusive_group_map = defaultdict(list) |
|||
for exclusive_encounter in exclusive_encounters: |
|||
exclusive_group_map[exclusive_encounter['group_id']].append(exclusive_encounter['encounter_id']) |
|||
|
|||
return pokemon_by_gen, game_by_gen, encounter_data, evolution_map, exclusive_group_map |
|||
|
|||
def generate_plan(self, generations_to_group_list=None): |
|||
"""Generate a game plan to catch all Pokemon in their origin generation.""" |
|||
if generations_to_group_list is None: |
|||
generations_to_group_list = [] |
|||
|
|||
pokemon_by_gen, game_by_gen, encounter_data, evolution_map, exclusive_group_map = self.build_pokemon_data() |
|||
|
|||
plan = [] |
|||
pokemon_to_catch = defaultdict(list) |
|||
caught_pokemon = set() |
|||
pokemon_names = {} |
|||
|
|||
# Handle multiple sets of grouped generations |
|||
grouped_generations_sets = [set(generations) for generations in generations_to_group_list] |
|||
for grouped_generations in grouped_generations_sets: |
|||
grouped_games = [] |
|||
grouped_pokemon_list = [] |
|||
for gen in grouped_generations: |
|||
if gen in game_by_gen: |
|||
grouped_games.extend(game_by_gen[gen]) |
|||
grouped_pokemon_list.extend(pokemon_by_gen[gen]) |
|||
|
|||
if grouped_games: |
|||
plan.extend(self.generate_plan_for_generation(grouped_games, grouped_pokemon_list, encounter_data, evolution_map, exclusive_group_map, pokemon_names, caught_pokemon, game_by_gen)) |
|||
|
|||
# Loop through each generation, generating a plan for each game |
|||
for gen in sorted(game_by_gen.keys()): |
|||
# Skip generations that have been grouped |
|||
if any(gen in grouped_generations for grouped_generations in grouped_generations_sets): |
|||
continue |
|||
games = game_by_gen[gen] |
|||
pokemon_list = pokemon_by_gen[gen] |
|||
|
|||
plan.extend(self.generate_plan_for_generation(games, pokemon_list, encounter_data, evolution_map, exclusive_group_map, pokemon_names, caught_pokemon, game_by_gen)) |
|||
|
|||
return plan |
|||
|
|||
def generate_plan_for_generation(self, games, pokemon_list, encounter_data, evolution_map, exclusive_group_map, pokemon_names, caught_pokemon, game_by_gen): |
|||
"""Generate a game plan for a specific generation or group of generations.""" |
|||
plan = [] |
|||
pokemon_to_catch = defaultdict(list) |
|||
pokemon_to_breed = defaultdict(list) |
|||
catchable_pokemon = set() |
|||
|
|||
# Create a set of baby Pokémon PFICs |
|||
baby_pokemon = set(pokemon['PFIC'] for pokemon in pokemon_list if pokemon['is_baby_form']) |
|||
|
|||
# Identify unique evolution chains and the starting point |
|||
for pokemon in pokemon_list: |
|||
pfic = pokemon['PFIC'] |
|||
pokemon_names[pfic] = f'{pokemon["name"]} ({pokemon["form_name"] if pokemon["form_name"] is not None else ""})' |
|||
if pfic not in evolution_map: # Not evolved from anything |
|||
catchable_pokemon.add(pfic) |
|||
else: |
|||
catchable_pokemon.add(pfic) |
|||
# Add all evolution stages to catchable list if they belong to the current or previous generations |
|||
current = pfic |
|||
current_generation = pokemon['generation'] |
|||
while current in evolution_map: |
|||
next_pfic, next_generation = evolution_map[current][0] |
|||
if any(p['PFIC'] == next_pfic for p in pokemon_list): |
|||
if next_generation <= current_generation: |
|||
catchable_pokemon.add(next_pfic) |
|||
current = next_pfic |
|||
|
|||
# Determine the best game to catch the most Pokemon |
|||
best_game = None |
|||
max_catchable = 0 |
|||
game_catchable_pokemon = defaultdict(set) |
|||
for game in games: |
|||
game_id = game['id'] |
|||
encounters = encounter_data[game_id] |
|||
|
|||
# Track how many unique Pokemon are available in this game |
|||
for encounter in encounters: |
|||
pfic = encounter['pfic'] |
|||
if pfic in catchable_pokemon: |
|||
game_catchable_pokemon[game_id].add(pfic) |
|||
|
|||
# Update the best game if this game has more catchable Pokemon |
|||
if len(game_catchable_pokemon[game_id]) > max_catchable: |
|||
max_catchable = len(game_catchable_pokemon[game_id]) |
|||
best_game = game |
|||
|
|||
# Use the best game to catch as many unique Pokemon as possible |
|||
remaining_games = [game for game in games if game != best_game] |
|||
if best_game: |
|||
game_id = best_game['id'] |
|||
game_name = best_game['name'] |
|||
encounters = encounter_data[game_id] |
|||
|
|||
for encounter in encounters: |
|||
pfic = encounter['pfic'] |
|||
if pfic in catchable_pokemon and pfic not in caught_pokemon: |
|||
evolution_chain = self.get_full_evolution_chain(pfic, evolution_map) |
|||
base_pokemon = evolution_chain[0] |
|||
if base_pokemon not in caught_pokemon: |
|||
# Calculate the number of Pokemon needed for the full evolution chain |
|||
count = len(evolution_chain) |
|||
if encounter['static_encounter']: |
|||
count = min(count, encounter['static_encounter_count'] or 1) |
|||
|
|||
if base_pokemon in baby_pokemon: |
|||
# For baby Pokémon, catch the first non-baby evolution and breed for the baby |
|||
non_baby_pokemon = next((p for p in evolution_chain if p not in baby_pokemon), None) |
|||
if non_baby_pokemon: |
|||
pokemon_to_catch[game_name].append({'pfic': non_baby_pokemon, 'count': 1}) |
|||
pokemon_to_breed[game_name].append({'pfic': base_pokemon, 'count': 1}) |
|||
else: |
|||
pokemon_to_catch[game_name].append({'pfic': base_pokemon, 'count': count}) |
|||
|
|||
caught_pokemon.update(evolution_chain) |
|||
|
|||
# Account for exclusive encounters |
|||
exclusive_group_ids = [group['group_id'] for group in exclusive_group_map if group['encounter_id'] == encounter['id']] |
|||
if exclusive_group_ids: |
|||
other_options = exclusive_group_map[exclusive_group_ids[0]] |
|||
if len(other_options) > 1: |
|||
# Pick one of the options, mark the rest to catch in another game |
|||
catchable_pokemon.remove(pfic) |
|||
else: |
|||
continue |
|||
|
|||
# Use remaining games to catch any remaining Pokemon |
|||
for game in remaining_games: |
|||
game_id = game['id'] |
|||
game_name = game['name'] |
|||
encounters = encounter_data[game_id] |
|||
|
|||
for encounter in encounters: |
|||
pfic = encounter['pfic'] |
|||
if pfic in catchable_pokemon and pfic not in caught_pokemon: |
|||
evolution_chain = self.get_full_evolution_chain(pfic, evolution_map) |
|||
base_pokemon = evolution_chain[0] |
|||
if base_pokemon not in caught_pokemon: |
|||
# Calculate the number of Pokemon needed for the full evolution chain |
|||
count = len(evolution_chain) |
|||
if encounter['static_encounter']: |
|||
count = min(count, encounter['static_encounter_count'] or 1) |
|||
|
|||
if base_pokemon in baby_pokemon: |
|||
# For baby Pokémon, catch the first non-baby evolution and breed for the baby |
|||
non_baby_pokemon = next((p for p in evolution_chain if p not in baby_pokemon), None) |
|||
if non_baby_pokemon: |
|||
pokemon_to_catch[game_name].append({'pfic': non_baby_pokemon, 'count': 1}) |
|||
pokemon_to_breed[game_name].append({'pfic': base_pokemon, 'count': 1}) |
|||
else: |
|||
pokemon_to_catch[game_name].append({'pfic': base_pokemon, 'count': count}) |
|||
|
|||
caught_pokemon.update(evolution_chain) |
|||
|
|||
# Handle new evolutions or forms added in later generations |
|||
for pfic in list(caught_pokemon): |
|||
if pfic in evolution_map: |
|||
for new_evolution, new_generation in evolution_map[pfic]: |
|||
# Check if the new evolution is from a later generation |
|||
full_evolution_chain = self.get_full_evolution_chain(pfic, evolution_map) |
|||
current_generation = min((pokemon['generation'] for pokemon in pokemon_list if pokemon['PFIC'] in full_evolution_chain), default=None) |
|||
if new_generation and current_generation and new_generation > current_generation: |
|||
# Add the baby/base form to be caught in the new generation if required |
|||
base_pokemon = full_evolution_chain[0] |
|||
new_gen_games = game_by_gen[new_generation] |
|||
for game in new_gen_games: |
|||
game_id = game['id'] |
|||
game_name = game['name'] |
|||
encounters = encounter_data[game_id] |
|||
for encounter in encounters: |
|||
if encounter['pfic'] == base_pokemon and base_pokemon not in caught_pokemon: |
|||
if base_pokemon in baby_pokemon: |
|||
non_baby_pokemon = next((p for p in full_evolution_chain if p not in baby_pokemon), None) |
|||
if non_baby_pokemon: |
|||
pokemon_to_catch[game_name].append({'pfic': non_baby_pokemon, 'count': 1}) |
|||
pokemon_to_breed[game_name].append({'pfic': base_pokemon, 'count': 1}) |
|||
else: |
|||
pokemon_to_catch[game_name].append({'pfic': base_pokemon, 'count': 1}) |
|||
caught_pokemon.add(base_pokemon) |
|||
break |
|||
|
|||
# Generate the plan for this generation or group of generations |
|||
for game_name in set(list(pokemon_to_catch.keys()) + list(pokemon_to_breed.keys())): |
|||
plan.append(f"Play {game_name}:") |
|||
if game_name in pokemon_to_catch: |
|||
plan.append(f" Catch {len(pokemon_to_catch[game_name])} unique Pokemon:") |
|||
for pokemon in pokemon_to_catch[game_name]: |
|||
plan.append(f" - {pokemon_names[pokemon['pfic']]}: {pokemon['count']} times") |
|||
|
|||
if game_name in pokemon_to_breed: |
|||
plan.append(f" Breed {len(pokemon_to_breed[game_name])} Pokemon:") |
|||
for pokemon in pokemon_to_breed[game_name]: |
|||
plan.append(f" - {pokemon_names[pokemon['pfic']]}: {pokemon['count']} times") |
|||
|
|||
return plan |
|||
|
|||
def get_full_evolution_chain(self, pfic, evolution_map): |
|||
"""Get the full evolution chain for a given PFIC.""" |
|||
chain = [pfic] |
|||
while pfic in evolution_map: |
|||
pfic = evolution_map[pfic][0][0] |
|||
chain.append(pfic) |
|||
return chain |
|||
|
|||
def display_plan(self, generations_to_group_list=None): |
|||
plan = self.generate_plan(generations_to_group_list) |
|||
for step in plan: |
|||
print(step) |
|||
|
|||
# Example usage |
|||
planner = OriginDexPlanner('pokemon_forms.db') |
|||
planner.display_plan(generations_to_group_list=[[1, 2], [3, 4, 5, 6]]) |
|||
@ -0,0 +1,262 @@ |
|||
import sqlite3 |
|||
import os |
|||
from collections import defaultdict |
|||
|
|||
class EfficiencyOriginDexPlanner: |
|||
def __init__(self, db_path): |
|||
self.conn = sqlite3.connect(db_path) |
|||
self.conn.row_factory = sqlite3.Row |
|||
|
|||
def get_all_data(self): |
|||
cursor = self.conn.cursor() |
|||
cursor.execute("SELECT * FROM games ORDER BY generation, id") |
|||
games = cursor.fetchall() |
|||
|
|||
cursor.execute("SELECT * FROM pokemon_forms ORDER BY generation, national_dex") |
|||
pokemon_forms = cursor.fetchall() |
|||
|
|||
cursor.execute("SELECT * FROM encounters") |
|||
encounters = cursor.fetchall() |
|||
|
|||
cursor.execute("SELECT * FROM evolution_chains") |
|||
evolutions = cursor.fetchall() |
|||
|
|||
return games, pokemon_forms, encounters, evolutions |
|||
|
|||
def build_data_structures(self): |
|||
games, pokemon_forms, encounters, evolutions = self.get_all_data() |
|||
|
|||
pokemon_by_gen = defaultdict(list) |
|||
game_by_gen = defaultdict(list) |
|||
encounter_data = defaultdict(list) |
|||
evolution_map = defaultdict(list) |
|||
|
|||
for game in games: |
|||
game_by_gen[game['generation']].append(game) |
|||
|
|||
for pokemon in pokemon_forms: |
|||
pokemon_by_gen[pokemon['generation']].append(pokemon) |
|||
|
|||
for encounter in encounters: |
|||
encounter_data[encounter['game_id']].append(encounter) |
|||
|
|||
for evolution in evolutions: |
|||
evolution_map[evolution['from_pfic']].append(evolution['to_pfic']) |
|||
|
|||
return pokemon_by_gen, game_by_gen, encounter_data, evolution_map |
|||
|
|||
def generate_efficient_plan(self, generation_groups): |
|||
pokemon_by_gen, game_by_gen, encounter_data, evolution_map = self.build_data_structures() |
|||
plan = [] |
|||
caught_pokemon = set() |
|||
|
|||
for group in generation_groups: |
|||
group_plan = self.plan_for_group(group, pokemon_by_gen, game_by_gen, encounter_data, evolution_map, caught_pokemon) |
|||
plan.extend(group_plan) |
|||
|
|||
return plan |
|||
|
|||
def plan_for_group(self, generations, pokemon_by_gen, game_by_gen, encounter_data, evolution_map, caught_pokemon): |
|||
group_plan = [] |
|||
needed_pokemon = set() |
|||
games_in_group = [] |
|||
|
|||
for gen in generations: |
|||
needed_pokemon.update(pokemon['PFIC'] for pokemon in pokemon_by_gen[gen]) |
|||
games_in_group.extend(game_by_gen[gen]) |
|||
|
|||
# Check for new evolutions of already caught Pokémon |
|||
new_evolutions = set() |
|||
for pfic in caught_pokemon: |
|||
if pfic in evolution_map: |
|||
for evolved_pfic in evolution_map[pfic]: |
|||
if self.get_pokemon_generation(evolved_pfic) in generations and evolved_pfic not in caught_pokemon: |
|||
new_evolutions.add(pfic) |
|||
needed_pokemon.add(evolved_pfic) |
|||
print(f"New evolution: {self.get_pokemon_name(pfic)} into {self.get_pokemon_name(evolved_pfic)}") |
|||
|
|||
games_in_group.sort(key=lambda g: len([e for e in encounter_data[g['id']] if e['pfic'] in needed_pokemon]), reverse=True) |
|||
|
|||
for game in games_in_group: |
|||
game_plan = self.plan_for_game(game, needed_pokemon, encounter_data, evolution_map, caught_pokemon, set(generations), new_evolutions) |
|||
if game_plan: |
|||
group_plan.extend(game_plan) |
|||
|
|||
return group_plan |
|||
|
|||
def plan_for_game(self, game, needed_pokemon, encounter_data, evolution_map, caught_pokemon, target_generations, new_evolutions): |
|||
game_plan = [] |
|||
to_catch = defaultdict(int) |
|||
to_evolve = defaultdict(list) |
|||
to_breed = defaultdict(list) |
|||
planned_evolutions = defaultdict(int) |
|||
processed_chains = set() |
|||
|
|||
# First, handle new evolutions for already caught Pokémon |
|||
for pfic in new_evolutions: |
|||
evolution_chains, visit_counts, baby_forms = self.get_evolution_chains(pfic, evolution_map, target_generations) |
|||
|
|||
new_evolutions_count = sum(1 for chain in evolution_chains for pokemon in chain if pokemon not in caught_pokemon) |
|||
|
|||
if new_evolutions_count > 0: |
|||
if any(encounter['pfic'] == pfic for encounter in encounter_data[game['id']]): |
|||
to_catch[pfic] += new_evolutions_count - planned_evolutions[pfic] |
|||
planned_evolutions[pfic] += new_evolutions_count |
|||
|
|||
for chain in evolution_chains: |
|||
if len(chain) > 1: |
|||
for i in range(len(chain) - 1): |
|||
from_pfic, to_pfic = chain[i], chain[i+1] |
|||
if to_pfic not in caught_pokemon: |
|||
to_evolve[from_pfic].append(to_pfic) |
|||
caught_pokemon.add(to_pfic) |
|||
needed_pokemon.discard(to_pfic) |
|||
|
|||
# Then proceed with the regular planning |
|||
for encounter in encounter_data[game['id']]: |
|||
if encounter['pfic'] in needed_pokemon and encounter['pfic'] not in caught_pokemon: |
|||
evolution_chains, visit_counts, baby_forms = self.get_evolution_chains(encounter['pfic'], evolution_map, target_generations) |
|||
base_form = evolution_chains[0][0] |
|||
|
|||
if base_form not in caught_pokemon: |
|||
chain_key = tuple(sorted(set(pokemon for chain in evolution_chains for pokemon in chain))) |
|||
if chain_key not in processed_chains: |
|||
processed_chains.add(chain_key) |
|||
|
|||
num_to_catch = max(0, visit_counts[base_form] - planned_evolutions[base_form]) |
|||
if num_to_catch > 0: |
|||
to_catch[encounter['pfic']] += num_to_catch |
|||
planned_evolutions[base_form] += num_to_catch |
|||
|
|||
for chain in evolution_chains: |
|||
if len(chain) > 1: |
|||
for i in range(len(chain) - 1): |
|||
from_pfic, to_pfic = chain[i], chain[i+1] |
|||
if from_pfic not in baby_forms: |
|||
if not self.is_final_evolution(to_pfic, evolution_map) or (self.is_final_evolution(to_pfic, evolution_map) and not self.is_in_to_evolve(to_pfic, to_evolve)): |
|||
to_evolve[from_pfic].append(to_pfic) |
|||
else: |
|||
to_catch[encounter['pfic']] += -1 |
|||
planned_evolutions[base_form] += -1 |
|||
|
|||
if from_pfic in baby_forms and from_pfic not in encounter_data[game['id']]: |
|||
if to_pfic not in to_breed or from_pfic not in to_breed[to_pfic]: |
|||
to_breed[to_pfic].append(from_pfic) |
|||
|
|||
caught_pokemon.update(pfic for chain in evolution_chains for pfic in chain) |
|||
needed_pokemon.difference_update(pfic for chain in evolution_chains for pfic in chain) |
|||
|
|||
if to_catch or to_evolve or to_breed: |
|||
game_plan.append(f"Play {game['name']}:") |
|||
if to_catch: |
|||
game_plan.append(" Catch:") |
|||
for pfic, count in to_catch.items(): |
|||
game_plan.append(f" - {self.get_pokemon_name(pfic)}: {count} time(s)") |
|||
if to_evolve: |
|||
game_plan.append(" Evolve:") |
|||
for from_pfic, to_pfics in to_evolve.items(): |
|||
for to_pfic in to_pfics: |
|||
game_plan.append(f" - {self.get_pokemon_name(from_pfic)} into {self.get_pokemon_name(to_pfic)}") |
|||
if to_breed: |
|||
game_plan.append(" Breed:") |
|||
for parent_pfic, baby_pfics in to_breed.items(): |
|||
for baby_pfic in baby_pfics: |
|||
game_plan.append(f" - {self.get_pokemon_name(parent_pfic)} to get {self.get_pokemon_name(baby_pfic)}") |
|||
|
|||
return game_plan |
|||
|
|||
def plan_evolutions(self, evolution_chains, evolution_map, caught_pokemon): |
|||
chains, visit_counts = self.get_evolution_chains(evolution_chains[0][0], evolution_map) |
|||
evolution_plan = [] |
|||
needed_counts = defaultdict(int) |
|||
|
|||
# Count how many of each Pokémon we need based on visit counts |
|||
for pfic, count in visit_counts.items(): |
|||
if pfic not in caught_pokemon: |
|||
needed_counts[pfic] = count |
|||
|
|||
# Plan evolutions |
|||
for chain in chains: |
|||
for i in range(len(chain) - 1): |
|||
from_pfic, to_pfic = chain[i], chain[i+1] |
|||
if needed_counts[to_pfic] > 0: |
|||
evolution_plan.append((from_pfic, to_pfic)) |
|||
needed_counts[to_pfic] -= 1 |
|||
needed_counts[from_pfic] -= 1 |
|||
|
|||
return evolution_plan, needed_counts |
|||
|
|||
def get_evolution_chains(self, pfic, evolution_map, target_generations): |
|||
visited = defaultdict(int) |
|||
unique_paths = set() |
|||
baby_forms = set() |
|||
|
|||
def dfs(current_pfic, path): |
|||
path_tuple = tuple(path) |
|||
if path_tuple in unique_paths: |
|||
return |
|||
|
|||
unique_paths.add(path_tuple) |
|||
for pfic in path: |
|||
visited[pfic] += 1 |
|||
if self.is_baby_pokemon(pfic): |
|||
baby_forms.add(pfic) |
|||
|
|||
if current_pfic in evolution_map: |
|||
for next_pfic in evolution_map[current_pfic]: |
|||
if self.get_pokemon_generation(next_pfic) in target_generations: |
|||
dfs(next_pfic, path + [next_pfic]) |
|||
|
|||
dfs(pfic, [pfic]) |
|||
chains = [list(path) for path in unique_paths] |
|||
|
|||
# Adjust visit counts for baby Pokémon |
|||
for baby in baby_forms: |
|||
visited[baby] = 1 |
|||
|
|||
return chains, visited, baby_forms |
|||
|
|||
def get_pokemon_name(self, pfic): |
|||
cursor = self.conn.cursor() |
|||
cursor.execute("SELECT name, form_name FROM pokemon_forms WHERE PFIC = ?", (pfic,)) |
|||
result = cursor.fetchone() |
|||
if result['form_name']: |
|||
return f"{result['name']} ({result['form_name']})" |
|||
return result['name'] |
|||
|
|||
def display_plan(self, generation_groups, output_file='efficiency_plan.txt'): |
|||
plan = self.generate_efficient_plan(generation_groups) |
|||
|
|||
# Print to console |
|||
for step in plan: |
|||
print(step) |
|||
|
|||
# Write to file |
|||
with open(output_file, 'w', encoding='utf-8') as f: |
|||
for step in plan: |
|||
f.write(f"{step}\n") |
|||
|
|||
print(f"\nPlan has been written to {os.path.abspath(output_file)}") |
|||
|
|||
def is_baby_pokemon(self, pfic): |
|||
cursor = self.conn.cursor() |
|||
cursor.execute("SELECT is_baby_form FROM pokemon_forms WHERE PFIC = ?", (pfic,)) |
|||
result = cursor.fetchone() |
|||
return result['is_baby_form'] if result else False |
|||
|
|||
def get_pokemon_generation(self, pfic): |
|||
cursor = self.conn.cursor() |
|||
cursor.execute("SELECT generation FROM pokemon_forms WHERE PFIC = ?", (pfic,)) |
|||
result = cursor.fetchone() |
|||
return result['generation'] if result else None |
|||
|
|||
def is_final_evolution(self, pfic, evolution_map): |
|||
return pfic not in evolution_map |
|||
|
|||
def is_in_to_evolve(self, pfic, to_evolve): |
|||
return any(pfic in to_pfics for to_pfics in to_evolve.values()) |
|||
|
|||
# Example usage |
|||
planner = EfficiencyOriginDexPlanner('pokemon_forms.db') |
|||
planner.display_plan([[1, 2], [3, 4, 5, 6], [7], [8], [9]]) |
|||
|
|||
@ -1,87 +0,0 @@ |
|||
<!DOCTYPE html> |
|||
<html> |
|||
<head> |
|||
<title>Pokémon Database Visualizer</title> |
|||
<link rel="stylesheet" href="styles.css"> |
|||
</head> |
|||
<body> |
|||
<div id="app"> |
|||
<div id="tabs"> |
|||
<button class="tab-button active" data-tab="forms">Pokémon Forms</button> |
|||
<button class="tab-button" data-tab="evolutions">Evolution Chains</button> |
|||
</div> |
|||
|
|||
<div id="forms-tab" class="tab-content active"> |
|||
<div class="forms-container"> |
|||
<div id="pokemon-forms-list"> |
|||
<input type="text" id="forms-filter" placeholder="Filter Pokémon..."> |
|||
<ul id="forms-list-items"></ul> |
|||
</div> |
|||
<div id="pokemon-details"> |
|||
<h2>Pokémon Details</h2> |
|||
<div id="details-content"> |
|||
<div id="pokemon-basic-info"> |
|||
<img id="pokemon-image" src="" alt="Pokémon Image"> |
|||
<div id="pokemon-info"> |
|||
<h3 id="pokemon-name"></h3> |
|||
<p id="pokemon-pfic"></p> |
|||
<p id="pokemon-national-dex"></p> |
|||
<p id="pokemon-generation"></p> |
|||
<div id="pokemon-storable"> |
|||
<label for="storable-checkbox">Storable in Home:</label> |
|||
<input type="checkbox" id="storable-checkbox"> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div id="pokemon-evolution-chain"> |
|||
<h4>Evolution Chain</h4> |
|||
<div id="details-evolution-chain-content"> |
|||
<table id="evolution-table"> |
|||
<!-- The table will be populated dynamically --> |
|||
</table> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
|
|||
|
|||
<div id="evolutions-tab" class="tab-content"> |
|||
<div class="evolution-container"> |
|||
<div id="pokemon-list"> |
|||
<input type="text" id="pokemon-filter" placeholder="Filter Pokémon..."> |
|||
<ul id="pokemon-list-items"></ul> |
|||
</div> |
|||
<div id="evolution-chain-container"> |
|||
<div id="evolution-chain"></div> |
|||
<div id="evolution-controls"> |
|||
<button id="add-stage">Add Stage</button> |
|||
<button id="save-evolution-changes">Save Changes</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<script src="renderer.js"></script> |
|||
|
|||
<!-- Add this just before the closing </body> tag --> |
|||
<div id="edit-pokemon-modal" class="modal"> |
|||
<div class="modal-content"> |
|||
<span class="close">×</span> |
|||
<h2>Edit Pokémon</h2> |
|||
<form id="edit-pokemon-form"> |
|||
<label for="pokemon-select">Select Pokémon:</label> |
|||
<select id="pokemon-select" required> |
|||
<!-- Options will be populated dynamically --> |
|||
</select> |
|||
<label for="evolution-method">Evolution Method:</label> |
|||
<input type="text" id="evolution-method" required> |
|||
<button type="submit">Save Changes</button> |
|||
</form> |
|||
</div> |
|||
</div> |
|||
</body> |
|||
</html> |
|||
@ -1,200 +0,0 @@ |
|||
const { app, BrowserWindow, ipcMain } = require('electron'); |
|||
const path = require('path'); |
|||
const sqlite3 = require('sqlite3').verbose(); |
|||
|
|||
app.commandLine.appendSwitch('remote-debugging-port', '9222'); |
|||
|
|||
let mainWindow; |
|||
let db; |
|||
|
|||
function createWindow() { |
|||
mainWindow = new BrowserWindow({ |
|||
width: 1200, |
|||
height: 800, |
|||
webPreferences: { |
|||
nodeIntegration: true, |
|||
contextIsolation: false |
|||
} |
|||
}); |
|||
|
|||
mainWindow.loadFile('index.html'); |
|||
|
|||
db = new sqlite3.Database('../pokemon_forms.db', (err) => { |
|||
if (err) { |
|||
console.error('Database opening error: ', err); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
app.whenReady().then(createWindow); |
|||
|
|||
app.on('window-all-closed', () => { |
|||
if (process.platform !== 'darwin') { |
|||
app.quit(); |
|||
} |
|||
}); |
|||
|
|||
app.on('activate', () => { |
|||
if (BrowserWindow.getAllWindows().length === 0) { |
|||
createWindow(); |
|||
} |
|||
}); |
|||
|
|||
ipcMain.on('load-forms-data', (event) => { |
|||
db.all(` |
|||
SELECT pf.PFIC, pf.name, pf.form_name, pf.national_dex, pf.generation, ps.storable_in_home |
|||
FROM pokemon_forms pf |
|||
LEFT JOIN pokemon_storage ps ON pf.PFIC = ps.PFIC |
|||
`, (err, rows) => {
|
|||
if (err) { |
|||
console.error('Error fetching data: ', err); |
|||
event.reply('forms-data-response', { error: err.message }); |
|||
} else { |
|||
event.reply('forms-data-response', { data: rows }); |
|||
} |
|||
}); |
|||
}); |
|||
|
|||
ipcMain.on('search-evolution', (event, searchTerm) => { |
|||
db.all(` |
|||
SELECT DISTINCT name, PFIC |
|||
FROM pokemon_forms |
|||
WHERE LOWER(name) LIKE ? |
|||
`, [`%${searchTerm.toLowerCase()}%`], (err, rows) => {
|
|||
if (err) { |
|||
console.error('Error searching evolution: ', err); |
|||
event.reply('evolution-search-response', { error: err.message }); |
|||
} else { |
|||
event.reply('evolution-search-response', { data: rows }); |
|||
} |
|||
}); |
|||
}); |
|||
|
|||
ipcMain.on('get-evolution-chain', (event, pfic) => { |
|||
function getEvolutions(currentPfic) { |
|||
return new Promise((resolve, reject) => { |
|||
db.all(` |
|||
SELECT ec.to_pfic as pfic, pf.name, pf.form_name, ec.method, ec.from_pfic |
|||
FROM evolution_chains ec |
|||
JOIN pokemon_forms pf ON ec.to_pfic = pf.PFIC |
|||
WHERE ec.from_pfic = ? |
|||
`, [currentPfic], (err, rows) => {
|
|||
if (err) { |
|||
reject(err); |
|||
} else { |
|||
resolve(rows); |
|||
} |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
async function buildChain(pfic) { |
|||
const pokemon = await new Promise((resolve, reject) => { |
|||
db.get(`SELECT PFIC as pfic, name, form_name FROM pokemon_forms WHERE PFIC = ?`, [pfic], (err, row) => { |
|||
if (err) reject(err); |
|||
else resolve(row); |
|||
}); |
|||
}); |
|||
|
|||
if (!pokemon) return null; |
|||
|
|||
const evolutions = await getEvolutions(pfic); |
|||
pokemon.evolutions = await Promise.all(evolutions.map(evo => buildChain(evo.pfic))); |
|||
|
|||
return pokemon; |
|||
} |
|||
|
|||
buildChain(pfic) |
|||
.then(chain => { |
|||
event.reply('evolution-chain-response', { data: chain }); |
|||
}) |
|||
.catch(err => { |
|||
console.error('Error building evolution chain:', err); |
|||
event.reply('evolution-chain-response', { error: err.message }); |
|||
}); |
|||
}); |
|||
|
|||
ipcMain.on('edit-pokemon', (event, data) => { |
|||
// Implement the logic to update the Pokémon in the database
|
|||
console.log('Editing Pokémon:', data); |
|||
// Update the database and send a response back to the renderer
|
|||
}); |
|||
|
|||
ipcMain.on('remove-pokemon', (event, data) => { |
|||
// Implement the logic to remove the Pokémon from the evolution chain in the database
|
|||
console.log('Removing Pokémon:', data); |
|||
// Update the database and send a response back to the renderer
|
|||
}); |
|||
|
|||
ipcMain.on('add-stage', (event, data) => { |
|||
// Implement the logic to add a new stage to the evolution chain in the database
|
|||
console.log('Adding new stage:', data); |
|||
// Update the database and send a response back to the renderer
|
|||
}); |
|||
|
|||
ipcMain.on('save-evolution-changes', (event, data) => { |
|||
// Implement the logic to save all changes to the evolution chain in the database
|
|||
console.log('Saving evolution changes:', data); |
|||
|
|||
// Here you would update the database with the new evolution chain data
|
|||
// This is a placeholder implementation
|
|||
setTimeout(() => { |
|||
event.reply('save-evolution-changes-response', { success: true }); |
|||
}, 1000); |
|||
|
|||
// If there's an error, you would reply with:
|
|||
// event.reply('save-evolution-changes-response', { error: 'Error message' });
|
|||
}); |
|||
|
|||
// Add more IPC handlers for other database operations
|
|||
|
|||
// Add this IPC handler
|
|||
ipcMain.on('load-all-pokemon', (event) => { |
|||
db.all(` |
|||
SELECT PFIC, name, form_name |
|||
FROM pokemon_forms |
|||
ORDER BY |
|||
CAST(SUBSTR(PFIC, 1, 4) AS INTEGER), -- National Dex number |
|||
CAST(SUBSTR(PFIC, 6, 2) AS INTEGER), -- Region code |
|||
CAST(SUBSTR(PFIC, 9, 3) AS INTEGER), -- Form index |
|||
CAST(SUBSTR(PFIC, 13, 1) AS INTEGER) -- Gender code |
|||
`, (err, rows) => {
|
|||
if (err) { |
|||
console.error('Error fetching all Pokémon:', err); |
|||
event.reply('all-pokemon-response', { error: err.message }); |
|||
} else { |
|||
event.reply('all-pokemon-response', { data: rows }); |
|||
} |
|||
}); |
|||
}); |
|||
|
|||
ipcMain.on('get-pokemon-details', (event, pfic) => { |
|||
db.get(` |
|||
SELECT pf.PFIC, pf.name, pf.form_name, pf.national_dex, pf.generation, ps.storable_in_home |
|||
FROM pokemon_forms pf |
|||
LEFT JOIN pokemon_storage ps ON pf.PFIC = ps.PFIC |
|||
WHERE pf.PFIC = ? |
|||
`, [pfic], (err, row) => {
|
|||
if (err) { |
|||
console.error('Error fetching Pokémon details:', err); |
|||
event.reply('pokemon-details-response', { error: err.message }); |
|||
} else { |
|||
event.reply('pokemon-details-response', { data: row }); |
|||
} |
|||
}); |
|||
}); |
|||
|
|||
ipcMain.on('update-storable-in-home', (event, data) => { |
|||
db.run(` |
|||
UPDATE pokemon_storage |
|||
SET storable_in_home = ? |
|||
WHERE PFIC = ? |
|||
`, [data.storable ? 1 : 0, data.pfic], (err) => {
|
|||
if (err) { |
|||
console.error('Error updating storable in home:', err); |
|||
event.reply('update-storable-in-home-response', { error: err.message }); |
|||
} else { |
|||
event.reply('update-storable-in-home-response', { success: true }); |
|||
} |
|||
}); |
|||
}); |
|||
File diff suppressed because it is too large
@ -1,19 +0,0 @@ |
|||
{ |
|||
"name": "dbvisualiser", |
|||
"version": "1.0.0", |
|||
"main": "main.js", |
|||
"scripts": { |
|||
"test": "echo \"Error: no test specified\" && exit 1", |
|||
"start": "electron . --inspect=5858" |
|||
}, |
|||
"keywords": [], |
|||
"author": "", |
|||
"license": "ISC", |
|||
"description": "", |
|||
"dependencies": { |
|||
"electron": "^32.1.2", |
|||
"electron-builder": "^25.1.7", |
|||
"sqlite3": "^5.1.7" |
|||
} |
|||
|
|||
} |
|||
@ -1,483 +0,0 @@ |
|||
const { ipcRenderer } = require('electron'); |
|||
|
|||
// Add these variables at the top of the file
|
|||
let currentEditingStageIndex, currentEditingPokemonIndex; |
|||
let allPokemon = []; // This will store all Pokémon for the select dropdown
|
|||
let currentEvolutionChain = null; // Add this line
|
|||
let allPokemonForms = []; |
|||
|
|||
document.addEventListener('DOMContentLoaded', () => { |
|||
loadFormsData(); |
|||
setupTabButtons(); |
|||
setupSearchButtons(); |
|||
setupSaveChangesButton(); |
|||
|
|||
const modal = document.getElementById('edit-pokemon-modal'); |
|||
const closeBtn = modal.querySelector('.close'); |
|||
const form = document.getElementById('edit-pokemon-form'); |
|||
|
|||
closeBtn.onclick = () => { |
|||
modal.style.display = 'none'; |
|||
}; |
|||
|
|||
window.onclick = (event) => { |
|||
if (event.target === modal) { |
|||
modal.style.display = 'none'; |
|||
} |
|||
}; |
|||
|
|||
form.onsubmit = (e) => { |
|||
e.preventDefault(); |
|||
const newPfic = document.getElementById('pokemon-select').value; |
|||
const newMethod = document.getElementById('evolution-method').value; |
|||
updatePokemonInChain(currentEditingStageIndex, currentEditingPokemonIndex, newPfic, newMethod); |
|||
modal.style.display = 'none'; |
|||
}; |
|||
|
|||
loadAllPokemon(); |
|||
setupPokemonFilter(); |
|||
}); |
|||
|
|||
function setupTabButtons() { |
|||
const tabButtons = document.querySelectorAll('.tab-button'); |
|||
tabButtons.forEach(button => { |
|||
button.addEventListener('click', () => { |
|||
const tabName = button.getAttribute('data-tab'); |
|||
activateTab(tabName); |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
function activateTab(tabName) { |
|||
document.querySelectorAll('.tab-button').forEach(btn => btn.classList.remove('active')); |
|||
document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active')); |
|||
|
|||
document.querySelector(`.tab-button[data-tab="${tabName}"]`).classList.add('active'); |
|||
document.getElementById(`${tabName}-tab`).classList.add('active'); |
|||
} |
|||
|
|||
function setupSearchButtons() { |
|||
//document.getElementById('forms-search-button').addEventListener('click', searchForms);
|
|||
//document.getElementById('evolution-search-button').addEventListener('click', searchEvolution);
|
|||
} |
|||
|
|||
function setupSaveChangesButton() { |
|||
//document.getElementById('save-changes').addEventListener('click', saveChanges);
|
|||
} |
|||
|
|||
function loadFormsData() { |
|||
ipcRenderer.send('load-all-pokemon'); |
|||
} |
|||
|
|||
ipcRenderer.on('all-pokemon-response', (event, response) => { |
|||
if (response.error) { |
|||
console.error('Error loading all Pokémon:', response.error); |
|||
} else { |
|||
allPokemonForms = response.data; |
|||
populateFormsList(); |
|||
setupFormsFilter(); |
|||
} |
|||
}); |
|||
|
|||
function populateFormsList() { |
|||
const listElement = document.getElementById('forms-list-items'); |
|||
listElement.innerHTML = ''; |
|||
allPokemonForms.forEach(pokemon => { |
|||
const li = document.createElement('li'); |
|||
li.textContent = `${pokemon.name} ${pokemon.form_name ? `(${pokemon.form_name})` : ''}`; |
|||
li.addEventListener('click', () => showPokemonDetails(pokemon.PFIC)); |
|||
listElement.appendChild(li); |
|||
}); |
|||
} |
|||
|
|||
function setupFormsFilter() { |
|||
const filterInput = document.getElementById('forms-filter'); |
|||
filterInput.addEventListener('input', () => { |
|||
const filterValue = filterInput.value.toLowerCase(); |
|||
const listItems = document.querySelectorAll('#forms-list-items li'); |
|||
listItems.forEach(item => { |
|||
const text = item.textContent.toLowerCase(); |
|||
item.style.display = text.includes(filterValue) ? '' : 'none'; |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
function showPokemonDetails(pfic) { |
|||
ipcRenderer.send('get-pokemon-details', pfic); |
|||
} |
|||
|
|||
ipcRenderer.on('pokemon-details-response', (event, response) => { |
|||
if (response.error) { |
|||
console.error('Error fetching Pokémon details:', response.error); |
|||
} else { |
|||
displayPokemonDetails(response.data); |
|||
} |
|||
}); |
|||
|
|||
function displayPokemonDetails(pokemon) { |
|||
const detailsContent = document.getElementById('details-content'); |
|||
const pokemonImage = document.getElementById('pokemon-image'); |
|||
const pokemonName = document.getElementById('pokemon-name'); |
|||
const pokemonPfic = document.getElementById('pokemon-pfic'); |
|||
const pokemonNationalDex = document.getElementById('pokemon-national-dex'); |
|||
const pokemonGeneration = document.getElementById('pokemon-generation'); |
|||
const storableCheckbox = document.getElementById('storable-checkbox'); |
|||
const evolutionChainContent = document.getElementById('details-evolution-chain-content'); |
|||
|
|||
pokemonImage.src = `../images-new/${pokemon.PFIC}.png`; |
|||
pokemonImage.onerror = () => { pokemonImage.src = 'placeholder.png'; }; |
|||
|
|||
pokemonName.textContent = `${pokemon.name} ${pokemon.form_name ? `(${pokemon.form_name})` : ''}`; |
|||
pokemonPfic.textContent = `PFIC: ${pokemon.PFIC}`; |
|||
pokemonNationalDex.textContent = `National Dex: ${pokemon.national_dex.toString().padStart(4, '0')}`; |
|||
pokemonGeneration.textContent = `Generation: ${pokemon.generation}`; |
|||
|
|||
storableCheckbox.checked = pokemon.storable_in_home; |
|||
storableCheckbox.addEventListener('change', () => updateStorableInHome(pokemon.PFIC, storableCheckbox.checked)); |
|||
|
|||
// Load and display evolution chain
|
|||
loadEvolutionChain(pokemon.PFIC); |
|||
} |
|||
|
|||
function updateStorableInHome(pfic, storable) { |
|||
ipcRenderer.send('update-storable-in-home', { pfic, storable }); |
|||
} |
|||
|
|||
function loadEvolutionChain(pfic) { |
|||
ipcRenderer.send('get-evolution-chain', pfic); |
|||
} |
|||
|
|||
ipcRenderer.on('evolution-chain-response', (event, response) => { |
|||
if (response.error) { |
|||
console.error('Error fetching evolution chain:', response.error); |
|||
} else { |
|||
displayEvolutionChain(response.data); |
|||
} |
|||
}); |
|||
|
|||
function displayEvolutionChain(chain) { |
|||
const table = document.getElementById('evolution-table'); |
|||
table.innerHTML = ''; |
|||
|
|||
const stages = splitIntoStages(chain); |
|||
const maxForms = Math.max(...stages.map(stage => stage.length)); |
|||
const rowCount = maxForms % 2 === 0 ? maxForms + 1 : maxForms; |
|||
const middleRow = Math.floor(rowCount / 2) |
|||
|
|||
for (let i = 0; i < rowCount; i++) { |
|||
const row = table.insertRow(); |
|||
for (let j = 0; j < stages.length; j++) { |
|||
const cell = row.insertCell(); |
|||
} |
|||
} |
|||
|
|||
stages.forEach((stage, stageIndex) => { |
|||
if (stage.length == 1) |
|||
{ |
|||
const pokemon = stage[0]; |
|||
table.rows[middleRow].cells[stageIndex].appendChild(createPokemonElement(pokemon)); |
|||
} else { |
|||
let start = middleRow - Math.floor(stage.length / 2) |
|||
|
|||
stage.forEach((pokemon, index) => { |
|||
let rowIndex = start + index; |
|||
|
|||
// If the number of elements is even, skip the middle row
|
|||
if (stage.length % 2 === 0 && rowIndex >= middleRow) { |
|||
rowIndex++; |
|||
} |
|||
|
|||
table.rows[rowIndex].cells[stageIndex].appendChild(createPokemonElement(pokemon)); |
|||
}); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
function createPokemonElement(pokemon) { |
|||
const element = document.createElement('div'); |
|||
element.className = 'pokemon-card'; |
|||
|
|||
const img = document.createElement('img'); |
|||
img.src = `../images-new/${pokemon.pfic}.png`; |
|||
img.alt = pokemon.name; |
|||
img.onerror = () => { img.src = 'placeholder.png'; }; |
|||
|
|||
const name = document.createElement('div'); |
|||
name.className = 'pokemon-name'; |
|||
name.textContent = pokemon.name; |
|||
|
|||
const form = document.createElement('div'); |
|||
form.className = 'pokemon-form'; |
|||
form.textContent = pokemon.form_name || ''; |
|||
|
|||
element.appendChild(img); |
|||
element.appendChild(name); |
|||
element.appendChild(form); |
|||
|
|||
return element; |
|||
} |
|||
|
|||
function createEvolutionArrow() { |
|||
const arrow = document.createElement('div'); |
|||
arrow.className = 'evolution-arrow'; |
|||
arrow.textContent = '→'; |
|||
return arrow; |
|||
} |
|||
|
|||
function createBranchElement(evolutions) { |
|||
const branchElement = document.createElement('div'); |
|||
branchElement.className = 'evolution-branch'; |
|||
const arrowElement = document.createElement('span'); |
|||
arrowElement.className = 'evolution-arrow'; |
|||
arrowElement.textContent = '→'; |
|||
branchElement.appendChild(arrowElement); |
|||
const methodElement = document.createElement('span'); |
|||
methodElement.className = 'evolution-method'; |
|||
methodElement.textContent = evolutions[0].method || ''; |
|||
branchElement.appendChild(methodElement); |
|||
return branchElement; |
|||
} |
|||
|
|||
function searchForms() { |
|||
const searchTerm = document.getElementById('forms-search').value.toLowerCase(); |
|||
const rows = document.querySelectorAll('#forms-table tbody tr'); |
|||
|
|||
rows.forEach(row => { |
|||
const text = row.textContent.toLowerCase(); |
|||
row.style.display = text.includes(searchTerm) ? '' : 'none'; |
|||
}); |
|||
} |
|||
|
|||
function searchEvolution() { |
|||
const searchTerm = document.getElementById('evolution-search').value; |
|||
ipcRenderer.send('search-evolution', searchTerm); |
|||
} |
|||
|
|||
ipcRenderer.on('evolution-search-response', (event, response) => { |
|||
if (response.error) { |
|||
console.error('Error searching evolution:', response.error); |
|||
} else if (response.data.length > 0) { |
|||
const pfic = response.data[0].PFIC; |
|||
ipcRenderer.send('get-evolution-chain', pfic); |
|||
} else { |
|||
document.getElementById('evolution-chain').innerHTML = 'No Pokémon found.'; |
|||
} |
|||
}); |
|||
|
|||
ipcRenderer.on('evolution-chain-response', (event, response) => { |
|||
if (response.error) { |
|||
console.error('Error fetching evolution chain:', response.error); |
|||
} else { |
|||
currentEvolutionChain = response.data; // Add this line
|
|||
displayEvolutionChain(currentEvolutionChain); |
|||
} |
|||
}); |
|||
|
|||
function createPokemonElement(pokemon, stageIndex, pokemonIndex) { |
|||
const element = document.createElement('div'); |
|||
element.className = 'pokemon-card'; |
|||
|
|||
const img = document.createElement('img'); |
|||
img.src = `../images-new/${pokemon.pfic}.png`; |
|||
img.alt = pokemon.name; |
|||
img.onerror = () => { img.src = 'placeholder.png'; }; |
|||
|
|||
const name = document.createElement('div'); |
|||
name.className = 'pokemon-name'; |
|||
name.textContent = pokemon.name; |
|||
|
|||
const form = document.createElement('div'); |
|||
form.className = 'pokemon-form'; |
|||
form.textContent = pokemon.form_name || ''; |
|||
|
|||
const editButton = document.createElement('button'); |
|||
editButton.textContent = 'Edit'; |
|||
editButton.className = 'edit-pokemon'; |
|||
editButton.addEventListener('click', () => editPokemon(stageIndex, pokemonIndex)); |
|||
|
|||
const editButtons = document.createElement('div'); |
|||
editButtons.className = 'edit-buttons'; |
|||
editButtons.appendChild(editButton); |
|||
|
|||
element.appendChild(img); |
|||
element.appendChild(name); |
|||
element.appendChild(form); |
|||
element.appendChild(editButtons); |
|||
|
|||
return element; |
|||
} |
|||
|
|||
function setupEvolutionControls() { |
|||
document.getElementById('add-stage').addEventListener('click', addStage); |
|||
document.getElementById('save-evolution-changes').addEventListener('click', saveEvolutionChanges); |
|||
} |
|||
|
|||
function editPokemon(stageIndex, pokemonIndex) { |
|||
console.log('Editing Pokemon:', stageIndex, pokemonIndex); |
|||
if (!currentEvolutionChain) { |
|||
console.error('No evolution chain loaded'); |
|||
return; |
|||
} |
|||
currentEditingStageIndex = stageIndex; |
|||
currentEditingPokemonIndex = pokemonIndex; |
|||
|
|||
const modal = document.getElementById('edit-pokemon-modal'); |
|||
console.log('Modal element:', modal); |
|||
const pokemonSelect = document.getElementById('pokemon-select'); |
|||
const evolutionMethod = document.getElementById('evolution-method'); |
|||
|
|||
// Set current values
|
|||
const currentPokemon = getCurrentPokemon(stageIndex, pokemonIndex); |
|||
console.log('Current Pokemon:', currentPokemon); |
|||
if (currentPokemon) { |
|||
pokemonSelect.value = currentPokemon.pfic; |
|||
evolutionMethod.value = currentPokemon.method || ''; |
|||
|
|||
modal.style.display = 'block'; |
|||
console.log('Modal display set to block'); |
|||
} else { |
|||
console.error('Could not find the current Pokémon'); |
|||
} |
|||
} |
|||
|
|||
function removePokemon(stageIndex, pokemonIndex) { |
|||
// Implement remove functionality
|
|||
console.log(`Removing Pokémon at stage ${stageIndex}, index ${pokemonIndex}`); |
|||
// Remove the Pokémon from the DOM and update the data structure
|
|||
} |
|||
|
|||
function addStage() { |
|||
// Implement add stage functionality
|
|||
console.log('Adding new stage'); |
|||
// You can open a modal or inline form to add a new stage
|
|||
} |
|||
|
|||
function saveEvolutionChanges() { |
|||
console.log('Saving evolution changes'); |
|||
ipcRenderer.send('save-evolution-changes', currentEvolutionChain); |
|||
} |
|||
|
|||
function splitIntoStages(chain) { |
|||
const stages = []; |
|||
let currentStage = [chain]; |
|||
|
|||
while (currentStage.length > 0) { |
|||
stages.push(currentStage); |
|||
const nextStage = []; |
|||
currentStage.forEach(pokemon => { |
|||
nextStage.push(...pokemon.evolutions); |
|||
}); |
|||
currentStage = nextStage; |
|||
} |
|||
|
|||
return stages; |
|||
} |
|||
|
|||
function saveChanges() { |
|||
// Implement the logic to save changes
|
|||
// This will involve collecting the data from the forms table
|
|||
// and sending it back to the main process to update the database
|
|||
} |
|||
|
|||
// Add this function to load all Pokémon
|
|||
function loadAllPokemon() { |
|||
ipcRenderer.send('load-all-pokemon'); |
|||
} |
|||
|
|||
// Add this event listener
|
|||
ipcRenderer.on('all-pokemon-response', (event, response) => { |
|||
if (response.error) { |
|||
console.error('Error loading all Pokémon:', response.error); |
|||
} else { |
|||
allPokemon = response.data; |
|||
populatePokemonSelect(); |
|||
populatePokemonList(); |
|||
} |
|||
}); |
|||
|
|||
// Add this function to populate the Pokémon select dropdown
|
|||
function populatePokemonSelect() { |
|||
const select = document.getElementById('pokemon-select'); |
|||
select.innerHTML = ''; |
|||
allPokemon.forEach(pokemon => { |
|||
const option = document.createElement('option'); |
|||
option.value = pokemon.PFIC; |
|||
option.textContent = `${pokemon.PFIC} - ${pokemon.name} ${pokemon.form_name ? `(${pokemon.form_name})` : ''}`; |
|||
select.appendChild(option); |
|||
}); |
|||
} |
|||
|
|||
// Add this function to get the current Pokémon being edited
|
|||
function getCurrentPokemon(stageIndex, pokemonIndex) { |
|||
if (!currentEvolutionChain) { |
|||
console.error('No evolution chain loaded'); |
|||
return null; |
|||
} |
|||
const stages = splitIntoStages(currentEvolutionChain); |
|||
if (stageIndex < 0 || stageIndex >= stages.length || pokemonIndex < 0 || pokemonIndex >= stages[stageIndex].length) { |
|||
console.error('Invalid stage or pokemon index'); |
|||
return null; |
|||
} |
|||
return stages[stageIndex][pokemonIndex]; |
|||
} |
|||
|
|||
// Add this function to update the Pokémon in the chain
|
|||
function updatePokemonInChain(stageIndex, pokemonIndex, newPfic, newMethod) { |
|||
const stages = splitIntoStages(currentEvolutionChain); |
|||
const pokemon = stages[stageIndex][pokemonIndex]; |
|||
|
|||
// Update the Pokémon data
|
|||
pokemon.pfic = newPfic; |
|||
pokemon.name = allPokemon.find(p => p.PFIC === newPfic).name; |
|||
pokemon.form_name = allPokemon.find(p => p.PFIC === newPfic).form_name; |
|||
|
|||
// Update the evolution method if it's not the first stage
|
|||
if (stageIndex > 0) { |
|||
const previousStagePokemon = stages[stageIndex - 1].find(p => p.evolutions.includes(pokemon)); |
|||
const evolutionIndex = previousStagePokemon.evolutions.indexOf(pokemon); |
|||
previousStagePokemon.evolutions[evolutionIndex].method = newMethod; |
|||
} |
|||
|
|||
// Redisplay the evolution chain
|
|||
displayEvolutionChain(currentEvolutionChain); |
|||
} |
|||
|
|||
// Add this event listener for the save response
|
|||
ipcRenderer.on('save-evolution-changes-response', (event, response) => { |
|||
if (response.error) { |
|||
console.error('Error saving evolution changes:', response.error); |
|||
alert('Failed to save changes. Please try again.'); |
|||
} else { |
|||
alert('Changes saved successfully!'); |
|||
} |
|||
}); |
|||
|
|||
// Add this function to populate the Pokémon list
|
|||
function populatePokemonList() { |
|||
const listElement = document.getElementById('pokemon-list-items'); |
|||
listElement.innerHTML = ''; |
|||
allPokemon.forEach(pokemon => { |
|||
const li = document.createElement('li'); |
|||
li.textContent = `${pokemon.name} ${pokemon.form_name ? `(${pokemon.form_name})` : ''}`; |
|||
li.addEventListener('click', () => loadEvolutionChain(pokemon.PFIC)); |
|||
listElement.appendChild(li); |
|||
}); |
|||
} |
|||
|
|||
// Add this function to set up the Pokémon filter
|
|||
function setupPokemonFilter() { |
|||
const filterInput = document.getElementById('pokemon-filter'); |
|||
filterInput.addEventListener('input', () => { |
|||
const filterValue = filterInput.value.toLowerCase(); |
|||
const listItems = document.querySelectorAll('#pokemon-list-items li'); |
|||
listItems.forEach(item => { |
|||
const text = item.textContent.toLowerCase(); |
|||
item.style.display = text.includes(filterValue) ? '' : 'none'; |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
// Modify the loadEvolutionChain function
|
|||
function loadEvolutionChain(pfic) { |
|||
ipcRenderer.send('get-evolution-chain', pfic); |
|||
} |
|||
@ -1,338 +0,0 @@ |
|||
body { |
|||
font-family: Arial, sans-serif; |
|||
margin: 0; |
|||
padding: 20px; |
|||
} |
|||
|
|||
#tabs { |
|||
margin-bottom: 20px; |
|||
} |
|||
|
|||
.tab-button { |
|||
padding: 10px 20px; |
|||
cursor: pointer; |
|||
} |
|||
|
|||
.tab-button.active { |
|||
background-color: #ddd; |
|||
} |
|||
|
|||
.tab-content { |
|||
display: none; |
|||
} |
|||
|
|||
.tab-content.active { |
|||
display: block; |
|||
} |
|||
|
|||
.search-bar { |
|||
margin-bottom: 20px; |
|||
} |
|||
|
|||
table { |
|||
width: 100%; |
|||
border-collapse: collapse; |
|||
} |
|||
|
|||
th, td { |
|||
border: 1px solid #ddd; |
|||
padding: 8px; |
|||
text-align: left; |
|||
} |
|||
|
|||
th { |
|||
background-color: #f2f2f2; |
|||
} |
|||
|
|||
#evolution-chain { |
|||
display: flex; |
|||
overflow-x: auto; |
|||
padding: 20px; |
|||
align-items: flex-start; |
|||
} |
|||
|
|||
.evolution-stage { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
margin-right: 40px; |
|||
} |
|||
|
|||
.pokemon-card { |
|||
background-color: #f9f9f9; |
|||
border: 1px solid #ddd; |
|||
border-radius: 10px; |
|||
padding: 10px; |
|||
text-align: center; |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
cursor: pointer; |
|||
width: 120px; /* Fixed width */ |
|||
height: 100px; /* Fixed height */ |
|||
} |
|||
|
|||
.pokemon-card img { |
|||
width: 64px; |
|||
height: 64px; |
|||
object-fit: contain; |
|||
} |
|||
|
|||
.pokemon-name { |
|||
font-weight: bold; |
|||
margin-top: 5px; |
|||
} |
|||
|
|||
.pokemon-form { |
|||
font-size: 0.8em; |
|||
color: #666; |
|||
} |
|||
|
|||
.evolution-branch { |
|||
position: absolute; |
|||
top: 50%; |
|||
left: 100%; |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
} |
|||
|
|||
.evolution-arrow { |
|||
font-size: 24px; |
|||
color: #666; |
|||
margin: 0 10px; |
|||
} |
|||
|
|||
.evolution-method { |
|||
font-size: 0.8em; |
|||
color: #666; |
|||
max-width: 100px; |
|||
text-align: center; |
|||
} |
|||
|
|||
.pokemon-card .edit-buttons { |
|||
display: none; |
|||
position: absolute; |
|||
top: 5px; |
|||
right: 5px; |
|||
} |
|||
|
|||
.pokemon-card:hover .edit-buttons { |
|||
display: block; |
|||
} |
|||
|
|||
.edit-buttons button { |
|||
margin-left: 5px; |
|||
padding: 2px 5px; |
|||
font-size: 0.8em; |
|||
} |
|||
|
|||
#evolution-controls { |
|||
margin-top: 20px; |
|||
} |
|||
|
|||
#evolution-controls button { |
|||
margin-right: 10px; |
|||
} |
|||
|
|||
/* Add these styles at the end of the file */ |
|||
.modal { |
|||
display: none; |
|||
position: fixed; |
|||
z-index: 1; |
|||
left: 0; |
|||
top: 0; |
|||
width: 100%; |
|||
height: 100%; |
|||
overflow: auto; |
|||
background-color: rgba(0,0,0,0.4); |
|||
} |
|||
|
|||
.modal-content { |
|||
background-color: #fefefe; |
|||
margin: 15% auto; |
|||
padding: 20px; |
|||
border: 1px solid #888; |
|||
width: 80%; |
|||
max-width: 500px; |
|||
} |
|||
|
|||
.close { |
|||
color: #aaa; |
|||
float: right; |
|||
font-size: 28px; |
|||
font-weight: bold; |
|||
cursor: pointer; |
|||
} |
|||
|
|||
.close:hover, |
|||
.close:focus { |
|||
color: black; |
|||
text-decoration: none; |
|||
cursor: pointer; |
|||
} |
|||
|
|||
#edit-pokemon-form { |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
|
|||
#edit-pokemon-form label, |
|||
#edit-pokemon-form select, |
|||
#edit-pokemon-form input, |
|||
#edit-pokemon-form button { |
|||
margin-top: 10px; |
|||
} |
|||
|
|||
.evolution-container { |
|||
display: flex; |
|||
height: calc(100vh - 100px); /* Adjust based on your layout */ |
|||
} |
|||
|
|||
#pokemon-list { |
|||
width: 250px; |
|||
border-right: 1px solid #ccc; |
|||
overflow-y: auto; |
|||
padding: 10px; |
|||
} |
|||
|
|||
#pokemon-filter { |
|||
width: 100%; |
|||
margin-bottom: 10px; |
|||
} |
|||
|
|||
#pokemon-list-items { |
|||
list-style-type: none; |
|||
padding: 0; |
|||
} |
|||
|
|||
#pokemon-list-items li { |
|||
cursor: pointer; |
|||
padding: 5px; |
|||
} |
|||
|
|||
#pokemon-list-items li:hover { |
|||
background-color: #f0f0f0; |
|||
} |
|||
|
|||
#evolution-chain-container { |
|||
flex-grow: 1; |
|||
padding: 20px; |
|||
overflow-y: auto; |
|||
} |
|||
|
|||
.forms-container { |
|||
display: flex; |
|||
height: calc(100vh - 100px); /* Adjust based on your layout */ |
|||
} |
|||
|
|||
#pokemon-forms-list { |
|||
width: 250px; |
|||
border-right: 1px solid #ccc; |
|||
overflow-y: auto; |
|||
padding: 10px; |
|||
} |
|||
|
|||
#forms-filter { |
|||
width: 100%; |
|||
margin-bottom: 10px; |
|||
} |
|||
|
|||
#forms-list-items { |
|||
list-style-type: none; |
|||
padding: 0; |
|||
} |
|||
|
|||
#forms-list-items li { |
|||
cursor: pointer; |
|||
padding: 5px; |
|||
} |
|||
|
|||
#forms-list-items li:hover { |
|||
background-color: #f0f0f0; |
|||
} |
|||
|
|||
#pokemon-details { |
|||
flex-grow: 1; |
|||
padding: 20px; |
|||
overflow-y: auto; |
|||
} |
|||
|
|||
#details-content { |
|||
margin-top: 20px; |
|||
} |
|||
|
|||
#pokemon-basic-info { |
|||
display: flex; |
|||
margin-bottom: 20px; |
|||
} |
|||
|
|||
#pokemon-image { |
|||
width: 200px; |
|||
height: 200px; |
|||
object-fit: contain; |
|||
margin-right: 20px; |
|||
} |
|||
|
|||
#pokemon-info { |
|||
flex-grow: 1; |
|||
} |
|||
|
|||
#pokemon-evolution-chain { |
|||
margin-top: 20px; |
|||
} |
|||
|
|||
#details-evolution-chain-content { |
|||
overflow-x: auto; |
|||
margin-top: 20px; |
|||
} |
|||
|
|||
#evolution-table { |
|||
width: auto; |
|||
border-collapse: collapse; |
|||
border-spacing: 0px; |
|||
} |
|||
|
|||
#evolution-table td { |
|||
vertical-align: middle; |
|||
text-align: center; |
|||
padding: 0%; |
|||
border-color: transparent; |
|||
} |
|||
|
|||
#details-evolution-chain-content .evolution-stage { |
|||
display: inline-flex; |
|||
flex-direction: row; |
|||
justify-content: flex-start; |
|||
align-items: center; |
|||
margin-right: 20px; |
|||
} |
|||
|
|||
#details-evolution-chain-content .pokemon-card { |
|||
text-align: center; |
|||
margin: 0 10px; |
|||
position: relative; |
|||
border: 1px solid #ddd; |
|||
padding: 10px; |
|||
border-radius: 5px; |
|||
display: inline-block; |
|||
} |
|||
|
|||
#details-evolution-chain-content .pokemon-card img { |
|||
width: 64px; |
|||
height: 64px; |
|||
object-fit: contain; |
|||
} |
|||
|
|||
#details-evolution-chain-content .evolution-branch { |
|||
display: inline-flex; |
|||
flex-direction: row; |
|||
align-items: center; |
|||
margin: 0 10px; |
|||
} |
|||
|
|||
#details-evolution-chain-content .evolution-arrow, |
|||
#details-evolution-chain-content .evolution-method { |
|||
margin: 0 5px; |
|||
} |
|||
@ -1,401 +0,0 @@ |
|||
import sqlite3 |
|||
import csv |
|||
import re |
|||
|
|||
def create_connection(): |
|||
conn = sqlite3.connect('pokemon_database.db') |
|||
conn.text_factory = str |
|||
return conn |
|||
|
|||
def create_tables(conn): |
|||
cursor = conn.cursor() |
|||
|
|||
# Create games table |
|||
cursor.execute(''' |
|||
CREATE TABLE IF NOT EXISTS games ( |
|||
id INTEGER PRIMARY KEY, |
|||
name TEXT NOT NULL, |
|||
generation INTEGER NOT NULL |
|||
) |
|||
''') |
|||
|
|||
# Create marks table |
|||
cursor.execute(''' |
|||
CREATE TABLE IF NOT EXISTS marks ( |
|||
id INTEGER PRIMARY KEY, |
|||
name TEXT NOT NULL, |
|||
icon_path TEXT NOT NULL |
|||
) |
|||
''') |
|||
|
|||
# Create mark_game_associations table |
|||
cursor.execute(''' |
|||
CREATE TABLE IF NOT EXISTS mark_game_associations ( |
|||
mark_id INTEGER, |
|||
game_id INTEGER, |
|||
FOREIGN KEY (mark_id) REFERENCES marks (id), |
|||
FOREIGN KEY (game_id) REFERENCES games (id), |
|||
PRIMARY KEY (mark_id, game_id) |
|||
) |
|||
''') |
|||
|
|||
# Create pokemon table |
|||
cursor.execute(''' |
|||
CREATE TABLE IF NOT EXISTS pokemon ( |
|||
national_dex_number INTEGER PRIMARY KEY, |
|||
name TEXT NOT NULL, |
|||
introduced_in_gen INTEGER |
|||
) |
|||
''') |
|||
|
|||
# Create pokemon_forms table |
|||
cursor.execute(''' |
|||
CREATE TABLE IF NOT EXISTS pokemon_forms ( |
|||
id INTEGER PRIMARY KEY, |
|||
pokemon_id INTEGER NOT NULL, |
|||
form_name TEXT NOT NULL, |
|||
is_default BOOLEAN NOT NULL, |
|||
image_path TEXT NOT NULL, |
|||
FOREIGN KEY (pokemon_id) REFERENCES pokemon (national_dex_number), |
|||
UNIQUE (pokemon_id, form_name) |
|||
) |
|||
''') |
|||
|
|||
# Create encounter_methods table |
|||
cursor.execute(''' |
|||
CREATE TABLE IF NOT EXISTS encounter_methods ( |
|||
id INTEGER PRIMARY KEY, |
|||
name TEXT UNIQUE NOT NULL |
|||
) |
|||
''') |
|||
|
|||
# Create locations table |
|||
cursor.execute(''' |
|||
CREATE TABLE IF NOT EXISTS locations ( |
|||
id INTEGER PRIMARY KEY, |
|||
name TEXT UNIQUE NOT NULL, |
|||
description TEXT |
|||
) |
|||
''') |
|||
|
|||
# Create form_encounters table |
|||
cursor.execute(''' |
|||
CREATE TABLE IF NOT EXISTS form_encounters ( |
|||
id INTEGER PRIMARY KEY, |
|||
form_id INTEGER NOT NULL, |
|||
game_id INTEGER NOT NULL, |
|||
location_id INTEGER NOT NULL, |
|||
encounter_method_id INTEGER NOT NULL, |
|||
FOREIGN KEY (form_id) REFERENCES pokemon_forms (id), |
|||
FOREIGN KEY (game_id) REFERENCES games (id), |
|||
FOREIGN KEY (location_id) REFERENCES locations (id), |
|||
FOREIGN KEY (encounter_method_id) REFERENCES encounter_methods (id), |
|||
UNIQUE (form_id, game_id, location_id, encounter_method_id) |
|||
) |
|||
''') |
|||
|
|||
# Create alternate_game_names table |
|||
cursor.execute(''' |
|||
CREATE TABLE IF NOT EXISTS alternate_game_names ( |
|||
id INTEGER PRIMARY KEY, |
|||
game_id INTEGER NOT NULL, |
|||
alternate_name TEXT NOT NULL, |
|||
FOREIGN KEY (game_id) REFERENCES games (id), |
|||
UNIQUE (alternate_name COLLATE NOCASE) |
|||
) |
|||
''') |
|||
|
|||
conn.commit() |
|||
|
|||
def tidy_location_name(name): |
|||
# Replace '-' with spaces |
|||
name = name.replace('-', ' ') |
|||
|
|||
name = name.replace('#', '') |
|||
|
|||
# Remove 'area' from the end if present |
|||
name = re.sub(r'\sarea$', '', name, flags=re.IGNORECASE) |
|||
|
|||
# Check for cardinal directions at the end |
|||
cardinal_directions = ['north', 'south', 'east', 'west', 'northeast', 'northwest', 'southeast', 'southwest'] |
|||
for direction in cardinal_directions: |
|||
if name.lower().endswith(f' {direction}'): |
|||
# Remove the direction from the end and add it in brackets |
|||
name = name[:-len(direction)].strip() |
|||
name += f' ({direction.capitalize()})' |
|||
break |
|||
|
|||
if name.isdigit(): |
|||
name = "Route " + name |
|||
|
|||
name = name.replace("Routes", "Route") |
|||
|
|||
# Capitalize the first letter of the first word |
|||
name = name.capitalize() |
|||
|
|||
return name |
|||
|
|||
def generate_location_description(name): |
|||
# Generate a simple description based on the name |
|||
description = f"A location in the Pokémon world known as {name}." |
|||
return description |
|||
|
|||
def load_game_data(conn): |
|||
cursor = conn.cursor() |
|||
|
|||
games = [ |
|||
("Red", 1, ["Red Version"]), |
|||
("Blue", 1, ["Blue Version"]), |
|||
("Yellow", 1, ["Yellow Version"]), |
|||
("Gold", 2, ["Gold Version"]), |
|||
("Silver", 2, ["Silver Version"]), |
|||
("Crystal", 2, ["Crystal Version"]), |
|||
("Ruby", 3, ["Ruby Version"]), |
|||
("Sapphire", 3, ["Sapphire Version"]), |
|||
("Emerald", 3, ["Emerald Version"]), |
|||
("FireRed", 3, ["Fire Red", "Fire-Red"]), |
|||
("LeafGreen", 3, ["Leaf Green", "Leaf-Green"]), |
|||
("Diamond", 4, ["Diamond Version"]), |
|||
("Pearl", 4, ["Pearl Version"]), |
|||
("Platinum", 4, ["Platinum Version"]), |
|||
("HeartGold", 4, ["Heart Gold", "Heart-Gold"]), |
|||
("SoulSilver", 4, ["Soul Silver", "Soul-Silver"]), |
|||
("Black", 5, ["Black Version"]), |
|||
("White", 5, ["White Version"]), |
|||
("Black 2", 5, ["Black Version 2", "Black-2"]), |
|||
("White 2", 5, ["White Version 2", "White-2"]), |
|||
("X", 6, ["X Version"]), |
|||
("Y", 6, ["Y Version"]), |
|||
("Omega Ruby", 6, ["Omega Ruby Version", "Omega-Ruby"]), |
|||
("Alpha Sapphire", 6, ["Alpha Sapphire Version", "Alpha-Sapphire"]), |
|||
("Sun", 7, ["Sun Version"]), |
|||
("Moon", 7, ["Moon Version"]), |
|||
("Ultra Sun", 7, ["Ultra Sun Version", "Ultra-Sun"]), |
|||
("Ultra Moon", 7, ["Ultra Moon Version", "Ultra-Moon"]), |
|||
("Let's Go Pikachu", 7, ["Let's Go, Pikachu!", "Lets Go Pikachu"]), |
|||
("Let's Go Eevee", 7, ["Let's Go, Eevee!", "Lets Go Eevee"]), |
|||
("Sword", 8, ["Sword Version"]), |
|||
("Shield", 8, ["Shield Version"]), |
|||
("Expansion Pass", 8, ["Expansion Pass (Sword)", "Expansion Pass (Shield)"]), |
|||
("Brilliant Diamond", 8, ["Brilliant Diamond Version", "Brilliant-Diamond"]), |
|||
("Shining Pearl", 8, ["Shining Pearl Version", "Shining-Pearl"]), |
|||
("Legends Arceus", 8, ["Legends: Arceus", "Legends-Arceus"]), |
|||
("Scarlet", 9, ["Scarlet Version"]), |
|||
("Violet", 9, ["Violet Version"]), |
|||
("The Teal Mask", 9, ["The Teal Mask Version", "The Teal Mask (Scarlet)", "The Teal Mask (Violet)"]), |
|||
("The Hidden Treasure of Area Zero", 9, ["The Hidden Treasure of Area Zero Version", "The Hidden Treasure of Area Zero (Scarlet)", "The Hidden Treasure of Area Zero (Violet)"]), |
|||
|
|||
("Pokémon Home", 98, ["Pokémon HOME"]), |
|||
("Pokémon Go", 99, ["Pokémon GO"]), |
|||
] |
|||
|
|||
for game in games: |
|||
cursor.execute(''' |
|||
INSERT OR IGNORE INTO games (name, generation) |
|||
VALUES (?, ?) |
|||
''', (game[0], game[1])) |
|||
|
|||
game_id = cursor.lastrowid |
|||
|
|||
# Insert alternate names |
|||
for alt_name in game[2]: |
|||
cursor.execute(''' |
|||
INSERT OR IGNORE INTO alternate_game_names (game_id, alternate_name) |
|||
VALUES (?, ?) |
|||
''', (game_id, alt_name)) |
|||
|
|||
conn.commit() |
|||
|
|||
def load_mark_data(conn): |
|||
cursor = conn.cursor() |
|||
|
|||
marks = [ |
|||
("Game Boy", "images/marks/GB_icon_HOME.png", ["Red", "Blue", "Yellow", "Gold", "Silver", "Crystal", "Ruby", "Sapphire", "Emerald", "FireRed", "LeafGreen"]), |
|||
("Kalos", "images/marks/Blue_pentagon_HOME.png", ["X", "Y", "Omega Ruby", "Alpha Sapphire"]), |
|||
("Alola", "images/marks/Black_clover_HOME.png", ["Sun", "Moon", "Ultra Sun", "Ultra Moon"]), |
|||
("Let's Go", "images/marks/Let's_Go_icon_HOME.png", ["Let's Go Pikachu", "Let's Go Eevee"]), |
|||
("Galar", "images/marks/Galar_symbol_HOME.png", ["Sword", "Shield"]), |
|||
("Sinnoh", "images/marks/BDSP_icon_HOME.png", ["Brilliant Diamond", "Shining Pearl"]), |
|||
("Hisui", "images/marks/Arceus_mark_HOME.png", ["Legends Arceus"]), |
|||
("Paldea", "images/marks/Paldea_icon_HOME.png", ["Scarlet", "Violet"]), |
|||
] |
|||
|
|||
for mark in marks: |
|||
cursor.execute(''' |
|||
INSERT OR IGNORE INTO marks (name, icon_path) |
|||
VALUES (?, ?) |
|||
''', (mark[0], mark[1])) |
|||
|
|||
mark_id = cursor.lastrowid |
|||
|
|||
for game_name in mark[2]: |
|||
cursor.execute(''' |
|||
INSERT OR IGNORE INTO mark_game_associations (mark_id, game_id) |
|||
SELECT ?, id FROM games WHERE name = ? |
|||
''', (mark_id, game_name)) |
|||
|
|||
conn.commit() |
|||
|
|||
def load_pokemon_data(conn): |
|||
cursor = conn.cursor() |
|||
|
|||
with open('pokemon_home_list.csv', 'r', encoding='utf-8') as f: |
|||
reader = csv.reader(f) |
|||
next(reader) # Skip header row if it exists |
|||
for row in reader: |
|||
national_dex_number = int(row[0]) |
|||
full_name = row[1] |
|||
|
|||
# Extract the base name and form name |
|||
match = re.match(r'([^(]+)(?:\s*\(([^)]+)\))?', full_name) |
|||
if match: |
|||
base_name = match.group(1).strip() |
|||
form_name = match.group(2).strip() if match.group(2) else "Default" |
|||
else: |
|||
base_name = full_name |
|||
form_name = "Default" |
|||
|
|||
# Insert or update the pokemon entry |
|||
cursor.execute(''' |
|||
INSERT OR IGNORE INTO pokemon (national_dex_number, name) |
|||
VALUES (?, ?) |
|||
''', (national_dex_number, base_name)) |
|||
|
|||
# Create the image path |
|||
padded_dex = f"{national_dex_number:04d}" |
|||
if form_name == "Default": |
|||
image_path = f"images/pokemon/{padded_dex}_{base_name}.png" |
|||
else: |
|||
image_path = f"images/pokemon/{padded_dex}_{base_name}_({form_name}).png".replace(" ", "_") |
|||
|
|||
# Insert the form entry |
|||
cursor.execute(''' |
|||
INSERT OR IGNORE INTO pokemon_forms (pokemon_id, form_name, is_default, image_path) |
|||
VALUES (?, ?, ?, ?) |
|||
''', (national_dex_number, form_name, form_name == "Default", image_path)) |
|||
|
|||
conn.commit() |
|||
|
|||
def load_encounter_data(conn): |
|||
cursor = conn.cursor() |
|||
|
|||
with open('pokemon_earliest_games.csv', 'r', encoding='utf-8') as f: |
|||
reader = csv.DictReader(f) |
|||
for row in reader: |
|||
national_dex_number = int(row['number']) |
|||
full_name = row['name'] |
|||
earliest_game = row['earliest_game'] |
|||
obtain_method = row['obtain_method'] |
|||
encounter_locations = row['encounter_locations'] |
|||
introduced_in_gen = row['introduced_in_gen'] |
|||
# Extract the base name and form name |
|||
match = re.match(r'([^(]+)(?:\s*\(([^)]+)\))?', full_name) |
|||
if match: |
|||
base_name = match.group(1).strip() |
|||
form_name = match.group(2).strip() if match.group(2) else "Default" |
|||
if form_name == "None": |
|||
form_name = "Default" |
|||
else: |
|||
base_name = full_name |
|||
form_name = "Default" |
|||
|
|||
# Update the Pokémon entry with introduced_in_gen |
|||
cursor.execute(''' |
|||
INSERT OR REPLACE INTO pokemon (national_dex_number, name, introduced_in_gen) |
|||
VALUES (?, ?, ?) |
|||
''', (national_dex_number, base_name, introduced_in_gen)) |
|||
|
|||
cursor.execute(''' |
|||
INSERT OR IGNORE INTO pokemon_forms (pokemon_id, form_name, is_default, image_path) |
|||
VALUES (?, ?, ?, ?) |
|||
''', (national_dex_number, form_name, form_name == "Default", f"images/pokemon/{national_dex_number:04d}_{base_name}.png")) |
|||
|
|||
# Skip encounter data if it's unknown or N/A |
|||
if earliest_game == "Unknown" or obtain_method == "Unknown": |
|||
continue |
|||
|
|||
# Get the form_id |
|||
cursor.execute(''' |
|||
SELECT id FROM pokemon_forms |
|||
WHERE pokemon_id = ? AND form_name = ? |
|||
''', (national_dex_number, form_name)) |
|||
form_id = cursor.fetchone()[0] |
|||
|
|||
# Get the game_id (now case-insensitive and including alternate names) |
|||
cursor.execute(''' |
|||
SELECT g.id FROM games g |
|||
LEFT JOIN alternate_game_names agn ON g.id = agn.game_id |
|||
WHERE g.name = ? COLLATE NOCASE OR agn.alternate_name = ? COLLATE NOCASE |
|||
''', (earliest_game, earliest_game)) |
|||
result = cursor.fetchone() |
|||
if result: |
|||
game_id = result[0] |
|||
else: |
|||
print(f"Warning: Game '{earliest_game}' not found for {full_name}") |
|||
continue |
|||
|
|||
# Handle gift Pokémon |
|||
if obtain_method.lower() == "gift" and (encounter_locations == "N/A" or not encounter_locations): |
|||
# Insert or get the "Gift" location |
|||
cursor.execute(''' |
|||
INSERT OR IGNORE INTO locations (name, description) |
|||
VALUES (?, ?) |
|||
''', ("Gift", "Pokémon received as a gift")) |
|||
cursor.execute('SELECT id FROM locations WHERE name = ?', ("Gift",)) |
|||
location_id = cursor.fetchone()[0] |
|||
|
|||
# Insert or get the "gift" encounter method |
|||
cursor.execute('INSERT OR IGNORE INTO encounter_methods (name) VALUES (?)', ("gift",)) |
|||
cursor.execute('SELECT id FROM encounter_methods WHERE name = ?', ("gift",)) |
|||
method_id = cursor.fetchone()[0] |
|||
|
|||
# Insert form_encounter for gift Pokémon |
|||
cursor.execute(''' |
|||
INSERT OR IGNORE INTO form_encounters |
|||
(form_id, game_id, location_id, encounter_method_id) |
|||
VALUES (?, ?, ?, ?) |
|||
''', (form_id, game_id, location_id, method_id)) |
|||
elif encounter_locations != "N/A" and encounter_locations: |
|||
# Process each encounter location |
|||
for location_info in encounter_locations.split('|'): |
|||
location = location_info.strip() |
|||
|
|||
location = tidy_location_name(location) |
|||
|
|||
# Tidy up the location name and generate a description |
|||
description = tidy_location_name(location) |
|||
|
|||
# Insert or get location_id |
|||
cursor.execute(''' |
|||
INSERT OR IGNORE INTO locations (name, description) |
|||
VALUES (?, ?) |
|||
''', (location, description)) |
|||
cursor.execute('SELECT id FROM locations WHERE name = ?', (location,)) |
|||
location_id = cursor.fetchone()[0] |
|||
|
|||
# Insert or get encounter_method_id |
|||
cursor.execute('INSERT OR IGNORE INTO encounter_methods (name) VALUES (?)', (obtain_method,)) |
|||
cursor.execute('SELECT id FROM encounter_methods WHERE name = ?', (obtain_method,)) |
|||
method_id = cursor.fetchone()[0] |
|||
|
|||
# Insert form_encounter |
|||
cursor.execute(''' |
|||
INSERT OR IGNORE INTO form_encounters |
|||
(form_id, game_id, location_id, encounter_method_id) |
|||
VALUES (?, ?, ?, ?) |
|||
''', (form_id, game_id, location_id, method_id)) |
|||
|
|||
conn.commit() |
|||
|
|||
def main(): |
|||
conn = create_connection() |
|||
create_tables(conn) |
|||
load_game_data(conn) |
|||
load_mark_data(conn) |
|||
load_pokemon_data(conn) |
|||
load_encounter_data(conn) |
|||
conn.close() |
|||
print("All data has been successfully added to the database.") |
|||
|
|||
if __name__ == "__main__": |
|||
main() |
|||
@ -1,136 +0,0 @@ |
|||
import sqlite3 |
|||
import csv |
|||
from typing import List, Dict, Optional |
|||
from bs4 import BeautifulSoup |
|||
import requests |
|||
import re |
|||
from fuzzywuzzy import fuzz |
|||
|
|||
# Import necessary functions from DetermineOriginGame.py |
|||
from DetermineOriginGame import ( |
|||
create_pokemon_index, |
|||
get_intro_generation, |
|||
get_locations_from_bulbapedia, |
|||
get_evolution_data_from_bulbapedia, |
|||
split_td_contents, |
|||
parse_form_information, |
|||
get_cached_data, |
|||
all_games, |
|||
pokemon_index, |
|||
cache, |
|||
read_pokemon_list |
|||
) |
|||
|
|||
class Pokemon: |
|||
def __init__(self, number: int, name: str, form: Optional[str] = None): |
|||
self.number = number |
|||
self.name = name |
|||
self.form = form |
|||
self.introduced_in_gen: Optional[int] = None |
|||
self.encounters: Dict[str, List[str]] = {} |
|||
self.evolution_chain: List[Dict] = [] |
|||
self.stage: Optional[str] = None |
|||
|
|||
def create_database(): |
|||
conn = sqlite3.connect('unprocessed_pokemon_database.db') |
|||
cursor = conn.cursor() |
|||
|
|||
# Create tables |
|||
cursor.execute(''' |
|||
CREATE TABLE IF NOT EXISTS pokemon ( |
|||
id INTEGER PRIMARY KEY, |
|||
national_dex_number INTEGER, |
|||
name TEXT, |
|||
form TEXT, |
|||
introduced_in_gen INTEGER |
|||
) |
|||
''') |
|||
|
|||
cursor.execute(''' |
|||
CREATE TABLE IF NOT EXISTS encounters ( |
|||
id INTEGER PRIMARY KEY, |
|||
pokemon_id INTEGER, |
|||
game TEXT, |
|||
location TEXT, |
|||
FOREIGN KEY (pokemon_id) REFERENCES pokemon (id) |
|||
) |
|||
''') |
|||
|
|||
cursor.execute(''' |
|||
CREATE TABLE IF NOT EXISTS evolution_chain ( |
|||
id INTEGER PRIMARY KEY, |
|||
pokemon_id INTEGER, |
|||
stage INTEGER, |
|||
evolves_from TEXT, |
|||
evolution_method TEXT, |
|||
FOREIGN KEY (pokemon_id) REFERENCES pokemon (id) |
|||
) |
|||
''') |
|||
|
|||
conn.commit() |
|||
return conn |
|||
|
|||
def extract_pokemon_data(pokemon_list: List[Pokemon], conn: sqlite3.Connection): |
|||
cursor = conn.cursor() |
|||
|
|||
for pokemon in pokemon_list: |
|||
print(f"Processing {pokemon.name} ({pokemon.form})") |
|||
|
|||
# Get introduction generation |
|||
pokemon.introduced_in_gen = get_intro_generation(pokemon.name, pokemon.form, cache) |
|||
|
|||
# Get encounter data |
|||
encounter_data = get_locations_from_bulbapedia(pokemon.name, pokemon.form, cache) |
|||
for game, locations in encounter_data.items(): |
|||
pokemon.encounters[game] = locations |
|||
|
|||
# Get evolution data |
|||
pokemon.evolution_chain = get_evolution_data_from_bulbapedia(pokemon.name, pokemon.form, cache) |
|||
|
|||
# Insert data into database |
|||
cursor.execute(''' |
|||
INSERT INTO pokemon (national_dex_number, name, form, introduced_in_gen) |
|||
VALUES (?, ?, ?, ?) |
|||
''', (pokemon.number, pokemon.name, pokemon.form, pokemon.introduced_in_gen)) |
|||
pokemon_id = cursor.lastrowid |
|||
|
|||
for game, locations in pokemon.encounters.items(): |
|||
for location in locations: |
|||
cursor.execute(''' |
|||
INSERT INTO encounters (pokemon_id, game, location) |
|||
VALUES (?, ?, ?) |
|||
''', (pokemon_id, game, location)) |
|||
|
|||
if pokemon.evolution_chain: |
|||
for i, stage in enumerate(pokemon.evolution_chain): |
|||
previous_stage = None |
|||
if stage.previous_stage: |
|||
previous_stage = stage.previous_stage.pokemon |
|||
cursor.execute(''' |
|||
INSERT INTO evolution_chain (pokemon_id, stage, evolves_from, evolution_method) |
|||
VALUES (?, ?, ?, ?) |
|||
''', (pokemon_id, i, previous_stage, stage.method)) |
|||
|
|||
conn.commit() |
|||
|
|||
def read_and_convert_pokemon_list(filename: str) -> List[Pokemon]: |
|||
pokemon_list = read_pokemon_list(filename, 3000) |
|||
local_list = [] |
|||
for entry in pokemon_list: |
|||
number = entry.number |
|||
name = entry.name |
|||
form = entry.form |
|||
local_list.append(Pokemon(number, name, form)) |
|||
return local_list |
|||
|
|||
def main(): |
|||
get_cached_data() |
|||
conn = create_database() |
|||
pokemon_list = read_and_convert_pokemon_list('pokemon_home_list.csv') |
|||
create_pokemon_index(pokemon_list) |
|||
extract_pokemon_data(pokemon_list, conn) |
|||
conn.close() |
|||
print("Data extraction complete and stored in the database.") |
|||
|
|||
if __name__ == "__main__": |
|||
main() |
|||
@ -1,609 +0,0 @@ |
|||
import sys |
|||
import sqlite3 |
|||
import json |
|||
import os |
|||
from datetime import datetime |
|||
from PyQt6.QtWidgets import (QApplication, QMainWindow, QListWidget, QLabel, |
|||
QVBoxLayout, QHBoxLayout, QWidget, QPushButton, |
|||
QTableWidget, QTableWidgetItem, QHeaderView, |
|||
QLineEdit, QCheckBox, QFormLayout, QMessageBox, |
|||
QDialog, QComboBox, QFileDialog, QTabWidget) |
|||
from PyQt6.QtGui import QPixmap |
|||
from PyQt6.QtCore import Qt |
|||
|
|||
class PokemonDatabaseApp(QMainWindow): |
|||
def __init__(self): |
|||
super().__init__() |
|||
self.setWindowTitle("Pokémon Database Viewer") |
|||
self.setGeometry(100, 100, 1200, 600) |
|||
|
|||
# Create an in-memory database |
|||
self.conn = sqlite3.connect(':memory:') |
|||
self.cursor = self.conn.cursor() |
|||
|
|||
# Load the database from disk into memory |
|||
self.load_database() |
|||
|
|||
# Apply all existing patches |
|||
self.apply_all_patches() |
|||
|
|||
self.current_pokemon_id = None |
|||
self.current_pokemon_name = None |
|||
self.current_form_id = None |
|||
self.all_pokemon = [] |
|||
self.patches = {} |
|||
self.init_ui() |
|||
self.load_locations_list() |
|||
|
|||
def init_ui(self): |
|||
main_layout = QVBoxLayout() |
|||
|
|||
# Create tab widget |
|||
self.tab_widget = QTabWidget() |
|||
|
|||
# Create and add Pokemon tab |
|||
pokemon_tab = self.create_pokemon_tab() |
|||
self.tab_widget.addTab(pokemon_tab, "Pokémon") |
|||
|
|||
# Create and add Locations tab |
|||
locations_tab = self.create_locations_tab() |
|||
self.tab_widget.addTab(locations_tab, "Locations") |
|||
|
|||
main_layout.addWidget(self.tab_widget) |
|||
|
|||
# Add export button |
|||
self.export_button = QPushButton("Export Production Database") |
|||
self.export_button.clicked.connect(self.export_production_database) |
|||
main_layout.addWidget(self.export_button) |
|||
|
|||
container = QWidget() |
|||
container.setLayout(main_layout) |
|||
self.setCentralWidget(container) |
|||
|
|||
self.load_pokemon_list() |
|||
|
|||
def create_pokemon_tab(self): |
|||
pokemon_tab = QWidget() |
|||
tab_layout = QHBoxLayout() |
|||
|
|||
# Pokémon list section |
|||
pokemon_list_layout = QVBoxLayout() |
|||
|
|||
# Search box |
|||
self.search_box = QLineEdit() |
|||
self.search_box.setPlaceholderText("Search Pokémon...") |
|||
self.search_box.textChanged.connect(self.filter_pokemon_list) |
|||
pokemon_list_layout.addWidget(self.search_box) |
|||
|
|||
# Pokémon list |
|||
self.pokemon_list = QListWidget() |
|||
self.pokemon_list.itemClicked.connect(self.load_pokemon_data) |
|||
pokemon_list_layout.addWidget(self.pokemon_list) |
|||
|
|||
tab_layout.addLayout(pokemon_list_layout, 1) |
|||
|
|||
# Pokémon details |
|||
details_layout = QVBoxLayout() |
|||
|
|||
image_and_form_layout = QHBoxLayout() |
|||
|
|||
# Image |
|||
self.image_label = QLabel() |
|||
self.image_label.setFixedSize(200, 200) |
|||
self.image_label.setAlignment(Qt.AlignmentFlag.AlignCenter) |
|||
image_and_form_layout.addWidget(self.image_label) |
|||
|
|||
# Form details |
|||
form_details_layout = QFormLayout() |
|||
self.form_name_edit = QLineEdit() |
|||
self.is_default_checkbox = QCheckBox() |
|||
self.image_path_edit = QLineEdit() |
|||
form_details_layout.addRow("Form Name:", self.form_name_edit) |
|||
form_details_layout.addRow("Default Form:", self.is_default_checkbox) |
|||
form_details_layout.addRow("Image Path:", self.image_path_edit) |
|||
|
|||
# Save button |
|||
self.save_button = QPushButton("Save Form Changes") |
|||
self.save_button.clicked.connect(self.save_form_changes) |
|||
form_details_layout.addRow(self.save_button) |
|||
|
|||
image_and_form_layout.addLayout(form_details_layout) |
|||
details_layout.addLayout(image_and_form_layout) |
|||
|
|||
# Forms list and add new form button |
|||
forms_layout = QHBoxLayout() |
|||
self.forms_list = QListWidget() |
|||
self.forms_list.itemClicked.connect(self.load_form_data) |
|||
forms_layout.addWidget(self.forms_list) |
|||
|
|||
add_form_button = QPushButton("Add New Form") |
|||
add_form_button.clicked.connect(self.add_new_form) |
|||
details_layout.addWidget(add_form_button) |
|||
|
|||
details_layout.addWidget(QLabel("Forms:")) |
|||
details_layout.addLayout(forms_layout) |
|||
|
|||
# Encounters table and add new encounter button |
|||
self.encounters_table = QTableWidget() |
|||
self.encounters_table.setColumnCount(3) |
|||
self.encounters_table.setHorizontalHeaderLabels(['Game', 'Location', 'Method']) |
|||
self.encounters_table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch) |
|||
details_layout.addWidget(QLabel("Encounters:")) |
|||
details_layout.addWidget(self.encounters_table) |
|||
|
|||
add_encounter_button = QPushButton("Add New Encounter") |
|||
add_encounter_button.clicked.connect(self.add_new_encounter) |
|||
details_layout.addWidget(add_encounter_button) |
|||
|
|||
tab_layout.addLayout(details_layout, 2) |
|||
|
|||
pokemon_tab.setLayout(tab_layout) |
|||
return pokemon_tab |
|||
|
|||
def create_locations_tab(self): |
|||
locations_tab = QWidget() |
|||
tab_layout = QHBoxLayout() |
|||
|
|||
# Locations list |
|||
locations_list_layout = QVBoxLayout() |
|||
|
|||
# Search box for locations |
|||
self.locations_search_box = QLineEdit() |
|||
self.locations_search_box.setPlaceholderText("Search Locations...") |
|||
self.locations_search_box.textChanged.connect(self.filter_locations_list) |
|||
locations_list_layout.addWidget(self.locations_search_box) |
|||
|
|||
# Locations list |
|||
self.locations_list = QListWidget() |
|||
self.locations_list.itemClicked.connect(self.load_location_data) |
|||
locations_list_layout.addWidget(self.locations_list) |
|||
|
|||
# Add new location button |
|||
add_location_button = QPushButton("Add New Location") |
|||
add_location_button.clicked.connect(self.add_new_location) |
|||
locations_list_layout.addWidget(add_location_button) |
|||
|
|||
tab_layout.addLayout(locations_list_layout, 1) |
|||
|
|||
# Location details |
|||
location_details_layout = QFormLayout() |
|||
|
|||
self.location_name_edit = QLineEdit() |
|||
self.location_description_edit = QLineEdit() |
|||
|
|||
location_details_layout.addRow("Name:", self.location_name_edit) |
|||
location_details_layout.addRow("description:", self.location_description_edit) |
|||
|
|||
# Save location changes button |
|||
save_location_button = QPushButton("Save Location Changes") |
|||
save_location_button.clicked.connect(self.save_location_changes) |
|||
location_details_layout.addRow(save_location_button) |
|||
|
|||
tab_layout.addLayout(location_details_layout, 2) |
|||
|
|||
locations_tab.setLayout(tab_layout) |
|||
return locations_tab |
|||
|
|||
def load_database(self): |
|||
disk_conn = sqlite3.connect('pokemon_database.db') |
|||
disk_conn.backup(self.conn) |
|||
disk_conn.close() |
|||
|
|||
def apply_all_patches(self): |
|||
patches_dir = "patches" |
|||
if not os.path.exists(patches_dir): |
|||
return |
|||
|
|||
patch_files = sorted([f for f in os.listdir(patches_dir) if f.endswith('.json')]) |
|||
for patch_file in patch_files: |
|||
with open(os.path.join(patches_dir, patch_file), 'r') as f: |
|||
patches = json.load(f) |
|||
self.apply_patches(patches) |
|||
|
|||
def apply_patches(self, patches): |
|||
try: |
|||
for timestamp, patch in patches.items(): |
|||
if patch["type"] == "form_update": |
|||
self.cursor.execute(""" |
|||
UPDATE pokemon_forms |
|||
SET form_name = ?, is_default = ?, image_path = ? |
|||
WHERE id = ? |
|||
""", (patch["form_name"], patch["is_default"], patch["image_path"], patch["form_id"])) |
|||
elif patch["type"] == "new_form": |
|||
self.cursor.execute(""" |
|||
INSERT INTO pokemon_forms (pokemon_id, form_name, is_default, image_path) |
|||
VALUES (?, ?, ?, ?) |
|||
""", (patch["pokemon_id"], patch["form_name"], patch["is_default"], patch["image_path"])) |
|||
elif patch["type"] == "new_encounter": |
|||
self.cursor.execute(""" |
|||
INSERT INTO form_encounters (form_id, game_id, location_id, encounter_method_id) |
|||
VALUES (?, |
|||
(SELECT id FROM games WHERE name = ?), |
|||
(SELECT id FROM locations WHERE name = ?), |
|||
(SELECT id FROM encounter_methods WHERE name = ?)) |
|||
""", (patch["form_id"], patch["game"], patch["location"], patch["method"])) |
|||
elif patch["type"] == "location_update": |
|||
self.cursor.execute(""" |
|||
UPDATE locations |
|||
SET name = ?, description = ? |
|||
WHERE name = ? |
|||
""", (patch["new_name"], patch["description"], patch["old_name"])) |
|||
elif patch["type"] == "new_location": |
|||
self.cursor.execute(""" |
|||
INSERT INTO locations (name, description) |
|||
VALUES (?, ?) |
|||
""", (patch["name"], patch["description"])) |
|||
|
|||
self.conn.commit() |
|||
except sqlite3.Error as e: |
|||
print(f"An error occurred while applying patches: {e}") |
|||
|
|||
def load_pokemon_list(self): |
|||
self.cursor.execute("SELECT national_dex_number, name FROM pokemon ORDER BY national_dex_number") |
|||
self.all_pokemon = [f"{row[0]:03d} - {row[1]}" for row in self.cursor.fetchall()] |
|||
self.pokemon_list.addItems(self.all_pokemon) |
|||
|
|||
def filter_pokemon_list(self): |
|||
search_text = self.search_box.text().lower() |
|||
self.pokemon_list.clear() |
|||
for pokemon in self.all_pokemon: |
|||
if search_text in pokemon.lower(): |
|||
self.pokemon_list.addItem(pokemon) |
|||
|
|||
def load_pokemon_data(self, item): |
|||
self.current_pokemon_id = int(item.text().split('-')[0]) |
|||
self.current_pokemon_name = item.text().split('-')[1] |
|||
# Load forms |
|||
self.forms_list.clear() |
|||
self.cursor.execute(""" |
|||
SELECT form_name FROM pokemon_forms |
|||
WHERE pokemon_id = ? |
|||
ORDER BY is_default DESC, form_name |
|||
""", (self.current_pokemon_id,)) |
|||
for row in self.cursor.fetchall(): |
|||
self.forms_list.addItem(row[0]) |
|||
|
|||
# Load default form data |
|||
self.forms_list.setCurrentRow(0) |
|||
self.load_form_data(self.forms_list.item(0)) |
|||
|
|||
def load_form_data(self, item): |
|||
if not item: |
|||
return |
|||
|
|||
form_name = item.text() |
|||
|
|||
# Load form data |
|||
self.cursor.execute(""" |
|||
SELECT id, form_name, is_default, image_path FROM pokemon_forms |
|||
WHERE pokemon_id = ? AND form_name = ? |
|||
""", (self.current_pokemon_id, form_name)) |
|||
form_data = self.cursor.fetchone() |
|||
if form_data: |
|||
self.current_form_id, form_name, is_default, image_path = form_data |
|||
|
|||
# Update form details |
|||
self.form_name_edit.setText(form_name) |
|||
self.is_default_checkbox.setChecked(bool(is_default)) |
|||
self.image_path_edit.setText(image_path) |
|||
|
|||
# Load image |
|||
pixmap = QPixmap(image_path) |
|||
self.image_label.setPixmap(pixmap.scaled(200, 200, Qt.AspectRatioMode.KeepAspectRatio)) |
|||
|
|||
# Load encounters |
|||
self.encounters_table.setRowCount(0) |
|||
self.cursor.execute(""" |
|||
SELECT g.name, l.name, em.name |
|||
FROM form_encounters fe |
|||
JOIN games g ON fe.game_id = g.id |
|||
JOIN locations l ON fe.location_id = l.id |
|||
JOIN encounter_methods em ON fe.encounter_method_id = em.id |
|||
WHERE fe.form_id = ? |
|||
ORDER BY g.name, l.name |
|||
""", (self.current_form_id,)) |
|||
for row in self.cursor.fetchall(): |
|||
current_row = self.encounters_table.rowCount() |
|||
self.encounters_table.insertRow(current_row) |
|||
for col, value in enumerate(row): |
|||
self.encounters_table.setItem(current_row, col, QTableWidgetItem(str(value))) |
|||
|
|||
def save_form_changes(self): |
|||
if not self.current_form_id: |
|||
return |
|||
|
|||
new_form_name = self.form_name_edit.text() |
|||
new_is_default = self.is_default_checkbox.isChecked() |
|||
new_image_path = self.image_path_edit.text() |
|||
|
|||
# Add changes to patches |
|||
patch = { |
|||
"type": "form_update", |
|||
"form_id": self.current_form_id, |
|||
"form_name": new_form_name, |
|||
"is_default": new_is_default, |
|||
"image_path": new_image_path |
|||
} |
|||
self.add_to_patches(patch) |
|||
|
|||
try: |
|||
self.cursor.execute(""" |
|||
UPDATE pokemon_forms |
|||
SET form_name = ?, is_default = ?, image_path = ? |
|||
WHERE id = ? |
|||
""", (new_form_name, new_is_default, new_image_path, self.current_form_id)) |
|||
self.conn.commit() |
|||
QMessageBox.information(self, "Success", "Form data updated successfully!") |
|||
|
|||
# Refresh the forms list and current form data |
|||
self.load_pokemon_data(self.pokemon_list.currentItem()) |
|||
except sqlite3.Error as e: |
|||
QMessageBox.warning(self, "Error", f"An error occurred: {e}") |
|||
|
|||
def add_new_form(self): |
|||
if not self.current_pokemon_id: |
|||
QMessageBox.warning(self, "Error", "Please select a Pokémon first.") |
|||
return |
|||
|
|||
dialog = QDialog(self) |
|||
dialog.setWindowTitle("Add New Form") |
|||
layout = QFormLayout(dialog) |
|||
|
|||
form_name_edit = QLineEdit() |
|||
layout.addRow("Form Name:", form_name_edit) |
|||
|
|||
buttons = QHBoxLayout() |
|||
save_button = QPushButton("Save") |
|||
save_button.clicked.connect(dialog.accept) |
|||
cancel_button = QPushButton("Cancel") |
|||
cancel_button.clicked.connect(dialog.reject) |
|||
buttons.addWidget(save_button) |
|||
buttons.addWidget(cancel_button) |
|||
layout.addRow(buttons) |
|||
|
|||
if dialog.exec() == QDialog.DialogCode.Accepted: |
|||
form_name = form_name_edit.text() |
|||
|
|||
try: |
|||
self.cursor.execute(""" |
|||
INSERT INTO pokemon_forms (pokemon_id, form_name, is_default, image_path) |
|||
VALUES (?, ?, ?, ?) |
|||
""", (self.current_pokemon_id, form_name, False, f"images/pokemon/{self.current_pokemon_id:04d}_{self.current_pokemon_name}_({form_name}).png".replace(" ", "_"))) |
|||
self.conn.commit() |
|||
new_form_id = self.cursor.lastrowid |
|||
|
|||
# Add new form to patches |
|||
patch = { |
|||
"type": "new_form", |
|||
"form_id": new_form_id, |
|||
"pokemon_id": self.current_pokemon_id, |
|||
"form_name": form_name, |
|||
"is_default": False, |
|||
"image_path": f"images/pokemon/{self.current_pokemon_id:04d}_{self.current_pokemon_name}_({form_name}).png".replace(" ", "_") |
|||
} |
|||
self.add_to_patches(patch) |
|||
|
|||
QMessageBox.information(self, "Success", "New form added successfully!") |
|||
self.load_pokemon_data(self.pokemon_list.currentItem()) |
|||
except sqlite3.Error as e: |
|||
QMessageBox.warning(self, "Error", f"An error occurred: {e}") |
|||
|
|||
def add_new_encounter(self): |
|||
if not self.current_form_id: |
|||
QMessageBox.warning(self, "Error", "Please select a form first.") |
|||
return |
|||
|
|||
dialog = QDialog(self) |
|||
dialog.setWindowTitle("Add New Encounter") |
|||
layout = QFormLayout(dialog) |
|||
|
|||
game_combo = QComboBox() |
|||
self.cursor.execute("SELECT name FROM games ORDER BY name") |
|||
games = [row[0] for row in self.cursor.fetchall()] |
|||
game_combo.addItems(games) |
|||
layout.addRow("Game:", game_combo) |
|||
|
|||
location_combo = QComboBox() |
|||
self.cursor.execute("SELECT name FROM locations ORDER BY name") |
|||
locations = [row[0] for row in self.cursor.fetchall()] |
|||
location_combo.addItems(locations) |
|||
layout.addRow("Location:", location_combo) |
|||
|
|||
method_combo = QComboBox() |
|||
self.cursor.execute("SELECT name FROM encounter_methods ORDER BY name") |
|||
methods = [row[0] for row in self.cursor.fetchall()] |
|||
method_combo.addItems(methods) |
|||
layout.addRow("Method:", method_combo) |
|||
|
|||
buttons = QHBoxLayout() |
|||
save_button = QPushButton("Save") |
|||
save_button.clicked.connect(dialog.accept) |
|||
cancel_button = QPushButton("Cancel") |
|||
cancel_button.clicked.connect(dialog.reject) |
|||
buttons.addWidget(save_button) |
|||
buttons.addWidget(cancel_button) |
|||
layout.addRow(buttons) |
|||
|
|||
if dialog.exec() == QDialog.DialogCode.Accepted: |
|||
game = game_combo.currentText() |
|||
location = location_combo.currentText() |
|||
method = method_combo.currentText() |
|||
|
|||
try: |
|||
self.cursor.execute(""" |
|||
INSERT INTO form_encounters (form_id, game_id, location_id, encounter_method_id) |
|||
VALUES (?, |
|||
(SELECT id FROM games WHERE name = ?), |
|||
(SELECT id FROM locations WHERE name = ?), |
|||
(SELECT id FROM encounter_methods WHERE name = ?)) |
|||
""", (self.current_form_id, game, location, method)) |
|||
self.conn.commit() |
|||
new_encounter_id = self.cursor.lastrowid |
|||
|
|||
# Add new encounter to patches |
|||
patch = { |
|||
"type": "new_encounter", |
|||
"encounter_id": new_encounter_id, |
|||
"form_id": self.current_form_id, |
|||
"game": game, |
|||
"location": location, |
|||
"method": method |
|||
} |
|||
self.add_to_patches(patch) |
|||
|
|||
QMessageBox.information(self, "Success", "New encounter added successfully!") |
|||
self.load_form_data(self.forms_list.currentItem()) |
|||
except sqlite3.Error as e: |
|||
QMessageBox.warning(self, "Error", f"An error occurred: {e}") |
|||
|
|||
def add_to_patches(self, patch): |
|||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") |
|||
self.patches[f"{timestamp}_{len(self.patches)}"] = patch |
|||
self.auto_save_patches() |
|||
|
|||
def auto_save_patches(self): |
|||
patches_dir = "patches" |
|||
if not os.path.exists(patches_dir): |
|||
os.makedirs(patches_dir) |
|||
|
|||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") |
|||
file_name = f"{patches_dir}/patch_{timestamp}.json" |
|||
|
|||
with open(file_name, 'w') as f: |
|||
json.dump(self.patches, f, indent=2) |
|||
|
|||
print(f"Patches auto-saved to {file_name}") |
|||
|
|||
def closeEvent(self, event): |
|||
reply = QMessageBox.question(self, 'Save Changes', |
|||
"Do you want to save changes to the disk database?", |
|||
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, |
|||
QMessageBox.StandardButton.No) |
|||
|
|||
if reply == QMessageBox.StandardButton.Yes: |
|||
# Save changes to disk |
|||
disk_conn = sqlite3.connect('pokemon_database.db') |
|||
self.conn.backup(disk_conn) |
|||
disk_conn.close() |
|||
QMessageBox.information(self, "Success", "Changes saved to disk database.") |
|||
|
|||
self.conn.close() |
|||
event.accept() |
|||
|
|||
def filter_locations_list(self): |
|||
search_text = self.locations_search_box.text().lower() |
|||
for i in range(self.locations_list.count()): |
|||
item = self.locations_list.item(i) |
|||
if search_text in item.text().lower(): |
|||
item.setHidden(False) |
|||
else: |
|||
item.setHidden(True) |
|||
|
|||
def load_location_data(self, item): |
|||
location_name = item.text() |
|||
self.cursor.execute("SELECT name, description FROM locations WHERE name = ?", (location_name,)) |
|||
location_data = self.cursor.fetchone() |
|||
if location_data: |
|||
self.location_name_edit.setText(location_data[0]) |
|||
self.location_description_edit.setText(location_data[1]) |
|||
|
|||
def save_location_changes(self): |
|||
old_name = self.locations_list.currentItem().text() |
|||
new_name = self.location_name_edit.text() |
|||
new_description = self.location_description_edit.text() |
|||
|
|||
try: |
|||
self.cursor.execute(""" |
|||
UPDATE locations |
|||
SET name = ?, description = ? |
|||
WHERE name = ? |
|||
""", (new_name, new_description, old_name)) |
|||
self.conn.commit() |
|||
|
|||
# Add changes to patches |
|||
patch = { |
|||
"type": "location_update", |
|||
"old_name": old_name, |
|||
"new_name": new_name, |
|||
"description": new_description |
|||
} |
|||
self.add_to_patches(patch) |
|||
|
|||
QMessageBox.information(self, "Success", "Location data updated successfully!") |
|||
self.load_locations_list() |
|||
except sqlite3.Error as e: |
|||
QMessageBox.warning(self, "Error", f"An error occurred: {e}") |
|||
|
|||
def add_new_location(self): |
|||
dialog = QDialog(self) |
|||
dialog.setWindowTitle("Add New Location") |
|||
layout = QFormLayout(dialog) |
|||
|
|||
name_edit = QLineEdit() |
|||
description_edit = QLineEdit() |
|||
layout.addRow("Name:", name_edit) |
|||
layout.addRow("description:", description_edit) |
|||
|
|||
buttons = QHBoxLayout() |
|||
save_button = QPushButton("Save") |
|||
save_button.clicked.connect(dialog.accept) |
|||
cancel_button = QPushButton("Cancel") |
|||
cancel_button.clicked.connect(dialog.reject) |
|||
buttons.addWidget(save_button) |
|||
buttons.addWidget(cancel_button) |
|||
layout.addRow(buttons) |
|||
|
|||
if dialog.exec() == QDialog.DialogCode.Accepted: |
|||
name = name_edit.text() |
|||
description = description_edit.text() |
|||
|
|||
try: |
|||
self.cursor.execute(""" |
|||
INSERT INTO locations (name, description) |
|||
VALUES (?, ?) |
|||
""", (name, description)) |
|||
self.conn.commit() |
|||
|
|||
# Add new location to patches |
|||
patch = { |
|||
"type": "new_location", |
|||
"name": name, |
|||
"description": description |
|||
} |
|||
self.add_to_patches(patch) |
|||
|
|||
QMessageBox.information(self, "Success", "New location added successfully!") |
|||
self.load_locations_list() |
|||
except sqlite3.Error as e: |
|||
QMessageBox.warning(self, "Error", f"An error occurred: {e}") |
|||
|
|||
def load_locations_list(self): |
|||
self.locations_list.clear() |
|||
self.cursor.execute("SELECT name FROM locations ORDER BY name") |
|||
locations = [row[0] for row in self.cursor.fetchall()] |
|||
self.locations_list.addItems(locations) |
|||
|
|||
def export_production_database(self): |
|||
try: |
|||
# Create a new connection for the production database |
|||
production_db_path = QFileDialog.getSaveFileName(self, "Save Production Database", "", "SQLite Database (*.db)")[0] |
|||
if not production_db_path: |
|||
return # User cancelled the file dialog |
|||
|
|||
production_conn = sqlite3.connect(production_db_path) |
|||
|
|||
# Copy the current in-memory database to the production database |
|||
self.conn.backup(production_conn) |
|||
|
|||
# Close the production database connection |
|||
production_conn.close() |
|||
|
|||
QMessageBox.information(self, "Success", f"Production database exported successfully to {production_db_path}") |
|||
except sqlite3.Error as e: |
|||
QMessageBox.warning(self, "Error", f"An error occurred while exporting the production database: {e}") |
|||
|
|||
if __name__ == '__main__': |
|||
app = QApplication(sys.argv) |
|||
window = PokemonDatabaseApp() |
|||
window.show() |
|||
sys.exit(app.exec()) |
|||
@ -1,296 +0,0 @@ |
|||
import sys |
|||
import os |
|||
import sqlite3 |
|||
from PyQt5.QtWidgets import (QApplication, QMainWindow, QTableWidget, QTableWidgetItem, QPushButton, QVBoxLayout, |
|||
QHBoxLayout, QWidget, QLineEdit, QLabel, QMessageBox, QTabWidget, QScrollArea, QFrame, QGridLayout) |
|||
from PyQt5.QtCore import Qt |
|||
from PyQt5.QtGui import QPixmap |
|||
|
|||
class PokemonEvolutionWidget(QWidget): |
|||
def __init__(self, conn, pfic): |
|||
super().__init__() |
|||
self.conn = conn |
|||
self.pfic = pfic |
|||
self.layout = QVBoxLayout() |
|||
self.setLayout(self.layout) |
|||
self.stage_width = 200 # Fixed width for each evolution stage |
|||
self.stage_height = 250 # Fixed height for each evolution stage |
|||
self.build_evolution_chain() |
|||
|
|||
def build_evolution_chain(self): |
|||
chain = self.get_full_evolution_chain(self.pfic) |
|||
self.display_evolution_chain(chain) |
|||
|
|||
def get_full_evolution_chain(self, pfic): |
|||
cursor = self.conn.cursor() |
|||
chain = [] |
|||
visited = set() |
|||
|
|||
def build_chain(current_pfic): |
|||
if current_pfic in visited: |
|||
return None |
|||
visited.add(current_pfic) |
|||
|
|||
cursor.execute('SELECT name, form_name FROM pokemon_forms WHERE PFIC = ?', (current_pfic,)) |
|||
pokemon = cursor.fetchone() |
|||
if not pokemon: |
|||
return None |
|||
|
|||
name, form_name = pokemon |
|||
node = {"pfic": current_pfic, "name": name, "form_name": form_name, "evolutions": []} |
|||
|
|||
cursor.execute(''' |
|||
SELECT ec.to_pfic, pf.name, pf.form_name, ec.method |
|||
FROM evolution_chains ec |
|||
JOIN pokemon_forms pf ON ec.to_pfic = pf.PFIC |
|||
WHERE ec.from_pfic = ? |
|||
''', (current_pfic,)) |
|||
evolutions = cursor.fetchall() |
|||
|
|||
for evo_pfic, evo_name, evo_form, method in evolutions: |
|||
evo_node = build_chain(evo_pfic) |
|||
if evo_node: |
|||
node["evolutions"].append({"node": evo_node, "method": method}) |
|||
|
|||
return node |
|||
|
|||
chain = build_chain(pfic) |
|||
return chain |
|||
|
|||
def display_evolution_chain(self, chain): |
|||
if not chain: |
|||
return |
|||
|
|||
main_layout = QHBoxLayout() |
|||
self.layout.addLayout(main_layout) |
|||
|
|||
stages = self.split_into_stages(chain) |
|||
for stage_index, stage in enumerate(stages): |
|||
stage_widget = QWidget() |
|||
stage_layout = QGridLayout() |
|||
stage_widget.setLayout(stage_layout) |
|||
stage_widget.setFixedSize(self.stage_width, self.stage_height * len(stage)) |
|||
|
|||
for row, pokemon in enumerate(stage): |
|||
pokemon_widget = self.create_pokemon_widget(pokemon["pfic"], pokemon["name"], pokemon["form_name"]) |
|||
stage_layout.addWidget(pokemon_widget, row, 0, Qt.AlignCenter) |
|||
|
|||
if stage_index < len(stages) - 1 and pokemon.get("method"): |
|||
arrow_label = QLabel("→") |
|||
arrow_label.setStyleSheet("font-size: 24px;") |
|||
stage_layout.addWidget(arrow_label, row, 1, Qt.AlignCenter) |
|||
|
|||
method_label = QLabel(pokemon["method"]) |
|||
method_label.setWordWrap(True) |
|||
method_label.setFixedWidth(80) |
|||
stage_layout.addWidget(method_label, row, 2, Qt.AlignCenter) |
|||
|
|||
main_layout.addWidget(stage_widget) |
|||
|
|||
def split_into_stages(self, chain): |
|||
stages = [] |
|||
current_stage = [{"pfic": chain["pfic"], "name": chain["name"], "form_name": chain["form_name"]}] |
|||
stages.append(current_stage) |
|||
|
|||
while current_stage: |
|||
next_stage = [] |
|||
for pokemon in current_stage: |
|||
evolutions = self.get_evolutions(pokemon["pfic"]) |
|||
for evolution in evolutions: |
|||
next_stage.append({ |
|||
"pfic": evolution["to_pfic"], |
|||
"name": evolution["name"], |
|||
"form_name": evolution["form_name"], |
|||
"method": evolution["method"] |
|||
}) |
|||
if next_stage: |
|||
stages.append(next_stage) |
|||
current_stage = next_stage |
|||
|
|||
return stages |
|||
|
|||
def get_evolutions(self, pfic): |
|||
cursor = self.conn.cursor() |
|||
cursor.execute(''' |
|||
SELECT ec.to_pfic, pf.name, pf.form_name, ec.method |
|||
FROM evolution_chains ec |
|||
JOIN pokemon_forms pf ON ec.to_pfic = pf.PFIC |
|||
WHERE ec.from_pfic = ? |
|||
''', (pfic,)) |
|||
evolutions = cursor.fetchall() |
|||
return [{"to_pfic": to_pfic, "name": name, "form_name": form_name, "method": method} for to_pfic, name, form_name, method in evolutions] |
|||
|
|||
def create_pokemon_widget(self, pfic, name, form_name): |
|||
widget = QWidget() |
|||
layout = QVBoxLayout() |
|||
widget.setLayout(layout) |
|||
|
|||
image_path = f"images-new/{pfic}.png" |
|||
if os.path.exists(image_path): |
|||
pixmap = QPixmap(image_path) |
|||
image_label = QLabel() |
|||
image_label.setPixmap(pixmap.scaled(96, 96, Qt.KeepAspectRatio, Qt.SmoothTransformation)) |
|||
layout.addWidget(image_label, alignment=Qt.AlignCenter) |
|||
|
|||
name_label = QLabel(name) |
|||
name_label.setAlignment(Qt.AlignCenter) |
|||
layout.addWidget(name_label) |
|||
|
|||
if form_name: |
|||
form_label = QLabel(form_name) |
|||
form_label.setAlignment(Qt.AlignCenter) |
|||
layout.addWidget(form_label) |
|||
|
|||
return widget |
|||
|
|||
class DatabaseVisualizer(QMainWindow): |
|||
def __init__(self): |
|||
super().__init__() |
|||
self.setWindowTitle("Pokémon Database Visualizer") |
|||
self.setGeometry(100, 100, 1200, 800) |
|||
|
|||
self.conn = sqlite3.connect('pokemon_forms.db') |
|||
self.cursor = self.conn.cursor() |
|||
|
|||
self.central_widget = QWidget() |
|||
self.setCentralWidget(self.central_widget) |
|||
|
|||
self.layout = QVBoxLayout() |
|||
self.central_widget.setLayout(self.layout) |
|||
|
|||
self.tab_widget = QTabWidget() |
|||
self.layout.addWidget(self.tab_widget) |
|||
|
|||
self.forms_tab = QWidget() |
|||
self.evolutions_tab = QWidget() |
|||
self.tab_widget.addTab(self.forms_tab, "Pokémon Forms") |
|||
self.tab_widget.addTab(self.evolutions_tab, "Evolution Chains") |
|||
|
|||
self.setup_forms_tab() |
|||
self.setup_evolutions_tab() |
|||
|
|||
def setup_forms_tab(self): |
|||
layout = QVBoxLayout() |
|||
self.forms_tab.setLayout(layout) |
|||
|
|||
self.search_layout = QHBoxLayout() |
|||
self.search_label = QLabel("Search:") |
|||
self.search_input = QLineEdit() |
|||
self.search_button = QPushButton("Search") |
|||
self.search_button.clicked.connect(self.search_pokemon) |
|||
self.search_layout.addWidget(self.search_label) |
|||
self.search_layout.addWidget(self.search_input) |
|||
self.search_layout.addWidget(self.search_button) |
|||
layout.addLayout(self.search_layout) |
|||
|
|||
self.table = QTableWidget() |
|||
layout.addWidget(self.table) |
|||
|
|||
self.save_button = QPushButton("Save Changes") |
|||
self.save_button.clicked.connect(self.save_changes) |
|||
layout.addWidget(self.save_button) |
|||
|
|||
self.load_forms_data() |
|||
|
|||
def setup_evolutions_tab(self): |
|||
layout = QVBoxLayout() |
|||
self.evolutions_tab.setLayout(layout) |
|||
|
|||
self.evolution_search_layout = QHBoxLayout() |
|||
self.evolution_search_label = QLabel("Search Pokémon:") |
|||
self.evolution_search_input = QLineEdit() |
|||
self.evolution_search_button = QPushButton("Search") |
|||
self.evolution_search_button.clicked.connect(self.search_evolution) |
|||
self.evolution_search_layout.addWidget(self.evolution_search_label) |
|||
self.evolution_search_layout.addWidget(self.evolution_search_input) |
|||
self.evolution_search_layout.addWidget(self.evolution_search_button) |
|||
layout.addLayout(self.evolution_search_layout) |
|||
|
|||
self.evolution_scroll_area = QScrollArea() |
|||
self.evolution_scroll_area.setWidgetResizable(True) |
|||
layout.addWidget(self.evolution_scroll_area) |
|||
|
|||
def load_forms_data(self): |
|||
self.cursor.execute(''' |
|||
SELECT pf.PFIC, pf.name, pf.form_name, pf.national_dex, pf.generation, ps.storable_in_home |
|||
FROM pokemon_forms pf |
|||
LEFT JOIN pokemon_storage ps ON pf.PFIC = ps.PFIC |
|||
''') |
|||
data = self.cursor.fetchall() |
|||
|
|||
self.table.setColumnCount(6) |
|||
self.table.setHorizontalHeaderLabels(["PFIC", "Name", "Form Name", "National Dex", "Generation", "Storable in Home"]) |
|||
self.table.setRowCount(len(data)) |
|||
|
|||
for row, record in enumerate(data): |
|||
for col, value in enumerate(record): |
|||
item = QTableWidgetItem(str(value)) |
|||
if col == 0: # PFIC column |
|||
item.setFlags(item.flags() & ~Qt.ItemIsEditable) # Make PFIC non-editable |
|||
self.table.setItem(row, col, item) |
|||
|
|||
self.table.resizeColumnsToContents() |
|||
|
|||
def search_pokemon(self): |
|||
search_term = self.search_input.text().lower() |
|||
for row in range(self.table.rowCount()): |
|||
match = False |
|||
for col in range(self.table.columnCount()): |
|||
item = self.table.item(row, col) |
|||
if item and search_term in item.text().lower(): |
|||
match = True |
|||
break |
|||
self.table.setRowHidden(row, not match) |
|||
|
|||
def save_changes(self): |
|||
try: |
|||
for row in range(self.table.rowCount()): |
|||
pfic = self.table.item(row, 0).text() |
|||
name = self.table.item(row, 1).text() |
|||
form_name = self.table.item(row, 2).text() or None |
|||
national_dex = int(self.table.item(row, 3).text()) |
|||
generation = int(self.table.item(row, 4).text()) |
|||
storable_in_home = self.table.item(row, 5).text().lower() == 'true' |
|||
|
|||
self.cursor.execute(''' |
|||
UPDATE pokemon_forms |
|||
SET name = ?, form_name = ?, national_dex = ?, generation = ? |
|||
WHERE PFIC = ? |
|||
''', (name, form_name, national_dex, generation, pfic)) |
|||
|
|||
self.cursor.execute(''' |
|||
INSERT OR REPLACE INTO pokemon_storage (PFIC, storable_in_home) |
|||
VALUES (?, ?) |
|||
''', (pfic, storable_in_home)) |
|||
|
|||
self.conn.commit() |
|||
QMessageBox.information(self, "Success", "Changes saved successfully!") |
|||
except Exception as e: |
|||
QMessageBox.critical(self, "Error", f"An error occurred while saving changes: {str(e)}") |
|||
|
|||
def search_evolution(self): |
|||
search_term = self.evolution_search_input.text().lower() |
|||
|
|||
self.cursor.execute(''' |
|||
SELECT DISTINCT name, PFIC |
|||
FROM pokemon_forms |
|||
WHERE LOWER(name) LIKE ? |
|||
''', (f'%{search_term}%',)) |
|||
|
|||
matching_pokemon = self.cursor.fetchall() |
|||
|
|||
if matching_pokemon: |
|||
pokemon_name, pfic = matching_pokemon[0] |
|||
evolution_widget = PokemonEvolutionWidget(self.conn, pfic) |
|||
self.evolution_scroll_area.setWidget(evolution_widget) |
|||
else: |
|||
QMessageBox.information(self, "No Results", "No Pokémon found matching the search term.") |
|||
|
|||
def closeEvent(self, event): |
|||
self.conn.close() |
|||
|
|||
if __name__ == "__main__": |
|||
app = QApplication(sys.argv) |
|||
window = DatabaseVisualizer() |
|||
window.show() |
|||
sys.exit(app.exec_()) |
|||
File diff suppressed because it is too large
Loading…
Reference in new issue