""" Festival API endpoints for multi-band festival discovery. """ from typing import List, Optional from fastapi import APIRouter, Depends, HTTPException, Query from sqlmodel import Session, select from pydantic import BaseModel from database import get_session from models import Festival, ShowFestival, Show, Vertical, Venue router = APIRouter(prefix="/festivals", tags=["festivals"]) class FestivalRead(BaseModel): id: int name: str slug: str year: Optional[int] start_date: Optional[str] end_date: Optional[str] website_url: Optional[str] description: Optional[str] class FestivalShowRead(BaseModel): show_id: int show_slug: Optional[str] show_date: str vertical_name: str vertical_slug: str stage: Optional[str] set_time: Optional[str] class FestivalDetailRead(BaseModel): festival: FestivalRead shows: List[FestivalShowRead] bands_count: int @router.get("/", response_model=List[FestivalRead]) def list_festivals( year: Optional[int] = None, limit: int = Query(default=50, le=100), offset: int = 0, session: Session = Depends(get_session) ): """List all festivals, optionally filtered by year""" query = select(Festival) if year: query = query.where(Festival.year == year) query = query.order_by(Festival.year.desc(), Festival.name).offset(offset).limit(limit) festivals = session.exec(query).all() return [ FestivalRead( id=f.id, name=f.name, slug=f.slug, year=f.year, start_date=f.start_date.strftime("%Y-%m-%d") if f.start_date else None, end_date=f.end_date.strftime("%Y-%m-%d") if f.end_date else None, website_url=f.website_url, description=f.description ) for f in festivals ] @router.get("/{slug}", response_model=FestivalDetailRead) def get_festival(slug: str, session: Session = Depends(get_session)): """Get festival details with all shows across bands""" festival = session.exec( select(Festival).where(Festival.slug == slug) ).first() if not festival: raise HTTPException(status_code=404, detail="Festival not found") # Get all shows at this festival show_festivals = session.exec( select(ShowFestival).where(ShowFestival.festival_id == festival.id) ).all() shows = [] bands_seen = set() for sf in show_festivals: show = session.get(Show, sf.show_id) if show: vertical = session.get(Vertical, show.vertical_id) if vertical: bands_seen.add(vertical.id) shows.append(FestivalShowRead( show_id=show.id, show_slug=show.slug, show_date=show.date.strftime("%Y-%m-%d") if show.date else "Unknown", vertical_name=vertical.name if vertical else "Unknown", vertical_slug=vertical.slug if vertical else "unknown", stage=sf.stage, set_time=sf.set_time )) # Sort by date shows.sort(key=lambda x: x.show_date) return FestivalDetailRead( festival=FestivalRead( id=festival.id, name=festival.name, slug=festival.slug, year=festival.year, start_date=festival.start_date.strftime("%Y-%m-%d") if festival.start_date else None, end_date=festival.end_date.strftime("%Y-%m-%d") if festival.end_date else None, website_url=festival.website_url, description=festival.description ), shows=shows, bands_count=len(bands_seen) ) @router.get("/by-band/{vertical_slug}") def get_festivals_by_band(vertical_slug: str, session: Session = Depends(get_session)): """Get all festivals a band has played""" vertical = session.exec( select(Vertical).where(Vertical.slug == vertical_slug) ).first() if not vertical: raise HTTPException(status_code=404, detail="Band not found") # Get shows for this vertical that are linked to festivals shows = session.exec( select(Show).where(Show.vertical_id == vertical.id) ).all() show_ids = [s.id for s in shows] if not show_ids: return [] # Get festival links show_festivals = session.exec( select(ShowFestival).where(ShowFestival.show_id.in_(show_ids)) ).all() festival_ids = list(set(sf.festival_id for sf in show_festivals)) if not festival_ids: return [] festivals = session.exec( select(Festival).where(Festival.id.in_(festival_ids)) ).all() return [ FestivalRead( id=f.id, name=f.name, slug=f.slug, year=f.year, start_date=f.start_date.strftime("%Y-%m-%d") if f.start_date else None, end_date=f.end_date.strftime("%Y-%m-%d") if f.end_date else None, website_url=f.website_url, description=f.description ) for f in sorted(festivals, key=lambda x: x.year or 0, reverse=True) ]