- Add slug fields to Song, Venue, Show, Tour, Performance models - Update routers to support lookup by slug or ID - Create slugify.py utility for generating URL-safe slugs - Add migration script to generate slugs for existing data - Performance slugs use songslug-YYYY-MM-DD format
104 lines
3.6 KiB
Python
104 lines
3.6 KiB
Python
from typing import List
|
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
from sqlmodel import Session, select
|
|
from database import get_session
|
|
from models import Performance, PerformanceNickname, Tag, EntityTag
|
|
from schemas import PerformanceDetailRead, PerformanceNicknameCreate, PerformanceNicknameRead
|
|
from auth import get_current_user
|
|
|
|
router = APIRouter(prefix="/performances", tags=["performances"])
|
|
|
|
@router.get("/{performance_id_or_slug}", response_model=PerformanceDetailRead)
|
|
def read_performance(performance_id_or_slug: str, session: Session = Depends(get_session)):
|
|
performance = None
|
|
if performance_id_or_slug.isdigit():
|
|
performance = session.get(Performance, int(performance_id_or_slug))
|
|
|
|
if not performance:
|
|
# Try slug lookup
|
|
performance = session.exec(
|
|
select(Performance).where(Performance.slug == performance_id_or_slug)
|
|
).first()
|
|
|
|
if not performance:
|
|
raise HTTPException(status_code=404, detail="Performance not found")
|
|
|
|
performance_id = performance.id # Use actual ID for lookups
|
|
|
|
# --- Calculate Stats & Navigation ---
|
|
from sqlmodel import select, func, desc
|
|
from models import Show
|
|
|
|
# Get all performances of this song, ordered by date
|
|
# We need to join Show to order by date
|
|
all_perfs = session.exec(
|
|
select(Performance, Show.date)
|
|
.join(Show)
|
|
.where(Performance.song_id == performance.song_id)
|
|
.order_by(Show.date)
|
|
).all()
|
|
|
|
# Find current index
|
|
# all_perfs is a list of tuples (Performance, date)
|
|
current_index = -1
|
|
for i, (p, d) in enumerate(all_perfs):
|
|
if p.id == performance_id:
|
|
current_index = i
|
|
break
|
|
|
|
prev_id = None
|
|
next_id = None
|
|
gap = 0
|
|
times_played = current_index + 1 # 1-based count
|
|
|
|
if current_index > 0:
|
|
prev_id = all_perfs[current_index - 1][0].id
|
|
|
|
# Calculate Gap
|
|
# Gap is number of shows between prev performance and this one
|
|
prev_date = all_perfs[current_index - 1][1]
|
|
current_date = all_perfs[current_index][1]
|
|
|
|
gap = session.exec(
|
|
select(func.count(Show.id))
|
|
.where(Show.date > prev_date)
|
|
.where(Show.date < current_date)
|
|
).one()
|
|
|
|
if current_index < len(all_perfs) - 1:
|
|
next_id = all_perfs[current_index + 1][0].id
|
|
|
|
# Construct response manually to include extra fields
|
|
# We need to ensure nested models (show, song) are validated correctly
|
|
perf_dict = performance.model_dump()
|
|
perf_dict['show'] = performance.show
|
|
perf_dict['song'] = performance.song
|
|
perf_dict['nicknames'] = performance.nicknames
|
|
perf_dict['previous_performance_id'] = prev_id
|
|
perf_dict['next_performance_id'] = next_id
|
|
perf_dict['gap'] = gap
|
|
perf_dict['times_played'] = times_played
|
|
|
|
return perf_dict
|
|
|
|
@router.post("/{performance_id}/nicknames", response_model=PerformanceNicknameRead)
|
|
def suggest_nickname(
|
|
performance_id: int,
|
|
nickname: PerformanceNicknameCreate,
|
|
session: Session = Depends(get_session),
|
|
current_user = Depends(get_current_user)
|
|
):
|
|
# Check if performance exists
|
|
perf = session.get(Performance, performance_id)
|
|
if not perf:
|
|
raise HTTPException(status_code=404, detail="Performance not found")
|
|
|
|
db_nickname = PerformanceNickname.model_validate(nickname)
|
|
db_nickname.performance_id = performance_id
|
|
db_nickname.suggested_by = current_user.id
|
|
db_nickname.status = "pending" # Default to pending
|
|
|
|
session.add(db_nickname)
|
|
session.commit()
|
|
session.refresh(db_nickname)
|
|
return db_nickname
|