from fastapi import APIRouter, Depends, HTTPException from sqlmodel import Session, select from typing import List from database import get_session from models import SongCanon, Song, Vertical from pydantic import BaseModel router = APIRouter(prefix="/canon", tags=["canon"]) class SongVersionRead(BaseModel): id: int title: str slug: str | None vertical_id: int vertical_name: str vertical_slug: str class SongCanonRead(BaseModel): id: int title: str slug: str original_artist: str | None notes: str | None versions: List[SongVersionRead] class SongCanonCreate(BaseModel): title: str slug: str original_artist: str | None = None notes: str | None = None @router.get("/", response_model=List[SongCanonRead]) def list_canon_songs( limit: int = 50, offset: int = 0, session: Session = Depends(get_session) ): """List all canonical songs with their cross-band versions""" canons = session.exec( select(SongCanon).offset(offset).limit(limit) ).all() result = [] for canon in canons: versions = [] songs = session.exec( select(Song).where(Song.canon_id == canon.id) ).all() for song in songs: vertical = session.get(Vertical, song.vertical_id) versions.append({ "id": song.id, "title": song.title, "slug": song.slug, "vertical_id": song.vertical_id, "vertical_name": vertical.name if vertical else "Unknown", "vertical_slug": vertical.slug if vertical else "unknown" }) result.append({ "id": canon.id, "title": canon.title, "slug": canon.slug, "original_artist": canon.original_artist, "notes": canon.notes, "versions": versions }) return result @router.get("/{slug}", response_model=SongCanonRead) def get_canon_song(slug: str, session: Session = Depends(get_session)): """Get a canonical song with all its band-specific versions""" canon = session.exec( select(SongCanon).where(SongCanon.slug == slug) ).first() if not canon: raise HTTPException(status_code=404, detail="Canonical song not found") versions = [] songs = session.exec( select(Song).where(Song.canon_id == canon.id) ).all() for song in songs: vertical = session.get(Vertical, song.vertical_id) versions.append({ "id": song.id, "title": song.title, "slug": song.slug, "vertical_id": song.vertical_id, "vertical_name": vertical.name if vertical else "Unknown", "vertical_slug": vertical.slug if vertical else "unknown" }) return { "id": canon.id, "title": canon.title, "slug": canon.slug, "original_artist": canon.original_artist, "notes": canon.notes, "versions": versions } @router.get("/song/{song_id}/related", response_model=List[SongVersionRead]) def get_related_versions(song_id: int, session: Session = Depends(get_session)): """Get all versions of the same song across bands""" song = session.get(Song, song_id) if not song: raise HTTPException(status_code=404, detail="Song not found") if not song.canon_id: return [] # Get all songs with same canon_id (excluding this one) related = session.exec( select(Song) .where(Song.canon_id == song.canon_id) .where(Song.id != song_id) ).all() result = [] for s in related: vertical = session.get(Vertical, s.vertical_id) result.append({ "id": s.id, "title": s.title, "slug": s.slug, "vertical_id": s.vertical_id, "vertical_name": vertical.name if vertical else "Unknown", "vertical_slug": vertical.slug if vertical else "unknown" }) return result