from fastapi import APIRouter, Depends, Query, Request from fastapi.responses import HTMLResponse, JSONResponse from fastapi.templating import Jinja2Templates from sqlalchemy.orm import Session from database import get_db from models import Film from services.film_people import split_credit_names router = APIRouter(tags=["profile"]) templates = Jinja2Templates(directory="templates") templates.env.globals.update(split_credit_names=split_credit_names) def _diary_films(db: Session) -> list[Film]: return ( db.query(Film) .filter(Film.shelf == "diary", Film.date_watched.is_not(None)) .order_by(Film.date_watched.desc()) .all() ) def _build_profile_payload(films: list[Film]) -> dict: from collections import Counter from services.countries import split_country_names from services.film_people import split_credit_names if not films: return { "total_watched": 0, "average_stars": 0, "most_watched_directors": [], "star_distribution": [{"stars": stars, "count": 0} for stars in (0, 1, 2, 3)], "films_per_country": [], "recent_films": [], } countries = Counter() directors = Counter() star_counts = Counter({0: 0, 1: 0, 2: 0, 3: 0}) total_stars = 0 valid_ratings = 0 for film in films: country_names = split_country_names(film.country) countries.update(country_names) directors.update(split_credit_names(film.director)) stars = film.stars if film.stars in {0, 1, 2, 3} else 0 star_counts[stars] += 1 if film.stars in {0, 1, 2, 3}: total_stars += film.stars valid_ratings += 1 total_watched = len(films) average_stars = round(total_stars / valid_ratings, 1) if valid_ratings else 0 return { "total_watched": total_watched, "average_stars": average_stars, "most_watched_directors": [ {"director": director, "count": count} for director, count in sorted(directors.items(), key=lambda item: (-item[1], item[0]))[:5] ], "star_distribution": [{"stars": stars, "count": star_counts[stars]} for stars in (0, 1, 2, 3)], "films_per_country": [ {"country": country, "count": count} for country, count in sorted(countries.items(), key=lambda item: (-item[1], item[0]))[:10] ], "recent_films": [ { "id": film.id, "title": film.title, "poster_url": film.poster_url, "year": film.year, "director": split_credit_names(film.director)[0] if film.director else None, "date_watched": film.date_watched.isoformat() if film.date_watched else None, "stars": film.stars, } for film in films[:4] ], } @router.get("/tyler") async def public_profile(request: Request, db: Session = Depends(get_db)): films = _diary_films(db) payload = _build_profile_payload(films) return templates.TemplateResponse( request=request, name="profile.html", context={"request": request, **payload}, ) @router.get("/tyler/api/recent") async def public_api_recent( db: Session = Depends(get_db), limit: int = Query(default=10, ge=1, le=100), ): films = _diary_films(db)[:limit] return JSONResponse([ { "title": f.title, "year": f.year, "director": f.director, "date_watched": f.date_watched.isoformat() if f.date_watched else None, "stars": f.stars, "poster_url": f.poster_url, "tmdb_id": f.tmdb_id, } for f in films ]) @router.get("/tyler/films/partial") async def public_profile_films_partial( request: Request, offset: int = 0, limit: int = 20, db: Session = Depends(get_db), ): films = _diary_films(db) page = films[offset : offset + limit] has_more = (offset + limit) < len(films) html = templates.get_template("_public_feed_partial.html").render( request=request, films=page, ) response = HTMLResponse(html) response.headers["X-Has-More"] = "true" if has_more else "false" return response