156 lines
4.5 KiB
Python
156 lines
4.5 KiB
Python
"""Filmvorschläge fürs Rad.
|
|
|
|
Jede Sitzung darf einen Film vorschlagen. Die Sitzung erkennt ein Cookie —
|
|
ohne Anmeldung geht es nicht genauer, und für einen Filmabend im Freundeskreis
|
|
reicht es.
|
|
|
|
Titel, Jahr und Poster werden mitgespeichert. So bleibt ein Vorschlag
|
|
lesbar, auch wenn der Film nie im Katalog war oder Netflix ihn abgibt.
|
|
"""
|
|
|
|
import json
|
|
import random
|
|
import sqlite3
|
|
import uuid
|
|
from datetime import datetime, timezone
|
|
|
|
from . import users
|
|
from .db import connect
|
|
from .users import WEM, besitzer
|
|
|
|
SCHEMA = """
|
|
CREATE TABLE IF NOT EXISTS suggestions (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
session_id TEXT NOT NULL,
|
|
user_id INTEGER,
|
|
movie_id INTEGER NOT NULL,
|
|
title TEXT NOT NULL,
|
|
year INTEGER,
|
|
poster_path TEXT,
|
|
name TEXT,
|
|
created_at TEXT NOT NULL
|
|
);
|
|
"""
|
|
|
|
COOKIE = "filmabend_sid"
|
|
|
|
# So lange läuft die Animation im Browser. Der Server merkt sich das
|
|
# Ergebnis mit, damit eine Seite, die mitten im Dreh geladen wird, es
|
|
# trotzdem bekommt.
|
|
DREH_DAUER = 4.2
|
|
|
|
|
|
def init():
|
|
with connect() as conn:
|
|
conn.executescript(SCHEMA)
|
|
# Ein Vorschlag je Person
|
|
users.je_person(conn, "suggestions", SCHEMA)
|
|
|
|
|
|
def neue_session():
|
|
return uuid.uuid4().hex
|
|
|
|
|
|
def alle():
|
|
with connect() as conn:
|
|
rows = conn.execute(
|
|
# Laufzeit und Genres stehen im Katalog. Ein Vorschlag von
|
|
# außerhalb hat sie nicht — die Liste zeigt dann nur den Titel.
|
|
"SELECT s.*, m.runtime, m.genres FROM suggestions s "
|
|
"LEFT JOIN movies m ON m.id = s.movie_id "
|
|
"ORDER BY s.created_at ASC"
|
|
).fetchall()
|
|
return [
|
|
{
|
|
**{k: v for k, v in dict(r).items() if k != "genres"},
|
|
"genres": json.loads(r["genres"]) if r["genres"] else [],
|
|
}
|
|
for r in rows
|
|
]
|
|
|
|
|
|
def von_session(session_id):
|
|
"""Der eigene Vorschlag — auch aus einem anderen Browser."""
|
|
ich = besitzer(session_id)
|
|
with connect() as conn:
|
|
row = conn.execute(
|
|
f"SELECT * FROM suggestions WHERE {WEM}", (ich, ich, session_id)
|
|
).fetchone()
|
|
return dict(row) if row else None
|
|
|
|
|
|
def anlegen(session_id, movie, name=None):
|
|
"""Gibt den Vorschlag zurück, oder None wenn die Sitzung schon einen hat."""
|
|
eintrag = {
|
|
"session_id": session_id,
|
|
"user_id": besitzer(session_id),
|
|
"movie_id": movie["id"],
|
|
"title": movie["title"],
|
|
"year": movie.get("year"),
|
|
"poster_path": movie.get("poster_path"),
|
|
"name": (name or "").strip()[:40] or None,
|
|
"created_at": datetime.now(timezone.utc).isoformat(timespec="seconds"),
|
|
}
|
|
try:
|
|
with connect() as conn:
|
|
conn.execute(
|
|
"INSERT INTO suggestions "
|
|
"(session_id, user_id, movie_id, title, year, poster_path, "
|
|
"name, created_at) "
|
|
"VALUES (:session_id, :user_id, :movie_id, :title, :year, "
|
|
":poster_path, :name, :created_at)",
|
|
eintrag,
|
|
)
|
|
except sqlite3.IntegrityError:
|
|
return None
|
|
return von_session(session_id)
|
|
|
|
|
|
def ersetzen(session_id, movie, name=None):
|
|
"""Wer schon vorgeschlagen hat, darf seinen Vorschlag ändern."""
|
|
loeschen(session_id)
|
|
return anlegen(session_id, movie, name)
|
|
|
|
|
|
def ziehen():
|
|
"""Server lost aus, damit alle Seiten dasselbe Ergebnis sehen.
|
|
|
|
Der Zielwinkel gehört dazu: sonst dreht jede Seite woanders hin und
|
|
der Zeiger stünde am Ende auf einem anderen Segment.
|
|
"""
|
|
eintraege = alle()
|
|
if not eintraege:
|
|
return None
|
|
|
|
treffer = random.randrange(len(eintraege))
|
|
schritt = 360 / len(eintraege)
|
|
runden = 5 + random.randrange(3)
|
|
# Der Zeiger steht oben, also muss die Mitte des Zielsegments dorthin
|
|
winkel = runden * 360 + (360 - (treffer * schritt + schritt / 2))
|
|
|
|
return {
|
|
"index": treffer,
|
|
"winkel": winkel,
|
|
"dauer": DREH_DAUER,
|
|
"gewinner": {
|
|
k: v
|
|
for k, v in eintraege[treffer].items()
|
|
if k not in ("session_id", "user_id")
|
|
},
|
|
"anzahl": len(eintraege),
|
|
}
|
|
|
|
|
|
def loeschen(session_id):
|
|
ich = besitzer(session_id)
|
|
with connect() as conn:
|
|
return conn.execute(
|
|
f"DELETE FROM suggestions WHERE {WEM}", (ich, ich, session_id)
|
|
).rowcount
|
|
|
|
|
|
def alles_loeschen():
|
|
"""Host setzt die Runde zurück."""
|
|
with connect() as conn:
|
|
return conn.execute("DELETE FROM suggestions").rowcount
|