edh-elo/app/routers/decks.py
Jack Jackson 1c9aa30721 Add "Latest Score" in Decks view
Also experiment with auto-helm-update and caching, and make some updates
to seed data
2024-07-10 21:20:48 -07:00

148 lines
4.2 KiB
Python

from fastapi import APIRouter, Depends, HTTPException, Request
from fastapi.responses import HTMLResponse
from sqlalchemy import and_, or_, func
from sqlalchemy.orm import Session
from app.sql import models
from ..templates import jinja_templates
from ..sql import crud, schemas
from ..sql.database import get_db
from .players import list_players
from .games import _render_game_participants
api_router = APIRouter(prefix="/deck", tags=["deck"])
html_router = APIRouter(
prefix="/deck", include_in_schema=False, default_response_class=HTMLResponse
)
########
# API Routes
########
@api_router.post("/", response_model=schemas.Deck, status_code=201)
def create_deck(deck: schemas.DeckCreate, db: Session = Depends(get_db)):
db_player = crud.get_player_by_id(db, deck.owner_id)
if db_player is None:
raise HTTPException(
status_code=400, detail=f"Owner id {deck.owner_id} not found"
)
return crud.create_deck(db=db, deck=deck)
@api_router.get("/list", response_model=list[schemas.Deck])
def list_decks(skip: int = 0, limit: int = 100, db=Depends(get_db)):
return crud.get_decks(db, skip=skip, limit=limit)
@api_router.get("/{deck_id}", response_model=schemas.Deck)
def read_deck(deck_id: int, db=Depends(get_db)):
print(deck_id)
db_deck = crud.get_deck_by_id(db, deck_id)
if db_deck is None:
raise HTTPException(status_code=404, detail="Deck not found")
return db_deck
@api_router.delete("/{deck_id}", status_code=204)
def delete_deck(deck_id: str, db=Depends(get_db)):
crud.delete_deck_by_id(db, int(deck_id))
########
# HTML Routes
########
@html_router.get("/create")
def deck_create_html(request: Request, db=Depends(get_db)):
players = list_players(db=db)
return jinja_templates.TemplateResponse(
request, "decks/create.html", {"players": players}
)
# TODO - pagination
@html_router.get("/list")
def decks_html(request: Request, db=Depends(get_db)):
decks = list_decks(db=db)
for deck in decks:
setattr(
deck,
"games_played",
db.query(func.count())
.select_from(models.Game)
.filter(
or_(
*[
getattr(models.Game, f"deck_id_{i+1}") == deck.id
for i in range(6)
]
)
)
.scalar(),
)
setattr(
deck,
"latest_score",
db.query(models.EloScore)
.filter(models.EloScore.deck_id == deck.id)
.order_by(models.EloScore.after_game_id.desc())
.first()
.score,
)
return jinja_templates.TemplateResponse(
request, "decks/list.html", {"decks": decks}
)
# This must be after the static-path routes, lest it take priority over them
@html_router.get("/{deck_id}")
def deck_html(request: Request, deck_id: str, db=Depends(get_db)):
deck_info = read_deck(deck_id, db)
deck_score_history = _build_deck_score_history(deck_id, db)
return jinja_templates.TemplateResponse(
request,
"decks/detail.html",
{
"deck": deck_info,
"owner": deck_info.owner,
"game_history": deck_score_history,
},
)
def _build_deck_score_history(deck_id: str, db: Session):
# This is...horrible.
# But I can't find a way to execute a join _in_ SQLAlchemy in such a way that the response is actual objects rather
# than the underlying rows
# (https://stackoverflow.com/questions/78596316/)
games_involving_this_deck = (
db.query(models.Game)
.filter(
or_(*[getattr(models.Game, f"deck_id_{i+1}") == deck_id for i in range(6)])
)
.all()
)
# Having found the games, then add the score for this deck after that game
return [
{
"game": game,
"score": db.query(models.EloScore)
.filter(
and_(
models.EloScore.after_game_id == game.id,
models.EloScore.deck_id == deck_id,
)
)
.first()
.score,
"game_participants": _render_game_participants(game, db),
}
for game in games_involving_this_deck
]