Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Navi-27/Proyecto-UPC/llms.txt

Use this file to discover all available pages before exploring further.

The models/ directory contains five classes that represent the core domain objects of the application. Each class is responsible for its own data and behavior — there is no shared base class or ORM. Database models (Usuario, Equipo, and PokedexUsuario) use static methods that open and close their own SQLite connections per operation, while Pokemon and Pokedex are pure Python objects with no direct database coupling.
Pokemon is a pure data class. It stores all attributes for a single Pokémon entry and provides two instance methods for convenience. It carries no database logic and is constructed both by PokeAPI (from API responses or cache rows) and by Pokedex operations.Constructor
Pokemon(id, nombre, tipos, altura, peso, imagen, stats)
Attributes
AttributeTypeDescription
idintNational Pokédex number
nombrestrPokémon name (lowercase)
tiposlist[str]List of type names (e.g. ["fire", "flying"])
alturaintHeight in decimeters
pesointWeight in hectograms
imagenstrURL to the front-facing sprite image
statsdictBase stats keyed by name: hp, attack, defense, special-attack, special-defense, speed
Methods
MethodReturn typeDescription
get_tipo_principal()strReturns tipos[0] if the list is non-empty, otherwise returns "Normal"
__str__()strReturns a formatted string: Pokemon({id}: {nombre} - {tipos})
Source
class Pokemon:
    def __init__(self, id, nombre, tipos, altura, peso, imagen, stats):
        self.id = id
        self.nombre = nombre
        self.tipos = tipos
        self.altura = altura
        self.peso = peso
        self.imagen = imagen
        self.stats = stats

    def get_tipo_principal(self):
        return self.tipos[0] if self.tipos else "Normal"

    def __str__(self):
        return f"Pokemon({self.id}: {self.nombre} - {self.tipos})"
Pokedex is a collection class that wraps an ordered list of Pokemon objects. It provides search, filter, and bulk persistence capabilities. The guardar_en_db() method is the only place where Pokedex touches the database — it writes its current contents to the cache_pokemon SQLite table using INSERT OR IGNORE so re-runs are safe.Constructor
Pokedex()
Initializes an empty pokemones list ([]). No arguments are required.Methods
MethodReturn typeDescription
agregar_pokemon(pokemon)NoneAppends a Pokemon instance to the internal pokemones list
guardar_en_db()NoneIterates over all Pokemon in the list and inserts each into cache_pokemon using INSERT OR IGNORE; serializes tipos and stats as JSON strings
buscar_por_nombre(nombre)list[Pokemon]Case-insensitive substring search on pokemon.nombre; returns a filtered list
filtrar_por_tipo(tipo)list[Pokemon]Returns all Pokémon whose tipos list contains the given type string (case-insensitive)
obtener_todos()list[Pokemon]Returns the full pokemones list with no filtering
__len__()intReturns the count of Pokémon in the collection (enables len(pokedex))
listar()NonePrints each Pokémon entry as pokemon {nombre} to stdout — intended as a debug utility
guardar_en_db() commits after each individual Pokémon insert rather than batching all inserts into a single transaction. This means a failure partway through a bulk load will leave a partial cache rather than rolling back to an empty state.
Source excerpt — buscar_por_nombre and filtrar_por_tipo
def buscar_por_nombre(self, nombre):
    pokemones = []
    nombre = nombre.lower()
    for pokemon in self.pokemones:
        if nombre in pokemon.nombre.lower():
            pokemones.append(pokemon)
    return pokemones

def filtrar_por_tipo(self, tipo):
    tipo = tipo.lower()
    return [p for p in self.pokemones if tipo in p.tipos]
Usuario represents an authenticated user. Instance objects carry only id and username — the password hash is never stored in memory after authentication. All database operations are exposed as @staticmethod methods, so the class can be used without instantiating a user object first.Constructor
Usuario(id, username)
AttributeTypeDescription
idintPrimary key from the usuarios table
usernamestrThe user’s unique display name
Static methods
MethodReturn typeDescription
crear(username, password)boolHashes password with Werkzeug’s generate_password_hash, inserts a new row into usuarios, and returns True on success. Returns False if the username already exists (UNIQUE constraint violation).
login(username, password)Usuario | NoneFetches the row for username, verifies the plaintext password against the stored hash using check_password_hash, and returns a Usuario instance on match. Returns None if the user does not exist or the password is wrong.
Source
from werkzeug.security import generate_password_hash, check_password_hash
from models.database import get_connection

class Usuario:
    def __init__(self, id, username):
        self.id = id
        self.username = username

    @staticmethod
    def crear(username, password):
        conn = get_connection()
        try:
            conn.execute(
                "INSERT INTO usuarios (username, password) VALUES (?,?)",
                (username, generate_password_hash(password))
            )
            conn.commit()
            return True
        except:
            return False
        finally:
            conn.close()

    @staticmethod
    def login(username, password):
        conn = get_connection()
        row = conn.execute(
            "SELECT * FROM usuarios WHERE username = ?", (username,)
        ).fetchone()
        conn.close()

        if row and check_password_hash(row["password"], password):
            return Usuario(row["id"], row["username"])
        return None
Equipo manages a per-user team of Pokémon. It enforces a hard limit of six members via the MAX_POKEMONES class constant and prevents duplicate entries. All database rows are returned as sqlite3.Row objects (dict-like), not as Pokemon instances — this is intentional since the team table stores a denormalized snapshot of each Pokémon’s display data.Class constant
ConstantValueDescription
MAX_POKEMONES6Maximum number of Pokémon allowed on a single user’s team
Static methods
MethodReturn typeDescription
obtener_equipo(usuario_id)list[Row]Returns all rows from equipos where usuario_id matches
agregar_pokemon(usuario_id, pokemon_id, pokemon_nombre, pokemon_imagen, pokemon_tipos)(bool, str)Validates that the team is not full and the Pokémon is not already present, then inserts the row. Returns (True, "Pokemon Agregado") on success, or (False, reason) on failure.
eliminar_pokemon(usuario_id, pokemon_id)(bool, str)Searches the user’s current team for the given pokemon_id. If found, deletes the row and returns (True, "Pokémon eliminado"). Returns (False, "Este pokémon no está en tu equipo") if not found.
Validation logic in agregar_pokemon
@staticmethod
def agregar_pokemon(usuario_id, pokemon_id, pokemon_nombre, pokemon_imagen, pokemon_tipos):
    equipo = Equipo.obtener_equipo(usuario_id)
    if len(equipo) >= Equipo.MAX_POKEMONES:
        return False, "El equipo ya esta completo (6 Pokemones)"

    for p in equipo:
        if p["pokemon_id"] == pokemon_id:
            return False, "El pokemon ya se encuentra en tu equipo"

    conn = get_connection()
    conn.execute(
        "INSERT INTO equipos (usuario_id, pokemon_id, pokemon_nombre, pokemon_imagen, pokemon_tipos) VALUES (?,?,?,?,?)",
        (usuario_id, pokemon_id, pokemon_nombre, pokemon_imagen, pokemon_tipos)
    )
    conn.commit()
    conn.close()
    return True, "Pokemon Agregado"
PokedexUsuario tracks which Pokémon a logged-in user has viewed, recording a timestamp for each first visit. The underlying pokedex_usuario table has a UNIQUE(usuario_id, pokemon_id) constraint, so registrar_visto uses INSERT OR IGNORE to silently skip re-visits. This class has no constructor — all functionality is exposed via @staticmethod methods.Static methods
MethodReturn typeDescription
registrar_visto(usuario_id, pokemon_id, pokemon_nombre)NoneInserts a row into pokedex_usuario with the current timestamp. Silently ignores duplicates via INSERT OR IGNORE.
obtener_vistos(usuario_id)list[Row]Returns all rows for the given user ordered by fecha_visto DESC (most recently viewed first).
esta_visto(usuario_id, pokemon_id)boolReturns True if a row exists for the (usuario_id, pokemon_id) pair, False otherwise.
Source
from models.database import get_connection

class PokedexUsuario:

    @staticmethod
    def registrar_visto(usuario_id, pokemon_id, pokemon_nombre):
        conn = get_connection()
        conn.execute('''
            INSERT OR IGNORE INTO pokedex_usuario
            (usuario_id, pokemon_id, pokemon_nombre)
            VALUES (?,?,?)
        ''', (usuario_id, pokemon_id, pokemon_nombre))
        conn.commit()
        conn.close()

    @staticmethod
    def obtener_vistos(usuario_id):
        conn = get_connection()
        rows = conn.execute('''
            SELECT * FROM pokedex_usuario WHERE usuario_id = ? ORDER BY fecha_visto DESC
        ''', (usuario_id,)).fetchall()
        conn.close()
        return rows

    @staticmethod
    def esta_visto(usuario_id, pokemon_id):
        conn = get_connection()
        row = conn.execute(
            """SELECT 1 FROM pokedex_usuario
            WHERE usuario_id = ? AND pokemon_id = ?""",
            (usuario_id, pokemon_id)
        ).fetchone()
        conn.close()
        return row is not None

Database Schema Reference

All four tables are created automatically by models/database.py on first startup via init_db(). The schema is defined inline using executescript:
TablePrimary keyNotable constraints
usuariosid (AUTOINCREMENT)username UNIQUE NOT NULL
equiposid (AUTOINCREMENT)FK → usuarios.id
cache_pokemonid (Pokédex number)No AUTOINCREMENT — ID is set by the API
pokedex_usuarioid (AUTOINCREMENT)FK → usuarios.id; UNIQUE(usuario_id, pokemon_id)

Build docs developers (and LLMs) love