Sistema web completo para la Hermandad Cubana de Powerlifting. Permite registrar concursantes, competiciones y levantamientos con cálculo automático del IPF Score. Soporta fotos tipo carnet por concursante, filtros por categoría, competiciones y records nacionales.
- ✅ Directorio de concursantes con datos completos
- ✅ Registro de levantamientos por disciplina (sentadilla, press banca, peso muerto)
- ✅ Cálculo automático del IPF Score
- ✅ Rankings dinámicos por categoría de peso
- ✅ Dashboard con estadísticas en tiempo real
- ✅ API RESTful con FastAPI
- ✅ Frontend moderno con colores azul, negro y rojo
- ✅ Base de datos SQLite
- Navega a la carpeta backend:
cd backend- Crea un entorno virtual:
python -m venv venv
source venv/bin/activate # En Windows: venv\Scripts\activate- Instala las dependencias:
pip install -r requirements.txt- Ejecuta el servidor:
python main.pyEl servidor estará disponible en http://localhost:8000
- API:
http://localhost:8000/api - Swagger UI:
http://localhost:8000/docs
- Navega a la carpeta frontend:
cd ../frontend- Abre
index.htmlen tu navegador (o usa un servidor local):
# Con Python 3
python -m http.server 8001
# Luego abre http://localhost:8001powerlifting-app/
├── backend/
│ ├── main.py # Aplicación FastAPI principal
│ ├── models.py # Modelos de base de datos
│ ├── schemas.py # Esquemas de validación
│ ├── database.py # Configuración de BD
│ ├── ipf_calculator.py # Cálculo de puntos IPF
│ ├── requirements.txt # Dependencias Python
│ └── powerlifting.db # Base de datos SQLite (se crea automáticamente)
└── frontend/
├── index.html # Página principal
├── style.css # Estilos (colores azul, negro y rojo)
└── script.js # Lógica del frontend
GET /api/concursantes- Obtener todosPOST /api/concursantes- Crear nuevoGET /api/concursantes/{id}- Obtener por IDPUT /api/concursantes/{id}- ActualizarDELETE /api/concursantes/{id}- Eliminar
GET /api/competiciones?desde=2024- Obtener el historial desde un año dado, ordenado de más reciente a más antiguo. Si omitesdesde, el valor por defecto es 2024.POST /api/competiciones- Crear nuevaGET /api/competiciones/{id}- Obtener por ID
GET /api/levantamientos- Obtener todosPOST /api/levantamientos- Crear nuevo (calcula IPF automáticamente)GET /api/levantamientos/concursante/{id}- Obtener por concursante
GET /api/ranking- Ranking general por IPFGET /api/ranking/{categoria}- Ranking por categoría de peso
GET /api/estadisticas- Estadísticas generalesGET /- Health check
{
"id": 1,
"nombre": "Juan Pérez",
"edad": 28,
"peso_corporal": 74.5,
"sexo": "M",
"categoria_peso": "74",
"club": "Club Fuerte Cuba",
"ciudad": "La Habana",
"ano_inicio": 2019,
"fecha_registro": "2024-05-23T10:30:00"
}{
"id": 1,
"concursante_id": 1,
"competicion_id": 1,
"sentadilla": 180,
"press_banca": 120,
"peso_muerto": 200,
"ipf_score": 285.45,
"fecha_levantamiento": "2024-05-23T10:30:00"
}El IPF Score se calcula automáticamente según:
- Total levantado = Sentadilla + Press Banca + Peso Muerto
- Fórmula:
IPF = (Total / Coeficiente) × 100
Los coeficientes varían según:
- Categoría de peso
- Sexo del atleta
Categorías de peso (Hombres): 59, 66, 74, 83, 93, 105, 120, +120 kg Categorías de peso (Mujeres): 47, 52, 57, 63, 69, 76, 84, +84 kg
-
Colores principales:
- Azul: #1e90ff (primario)
- Negro: #1a1a2e (fondo)
- Rojo: #e74c3c (acentos y resaltes)
-
Responsivo: Funciona en desktop, tablet y móvil
-
Animaciones suaves: Transiciones y efectos visuales mejorados
curl -X POST http://localhost:8000/api/concursantes \
-H "Content-Type: application/json" \
-d '{
"nombre": "Carlos López",
"edad": 25,
"peso_corporal": 82.5,
"sexo": "M",
"categoria_peso": "83",
"club": "Powerlifting Heroes",
"ciudad": "Santa Clara",
"ano_inicio": 2020
}'curl -X POST http://localhost:8000/api/levantamientos \
-H "Content-Type: application/json" \
-d '{
"concursante_id": 1,
"competicion_id": 1,
"sentadilla": 180,
"press_banca": 110,
"peso_muerto": 210
}'curl http://localhost:8000/api/ranking- Backend: FastAPI, SQLAlchemy, SQLite
- Frontend: HTML5, CSS3, JavaScript Vanilla
- Base de datos: SQLite (portable, sin configuración)
© 2024 Asociación Cubana de Powerlifting
Creado con ❤️ para la comunidad de powerlifting cubana.
¿Necesitas ayuda? Consulta los endpoints en Swagger: http://localhost:8000/docs
-
Sube este repositorio a GitHub si no está ya en uno público/privado.
-
Ve a https://render.com y crea una cuenta (puedes usar la opción gratuita).
-
Conecta tu repositorio y en el panel de Render crea dos servicios:
- Backend (Web Service)
- Tipo:
Web Service-> Python - Root: selecciona la carpeta
backendo usarender.yaml(la repo ya contienerender.yaml). - Build command:
pip install -r backend/requirements.txt - Start command:
uvicorn main:app --host 0.0.0.0 --port $PORT - Environment variables: añade
ADMIN_KEYcon la clave que quieras usar (ej:mi_clave_segura).
- Tipo:
- Frontend (Static Site)
- Tipo:
Static Site - Publish directory:
frontend
- Tipo:
- Backend (Web Service)
-
Si usas el
render.yamlen la raíz, Render detectará y creará ambos servicios automáticamente al conectar el repo. -
Después del deploy, anota las URLs asignadas por Render: la del frontend debe apuntar a los archivos estáticos y el frontend ya está configurado para llamar a la API usando
http://localhost:8000— debes actualizarfrontend/script.jspara usar la URL del backend de Render (reemplazarAPI_URLyBACKEND_URL).
Ejemplo rápido para producción: en frontend/script.js cambia al inicio:
const API_URL = "https://<tu-backend>.onrender.com/api";
const BACKEND_URL = "https://<tu-backend>.onrender.com";-
Opcional: si quieres persistencia real, añade un servicio de base de datos (Postgres) en Render y actualiza
database.pypara usar la URL de la base. -
Nota sobre archivos estáticos: las fotos subidas (
/static/photos) quedan en el disco del contenedor y pueden perderse en redeploys; para producción considera usar un bucket (S3-compatible) o almacenar fotos en un servicio persistente.
Si quieres, hago los cambios finales para que frontend/script.js use una variable RENDER_BACKEND_URL tomada de window.__BACKEND_URL__ o del query param backend_url, y preparo el commit listo para desplegar.