feat(phase2): Add achievements and replay systems
Achievement System:
- Add Achievement model with condition types (streak, steal, specialist, etc.)
- Add AchievementManager service for tracking and awarding achievements
- Add Pydantic schemas for achievements (AchievementResponse, PlayerStats, etc.)
- Seed 18 achievements from design doc
- Add GET /api/game/achievements endpoint
Replay System:
- Add ReplayManager service for saving/loading game replays
- Add GET /api/replay/{code} and /api/replay/session/{id} endpoints
- Format replays for frontend consumption
Phase 2 tasks completed:
- F2.1: Achievement model and migration
- F2.2: Pydantic schemas
- F2.3: AchievementManager service
- F2.4: ReplayManager service
- F2.5: API endpoints
- F2.6: Seed 18 achievements data
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
176
backend/scripts/seed_achievements.py
Normal file
176
backend/scripts/seed_achievements.py
Normal file
@@ -0,0 +1,176 @@
|
||||
import asyncio
|
||||
import sys
|
||||
sys.path.insert(0, '/root/WebTriviasMulti/backend')
|
||||
|
||||
from sqlalchemy import select
|
||||
from app.models.base import get_async_session
|
||||
from app.models.achievement import Achievement
|
||||
|
||||
ACHIEVEMENTS = [
|
||||
{
|
||||
"name": "Primera Victoria",
|
||||
"description": "Gana tu primera partida",
|
||||
"icon": "🏆",
|
||||
"condition_type": "first_win",
|
||||
"condition_value": 1,
|
||||
"category_id": None
|
||||
},
|
||||
{
|
||||
"name": "Racha de 3",
|
||||
"description": "Responde 3 correctas seguidas",
|
||||
"icon": "🔥",
|
||||
"condition_type": "streak",
|
||||
"condition_value": 3,
|
||||
"category_id": None
|
||||
},
|
||||
{
|
||||
"name": "Racha de 5",
|
||||
"description": "Responde 5 correctas seguidas",
|
||||
"icon": "🔥🔥",
|
||||
"condition_type": "streak",
|
||||
"condition_value": 5,
|
||||
"category_id": None
|
||||
},
|
||||
{
|
||||
"name": "Ladrón Novato",
|
||||
"description": "Primer robo exitoso",
|
||||
"icon": "🦝",
|
||||
"condition_type": "steal_success",
|
||||
"condition_value": 1,
|
||||
"category_id": None
|
||||
},
|
||||
{
|
||||
"name": "Ladrón Maestro",
|
||||
"description": "5 robos exitosos en una partida",
|
||||
"icon": "🦝👑",
|
||||
"condition_type": "steal_success",
|
||||
"condition_value": 5,
|
||||
"category_id": None
|
||||
},
|
||||
{
|
||||
"name": "Especialista Nintendo",
|
||||
"description": "10 correctas en Nintendo",
|
||||
"icon": "🍄",
|
||||
"condition_type": "category_specialist",
|
||||
"condition_value": 10,
|
||||
"category_id": 1
|
||||
},
|
||||
{
|
||||
"name": "Especialista Xbox",
|
||||
"description": "10 correctas en Xbox",
|
||||
"icon": "🎮",
|
||||
"condition_type": "category_specialist",
|
||||
"condition_value": 10,
|
||||
"category_id": 2
|
||||
},
|
||||
{
|
||||
"name": "Especialista PlayStation",
|
||||
"description": "10 correctas en PlayStation",
|
||||
"icon": "🎯",
|
||||
"condition_type": "category_specialist",
|
||||
"condition_value": 10,
|
||||
"category_id": 3
|
||||
},
|
||||
{
|
||||
"name": "Especialista Anime",
|
||||
"description": "10 correctas en Anime",
|
||||
"icon": "⛩️",
|
||||
"condition_type": "category_specialist",
|
||||
"condition_value": 10,
|
||||
"category_id": 4
|
||||
},
|
||||
{
|
||||
"name": "Especialista Música",
|
||||
"description": "10 correctas en Música",
|
||||
"icon": "🎵",
|
||||
"condition_type": "category_specialist",
|
||||
"condition_value": 10,
|
||||
"category_id": 5
|
||||
},
|
||||
{
|
||||
"name": "Especialista Películas",
|
||||
"description": "10 correctas en Películas",
|
||||
"icon": "🎬",
|
||||
"condition_type": "category_specialist",
|
||||
"condition_value": 10,
|
||||
"category_id": 6
|
||||
},
|
||||
{
|
||||
"name": "Especialista Libros",
|
||||
"description": "10 correctas en Libros",
|
||||
"icon": "📚",
|
||||
"condition_type": "category_specialist",
|
||||
"condition_value": 10,
|
||||
"category_id": 7
|
||||
},
|
||||
{
|
||||
"name": "Especialista Historia",
|
||||
"description": "10 correctas en Historia-Cultura",
|
||||
"icon": "🏛️",
|
||||
"condition_type": "category_specialist",
|
||||
"condition_value": 10,
|
||||
"category_id": 8
|
||||
},
|
||||
{
|
||||
"name": "Invicto",
|
||||
"description": "Gana sin fallar ninguna pregunta",
|
||||
"icon": "⭐",
|
||||
"condition_type": "perfect_game",
|
||||
"condition_value": 1,
|
||||
"category_id": None
|
||||
},
|
||||
{
|
||||
"name": "Velocista",
|
||||
"description": "Responde correctamente en menos de 3 segundos",
|
||||
"icon": "⚡",
|
||||
"condition_type": "fast_answer",
|
||||
"condition_value": 3,
|
||||
"category_id": None
|
||||
},
|
||||
{
|
||||
"name": "Comeback",
|
||||
"description": "Gana estando 500+ puntos abajo",
|
||||
"icon": "🔄",
|
||||
"condition_type": "comeback",
|
||||
"condition_value": 500,
|
||||
"category_id": None
|
||||
},
|
||||
{
|
||||
"name": "Dominio Total",
|
||||
"description": "Responde las 5 preguntas de una categoría correctamente",
|
||||
"icon": "👑",
|
||||
"condition_type": "category_sweep",
|
||||
"condition_value": 5,
|
||||
"category_id": None
|
||||
},
|
||||
{
|
||||
"name": "Arriesgado",
|
||||
"description": "Responde correctamente 3 preguntas de 500 pts",
|
||||
"icon": "🎰",
|
||||
"condition_type": "high_stakes",
|
||||
"condition_value": 3,
|
||||
"category_id": None
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
async def seed_achievements():
|
||||
AsyncSessionLocal = get_async_session()
|
||||
async with AsyncSessionLocal() as session:
|
||||
# Verificar si ya existen
|
||||
result = await session.execute(select(Achievement))
|
||||
if result.scalars().first():
|
||||
print("Achievements ya existen, saltando seed...")
|
||||
return
|
||||
|
||||
# Insertar achievements
|
||||
for data in ACHIEVEMENTS:
|
||||
achievement = Achievement(**data)
|
||||
session.add(achievement)
|
||||
|
||||
await session.commit()
|
||||
print(f"Insertados {len(ACHIEVEMENTS)} achievements")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(seed_achievements())
|
||||
Reference in New Issue
Block a user