Add naming scheme selection to run configuration

Add a nullable naming_scheme column to NuzlockeRun so users can pick a
themed word category for nickname suggestions. Includes Alembic migration,
updated Pydantic schemas, a GET /runs/naming-categories endpoint backed by
a cached dictionary loader, and frontend dropdowns in both the NewRun
creation flow and the RunDashboard for mid-run changes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-11 21:36:50 +01:00
parent e61fce5f72
commit e324559476
15 changed files with 215 additions and 31 deletions

View File

@@ -0,0 +1,29 @@
"""add naming_scheme to nuzlocke_runs
Revision ID: e5f6a7b8c9d1
Revises: d4e5f6a7b9c0
Create Date: 2026-02-11 12:00:00.000000
"""
from collections.abc import Sequence
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "e5f6a7b8c9d1"
down_revision: str | Sequence[str] | None = "d4e5f6a7b9c0"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None
def upgrade() -> None:
op.add_column(
"nuzlocke_runs",
sa.Column("naming_scheme", sa.String(50), nullable=True),
)
def downgrade() -> None:
op.drop_column("nuzlocke_runs", "naming_scheme")

View File

@@ -19,10 +19,16 @@ from app.schemas.run import (
RunResponse,
RunUpdate,
)
from app.services.naming import get_naming_categories
router = APIRouter()
@router.get("/naming-categories", response_model=list[str])
async def list_naming_categories():
return get_naming_categories()
@router.post("", response_model=RunResponse, status_code=201)
async def create_run(data: RunCreate, session: AsyncSession = Depends(get_session)):
# Validate game exists
@@ -35,6 +41,7 @@ async def create_run(data: RunCreate, session: AsyncSession = Depends(get_sessio
name=data.name,
status="active",
rules=data.rules,
naming_scheme=data.naming_scheme,
)
session.add(run)
await session.commit()

View File

@@ -22,6 +22,7 @@ class NuzlockeRun(Base):
)
completed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
hof_encounter_ids: Mapped[list[int] | None] = mapped_column(JSONB, default=None)
naming_scheme: Mapped[str | None] = mapped_column(String(50), nullable=True)
game: Mapped["Game"] = relationship(back_populates="runs")
encounters: Mapped[list["Encounter"]] = relationship(back_populates="run")

View File

@@ -9,6 +9,7 @@ class RunCreate(CamelModel):
game_id: int
name: str
rules: dict = {}
naming_scheme: str | None = None
class RunUpdate(CamelModel):
@@ -16,6 +17,7 @@ class RunUpdate(CamelModel):
status: str | None = None
rules: dict | None = None
hof_encounter_ids: list[int] | None = None
naming_scheme: str | None = None
class RunResponse(CamelModel):
@@ -25,6 +27,7 @@ class RunResponse(CamelModel):
status: str
rules: dict
hof_encounter_ids: list[int] | None = None
naming_scheme: str | None = None
started_at: datetime
completed_at: datetime | None

View File

@@ -0,0 +1,18 @@
import json
from functools import lru_cache
from pathlib import Path
DICTIONARY_PATH = Path(__file__).resolve().parents[1] / "seeds" / "data" / "name_dictionary.json"
@lru_cache(maxsize=1)
def _load_dictionary() -> dict[str, list[str]]:
if not DICTIONARY_PATH.exists():
return {}
with open(DICTIONARY_PATH) as f:
return json.load(f)
def get_naming_categories() -> list[str]:
"""Return sorted list of available naming category names."""
return sorted(_load_dictionary().keys())