Browse Source

- Mostly all working now, no save data from the site though

master
Quildra 1 year ago
parent
commit
95289309af
  1. 75
      OriginDex.py
  2. 13
      Utilities/DatabaseBuilder.py
  3. 70
      Utilities/DetermineOriginGame.py
  4. 2574
      pokemon_earliest_games.csv
  5. BIN
      static/images/pokeball_color.png
  6. 158
      templates/index.html

75
OriginDex.py

@ -1,5 +1,7 @@
import sqlite3
from flask import Flask, render_template, jsonify
from collections import defaultdict
import os
app = Flask(__name__)
@ -10,22 +12,70 @@ def load_pokemon_data():
cursor = conn.cursor()
cursor.execute('''
SELECT p.national_dex_number, p.name, pf.form_name, pf.image_path, pf.is_default
SELECT p.national_dex_number, p.name, pf.form_name, pf.image_path, pf.is_default, p.introduced_in_gen,
g.name AS earliest_game, m.icon_path AS mark_icon
FROM pokemon p
JOIN pokemon_forms pf ON p.national_dex_number = pf.pokemon_id
LEFT JOIN form_encounters fe ON pf.id = fe.form_id
LEFT JOIN games g ON fe.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
GROUP BY p.national_dex_number, pf.id
ORDER BY p.national_dex_number, pf.is_default DESC, pf.form_name
''')
current_group = []
current_generation = None
current_dex_number = None
pokemon_forms = []
for row in cursor.fetchall():
national_dex_number, name, form_name, image_path, is_default = row
national_dex_number, name, form_name, image_path, is_default, introduced_in_gen, earliest_game, mark_icon = row
pokemon_list.append({
pokemon = {
'ID': national_dex_number,
'Name': name,
'Form': form_name,
'Image': image_path,
'IsDefault': is_default
})
'IsDefault': is_default,
'Generation': introduced_in_gen,
'EarliestGame': earliest_game,
'MarkIcon': mark_icon
}
if national_dex_number != current_dex_number:
if pokemon_forms:
for form in pokemon_forms:
current_group.append(form)
if len(current_group) == 30:
pokemon_list.append(current_group)
current_group = []
pokemon_forms = []
current_dex_number = national_dex_number
if is_default:
if current_generation is None or introduced_in_gen != current_generation:
if current_group:
while len(current_group) < 30:
current_group.append(None) # Add empty slots
pokemon_list.append(current_group)
current_group = []
current_generation = introduced_in_gen
pokemon_forms.append(pokemon)
# Add the last set of forms
for form in pokemon_forms:
current_group.append(form)
if len(current_group) == 30:
pokemon_list.append(current_group)
current_group = []
# Add any remaining Pokémon
if current_group:
while len(current_group) < 30:
current_group.append(None) # Add empty slots to the last group
pokemon_list.append(current_group)
conn.close()
return pokemon_list
@ -33,9 +83,7 @@ def load_pokemon_data():
@app.route('/')
def index():
pokemon_list = load_pokemon_data()
# Create a list of lists, each inner list containing up to 30 Pokémon forms
grouped_pokemon = [pokemon_list[i:i+30] for i in range(0, len(pokemon_list), 30)]
return render_template('index.html', grouped_pokemon=grouped_pokemon)
return render_template('index.html', grouped_pokemon=pokemon_list)
@app.route('/pokemon/<int:dex_number>')
def pokemon_details(dex_number):
@ -62,4 +110,13 @@ def pokemon_details(dex_number):
return jsonify(encounters)
if __name__ == '__main__':
app.run(debug=True)
extra_files = ['.']
extra_dirs = ['./templates/', './static/']
for extra_dir in extra_dirs:
for dirname, dirs, files in os.walk(extra_dir):
for filename in files:
filename = os.path.join(dirname, filename)
if os.path.isfile(filename):
extra_files.append(filename)
app.run(debug=True, extra_files=extra_files)

13
Utilities/DatabaseBuilder.py

@ -43,7 +43,8 @@ def create_tables(conn):
cursor.execute('''
CREATE TABLE IF NOT EXISTS pokemon (
national_dex_number INTEGER PRIMARY KEY,
name TEXT NOT NULL
name TEXT NOT NULL,
introduced_in_gen INTEGER
)
''')
@ -286,7 +287,7 @@ def load_encounter_data(conn):
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:
@ -298,11 +299,11 @@ def load_encounter_data(conn):
base_name = full_name
form_name = "Default"
# Ensure the Pokémon and form exist in the database
# Update the Pokémon entry with introduced_in_gen
cursor.execute('''
INSERT OR IGNORE INTO pokemon (national_dex_number, name)
VALUES (?, ?)
''', (national_dex_number, base_name))
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)

70
Utilities/DetermineOriginGame.py

@ -129,6 +129,30 @@ def find_pokemon(name, form=None, threshold=80):
return None
def roman_to_int(s):
roman_values = {
'I': 1,
'V': 5,
'X': 10,
'L': 50,
'C': 100,
'D': 500,
'M': 1000
}
total = 0
prev_value = 0
for char in reversed(s):
current_value = roman_values[char]
if current_value >= prev_value:
total += current_value
else:
total -= current_value
prev_value = current_value
return total
class Pokemon:
def __init__(self, name: str, number: int, form: Optional[str] = None):
self.name = name
@ -140,6 +164,7 @@ class Pokemon:
self.encounter_information: Optional[List['EncounterInformation']] = []
self.earliest_game: Optional['EncounterInformation'] = None
self.obtain_method: Optional[str] = None
self.introduced_in_gen = None
def get_earliest_game_and_method(self):
if self.evolution_chain:
@ -485,6 +510,45 @@ def parse_eevee_evolution_chain(table):
return [eevee_stage]
def get_intro_generation(pokemon_name, form, cache):
page_data = get_pokemon_data_bulbapedia(pokemon_name, cache)
if not page_data:
return None
soup = BeautifulSoup(page_data, 'html.parser')
locations_section = soup.find('span', id='Game_locations')
if not locations_section:
return None
locations_table = locations_section.find_next('table', class_='roundy')
if not locations_table:
return None
generation_tbody = locations_table.find('tbody', recursive=False)
generation_rows = generation_tbody.find_all('tr', recursive=False)
for generation_row in generation_rows:
random_nested_td = generation_row.find('td', recursive=False)
if not random_nested_td:
continue
random_nested_table = random_nested_td.find('table', recursive=False)
if not random_nested_table:
continue
random_nested_tbody = random_nested_table.find('tbody', recursive=False)
random_nested_rows = random_nested_tbody.find_all('tr', recursive=False)
for nested_row in random_nested_rows:
test_text = None
pattern = r"Generation\s+([IVXLCDM]+)"
match = re.search(pattern, nested_row.get_text(strip=True))
if match:
test_text = match.group(1) # This returns just the Roman numeral
if test_text:
return roman_to_int(test_text.replace("Generation ", "").strip())
return None
def get_locations_from_bulbapedia(pokemon_name, form, cache):
page_data = get_pokemon_data_bulbapedia(pokemon_name, cache)
if not page_data:
@ -519,6 +583,8 @@ def get_locations_from_bulbapedia(pokemon_name, form, cache):
continue
random_nested_tbody = random_nested_table.find('tbody', recursive=False)
random_nested_rows = random_nested_tbody.find_all('tr', recursive=False)
intro_gen = None
for nested_row in random_nested_rows:
if 'Generation' in nested_row.get_text(strip=True):
continue
@ -714,6 +780,7 @@ def determine_earliest_games(cache):
form_to_find = None
if pokemon.name in bad_tea_pokemon:
form_to_find = get_bad_tea_form(pokemon)
pokemon.introduced_in_gen = get_intro_generation(pokemon.name, form_to_find, cache)
encounter_data = get_locations_from_bulbapedia(pokemon.name, form_to_find, cache)
for encounter in encounter_data:
encounter_information = EncounterInformation(encounter, encounter_data[encounter])
@ -748,7 +815,7 @@ def adjust_for_evolution(cache):
def save_to_csv(filename='pokemon_earliest_games.csv'):
with open(filename, 'w', newline='', encoding='utf-8') as csvfile:
fieldnames = ['number', 'name', 'earliest_game', 'obtain_method', 'encounter_locations']
fieldnames = ['number', 'name', 'introduced_in_gen', 'earliest_game', 'obtain_method', 'encounter_locations']
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
@ -760,6 +827,7 @@ def save_to_csv(filename='pokemon_earliest_games.csv'):
writer.writerow({
'number': pokemon.number,
'name': f"{pokemon.name} ({pokemon.form})",
'introduced_in_gen': pokemon.introduced_in_gen,
'earliest_game': pokemon.earliest_game.game,
'obtain_method': pokemon.earliest_game.method,
'encounter_locations': ' | '.join((str(item) for item in encounter_locations))

2574
pokemon_earliest_games.csv

File diff suppressed because it is too large

BIN
static/images/pokeball_color.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

158
templates/index.html

@ -55,25 +55,40 @@
align-items: center;
justify-content: space-between;
cursor: pointer;
/*width: 120px;*/ /* Fixed width */
height: 169.4px; /* Fixed height */
}
.pokemon-cell.empty {
background-color: #e0e0e0;
cursor: default;
}
.pokemon-name {
font-weight: bold;
font-size: 0.8em;
margin-bottom: 5px;
height: 2.4em; /* Allows for 2 lines of text */
display: flex;
align-items: center;
justify-content: center;
width: 100%;
}
.pokemon-image {
width: 96px;
height: 96px;
object-fit: contain;
background-color: #f9f9f9; /* Light gray background for placeholder */
}
.pokemon-form {
font-style: italic;
font-size: 0.7em;
margin-top: 5px;
height: 1.4em; /* Allows for 1 line of text */
line-height: 1.4em; /* Vertically centers the text or dashes */
}
.pokemon-number {
color: #777;
font-size: 0.7em;
margin-top: 5px;
}
#details-panel {
width: 300px;
@ -93,6 +108,59 @@
.main-content-shifted {
margin-right: 340px;
}
.pokemon-info {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
margin-top: 5px;
position: relative;
}
.pokemon-number {
color: #777;
font-size: 0.7em;
text-align: center;
width: 100%;
}
.origin-mark {
width: 20px;
height: 20px;
object-fit: contain;
filter: invert(100%);
}
/* Optional: Add a background circle for better visibility */
.origin-mark-container {
background-color: rgba(0, 0, 0, 0.1);
border-radius: 50%;
padding: 2px;
display: flex;
align-items: center;
justify-content: center;
position: absolute;
right: 0;
bottom: 0;
}
.pokeball-container {
position: absolute;
left: 0;
bottom: 0;
}
.pokeball-icon {
width: 20px;
height: 20px;
object-fit: contain;
cursor: pointer;
transition: filter 0.3s ease;
}
.pokeball-icon.grayscale {
filter: grayscale(100%);
}
</style>
</head>
<body>
@ -104,14 +172,32 @@
<div class="box-title">Box {{ '%03d' | format(loop.index) }}</div>
<div class="pokemon-grid">
{% for pokemon in group %}
<div class="pokemon-cell" onclick="showDetails({{ pokemon.ID }}, '{{ pokemon.Form }}', event)" data-dex-number="{{ pokemon.ID }}">
<div class="pokemon-name">{{ pokemon.Name }}</div>
<img src="{{ url_for('static', filename=pokemon.Image) }}" alt="{{ pokemon.Name }} {{ pokemon.Form }}" class="pokemon-image">
{% if pokemon.Form != 'Default' %}
<div class="pokemon-form">{{ pokemon.Form }}</div>
{% endif %}
<div class="pokemon-number">#{{ '%04d'|format(pokemon.ID) }}</div>
</div>
{% if pokemon %}
<div class="pokemon-cell" onclick="showDetails({{ pokemon.ID }}, '{{ pokemon.Form }}', event)" data-dex-number="{{ pokemon.ID }}">
<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') }}">
<div class="pokemon-form">
{% if pokemon.Form != 'Default' %}
{{ pokemon.Form }}
{% else %}
-----
{% endif %}
</div>
<div class="pokemon-info">
<div class="pokeball-container">
<img src="{{ url_for('static', filename='images/pokeball_color.png') }}" alt="Pokeball" class="pokeball-icon grayscale" onclick="togglePokeball(event, this)">
</div>
<div class="pokemon-number">#{{ '%04d'|format(pokemon.ID) }}</div>
{% if pokemon.MarkIcon %}
<div class="origin-mark-container">
<img src="{{ url_for('static', filename=pokemon.MarkIcon) }}" alt="Origin Mark" class="origin-mark" title="{{ pokemon.EarliestGame }}">
</div>
{% endif %}
</div>
</div>
{% else %}
<div class="pokemon-cell empty"></div>
{% endif %}
{% endfor %}
</div>
</div>
@ -153,6 +239,62 @@
detailsPanel.addEventListener('click', function(event) {
event.stopPropagation();
});
function togglePokeball(event, element) {
event.stopPropagation();
element.classList.toggle('grayscale');
}
// Lazy loading for images
document.addEventListener("DOMContentLoaded", function() {
var lazyloadImages;
if ("IntersectionObserver" in window) {
lazyloadImages = document.querySelectorAll(".lazy-load");
var imageObserver = new IntersectionObserver(function(entries, observer) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
var image = entry.target;
image.src = image.dataset.src;
image.classList.remove("lazy-load");
imageObserver.unobserve(image);
}
});
});
lazyloadImages.forEach(function(image) {
imageObserver.observe(image);
});
} else {
var lazyloadThrottleTimeout;
lazyloadImages = document.querySelectorAll(".lazy-load");
function lazyload () {
if(lazyloadThrottleTimeout) {
clearTimeout(lazyloadThrottleTimeout);
}
lazyloadThrottleTimeout = setTimeout(function() {
var scrollTop = window.pageYOffset;
lazyloadImages.forEach(function(img) {
if(img.offsetTop < (window.innerHeight + scrollTop)) {
img.src = img.dataset.src;
img.classList.remove('lazy-load');
}
});
if(lazyloadImages.length == 0) {
document.removeEventListener("scroll", lazyload);
window.removeEventListener("resize", lazyload);
window.removeEventListener("orientationChange", lazyload);
}
}, 20);
}
document.addEventListener("scroll", lazyload);
window.addEventListener("resize", lazyload);
window.addEventListener("orientationChange", lazyload);
}
});
</script>
</body>
</html>
Loading…
Cancel
Save