Browse Source

- Update the editor to save the DB, get the web ui working again

master
Dan 1 year ago
parent
commit
a37c23615f
  1. 6
      DBEditor/db_controller.py
  2. 4
      DBEditor/pokemon_db_ui.py
  3. 10
      Determining an origin mark.txt
  4. 107
      Site/OriginDex.py
  5. 105
      Site/templates/index.html

6
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('get_games_list', self.get_games_list)
event_system.add_listener('add_new_exclusive_set', self.add_new_exclusive_set) 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('save_changes', self.save_changes)
event_system.add_listener('export_database', self.export_database)
def init_database(self): def init_database(self):
disk_conn = sqlite3.connect('pokemon_forms.db') disk_conn = sqlite3.connect('pokemon_forms.db')
@ -442,3 +443,8 @@ class DBController:
disk_conn = sqlite3.connect('pokemon_forms.db') disk_conn = sqlite3.connect('pokemon_forms.db')
self.conn.backup(disk_conn) self.conn.backup(disk_conn)
disk_conn.close() disk_conn.close()
def export_database(self, data):
export_conn = sqlite3.connect('pokemon_forms_production.db')
self.conn.backup(export_conn)
export_conn.close()

4
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.") QMessageBox.information(self, "Save Complete", "Changes have been saved to the database.")
def export_database(self): def export_database(self):
export_conn = sqlite3.connect('pokemon_forms_production.db') event_system.emit_sync('export_database')
self.conn.backup(export_conn)
export_conn.close()
def add_new_evolution(self): def add_new_evolution(self):
if not hasattr(self, 'current_pfic'): if not hasattr(self, 'current_pfic'):

10
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.

107
Site/OriginDex.py

@ -11,57 +11,86 @@ def load_pokemon_data():
conn = sqlite3.connect('pokemon_forms.db') conn = sqlite3.connect('pokemon_forms.db')
cursor = conn.cursor() cursor = conn.cursor()
# First query: Get all Pokémon data
cursor.execute(''' cursor.execute('''
WITH RECURSIVE EvolutionChain AS ( WITH EarliestGamePerPokemon AS (
SELECT PFIC, from_pfic SELECT e.pfic, MIN(g.generation) as min_generation, MIN(g.id) as earliest_game_id
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
FROM encounters e FROM encounters e
JOIN games g ON e.game = g.name JOIN games g ON e.game_id = g.id
GROUP BY e.pfic 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 SELECT
pf.national_dex, pf.name, pf.form_name, pf.PFIC, pf.generation, pf.national_dex, pf.name, pf.form_name, pf.PFIC, pf.generation,
ps.storable_in_home, eg.earliest_game, ps.storable_in_home, g.name as earliest_game,
m.icon_path as mark_icon, m.id as mark_id m.icon_path as mark_icon, m.id as mark_id, pf.is_baby_form
FROM pokemon_forms pf FROM pokemon_forms pf
JOIN pokemon_storage ps ON pf.PFIC = ps.PFIC JOIN pokemon_storage ps ON pf.PFIC = ps.PFIC
LEFT JOIN BaseForm bf ON pf.PFIC = bf.PFIC LEFT JOIN EarliestGamePerPokemon eg ON pf.PFIC = eg.pfic
LEFT JOIN EarliestGamePerPokemon eg ON COALESCE(bf.base_pfic, pf.PFIC) = eg.pfic LEFT JOIN games g ON eg.earliest_game_id = g.id
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 mark_game_associations mga ON g.id = mga.game_id LEFT JOIN mark_game_associations mga ON g.id = mga.game_id
LEFT JOIN marks m ON mga.mark_id = m.id LEFT JOIN marks m ON mga.mark_id = m.id
WHERE ps.storable_in_home = 1 WHERE ps.storable_in_home = 1
ORDER BY pf.PFIC 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_group = []
current_generation = None current_generation = None
current_dex_number = None current_dex_number = None
pokemon_forms = [] pokemon_forms = []
for row in cursor.fetchall(): for row in pokemon_data:
national_dex, name, form_name, pfic, generation, storable_in_home, earliest_game, mark_icon, mark_id = row 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 = { pokemon = {
'pfic': pfic,
'ID': national_dex, 'ID': national_dex,
'Name': name, 'Name': name,
'Form': form_name if form_name else "Default", 'Form': form_name if form_name else "Default",
@ -118,25 +147,27 @@ def index():
pokemon_list = load_pokemon_data() pokemon_list = load_pokemon_data()
return render_template('index.html', grouped_pokemon=pokemon_list) return render_template('index.html', grouped_pokemon=pokemon_list)
@app.route('/pokemon/<int:dex_number>') @app.route('/pokemon/<string:pfic>')
def pokemon_details(dex_number): def pokemon_details(pfic):
conn = sqlite3.connect('pokemon_forms.db') # Updated database name conn = sqlite3.connect('pokemon_forms.db')
cursor = conn.cursor() cursor = conn.cursor()
cursor.execute(''' 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.dual_slot, e.static_encounter, e.static_encounter_count,
e.extra_text, e.stars, e.fishing, e.rods, e.starter e.extra_text, e.stars, e.fishing, e.rods, e.starter
FROM pokemon_forms pf FROM pokemon_forms pf
JOIN encounters e ON pf.PFIC = e.pfic JOIN encounters e ON pf.PFIC = e.pfic
WHERE pf.national_dex = ? JOIN games g ON e.game_id = g.id
ORDER BY pf.form_name, e.game, e.location WHERE pf.pfic = ?
''', (dex_number,)) ORDER BY g.id, pf.form_name, e.location
''', (pfic,))
encounters = [ encounters = [
{ {
'form': form, 'form': form,
'game': game, 'game': game,
'game_id': game_id,
'location': location, 'location': location,
'day': day, 'day': day,
'time': time, 'time': time,
@ -149,7 +180,7 @@ def pokemon_details(dex_number):
'rods': rods, 'rods': rods,
'starter': starter '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() extra_text, stars, fishing, rods, starter in cursor.fetchall()
] ]

105
Site/templates/index.html

@ -161,6 +161,39 @@
.pokeball-icon.grayscale { .pokeball-icon.grayscale {
filter: grayscale(100%); 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;
}
</style> </style>
</head> </head>
<body> <body>
@ -173,7 +206,7 @@
<div class="pokemon-grid"> <div class="pokemon-grid">
{% for pokemon in group %} {% for pokemon in group %}
{% if pokemon %} {% if pokemon %}
<div class="pokemon-cell" onclick="showDetails({{ pokemon.ID }}, '{{ pokemon.Form }}', event)" data-dex-number="{{ pokemon.ID }}"> <div class="pokemon-cell" onclick="showDetails('{{ pokemon.ID }}', '{{ pokemon.Form }}', '{{ pokemon.pfic }}', event)" data-dex-number="{{ pokemon.ID }}">
<div class="pokemon-name">{{ pokemon.Name }}</div> <div class="pokemon-name">{{ pokemon.Name }}</div>
<img data-src="{{ url_for('static', filename=pokemon.Image) }}" alt="{{ pokemon.Name }} {{ pokemon.Form }}" class="pokemon-image lazy-load" src="{{ url_for('static', filename='images/placeholder.png') }}"> <img data-src="{{ url_for('static', filename=pokemon.Image) }}" alt="{{ pokemon.Name }} {{ pokemon.Form }}" class="pokemon-image lazy-load" src="{{ url_for('static', filename='images/placeholder.png') }}">
<div class="pokemon-form"> <div class="pokemon-form">
@ -209,20 +242,72 @@
const detailsPanel = document.getElementById('details-panel'); const detailsPanel = document.getElementById('details-panel');
const mainContent = document.getElementById('main-content'); const mainContent = document.getElementById('main-content');
function showDetails(dexNumber, formName, event) { function showDetails(dexNumber, formName, pfic, event) {
event.stopPropagation(); event.stopPropagation();
fetch(`/pokemon/${dexNumber}`) fetch(`/pokemon/${pfic}`)
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
const formData = data.filter(encounter => encounter.form === formName); const encountersByGame = groupEncountersByGame(data);
detailsPanel.innerHTML = `<h2>Pokémon #${dexNumber} - ${formName} Form</h2>`; detailsPanel.innerHTML = `
detailsPanel.innerHTML += '<h3>Encounters:</h3>'; <h2>Pokémon #${dexNumber} - ${formName} Form</h2>
detailsPanel.innerHTML += formData.map(encounter => <h3>Encounters:</h3>
`<p><strong>${encounter.game}</strong>: ${encounter.location} (${encounter.method})</p>` ${generateEncounterHTML(encountersByGame)}
).join(''); `;
detailsPanel.classList.add('open'); detailsPanel.classList.add('open');
mainContent.classList.add('main-content-shifted'); 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]) => `
<div class="game-encounters">
<h4 class="collapsible">${game} <span class="toggle-icon"></span></h4>
<div class="encounter-list">
${data.encounters.map(encounter => `
<p>${encounter.location}
${encounter.day ? `(${encounter.day})` : ''}
${encounter.time ? `(${encounter.time})` : ''}
${encounter.static_encounter ? '(Static)' : ''}
${encounter.fishing ? '(Fishing)' : ''}
${encounter.starter ? '(Starter)' : ''}
</p>
`).join('')}
</div>
</div>
`).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() { function closeDetailsPanel() {
@ -297,4 +382,4 @@
}); });
</script> </script>
</body> </body>
</html> </html>

Loading…
Cancel
Save