1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
from fastapi import APIRouter, Depends, Request
from fastapi.responses import HTMLResponse
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/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
|