- Evolution model with trigger, level, item, and condition fields
- Encounter.current_pokemon_id tracks evolved species separately
- Alembic migration for evolutions table and current_pokemon_id column
- Seed pipeline loads evolution data with manual overrides
- GET /pokemon/{id}/evolutions and PATCH /encounters/{id} endpoints
- Evolve button in StatusChangeModal with evolution method details
- PokemonCard shows evolved species with "Originally" label
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
100 lines
2.9 KiB
Python
100 lines
2.9 KiB
Python
from fastapi import APIRouter, Depends, HTTPException, Response
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from sqlalchemy.orm import joinedload
|
|
|
|
from app.core.database import get_session
|
|
from app.models.encounter import Encounter
|
|
from app.models.nuzlocke_run import NuzlockeRun
|
|
from app.models.pokemon import Pokemon
|
|
from app.models.route import Route
|
|
from app.schemas.encounter import (
|
|
EncounterCreate,
|
|
EncounterDetailResponse,
|
|
EncounterResponse,
|
|
EncounterUpdate,
|
|
)
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
@router.post(
|
|
"/runs/{run_id}/encounters",
|
|
response_model=EncounterResponse,
|
|
status_code=201,
|
|
)
|
|
async def create_encounter(
|
|
run_id: int,
|
|
data: EncounterCreate,
|
|
session: AsyncSession = Depends(get_session),
|
|
):
|
|
# Validate run exists
|
|
run = await session.get(NuzlockeRun, run_id)
|
|
if run is None:
|
|
raise HTTPException(status_code=404, detail="Run not found")
|
|
|
|
# Validate route exists
|
|
route = await session.get(Route, data.route_id)
|
|
if route is None:
|
|
raise HTTPException(status_code=404, detail="Route not found")
|
|
|
|
# Validate pokemon exists
|
|
pokemon = await session.get(Pokemon, data.pokemon_id)
|
|
if pokemon is None:
|
|
raise HTTPException(status_code=404, detail="Pokemon not found")
|
|
|
|
encounter = Encounter(
|
|
run_id=run_id,
|
|
route_id=data.route_id,
|
|
pokemon_id=data.pokemon_id,
|
|
nickname=data.nickname,
|
|
status=data.status,
|
|
catch_level=data.catch_level,
|
|
)
|
|
session.add(encounter)
|
|
await session.commit()
|
|
await session.refresh(encounter)
|
|
return encounter
|
|
|
|
|
|
@router.patch("/encounters/{encounter_id}", response_model=EncounterDetailResponse)
|
|
async def update_encounter(
|
|
encounter_id: int,
|
|
data: EncounterUpdate,
|
|
session: AsyncSession = Depends(get_session),
|
|
):
|
|
encounter = await session.get(Encounter, encounter_id)
|
|
if encounter is None:
|
|
raise HTTPException(status_code=404, detail="Encounter not found")
|
|
|
|
update_data = data.model_dump(exclude_unset=True)
|
|
for field, value in update_data.items():
|
|
setattr(encounter, field, value)
|
|
|
|
await session.commit()
|
|
|
|
# Reload with relationships for detail response
|
|
result = await session.execute(
|
|
select(Encounter)
|
|
.where(Encounter.id == encounter_id)
|
|
.options(
|
|
joinedload(Encounter.pokemon),
|
|
joinedload(Encounter.current_pokemon),
|
|
joinedload(Encounter.route),
|
|
)
|
|
)
|
|
return result.scalar_one()
|
|
|
|
|
|
@router.delete("/encounters/{encounter_id}", status_code=204)
|
|
async def delete_encounter(
|
|
encounter_id: int, session: AsyncSession = Depends(get_session)
|
|
):
|
|
encounter = await session.get(Encounter, encounter_id)
|
|
if encounter is None:
|
|
raise HTTPException(status_code=404, detail="Encounter not found")
|
|
|
|
await session.delete(encounter)
|
|
await session.commit()
|
|
return Response(status_code=204)
|