From a37c23615f88e352efdaa136404ae812dc01834e Mon Sep 17 00:00:00 2001 From: Dan Date: Fri, 25 Oct 2024 11:44:42 +0100 Subject: [PATCH] - Update the editor to save the DB, get the web ui working again --- DBEditor/db_controller.py | 6 ++ DBEditor/pokemon_db_ui.py | 4 +- Determining an origin mark.txt | 10 +++ Site/OriginDex.py | 107 +++++++++++++++++++++------------ Site/templates/index.html | 105 +++++++++++++++++++++++++++++--- 5 files changed, 181 insertions(+), 51 deletions(-) create mode 100644 Determining an origin mark.txt diff --git a/DBEditor/db_controller.py b/DBEditor/db_controller.py index 8f84cc2..d0b3ec2 100644 --- a/DBEditor/db_controller.py +++ b/DBEditor/db_controller.py @@ -33,6 +33,7 @@ class DBController: event_system.add_listener('get_games_list', self.get_games_list) event_system.add_listener('add_new_exclusive_set', self.add_new_exclusive_set) event_system.add_listener('save_changes', self.save_changes) + event_system.add_listener('export_database', self.export_database) def init_database(self): disk_conn = sqlite3.connect('pokemon_forms.db') @@ -442,3 +443,8 @@ class DBController: disk_conn = sqlite3.connect('pokemon_forms.db') self.conn.backup(disk_conn) disk_conn.close() + + def export_database(self, data): + export_conn = sqlite3.connect('pokemon_forms_production.db') + self.conn.backup(export_conn) + export_conn.close() diff --git a/DBEditor/pokemon_db_ui.py b/DBEditor/pokemon_db_ui.py index 24a19f1..5247ede 100644 --- a/DBEditor/pokemon_db_ui.py +++ b/DBEditor/pokemon_db_ui.py @@ -874,9 +874,7 @@ class PokemonUI(QWidget): # Change from QMainWindow to QWidget QMessageBox.information(self, "Save Complete", "Changes have been saved to the database.") def export_database(self): - export_conn = sqlite3.connect('pokemon_forms_production.db') - self.conn.backup(export_conn) - export_conn.close() + event_system.emit_sync('export_database') def add_new_evolution(self): if not hasattr(self, 'current_pfic'): diff --git a/Determining an origin mark.txt b/Determining an origin mark.txt new file mode 100644 index 0000000..4d77257 --- /dev/null +++ b/Determining an origin mark.txt @@ -0,0 +1,10 @@ +To determine an origin mark: + +1. If a pokemon form has a previous evolution from within the same generation, + use the mark of the previous evolution. This should be recursive within the same generation. +2. If a pokemon form has no previous evolution from within the same generation, + look at the encounters of the pokemon form and use the mark of the earliest game you can encounter that form in. +3. If there are no encounters for the pokemon form, + use the mark of the earliest game of the generation is marked as being introducted in. +4. If there is no mark for the earliest game of the generation, + then it has no origin mark. diff --git a/Site/OriginDex.py b/Site/OriginDex.py index 1ec1ecc..d1fc6bb 100644 --- a/Site/OriginDex.py +++ b/Site/OriginDex.py @@ -11,57 +11,86 @@ def load_pokemon_data(): conn = sqlite3.connect('pokemon_forms.db') cursor = conn.cursor() + # First query: Get all Pokémon data cursor.execute(''' - WITH RECURSIVE EvolutionChain AS ( - SELECT PFIC, from_pfic - FROM evolution_chains - UNION ALL - SELECT ec.PFIC, e.from_pfic - FROM evolution_chains ec - JOIN EvolutionChain e ON ec.from_pfic = e.PFIC - ), - BaseForm AS ( - SELECT PFIC, MIN(from_pfic) AS base_pfic - FROM EvolutionChain - GROUP BY PFIC - ), - EarliestGamePerPokemon AS ( - SELECT e.pfic, MIN(g.generation) as min_generation, g.name as earliest_game + WITH EarliestGamePerPokemon AS ( + SELECT e.pfic, MIN(g.generation) as min_generation, MIN(g.id) as earliest_game_id FROM encounters e - JOIN games g ON e.game = g.name + JOIN games g ON e.game_id = g.id GROUP BY e.pfic - ), - FirstGameInEarliestGen AS ( - SELECT eg.pfic, MIN(g.id) as first_game_id - FROM EarliestGamePerPokemon eg - JOIN games g ON eg.min_generation = g.generation - GROUP BY eg.pfic ) SELECT pf.national_dex, pf.name, pf.form_name, pf.PFIC, pf.generation, - ps.storable_in_home, eg.earliest_game, - m.icon_path as mark_icon, m.id as mark_id + ps.storable_in_home, g.name as earliest_game, + m.icon_path as mark_icon, m.id as mark_id, pf.is_baby_form FROM pokemon_forms pf JOIN pokemon_storage ps ON pf.PFIC = ps.PFIC - LEFT JOIN BaseForm bf ON pf.PFIC = bf.PFIC - LEFT JOIN EarliestGamePerPokemon eg ON COALESCE(bf.base_pfic, pf.PFIC) = eg.pfic - LEFT JOIN FirstGameInEarliestGen fg ON COALESCE(bf.base_pfic, pf.PFIC) = fg.pfic - LEFT JOIN games g ON fg.first_game_id = g.id + LEFT JOIN EarliestGamePerPokemon eg ON pf.PFIC = eg.pfic + LEFT JOIN games g ON eg.earliest_game_id = g.id LEFT JOIN mark_game_associations mga ON g.id = mga.game_id LEFT JOIN marks m ON mga.mark_id = m.id WHERE ps.storable_in_home = 1 ORDER BY pf.PFIC ''') + pokemon_data = cursor.fetchall() + + # Second query: Get evolution chain with generation info + cursor.execute(''' + WITH RECURSIVE EvolutionChain AS ( + SELECT to_pfic AS PFIC, from_pfic, pf.generation + FROM evolution_chains ec + JOIN pokemon_forms pf ON ec.to_pfic = pf.PFIC + UNION ALL + SELECT ec.to_pfic, e.from_pfic, pf.generation + FROM evolution_chains ec + JOIN EvolutionChain e ON ec.from_pfic = e.PFIC + JOIN pokemon_forms pf ON ec.to_pfic = pf.PFIC + ) + SELECT PFIC, MIN(from_pfic) AS base_pfic, MIN(generation) AS base_generation + FROM EvolutionChain + GROUP BY PFIC + ''') + + evolution_data = {row[0]: (row[1], row[2]) for row in cursor.fetchall()} + + # Process the data current_group = [] current_generation = None current_dex_number = None pokemon_forms = [] - for row in cursor.fetchall(): - national_dex, name, form_name, pfic, generation, storable_in_home, earliest_game, mark_icon, mark_id = row + for row in pokemon_data: + national_dex, name, form_name, pfic, generation, storable_in_home, earliest_game, mark_icon, mark_id, is_baby_form = row + # Find the base form for the mark, considering generation + base_pfic = pfic + base_generation = generation + while base_pfic in evolution_data: + prev_pfic, prev_generation = evolution_data[base_pfic] + if prev_pfic is not None and prev_generation == base_generation: + base_pfic = prev_pfic + else: + break + + # If the base form is different, we need to fetch its earliest game and mark + if base_pfic != pfic: + cursor.execute(''' + SELECT g.name, m.icon_path, m.id + FROM encounters e + JOIN games g ON e.game_id = g.id + LEFT JOIN mark_game_associations mga ON g.id = mga.game_id + LEFT JOIN marks m ON mga.mark_id = m.id + WHERE e.pfic = ? AND g.generation = ? + ORDER BY g.id + LIMIT 1 + ''', (base_pfic, base_generation)) + base_data = cursor.fetchone() + if base_data: + earliest_game, mark_icon, mark_id = base_data + pokemon = { + 'pfic': pfic, 'ID': national_dex, 'Name': name, 'Form': form_name if form_name else "Default", @@ -118,25 +147,27 @@ def index(): pokemon_list = load_pokemon_data() return render_template('index.html', grouped_pokemon=pokemon_list) -@app.route('/pokemon/') -def pokemon_details(dex_number): - conn = sqlite3.connect('pokemon_forms.db') # Updated database name +@app.route('/pokemon/') +def pokemon_details(pfic): + conn = sqlite3.connect('pokemon_forms.db') cursor = conn.cursor() cursor.execute(''' - SELECT pf.form_name, e.game, e.location, e.day, e.time, + SELECT pf.form_name, g.name, g.id as game_id, e.location, e.day, e.time, e.dual_slot, e.static_encounter, e.static_encounter_count, e.extra_text, e.stars, e.fishing, e.rods, e.starter FROM pokemon_forms pf JOIN encounters e ON pf.PFIC = e.pfic - WHERE pf.national_dex = ? - ORDER BY pf.form_name, e.game, e.location - ''', (dex_number,)) + JOIN games g ON e.game_id = g.id + WHERE pf.pfic = ? + ORDER BY g.id, pf.form_name, e.location + ''', (pfic,)) encounters = [ { 'form': form, 'game': game, + 'game_id': game_id, 'location': location, 'day': day, 'time': time, @@ -149,7 +180,7 @@ def pokemon_details(dex_number): 'rods': rods, 'starter': starter } - for form, game, location, day, time, dual_slot, static_encounter, static_encounter_count, + for form, game, game_id, location, day, time, dual_slot, static_encounter, static_encounter_count, extra_text, stars, fishing, rods, starter in cursor.fetchall() ] diff --git a/Site/templates/index.html b/Site/templates/index.html index 69087ec..41b590c 100644 --- a/Site/templates/index.html +++ b/Site/templates/index.html @@ -161,6 +161,39 @@ .pokeball-icon.grayscale { filter: grayscale(100%); } + + .game-encounters { + margin-bottom: 10px; + } + + .collapsible { + background-color: #f1f1f1; + color: #444; + cursor: pointer; + padding: 10px; + width: 100%; + text-align: left; + outline: none; + font-size: 15px; + transition: 0.4s; + border: none; + border-radius: 5px; + } + + .active, .collapsible:hover { + background-color: #ccc; + } + + .encounter-list { + display: none; + padding: 0 18px; + background-color: white; + overflow: hidden; + } + + .toggle-icon { + float: right; + } @@ -173,7 +206,7 @@
{% for pokemon in group %} {% if pokemon %} -
+
{{ pokemon.Name }}
{{ pokemon.Name }} {{ pokemon.Form }}
@@ -209,20 +242,72 @@ const detailsPanel = document.getElementById('details-panel'); const mainContent = document.getElementById('main-content'); - function showDetails(dexNumber, formName, event) { + function showDetails(dexNumber, formName, pfic, event) { event.stopPropagation(); - fetch(`/pokemon/${dexNumber}`) + fetch(`/pokemon/${pfic}`) .then(response => response.json()) .then(data => { - const formData = data.filter(encounter => encounter.form === formName); - detailsPanel.innerHTML = `

Pokémon #${dexNumber} - ${formName} Form

`; - detailsPanel.innerHTML += '

Encounters:

'; - detailsPanel.innerHTML += formData.map(encounter => - `

${encounter.game}: ${encounter.location} (${encounter.method})

` - ).join(''); + const encountersByGame = groupEncountersByGame(data); + detailsPanel.innerHTML = ` +

Pokémon #${dexNumber} - ${formName} Form

+

Encounters:

+ ${generateEncounterHTML(encountersByGame)} + `; detailsPanel.classList.add('open'); mainContent.classList.add('main-content-shifted'); + addCollapsibleListeners(); + }); + } + + function groupEncountersByGame(encounters) { + return encounters.reduce((acc, encounter) => { + if (!acc[encounter.game]) { + acc[encounter.game] = { + game_id: encounter.game_id, + encounters: [] + }; + } + acc[encounter.game].encounters.push(encounter); + return acc; + }, {}); + } + + function generateEncounterHTML(encountersByGame) { + return Object.entries(encountersByGame) + .sort((a, b) => a[1].game_id - b[1].game_id) + .map(([game, data]) => ` +
+

${game}

+
+ ${data.encounters.map(encounter => ` +

${encounter.location} + ${encounter.day ? `(${encounter.day})` : ''} + ${encounter.time ? `(${encounter.time})` : ''} + ${encounter.static_encounter ? '(Static)' : ''} + ${encounter.fishing ? '(Fishing)' : ''} + ${encounter.starter ? '(Starter)' : ''} +

+ `).join('')} +
+
+ `).join(''); + } + + function addCollapsibleListeners() { + const collapsibles = document.querySelectorAll('.collapsible'); + collapsibles.forEach(item => { + item.addEventListener('click', function() { + this.classList.toggle('active'); + const content = this.nextElementSibling; + if (content.style.display === 'block') { + content.style.display = 'none'; + this.querySelector('.toggle-icon').textContent = '▼'; + } else { + content.style.display = 'block'; + this.querySelector('.toggle-icon').textContent = '▲'; + } }); + }); } function closeDetailsPanel() { @@ -297,4 +382,4 @@ }); - \ No newline at end of file +