|
|
import requests |
|
|
import math |
|
|
import json |
|
|
import os |
|
|
import chromadb |
|
|
|
|
|
with open("fr_to_en_pokemon.json", "r", encoding="utf-8") as f: |
|
|
FR_TO_EN_MAP = json.load(f) |
|
|
|
|
|
client = chromadb.PersistentClient(path="./chroma_db") |
|
|
collection = client.get_or_create_collection(name="pokemon_cards_clip") |
|
|
print(f"📦 Nombre de cartesen base : {collection.count()}") |
|
|
|
|
|
def get_english_name(pokemon_name: str) -> str: |
|
|
name = pokemon_name.strip().lower().replace(" ", "-") |
|
|
|
|
|
if name in [v.replace(" ", "-") for v in FR_TO_EN_MAP.values()]: |
|
|
return name |
|
|
|
|
|
english = FR_TO_EN_MAP.get(name) |
|
|
if english: |
|
|
return english.lower().replace(" ", "-") |
|
|
|
|
|
return "❌ nom-non-reconnu" |
|
|
|
|
|
def calculate_stat(base, level, iv=31, ev=0, nature=1.0, stat_name="hp"): |
|
|
if stat_name == "hp": |
|
|
return math.floor(((2 * base + iv + (ev // 4)) * level) / 100) + level + 10 |
|
|
else: |
|
|
return math.floor((((2 * base + iv + (ev // 4)) * level) / 100 + 5) * nature) |
|
|
|
|
|
def estimate_level_from_hp(observed_hp, base_hp, iv=31, ev=0, tolerance=1): |
|
|
for level in range(1, 101): |
|
|
computed_hp = math.floor(((2 * base_hp + iv + (ev // 4)) * level) / 100) + level + 10 |
|
|
if abs(computed_hp - observed_hp) <= tolerance: |
|
|
return level |
|
|
return None |
|
|
|
|
|
def get_base_hp(pokemon_name: str) -> int: |
|
|
base_url = "https://pokeapi.co/api/v2/pokemon/" |
|
|
try: |
|
|
res = requests.get(base_url + pokemon_name.lower()) |
|
|
res.raise_for_status() |
|
|
data = res.json() |
|
|
|
|
|
|
|
|
for stat in data["stats"]: |
|
|
if stat["stat"]["name"] == "hp": |
|
|
return stat["base_stat"] |
|
|
|
|
|
raise ValueError("❌ Stat HP non trouvée.") |
|
|
|
|
|
except requests.exceptions.HTTPError: |
|
|
raise ValueError("❌ Pokémon introuvable. Vérifie le nom.") |
|
|
except Exception as e: |
|
|
raise RuntimeError(f"⚠️ Erreur : {str(e)}") |
|
|
|
|
|
|
|
|
def get_move_details(move_url: str) -> dict: |
|
|
res = requests.get(move_url) |
|
|
res.raise_for_status() |
|
|
move_data = res.json() |
|
|
|
|
|
|
|
|
effect = next( |
|
|
(entry['effect'] for entry in move_data['effect_entries'] if entry['language']['name'] == 'en'), |
|
|
move_data['effect_entries'][0]['effect'] if move_data['effect_entries'] else "No known effect." |
|
|
) |
|
|
|
|
|
effect = effect.replace("$effect_chance", str(move_data.get("effect_chance", ""))) |
|
|
|
|
|
return { |
|
|
"name": move_data['name'], |
|
|
"type": move_data['type']['name'], |
|
|
"power": move_data['power'], |
|
|
"accuracy": move_data['accuracy'], |
|
|
"category": move_data['damage_class']['name'], |
|
|
"effect": effect |
|
|
} |
|
|
|
|
|
def get_pokemon_info(pokemon_name: str, level=50, version_group='sword-shield') -> str: |
|
|
base_url = "https://pokeapi.co/api/v2/" |
|
|
try: |
|
|
res_pokemon = requests.get(base_url + f"pokemon/{pokemon_name.lower()}") |
|
|
res_pokemon.raise_for_status() |
|
|
data = res_pokemon.json() |
|
|
|
|
|
res_species = requests.get(base_url + f"pokemon-species/{pokemon_name.lower()}") |
|
|
res_species.raise_for_status() |
|
|
species = res_species.json() |
|
|
|
|
|
evo_url = species['evolution_chain']['url'] |
|
|
res_evo = requests.get(evo_url) |
|
|
res_evo.raise_for_status() |
|
|
evolution_chain = res_evo.json() |
|
|
|
|
|
name = data['name'].capitalize() |
|
|
pokemon_id = data['id'] |
|
|
types = ", ".join([t['type']['name'].capitalize() for t in data['types']]) |
|
|
height = data['height'] / 10 |
|
|
weight = data['weight'] / 10 |
|
|
base_stats = {s['stat']['name']: s['base_stat'] for s in data['stats']} |
|
|
stats_lv = {k: calculate_stat(v, level, stat_name=k) for k, v in base_stats.items()} |
|
|
|
|
|
flavor_text = next( |
|
|
(entry['flavor_text'].replace("\n", " ").replace("\f", " ") |
|
|
for entry in species['flavor_text_entries'] |
|
|
if entry['language']['name'] == 'en'), |
|
|
"No description available." |
|
|
) |
|
|
|
|
|
def get_evolutions(chain): |
|
|
evols = [chain['species']['name'].capitalize()] |
|
|
if chain['evolves_to']: |
|
|
for evo in chain['evolves_to']: |
|
|
evols.extend(get_evolutions(evo)) |
|
|
return evols |
|
|
|
|
|
def get_moves(data, level_max, version_group): |
|
|
moves = [] |
|
|
for m in data['moves']: |
|
|
for v in m['version_group_details']: |
|
|
if ( |
|
|
v['version_group']['name'] == version_group and |
|
|
v['move_learn_method']['name'] == 'level-up' and |
|
|
v['level_learned_at'] <= level_max |
|
|
): |
|
|
move_details = get_move_details(m['move']['url']) |
|
|
move_details['level'] = v['level_learned_at'] |
|
|
moves.append(move_details) |
|
|
moves.sort(key=lambda m: m['level']) |
|
|
return moves |
|
|
|
|
|
evolutions = get_evolutions(evolution_chain['chain']) |
|
|
evolution_text = " → ".join(evolutions) |
|
|
|
|
|
moves = get_moves(data, level, version_group) |
|
|
|
|
|
|
|
|
if moves: |
|
|
moves_text = "\n".join([ |
|
|
f"### 🔹 Lv {m['level']} : **{m['name']}**\n" |
|
|
f"- Type: {m['type'].capitalize()} \n" |
|
|
f"- Category: {m['category'].capitalize()} \n" |
|
|
f"- Power: {m['power'] or '—'} \n" |
|
|
f"- Accuracy: {m['accuracy'] or '—'} \n" |
|
|
f"- Effect: {m['effect']}" |
|
|
for m in moves |
|
|
]) |
|
|
else: |
|
|
moves_text = "No available moves at this level." |
|
|
|
|
|
card_image_url = get_card_image_url(pokemon_name) |
|
|
card_image_md = f"" if card_image_url else "_Carte non trouvée dans la base._" |
|
|
|
|
|
markdown = f""" |
|
|
# 📘 Pokémon Sheet: **{name}** (#{pokemon_id}) |
|
|
|
|
|
--- |
|
|
|
|
|
{card_image_md} |
|
|
|
|
|
--- |
|
|
|
|
|
**Type(s)**: {types} |
|
|
**Height**: {height} m |
|
|
**Weight**: {weight} kg |
|
|
|
|
|
--- |
|
|
|
|
|
## 🔢 Base Stats |
|
|
|
|
|
| Stat | Value | |
|
|
|----------------|--------| |
|
|
| HP | {base_stats['hp']} | |
|
|
| Attack | {base_stats['attack']} | |
|
|
| Defense | {base_stats['defense']} | |
|
|
| Sp. Atk | {base_stats['special-attack']} | |
|
|
| Sp. Def | {base_stats['special-defense']} | |
|
|
| Speed | {base_stats['speed']} | |
|
|
|
|
|
--- |
|
|
|
|
|
## 🎯 Stats at Level {level} |
|
|
|
|
|
| Stat | Value | |
|
|
|----------------|--------| |
|
|
| HP | {stats_lv['hp']} | |
|
|
| Attack | {stats_lv['attack']} | |
|
|
| Defense | {stats_lv['defense']} | |
|
|
| Sp. Atk | {stats_lv['special-attack']} | |
|
|
| Sp. Def | {stats_lv['special-defense']} | |
|
|
| Speed | {stats_lv['speed']} | |
|
|
|
|
|
--- |
|
|
|
|
|
## 🔁 Evolution Chain |
|
|
|
|
|
{evolution_text} |
|
|
|
|
|
--- |
|
|
|
|
|
## 📜 Moves up to Level {level} (version: {version_group.replace('-', ' ').title()}) |
|
|
|
|
|
{moves_text} |
|
|
|
|
|
--- |
|
|
|
|
|
## 📖 Description |
|
|
|
|
|
> {flavor_text} |
|
|
""" |
|
|
return markdown.strip() |
|
|
|
|
|
except requests.exceptions.HTTPError: |
|
|
return "❌ Pokémon not found. Check the name." |
|
|
except Exception as e: |
|
|
return f"⚠️ Error: {str(e)}" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_card_image_url(pokemon_name: str) -> str: |
|
|
""" |
|
|
Recherche dans ChromaDB une carte dont le nom correspond au Pokémon donné. |
|
|
Retourne l'URL de l'image ou None. |
|
|
""" |
|
|
results = collection.get(where={"name": pokemon_name.capitalize()}) |
|
|
if results and results['metadatas']: |
|
|
return results['metadatas'][0].get('image_url') |
|
|
return None |
|
|
|