fediversion/backend/routers/discover.py
fullsizemalt 99e5924588
Some checks failed
Deploy Fediversion / deploy (push) Failing after 1s
feat: Sprint 2 - empty states, discovery, attendance stats
- Add EmptyState component with 6 variants
- Add discover.py router with smart filtering
  - GET /discover/shows (year, venue, city, tour filters)
  - GET /discover/years
  - GET /discover/recent
- Add GET /attendance/me/stats (by vertical breakdown)
2025-12-28 16:49:24 -08:00

190 lines
5.8 KiB
Python

"""
Show Discovery API - smart routing for finding shows.
"""
from datetime import date, timedelta
from typing import List, Optional
from fastapi import APIRouter, Depends, Query
from sqlmodel import Session, select, desc
from pydantic import BaseModel
from database import get_session
from models import Show, Venue, Vertical, Tour
router = APIRouter(prefix="/discover", tags=["discover"])
class DiscoverShow(BaseModel):
id: int
date: str
slug: str | None
venue_name: str | None
venue_city: str | None
vertical_name: str
vertical_slug: str
tour_name: str | None = None
class DiscoverResponse(BaseModel):
shows: List[DiscoverShow]
total: int
filters_applied: dict
@router.get("/shows", response_model=DiscoverResponse)
def discover_shows(
vertical: Optional[str] = None,
year: Optional[int] = None,
month: Optional[int] = None,
venue: Optional[str] = None,
tour: Optional[str] = None,
city: Optional[str] = None,
state: Optional[str] = None,
limit: int = 50,
offset: int = 0,
sort: str = "date_desc",
session: Session = Depends(get_session)
):
"""
Discover shows with smart filtering.
Sort options: date_desc, date_asc
"""
query = select(Show).where(Show.date.isnot(None))
filters = {}
# Filter by vertical (band)
if vertical:
v = session.exec(select(Vertical).where(Vertical.slug == vertical)).first()
if v:
query = query.where(Show.vertical_id == v.id)
filters["vertical"] = vertical
# Filter by year
if year:
start = date(year, 1, 1)
end = date(year, 12, 31)
query = query.where(Show.date >= start).where(Show.date <= end)
filters["year"] = year
# Filter by month (requires year)
if month and year:
start = date(year, month, 1)
if month == 12:
end = date(year + 1, 1, 1) - timedelta(days=1)
else:
end = date(year, month + 1, 1) - timedelta(days=1)
query = query.where(Show.date >= start).where(Show.date <= end)
filters["month"] = month
# Filter by tour
if tour:
t = session.exec(select(Tour).where(Tour.slug == tour)).first()
if t:
query = query.where(Show.tour_id == t.id)
filters["tour"] = tour
# Apply sorting
if sort == "date_asc":
query = query.order_by(Show.date)
else:
query = query.order_by(desc(Show.date))
# Get total count before pagination
all_shows = session.exec(query).all()
total = len(all_shows)
# Apply pagination
paginated = all_shows[offset:offset + limit]
# Filter by venue/city/state in Python (more flexible)
results = []
for show in paginated:
venue_obj = session.get(Venue, show.venue_id) if show.venue_id else None
vert_obj = session.get(Vertical, show.vertical_id)
tour_obj = session.get(Tour, show.tour_id) if show.tour_id else None
# City/state filters
if city and venue_obj and city.lower() not in venue_obj.city.lower():
continue
if state and venue_obj and venue_obj.state and state.lower() not in venue_obj.state.lower():
continue
if venue and venue_obj and venue.lower() not in venue_obj.name.lower():
continue
results.append(DiscoverShow(
id=show.id,
date=show.date.strftime("%Y-%m-%d") if show.date else "",
slug=show.slug,
venue_name=venue_obj.name if venue_obj else None,
venue_city=venue_obj.city if venue_obj else None,
vertical_name=vert_obj.name if vert_obj else "Unknown",
vertical_slug=vert_obj.slug if vert_obj else "unknown",
tour_name=tour_obj.name if tour_obj else None
))
if city:
filters["city"] = city
if state:
filters["state"] = state
if venue:
filters["venue"] = venue
return DiscoverResponse(
shows=results,
total=total,
filters_applied=filters
)
@router.get("/years")
def get_available_years(
vertical: Optional[str] = None,
session: Session = Depends(get_session)
):
"""Get list of years with shows for filtering UI"""
query = select(Show).where(Show.date.isnot(None))
if vertical:
v = session.exec(select(Vertical).where(Vertical.slug == vertical)).first()
if v:
query = query.where(Show.vertical_id == v.id)
shows = session.exec(query).all()
years = sorted(set(s.date.year for s in shows if s.date), reverse=True)
return {"years": years}
@router.get("/recent", response_model=List[DiscoverShow])
def get_recent_shows(
limit: int = 10,
vertical: Optional[str] = None,
session: Session = Depends(get_session)
):
"""Get most recent shows for quick discovery"""
query = select(Show).where(Show.date.isnot(None))
if vertical:
v = session.exec(select(Vertical).where(Vertical.slug == vertical)).first()
if v:
query = query.where(Show.vertical_id == v.id)
query = query.order_by(desc(Show.date)).limit(limit)
shows = session.exec(query).all()
results = []
for show in shows:
venue = session.get(Venue, show.venue_id) if show.venue_id else None
vert = session.get(Vertical, show.vertical_id)
results.append(DiscoverShow(
id=show.id,
date=show.date.strftime("%Y-%m-%d") if show.date else "",
slug=show.slug,
venue_name=venue.name if venue else None,
venue_city=venue.city if venue else None,
vertical_name=vert.name if vert else "Unknown",
vertical_slug=vert.slug if vert else "unknown"
))
return results