elmeg-demo/backend/routers/performances.py
fullsizemalt 8cd578b863
Some checks are pending
Deploy Elmeg / deploy (push) Waiting to run
fix(backend): Add missing slug fields to PerformanceDetailRead schema
2025-12-24 17:55:12 -08:00

138 lines
5 KiB
Python

from typing import List
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlmodel import Session, select, func
from database import get_session
from models import Performance, PerformanceNickname, Tag, EntityTag, Show, Venue, Rating
from schemas import PerformanceDetailRead, PerformanceNicknameCreate, PerformanceNicknameRead, PerformanceReadWithShow
from auth import get_current_user
router = APIRouter(prefix="/performances", tags=["performances"])
@router.get("/{slug}", response_model=PerformanceDetailRead)
def read_performance(slug: str, session: Session = Depends(get_session)):
performance = session.exec(
select(Performance).where(Performance.slug == 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 ---
# Get all performances of this song, ordered by date
# Join Show and Venue for list display
all_perfs_data = session.exec(
select(Performance, Show, Venue)
.join(Show, Performance.show_id == Show.id)
.outerjoin(Venue, Show.venue_id == Venue.id)
.where(Performance.song_id == performance.song_id)
.order_by(Show.date)
).all()
# Find current index
current_index = -1
for i, (p, s, v) in enumerate(all_perfs_data):
if p.id == performance_id:
current_index = i
break
prev_slug = None
next_slug = None
prev_id = None
next_id = None
gap = 0
times_played = current_index + 1 # 1-based count
if current_index > 0:
prev_perf = all_perfs_data[current_index - 1][0]
prev_id = prev_perf.id
prev_slug = prev_perf.slug
# Calculate Gap
prev_date = all_perfs_data[current_index - 1][1].date
current_date = all_perfs_data[current_index][1].date
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_data) - 1:
next_perf = all_perfs_data[current_index + 1][0]
next_id = next_perf.id
next_slug = next_perf.slug
# Fetch ratings for all performances of this song
rating_stats = session.exec(
select(Rating.performance_id, func.avg(Rating.score), func.count(Rating.id))
.where(Rating.song_id == performance.song_id)
.where(Rating.performance_id.is_not(None))
.group_by(Rating.performance_id)
).all()
rating_map = {row[0]: {"avg": row[1], "count": row[2]} for row in rating_stats}
# Build other_performances list
other_performances = []
for p, s, v in all_perfs_data:
if p.id == performance_id:
continue
stats = rating_map.get(p.id, {"avg": 0.0, "count": 0})
perf_read = PerformanceReadWithShow(
**p.model_dump(),
song=performance.song, # Reuse loaded song object
show_date=s.date,
show_slug=s.slug,
venue_name=v.name if v else "Unknown Venue",
venue_city=v.city if v else "Unknown City",
venue_state=v.state if v else None,
avg_rating=stats["avg"],
total_reviews=stats["count"],
nicknames=p.nicknames
)
other_performances.append(perf_read)
# Sort by rating desc, then date desc
other_performances.sort(key=lambda x: (x.avg_rating or 0, x.show_date), reverse=True)
# 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['previous_performance_slug'] = prev_slug
perf_dict['next_performance_id'] = next_id
perf_dict['next_performance_slug'] = next_slug
perf_dict['gap'] = gap
perf_dict['times_played'] = times_played
perf_dict['other_performances'] = other_performances
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