fediversion/backend/routers/attendance.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

126 lines
3.8 KiB
Python

from typing import List, Optional
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlmodel import Session, select
from database import get_session
from models import Attendance, User, Show
from schemas import AttendanceCreate, AttendanceRead
from auth import get_current_user
from services.gamification import award_xp, check_and_award_badges, update_streak, XP_REWARDS
router = APIRouter(prefix="/attendance", tags=["attendance"])
@router.post("/", response_model=AttendanceRead)
def mark_attendance(
attendance: AttendanceCreate,
session: Session = Depends(get_session),
current_user: User = Depends(get_current_user)
):
# Check if already attended
existing = session.exec(
select(Attendance)
.where(Attendance.user_id == current_user.id)
.where(Attendance.show_id == attendance.show_id)
).first()
if existing:
# Update notes if provided, or just return existing
if attendance.notes:
existing.notes = attendance.notes
session.add(existing)
session.commit()
session.refresh(existing)
return existing
db_attendance = Attendance(**attendance.model_dump(), user_id=current_user.id)
session.add(db_attendance)
# Award XP for marking attendance
new_xp, level_up = award_xp(session, current_user, XP_REWARDS["attendance_add"], "attendance")
update_streak(session, current_user)
new_badges = check_and_award_badges(session, current_user)
session.commit()
session.refresh(db_attendance)
return db_attendance
@router.delete("/{show_id}")
def remove_attendance(
show_id: int,
session: Session = Depends(get_session),
current_user: User = Depends(get_current_user)
):
attendance = session.exec(
select(Attendance)
.where(Attendance.user_id == current_user.id)
.where(Attendance.show_id == show_id)
).first()
if not attendance:
raise HTTPException(status_code=404, detail="Attendance not found")
session.delete(attendance)
session.commit()
return {"ok": True}
@router.get("/me", response_model=List[AttendanceRead])
def get_my_attendance(
session: Session = Depends(get_session),
current_user: User = Depends(get_current_user)
):
return session.exec(select(Attendance).where(Attendance.user_id == current_user.id)).all()
@router.get("/show/{show_id}", response_model=List[AttendanceRead])
def get_show_attendance(
show_id: int,
session: Session = Depends(get_session),
offset: int = 0,
limit: int = 100
):
return session.exec(
select(Attendance)
.where(Attendance.show_id == show_id)
.offset(offset)
.limit(limit)
).all()
@router.get("/me/stats")
def get_my_attendance_stats(
session: Session = Depends(get_session),
current_user: User = Depends(get_current_user)
):
"""Get attendance statistics grouped by band"""
from models import Vertical
attendances = session.exec(
select(Attendance).where(Attendance.user_id == current_user.id)
).all()
total = len(attendances)
by_vertical = {}
years = set()
for a in attendances:
show = session.get(Show, a.show_id)
if not show:
continue
if show.date:
years.add(show.date.year)
vertical = session.get(Vertical, show.vertical_id)
if vertical:
if vertical.slug not in by_vertical:
by_vertical[vertical.slug] = {
"name": vertical.name,
"count": 0
}
by_vertical[vertical.slug]["count"] += 1
return {
"total_shows": total,
"by_vertical": by_vertical,
"years_attended": sorted(years, reverse=True),
"year_count": len(years)
}