Browse Source

- cleaned up the scraper and db generator.

- Basic page work started
master
Quildra 1 year ago
parent
commit
02820a67de
  1. 67
      OriginDex.py
  2. 24
      Utilities/DBVisualiser.py
  3. 13
      Utilities/DatabaseBuilder.py
  4. 431
      Utilities/DetermineOriginGame.py
  5. 43
      index.html
  6. BIN
      static/images/marks/Arceus_mark_HOME.png
  7. BIN
      static/images/marks/BDSP_icon_HOME.png
  8. BIN
      static/images/marks/Black_clover_HOME.png
  9. BIN
      static/images/marks/Blue_pentagon_HOME.png
  10. BIN
      static/images/marks/GB_icon_HOME.png
  11. BIN
      static/images/marks/GO_icon_HOME.png
  12. BIN
      static/images/marks/Galar_symbol_HOME.png
  13. BIN
      static/images/marks/Let's_Go_icon_HOME.png
  14. BIN
      static/images/marks/Paldea_icon_HOME.png
  15. BIN
      static/images/pokemon/0001_Bulbasaur.png
  16. BIN
      static/images/pokemon/0002_Ivysaur.png
  17. BIN
      static/images/pokemon/0003_Venusaur.png
  18. BIN
      static/images/pokemon/0004_Charmander.png
  19. BIN
      static/images/pokemon/0005_Charmeleon.png
  20. BIN
      static/images/pokemon/0006_Charizard.png
  21. BIN
      static/images/pokemon/0007_Squirtle.png
  22. BIN
      static/images/pokemon/0008_Wartortle.png
  23. BIN
      static/images/pokemon/0009_Blastoise.png
  24. BIN
      static/images/pokemon/0010_Caterpie.png
  25. BIN
      static/images/pokemon/0011_Metapod.png
  26. BIN
      static/images/pokemon/0012_Butterfree.png
  27. BIN
      static/images/pokemon/0013_Weedle.png
  28. BIN
      static/images/pokemon/0014_Kakuna.png
  29. BIN
      static/images/pokemon/0015_Beedrill.png
  30. BIN
      static/images/pokemon/0016_Pidgey.png
  31. BIN
      static/images/pokemon/0017_Pidgeotto.png
  32. BIN
      static/images/pokemon/0018_Pidgeot.png
  33. BIN
      static/images/pokemon/0019_Rattata.png
  34. BIN
      static/images/pokemon/0019_Rattata_(Alolan_Form).png
  35. BIN
      static/images/pokemon/0020_Raticate.png
  36. BIN
      static/images/pokemon/0020_Raticate_(Alolan_Form).png
  37. BIN
      static/images/pokemon/0021_Spearow.png
  38. BIN
      static/images/pokemon/0022_Fearow.png
  39. BIN
      static/images/pokemon/0023_Ekans.png
  40. BIN
      static/images/pokemon/0024_Arbok.png
  41. BIN
      static/images/pokemon/0025_Pikachu.png
  42. BIN
      static/images/pokemon/0025_Pikachu_(Alola_Cap).png
  43. BIN
      static/images/pokemon/0025_Pikachu_(Hoenn_Cap).png
  44. BIN
      static/images/pokemon/0025_Pikachu_(Kalos_Cap).png
  45. BIN
      static/images/pokemon/0025_Pikachu_(Original_Cap).png
  46. BIN
      static/images/pokemon/0025_Pikachu_(Partner_Cap).png
  47. BIN
      static/images/pokemon/0025_Pikachu_(Sinnoh_Cap).png
  48. BIN
      static/images/pokemon/0025_Pikachu_(Unova_Cap).png
  49. BIN
      static/images/pokemon/0025_Pikachu_(World_Cap).png
  50. BIN
      static/images/pokemon/0026_Raichu.png
  51. BIN
      static/images/pokemon/0026_Raichu_(Alolan_Form).png
  52. BIN
      static/images/pokemon/0027_Sandshrew.png
  53. BIN
      static/images/pokemon/0027_Sandshrew_(Alolan_Form).png
  54. BIN
      static/images/pokemon/0028_Sandslash.png
  55. BIN
      static/images/pokemon/0028_Sandslash_(Alolan_Form).png
  56. BIN
      static/images/pokemon/0029_Nidoran♀.png
  57. BIN
      static/images/pokemon/0030_Nidorina.png
  58. BIN
      static/images/pokemon/0031_Nidoqueen.png
  59. BIN
      static/images/pokemon/0032_Nidoran♂.png
  60. BIN
      static/images/pokemon/0033_Nidorino.png
  61. BIN
      static/images/pokemon/0034_Nidoking.png
  62. BIN
      static/images/pokemon/0035_Clefairy.png
  63. BIN
      static/images/pokemon/0036_Clefable.png
  64. BIN
      static/images/pokemon/0037_Vulpix.png
  65. BIN
      static/images/pokemon/0037_Vulpix_(Alolan_Form).png
  66. BIN
      static/images/pokemon/0038_Ninetales.png
  67. BIN
      static/images/pokemon/0038_Ninetales_(Alolan_Form).png
  68. BIN
      static/images/pokemon/0039_Jigglypuff.png
  69. BIN
      static/images/pokemon/0040_Wigglytuff.png
  70. BIN
      static/images/pokemon/0041_Zubat.png
  71. BIN
      static/images/pokemon/0042_Golbat.png
  72. BIN
      static/images/pokemon/0043_Oddish.png
  73. BIN
      static/images/pokemon/0044_Gloom.png
  74. BIN
      static/images/pokemon/0045_Vileplume.png
  75. BIN
      static/images/pokemon/0046_Paras.png
  76. BIN
      static/images/pokemon/0047_Parasect.png
  77. BIN
      static/images/pokemon/0048_Venonat.png
  78. BIN
      static/images/pokemon/0049_Venomoth.png
  79. BIN
      static/images/pokemon/0050_Diglett.png
  80. BIN
      static/images/pokemon/0050_Diglett_(Alolan_Form).png
  81. BIN
      static/images/pokemon/0051_Dugtrio.png
  82. BIN
      static/images/pokemon/0051_Dugtrio_(Alolan_Form).png
  83. BIN
      static/images/pokemon/0052_Meowth.png
  84. BIN
      static/images/pokemon/0052_Meowth_(Alolan_Form).png
  85. BIN
      static/images/pokemon/0052_Meowth_(Galarian_Form).png
  86. BIN
      static/images/pokemon/0053_Persian.png
  87. BIN
      static/images/pokemon/0053_Persian_(Alolan_Form).png
  88. BIN
      static/images/pokemon/0054_Psyduck.png
  89. BIN
      static/images/pokemon/0055_Golduck.png
  90. BIN
      static/images/pokemon/0056_Mankey.png
  91. BIN
      static/images/pokemon/0057_Primeape.png
  92. BIN
      static/images/pokemon/0058_Growlithe.png
  93. BIN
      static/images/pokemon/0058_Growlithe_(Hisuian_Form).png
  94. BIN
      static/images/pokemon/0059_Arcanine.png
  95. BIN
      static/images/pokemon/0059_Arcanine_(Hisuian_Form).png
  96. BIN
      static/images/pokemon/0060_Poliwag.png
  97. BIN
      static/images/pokemon/0061_Poliwhirl.png
  98. BIN
      static/images/pokemon/0062_Poliwrath.png
  99. BIN
      static/images/pokemon/0063_Abra.png
  100. BIN
      static/images/pokemon/0064_Kadabra.png

67
OriginDex.py

@ -1,30 +1,65 @@
import csv import sqlite3
from flask import Flask, render_template from flask import Flask, render_template, jsonify
app = Flask(__name__) app = Flask(__name__)
def load_pokemon_data(): def load_pokemon_data():
pokemon_list = [] pokemon_list = []
earliest_games = {}
# Load Pokemon Home list conn = sqlite3.connect('pokemon_database.db')
with open('pokemon_home_list.csv', 'r') as file: cursor = conn.cursor()
reader = csv.DictReader(file)
for row in reader:
pokemon_list.append(row)
# Load earliest games data cursor.execute('''
with open('pokemon_earliest_games.csv', 'r') as file: SELECT p.national_dex_number, p.name, pf.form_name, pf.image_path, pf.is_default
reader = csv.DictReader(file) FROM pokemon p
for row in reader: JOIN pokemon_forms pf ON p.national_dex_number = pf.pokemon_id
earliest_games[row['Pokemon']] = row['Earliest Game'] ORDER BY p.national_dex_number, pf.is_default DESC, pf.form_name
''')
return pokemon_list, earliest_games for row in cursor.fetchall():
national_dex_number, name, form_name, image_path, is_default = row
pokemon_list.append({
'ID': national_dex_number,
'Name': name,
'Form': form_name,
'Image': image_path,
'IsDefault': is_default
})
conn.close()
return pokemon_list
@app.route('/') @app.route('/')
def index(): def index():
pokemon_list, earliest_games = load_pokemon_data() pokemon_list = load_pokemon_data()
return render_template('index.html', pokemon_list=pokemon_list, earliest_games=earliest_games) # 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)
@app.route('/pokemon/<int:dex_number>')
def pokemon_details(dex_number):
conn = sqlite3.connect('pokemon_database.db')
cursor = conn.cursor()
cursor.execute('''
SELECT pf.form_name, 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
JOIN pokemon_forms pf ON fe.form_id = pf.id
WHERE pf.pokemon_id = ?
ORDER BY pf.is_default DESC, pf.form_name, g.generation, g.name, l.name
''', (dex_number,))
encounters = [
{'form': form, 'game': game, 'location': location, 'method': method}
for form, game, location, method in cursor.fetchall()
]
conn.close()
return jsonify(encounters)
if __name__ == '__main__': if __name__ == '__main__':
app.run(debug=True) app.run(debug=True)

24
Utilities/DBVisualiser.py

@ -51,6 +51,11 @@ class PokemonDatabaseApp(QMainWindow):
main_layout.addWidget(self.tab_widget) 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 = QWidget()
container.setLayout(main_layout) container.setLayout(main_layout)
self.setCentralWidget(container) self.setCentralWidget(container)
@ -578,6 +583,25 @@ class PokemonDatabaseApp(QMainWindow):
locations = [row[0] for row in self.cursor.fetchall()] locations = [row[0] for row in self.cursor.fetchall()]
self.locations_list.addItems(locations) 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__': if __name__ == '__main__':
app = QApplication(sys.argv) app = QApplication(sys.argv)
window = PokemonDatabaseApp() window = PokemonDatabaseApp()

13
Utilities/DatabaseBuilder.py

@ -4,6 +4,7 @@ import re
def create_connection(): def create_connection():
conn = sqlite3.connect('pokemon_database.db') conn = sqlite3.connect('pokemon_database.db')
conn.text_factory = str
return conn return conn
def create_tables(conn): def create_tables(conn):
@ -109,12 +110,11 @@ def tidy_location_name(name):
# Replace '-' with spaces # Replace '-' with spaces
name = name.replace('-', ' ') name = name.replace('-', ' ')
name = name.replace('#', '')
# Remove 'area' from the end if present # Remove 'area' from the end if present
name = re.sub(r'\sarea$', '', name, flags=re.IGNORECASE) name = re.sub(r'\sarea$', '', name, flags=re.IGNORECASE)
# Capitalize the first letter of the first word
name = name.capitalize()
# Check for cardinal directions at the end # Check for cardinal directions at the end
cardinal_directions = ['north', 'south', 'east', 'west', 'northeast', 'northwest', 'southeast', 'southwest'] cardinal_directions = ['north', 'south', 'east', 'west', 'northeast', 'northwest', 'southeast', 'southwest']
for direction in cardinal_directions: for direction in cardinal_directions:
@ -129,6 +129,9 @@ def tidy_location_name(name):
name = name.replace("Routes", "Route") name = name.replace("Routes", "Route")
# Capitalize the first letter of the first word
name = name.capitalize()
return name return name
def generate_location_description(name): def generate_location_description(name):
@ -235,7 +238,7 @@ def load_mark_data(conn):
def load_pokemon_data(conn): def load_pokemon_data(conn):
cursor = conn.cursor() cursor = conn.cursor()
with open('pokemon_home_list.csv', 'r') as f: with open('pokemon_home_list.csv', 'r', encoding='utf-8') as f:
reader = csv.reader(f) reader = csv.reader(f)
next(reader) # Skip header row if it exists next(reader) # Skip header row if it exists
for row in reader: for row in reader:
@ -275,7 +278,7 @@ def load_pokemon_data(conn):
def load_encounter_data(conn): def load_encounter_data(conn):
cursor = conn.cursor() cursor = conn.cursor()
with open('pokemon_earliest_games.csv', 'r') as f: with open('pokemon_earliest_games.csv', 'r', encoding='utf-8') as f:
reader = csv.DictReader(f) reader = csv.DictReader(f)
for row in reader: for row in reader:
national_dex_number = int(row['number']) national_dex_number = int(row['number'])

431
Utilities/DetermineOriginGame.py

@ -335,12 +335,6 @@ def extract_stage_form(td: Tag) -> Optional[str]:
return stage_tag.get_text(strip=True) return stage_tag.get_text(strip=True)
return None return None
def extract_is_baby(td: Tag) -> bool:
stage_tag = td.find('table').find('small')
if stage_tag:
return 'Baby' in stage_tag.get_text(strip=True)
return False
def read_pokemon_list(filename, limit=50): def read_pokemon_list(filename, limit=50):
pokemon_list = [] pokemon_list = []
with open(filename, 'r', newline='', encoding='utf-8') as csvfile: with open(filename, 'r', newline='', encoding='utf-8') as csvfile:
@ -360,132 +354,6 @@ def read_pokemon_list(filename, limit=50):
return pokemon_list return pokemon_list
def sanitize_name_and_form(name, form):
adjusted_form = None
if form:
adjusted_form = form.lower()
#Some stupid special cases
if name.lower() == 'tauros':
if adjusted_form == 'paldean form':
adjusted_form = 'paldea combat breed'
elif 'blaze' in adjusted_form:
adjusted_form = 'paldea blaze breed'
elif 'aqua' in adjusted_form:
adjusted_form = 'paldea aqua breed'
replacements = {'forme': '',
'form': '',
'alolan': 'alola',
'galarian': 'galar',
'hisuian': 'hisui',
'paldean': 'paldea',
'size': '',
'10%': '10 power construct',
'hoopa': '',
'style': '',
'core': '',
'color': '',
'blood moon': 'bloodmoon'};
for old, new in replacements.items():
adjusted_form = adjusted_form.replace(old, new).strip()
missing_forms = ['burmy',
'shellos',
'gastrodon',
'wormadam',
'unown',
"deerling",
"sawsbuck",
"vivillon",
"flabébé",
"floette",
"florges",
"furfrou",
"sinistea",
"polteageist",
"alcremie",
"poltchageist",
"sinistcha"]
if name.lower() in missing_forms:
adjusted_form = None
if name.lower() == 'wormadam':
adjusted_form = adjusted_form.replace('cloak', '').strip()
if name.lower() == 'rotom':
adjusted_form = adjusted_form.replace('rotom', '').strip()
if name.lower() == 'darmanitan':
adjusted_form = adjusted_form + ' standard'
else:
default_forms = {'deoxys': 'normal',
'wormadam': 'plant',
'giratina': 'origin',
'tornadus': 'incarnate',
'shaymin': 'land',
'basculin': 'red-striped',
'darmanitan': 'standard',
'thundurus': 'incarnate',
'landorus': 'incarnate',
'enamorus': 'incarnate',
'keldeo': 'ordinary',
'meloetta': 'aria',
'meowstic': 'male',
'aegislash': 'shield',
'pumpkaboo': 'average',
'gourgeist': 'average',
'minior': 'red-meteor',
'zygarde': '50 power construct',
'oricorio': 'baile',
'lycanroc': 'midday',
'wishiwashi': 'solo',
'mimikyu': 'disguised',
'cramorant': 'gulping',
'toxtricity': 'low-key',
'eiscue': 'ice',
'indeedee': 'male',
'urshifu': 'single-strike',
'morpeko': 'full belly',
'oinkologne': 'male',
'maushold': 'family of three',
'squawkabilly': 'green plumage',
'palafin': 'zero',
'tatsugiri': 'curly',
'dudunsparce': 'two segment',
'basculegion': 'male'}
if name.lower() in default_forms:
adjusted_form = default_forms[name.lower()]
if adjusted_form:
api_name = f"{name.lower()}-{adjusted_form}"
else:
api_name = name.lower()
api_name = api_name.replace(' ', '-').replace("'", "").replace(".", "").replace('é', 'e').replace(':', '')
#more special cases
if api_name == 'oinkologne-male':
api_name = '916'
return api_name
def get_pokemon_data(pokemon_name, form, cache):
cache_key = f"pokemon_{pokemon_name}_{form}" if form else f"pokemon_{pokemon_name}"
if cache_key in cache:
return cache[cache_key]
api_name = sanitize_name_and_form(pokemon_name, form)
url = f"https://pokeapi.co/api/v2/pokemon/{api_name}"
print(f"Fetching Pokémon data for {pokemon_name}: {url}")
response = requests.get(url)
if response.status_code == 200:
data = response.json()
update_cache(cache_key, data)
return data
return None
def get_pokemon_data_bulbapedia(pokemon_name, cache): def get_pokemon_data_bulbapedia(pokemon_name, cache):
cache_key = f"pokemon_{pokemon_name}_bulbapedia" cache_key = f"pokemon_{pokemon_name}_bulbapedia"
if cache_key in cache: if cache_key in cache:
@ -499,23 +367,6 @@ def get_pokemon_data_bulbapedia(pokemon_name, cache):
update_cache(cache_key, data) update_cache(cache_key, data)
return data return data
def get_pokemon_encounter_data(pokemon_name, form, cache):
cache_key = f"pokemon_encounter_{pokemon_name}_{form}" if form else f"pokemon_encounter_{pokemon_name}"
if cache_key in cache:
return cache[cache_key]
api_name = sanitize_name_and_form(pokemon_name, form)
url = f"https://pokeapi.co/api/v2/pokemon/{api_name}/encounters"
print(f"Fetching encounter data for {pokemon_name}: {url}")
response = requests.get(url)
if response.status_code == 200:
data = response.json()
update_cache(cache_key, data)
return data
else:
return None
def split_td_contents(td): def split_td_contents(td):
groups = [] groups = []
current_group = [] current_group = []
@ -634,7 +485,6 @@ def parse_eevee_evolution_chain(table):
return [eevee_stage] return [eevee_stage]
def get_locations_from_bulbapedia(pokemon_name, form, cache): def get_locations_from_bulbapedia(pokemon_name, form, cache):
page_data = get_pokemon_data_bulbapedia(pokemon_name, cache) page_data = get_pokemon_data_bulbapedia(pokemon_name, cache)
if not page_data: if not page_data:
@ -738,7 +588,7 @@ def get_locations_from_bulbapedia(pokemon_name, form, cache):
sub_form_match = False if not sub_form else fuzz.partial_ratio(form.lower(), sub_form.lower()) >= 80 sub_form_match = False if not sub_form else fuzz.partial_ratio(form.lower(), sub_form.lower()) >= 80
if main_form_match or sub_form_match: if main_form_match or sub_form_match:
locations = raw_location.get_text().split(',') locations = raw_location.get_text().replace('and', ',').replace('#', '').split(',')
for location in locations: for location in locations:
if raw_game not in game_locations: if raw_game not in game_locations:
game_locations[raw_game] = [] game_locations[raw_game] = []
@ -760,46 +610,6 @@ def get_locations_from_bulbapedia(pokemon_name, form, cache):
return game_locations return game_locations
def get_earliest_game(encounter_data, pokemon_name, form):
if not encounter_data:
return "Unknown", "Unknown"
non_catchable_methods = ["trade", "event", "global link", "poké transfer", "time capsule", "unobtainable", "pokémon home"]
game_methods = {}
for game, locations in encounter_data.items():
for location in locations:
method = "Catchable"
for non_catchable in non_catchable_methods:
if non_catchable in location.lower():
method = None
break
if method is None:
continue
if "first partner" in location.lower():
method = "Starter"
elif "received" in location.lower():
method = "Gift"
elif "evolve" in location.lower():
method = "Evolve"
else:
method = "Catchable"
if method:
if game not in game_methods:
game_methods[game.lower()] = method
else:
if method == "Catchable":
game_methods[game.lower()] = method
for game in all_games:
if game.lower() in game_methods:
return game, game_methods[game.lower()]
return "Unknown", "Unknown"
def handle_unown(pokemon, encounter_data): def handle_unown(pokemon, encounter_data):
if not pokemon.name == "Unown": if not pokemon.name == "Unown":
return return
@ -831,17 +641,6 @@ def handle_unown(pokemon, encounter_data):
else: else:
pokemon.encounter_information = one_form_unown.encounter_information pokemon.encounter_information = one_form_unown.encounter_information
def handle_deoxys(pokemon, encounter_data):
if not pokemon.name == "Deoxys":
return
normal_form_deoxys = find_pokemon(pokemon.name, None)
if not normal_form_deoxys:
return
if pokemon.form:
pokemon.encounter_information = normal_form_deoxys.encounter_information
list_of_shifting_form_pokemon = [ list_of_shifting_form_pokemon = [
"Deoxys", "Deoxys",
"Burmy", "Burmy",
@ -903,7 +702,7 @@ def get_bad_tea_form(pokemon):
else: else:
return pokemon.form return pokemon.form
def determine_earliest_games(pokemon_list, cache): def determine_earliest_games(cache):
for pokemon in big_pokemon_list: for pokemon in big_pokemon_list:
print(f"Processing {pokemon}") print(f"Processing {pokemon}")
form_to_find = pokemon.form form_to_find = pokemon.form
@ -928,50 +727,6 @@ def determine_earliest_games(pokemon_list, cache):
pokemon.determine_earliest_game() pokemon.determine_earliest_game()
print(f"Processed {pokemon}: {pokemon.earliest_game.game} ({pokemon.earliest_game.method})") print(f"Processed {pokemon}: {pokemon.earliest_game.game} ({pokemon.earliest_game.method})")
#for pokemon in pokemon_list:
# print(f"Processing {pokemon['name']} (#{pokemon['number']})")
# encounter_data = get_locations_from_bulbapedia(pokemon['base_name'], pokemon['form'], cache)
# pokemon['earliest_game'], pokemon['obtain_method'] = get_earliest_game(encounter_data, pokemon['base_name'], pokemon['form'])
# print(f"Processed {pokemon['name']} (#{pokemon['number']}): {pokemon['earliest_game']} ({pokemon['obtain_method']})")
# #pokemon_data = get_pokemon_data(pokemon['base_name'], pokemon['form'], cache)
# #encounter_data = get_pokemon_encounter_data(pokemon['base_name'], pokemon['form'], cache)
# #pokemon['earliest_game'], pokemon['obtain_method'] = get_earliest_game(encounter_data)
# #print(f"Processed {pokemon['name']} (#{pokemon['number']}): {pokemon['earliest_game']} ({pokemon['obtain_method']})")
return pokemon_list
def get_species_data(pokemon_name, cache):
cache_key = f"species_{pokemon_name}"
if cache_key in cache:
return cache[cache_key]
api_name = sanitize_name_and_form(pokemon_name, None)
url = f"https://pokeapi.co/api/v2/pokemon-species/{api_name}/"
print(f"Fetching species data for {pokemon_name}: {url}")
response = requests.get(url)
if response.status_code == 200:
data = response.json()
update_cache(cache_key, data)
return data
return None
def get_evolution_chain(pokemon_name, cache):
species_data = get_species_data(pokemon_name, cache)
if not species_data:
return None
cache_key = f"evolution_{species_data['evolution_chain']['url']}"
if cache_key in cache:
return cache[cache_key]
evolution_response = requests.get(species_data['evolution_chain']['url'])
if evolution_response.status_code == 200:
evolution_data = evolution_response.json()
update_cache(cache_key, evolution_data)
return evolution_data
return None
def get_base_form(evolution_chain:List[EvolutionStage]): def get_base_form(evolution_chain:List[EvolutionStage]):
if not evolution_chain: if not evolution_chain:
return None return None
@ -984,64 +739,14 @@ def get_base_form(evolution_chain:List[EvolutionStage]):
return None return None
#current = evolution_chain['chain'] def adjust_for_evolution(cache):
#while current:
# species_name = current['species']['name']
# species_data = get_species_data(species_name, cache)
#
# if species_data and not species_data.get('is_baby', False):
# return species_name
#
# if not current['evolves_to']:
# return species_name
#
# current = current['evolves_to'][0]
return None
def adjust_for_evolution(pokemon_list, cache):
for pokemon in big_pokemon_list: for pokemon in big_pokemon_list:
evolution_chain = get_evolution_data_from_bulbapedia(pokemon.name, pokemon.form, cache) evolution_chain = get_evolution_data_from_bulbapedia(pokemon.name, pokemon.form, cache)
pokemon.add_evolution_chain(evolution_chain) pokemon.add_evolution_chain(evolution_chain)
game, method = pokemon.get_earliest_game_and_method() game, method = pokemon.get_earliest_game_and_method()
print(f"Adjusted {pokemon}: {game} ({method})") print(f"Adjusted {pokemon}: {game} ({method})")
return []
pokemon_dict = {f"{pokemon['base_name']}_{pokemon['form']}".lower(): pokemon for pokemon in pokemon_list}
for pokemon in pokemon_list:
evolution_chain = get_evolution_data_from_bulbapedia(pokemon['base_name'], pokemon['form'], cache)
if evolution_chain:
if evolution_chain[0].is_baby:
pokemon['obtain_method'] = 'Breed'
else:
base_form = get_base_form(evolution_chain)
base_key = f"{base_form}_{pokemon['form']}".lower()
if base_key in pokemon_dict:
base_pokemon = pokemon_dict[base_key]
if all_games.index(base_pokemon['earliest_game']) <= all_games.index(pokemon['earliest_game']) and base_pokemon['number'] != pokemon['number']:
pokemon['earliest_game'] = base_pokemon['earliest_game']
pokemon['obtain_method'] = 'Evolve'
#species_data = get_species_data(pokemon['base_name'], cache)
#evolution_chain = get_evolution_chain(pokemon['base_name'], cache)
#base_form = get_base_form(evolution_chain, cache)
# Check if the Pokémon is a baby
#if species_data and species_data.get('is_baby', False):
# pokemon['obtain_method'] = 'Breed'
#elif base_form:
# base_key = f"{base_form}_{pokemon['form']}".lower()
# if base_key in pokemon_dict:
# base_pokemon = pokemon_dict[base_key]
# if all_games.index(base_pokemon['earliest_game']) <= all_games.index(pokemon['earliest_game']) and base_pokemon['number'] != pokemon['number']:
# pokemon['earliest_game'] = base_pokemon['earliest_game']
# pokemon['obtain_method'] = 'Evolve'
print(f"Adjusted {pokemon['name']} (#{pokemon['number']}): {pokemon['earliest_game']} ({pokemon['obtain_method']})")
return pokemon_list
def save_to_csv(pokemon_list, filename='pokemon_earliest_games.csv'): def save_to_csv(filename='pokemon_earliest_games.csv'):
with open(filename, 'w', newline='', encoding='utf-8') as csvfile: with open(filename, 'w', newline='', encoding='utf-8') as csvfile:
fieldnames = ['number', 'name', 'earliest_game', 'obtain_method', 'encounter_locations'] fieldnames = ['number', 'name', 'earliest_game', 'obtain_method', 'encounter_locations']
writer = csv.DictWriter(csvfile, fieldnames=fieldnames) writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
@ -1060,128 +765,11 @@ def save_to_csv(pokemon_list, filename='pokemon_earliest_games.csv'):
'encounter_locations': ' | '.join((str(item) for item in encounter_locations)) 'encounter_locations': ' | '.join((str(item) for item in encounter_locations))
}) })
def parse_encounter_locations(encounter_data, game): def handle_unknown_encounters(cache):
locations = []
for location_area in encounter_data:
for version_detail in location_area['version_details']:
if version_detail['version']['name'] == game.lower():
location_name = location_area['location_area']['name']
for encounter_detail in version_detail['encounter_details']:
method = encounter_detail['method']['name']
condition = encounter_detail.get('condition', 'Any')
time = ', '.join(encounter_detail.get('time', ['Any']))
encounter_info = f"{location_name} ({method}"
if condition != 'Any':
encounter_info += f", {condition}"
if time != 'Any':
encounter_info += f", {time}"
encounter_info += ")"
if encounter_info not in locations:
locations.append(encounter_info)
return locations
def add_encounter_locations(pokemon_list, cache):
for pokemon in pokemon_list:
if pokemon['obtain_method'] == 'Catchable':
encounter_data = get_pokemon_encounter_data(pokemon['base_name'], pokemon['form'], cache)
locations = parse_encounter_locations(encounter_data, pokemon['earliest_game'])
pokemon['encounter_locations'] = ' | '.join(locations) if locations else 'Unknown'
else:
pokemon['encounter_locations'] = 'N/A'
print(f"Added encounter locations for {pokemon['name']} (#{pokemon['number']}) in {pokemon['earliest_game']}")
return pokemon_list
def get_marriland_page(pokemon_name, cache):
url_name = pokemon_name.lower().replace(' ', '-').replace('(', '').replace(')', '')
cache_key = f"marriland_{url_name}"
if cache_key in cache:
return cache[cache_key]
url = f"https://marriland.com/pokedex/{url_name}/"
try:
response = requests.get(url)
response.raise_for_status() # Raise an exception for bad status codes
data = response.text
update_cache(cache_key, data)
return data
except requests.RequestException as e:
print(f"Error accessing the page for {pokemon_name}: {e}")
return None
def is_event_pokemon(pokemon_name, cache):
page_data = get_marriland_page(pokemon_name, cache)
if not page_data:
return False
soup = BeautifulSoup(page_data, 'html.parser')
# Find the "Where to Find" section
location_section = soup.find('div', id='locations')
if not location_section:
print(f"Could not find 'Where to Find' section for {pokemon_name}")
return None
special_section = soup.find('div', class_='location-special')
location_tables = soup.find_all('table', class_='location-table')
event_only = "Only available from events or promotions.".lower()
if len(location_tables) == 0 and special_section and event_only in special_section.get_text(strip=True).lower():
return True
return False
def check_alternative_sources(pokemon, cache):
# This function will check alternative sources for Pokémon with "Unknown" encounter types
species_data = get_species_data(pokemon['base_name'], cache)
if species_data:
# Check if it's a mythical Pokémon
if species_data.get('is_mythical', False):
return "Event", "Event"
# Check if it's a legendary Pokémon
if species_data.get('is_legendary', False):
return pokemon['earliest_game'], "Legendary"
event_status = is_event_pokemon(pokemon['name'], cache)
if event_status:
return "Event", "Event"
#bulb_locations = get_locations_from_bulbapedia(pokemon['base_name'], pokemon['form'], cache)
#if bulb_locations:
# return bulb_locations[0], "Bulbapedia"
# Check generation introduced
#generation = species_data.get('generation', {}).get('name', '')
#if generation:
# gen_number = int(generation.split('-')[1])
# for game in all_games:
# if game != "Unknown" and get_generation(game) == gen_number:
# return game, "First appearance"
return "Unknown", "Unknown"
def handle_unknown_encounters(pokemon_list, cache):
for pokemon in big_pokemon_list: for pokemon in big_pokemon_list:
if pokemon.earliest_game == None or pokemon.earliest_game.method == None: if pokemon.earliest_game == None or pokemon.earliest_game.method == None:
print(f"Checking alternative sources for {pokemon.name}") print(f"Checking alternative sources for {pokemon.name}")
return
for pokemon in pokemon_list:
if pokemon['earliest_game'] == "Unknown" or pokemon['obtain_method'] == "Unknown":
new_game, new_method = check_alternative_sources(pokemon, cache)
if new_game != "Unknown":
pokemon['earliest_game'] = new_game
pokemon['obtain_method'] = new_method
pokemon['encounter_locations'] = 'N/A'
print(f"Checked alternative sources for {pokemon['name']} (#{pokemon['number']}): {pokemon['earliest_game']} ({pokemon['obtain_method']})")
return pokemon_list
# Update the main function # Update the main function
if __name__ == "__main__": if __name__ == "__main__":
get_cached_data() get_cached_data()
@ -1190,11 +778,10 @@ if __name__ == "__main__":
pokemon_index = create_pokemon_index(big_pokemon_list) pokemon_index = create_pokemon_index(big_pokemon_list)
pokemon_list_with_games = determine_earliest_games(pokemon_list, cache) determine_earliest_games(cache)
pokemon_list_adjusted = adjust_for_evolution(pokemon_list_with_games, cache) adjust_for_evolution(cache)
#pokemon_list_with_locations = add_encounter_locations(pokemon_list_adjusted, cache) handle_unknown_encounters(cache)
pokemon_list_final = handle_unknown_encounters(pokemon_list_adjusted, cache) save_to_csv()
save_to_csv(pokemon_list_final)
save_cached_data() # Save any remaining new entries save_cached_data() # Save any remaining new entries
conn.close() # Close the database connection conn.close() # Close the database connection

43
index.html

@ -1,43 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>OriginDex - Pokémon Tracker</title>
<style>
table {
border-collapse: collapse;
width: 100%;
}
th, td {
border: 1px solid black;
padding: 8px;
text-align: left;
}
th {
background-color: #f2f2f2;
}
</style>
</head>
<body>
<h1>OriginDex - Pokémon Tracker</h1>
<table>
<thead>
<tr>
<th>Pokémon</th>
<th>Earliest Game</th>
<th>Caught</th>
</tr>
</thead>
<tbody>
{% for pokemon in pokemon_list %}
<tr>
<td>{{ pokemon.Pokemon }}</td>
<td>{{ earliest_games.get(pokemon.Pokemon, 'Unknown') }}</td>
<td><input type="checkbox" name="caught" value="{{ pokemon.Pokemon }}"></td>
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>

BIN
static/images/marks/Arceus_mark_HOME.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
static/images/marks/BDSP_icon_HOME.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1005 B

BIN
static/images/marks/Black_clover_HOME.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 789 B

BIN
static/images/marks/Blue_pentagon_HOME.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 749 B

BIN
static/images/marks/GB_icon_HOME.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 780 B

BIN
static/images/marks/GO_icon_HOME.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
static/images/marks/Galar_symbol_HOME.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
static/images/marks/Let's_Go_icon_HOME.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 959 B

BIN
static/images/marks/Paldea_icon_HOME.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
static/images/pokemon/0001_Bulbasaur.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
static/images/pokemon/0002_Ivysaur.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
static/images/pokemon/0003_Venusaur.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
static/images/pokemon/0004_Charmander.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
static/images/pokemon/0005_Charmeleon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
static/images/pokemon/0006_Charizard.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
static/images/pokemon/0007_Squirtle.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
static/images/pokemon/0008_Wartortle.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
static/images/pokemon/0009_Blastoise.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
static/images/pokemon/0010_Caterpie.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
static/images/pokemon/0011_Metapod.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

BIN
static/images/pokemon/0012_Butterfree.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
static/images/pokemon/0013_Weedle.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

BIN
static/images/pokemon/0014_Kakuna.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

BIN
static/images/pokemon/0015_Beedrill.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
static/images/pokemon/0016_Pidgey.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

BIN
static/images/pokemon/0017_Pidgeotto.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
static/images/pokemon/0018_Pidgeot.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
static/images/pokemon/0019_Rattata.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
static/images/pokemon/0019_Rattata_(Alolan_Form).png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

BIN
static/images/pokemon/0020_Raticate.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
static/images/pokemon/0020_Raticate_(Alolan_Form).png

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
static/images/pokemon/0021_Spearow.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
static/images/pokemon/0022_Fearow.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
static/images/pokemon/0023_Ekans.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
static/images/pokemon/0024_Arbok.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
static/images/pokemon/0025_Pikachu.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

BIN
static/images/pokemon/0025_Pikachu_(Alola_Cap).png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

BIN
static/images/pokemon/0025_Pikachu_(Hoenn_Cap).png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

BIN
static/images/pokemon/0025_Pikachu_(Kalos_Cap).png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

BIN
static/images/pokemon/0025_Pikachu_(Original_Cap).png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

BIN
static/images/pokemon/0025_Pikachu_(Partner_Cap).png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

BIN
static/images/pokemon/0025_Pikachu_(Sinnoh_Cap).png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

BIN
static/images/pokemon/0025_Pikachu_(Unova_Cap).png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

BIN
static/images/pokemon/0025_Pikachu_(World_Cap).png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

BIN
static/images/pokemon/0026_Raichu.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
static/images/pokemon/0026_Raichu_(Alolan_Form).png

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
static/images/pokemon/0027_Sandshrew.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
static/images/pokemon/0027_Sandshrew_(Alolan_Form).png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
static/images/pokemon/0028_Sandslash.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
static/images/pokemon/0028_Sandslash_(Alolan_Form).png

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
static/images/pokemon/0029_Nidoran♀.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
static/images/pokemon/0030_Nidorina.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
static/images/pokemon/0031_Nidoqueen.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
static/images/pokemon/0032_Nidoran♂.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
static/images/pokemon/0033_Nidorino.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
static/images/pokemon/0034_Nidoking.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
static/images/pokemon/0035_Clefairy.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
static/images/pokemon/0036_Clefable.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
static/images/pokemon/0037_Vulpix.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
static/images/pokemon/0037_Vulpix_(Alolan_Form).png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
static/images/pokemon/0038_Ninetales.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
static/images/pokemon/0038_Ninetales_(Alolan_Form).png

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
static/images/pokemon/0039_Jigglypuff.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
static/images/pokemon/0040_Wigglytuff.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
static/images/pokemon/0041_Zubat.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
static/images/pokemon/0042_Golbat.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
static/images/pokemon/0043_Oddish.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
static/images/pokemon/0044_Gloom.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
static/images/pokemon/0045_Vileplume.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
static/images/pokemon/0046_Paras.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
static/images/pokemon/0047_Parasect.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
static/images/pokemon/0048_Venonat.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
static/images/pokemon/0049_Venomoth.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

BIN
static/images/pokemon/0050_Diglett.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

BIN
static/images/pokemon/0050_Diglett_(Alolan_Form).png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

BIN
static/images/pokemon/0051_Dugtrio.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
static/images/pokemon/0051_Dugtrio_(Alolan_Form).png

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
static/images/pokemon/0052_Meowth.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
static/images/pokemon/0052_Meowth_(Alolan_Form).png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

BIN
static/images/pokemon/0052_Meowth_(Galarian_Form).png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
static/images/pokemon/0053_Persian.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
static/images/pokemon/0053_Persian_(Alolan_Form).png

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
static/images/pokemon/0054_Psyduck.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
static/images/pokemon/0055_Golduck.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
static/images/pokemon/0056_Mankey.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

BIN
static/images/pokemon/0057_Primeape.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
static/images/pokemon/0058_Growlithe.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
static/images/pokemon/0058_Growlithe_(Hisuian_Form).png

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
static/images/pokemon/0059_Arcanine.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
static/images/pokemon/0059_Arcanine_(Hisuian_Form).png

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
static/images/pokemon/0060_Poliwag.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
static/images/pokemon/0061_Poliwhirl.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
static/images/pokemon/0062_Poliwrath.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
static/images/pokemon/0063_Abra.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
static/images/pokemon/0064_Kadabra.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save