update
This commit is contained in:
@@ -6,6 +6,7 @@ Jeder darf aufschreiben, was er sich wünscht. Alle sehen alle Wünsche,
|
||||
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from . import users
|
||||
from .db import connect
|
||||
from .users import besitzer, gehoert_mir
|
||||
|
||||
@@ -13,6 +14,7 @@ SCHEMA = """
|
||||
CREATE TABLE IF NOT EXISTS features (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
session_id TEXT NOT NULL,
|
||||
user_id INTEGER,
|
||||
text TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL
|
||||
);
|
||||
@@ -21,23 +23,10 @@ CREATE TABLE IF NOT EXISTS features (
|
||||
MAX_LAENGE = 500
|
||||
|
||||
|
||||
def _spalte_user_id(conn, tabelle):
|
||||
"""Nachrüsten und aus der Sitzung füllen — einmalig je Datenbank."""
|
||||
spalten = {r[1] for r in conn.execute(f"PRAGMA table_info({tabelle})")}
|
||||
if "user_id" in spalten:
|
||||
return
|
||||
conn.execute(f"ALTER TABLE {tabelle} ADD COLUMN user_id INTEGER")
|
||||
conn.execute(
|
||||
f"UPDATE {tabelle} SET user_id = ("
|
||||
" SELECT user_id FROM user_sessions s WHERE s.session_id = "
|
||||
f" {tabelle}.session_id)"
|
||||
)
|
||||
|
||||
|
||||
def init():
|
||||
with connect() as conn:
|
||||
conn.executescript(SCHEMA)
|
||||
_spalte_user_id(conn, "features")
|
||||
users.user_id_nachruesten(conn, "features")
|
||||
|
||||
|
||||
def alle(session_id):
|
||||
@@ -45,8 +34,7 @@ def alle(session_id):
|
||||
with connect() as conn:
|
||||
rows = conn.execute(
|
||||
"SELECT f.*, u.name FROM features f "
|
||||
"LEFT JOIN user_sessions s ON s.session_id = f.session_id "
|
||||
"LEFT JOIN users u ON u.id = s.user_id "
|
||||
"LEFT JOIN users u ON u.id = f.user_id "
|
||||
"ORDER BY f.created_at DESC"
|
||||
).fetchall()
|
||||
ich = besitzer(session_id)
|
||||
|
||||
@@ -80,9 +80,9 @@ async def guarded_sync():
|
||||
@app.on_event("startup")
|
||||
async def startup():
|
||||
db.init()
|
||||
users.init()
|
||||
suggestions.init()
|
||||
watched.init()
|
||||
users.init()
|
||||
wishlist.init()
|
||||
features.init()
|
||||
scheduler.add_job(guarded_sync, "cron", hour=4, minute=30, id="daily-sync")
|
||||
@@ -294,13 +294,15 @@ def letzter():
|
||||
def liste_vorschlaege(response: Response, filmabend_sid: str | None = Cookie(None)):
|
||||
sid = sitzung(response, filmabend_sid)
|
||||
eintraege = suggestions.alle()
|
||||
ich = users.besitzer(sid)
|
||||
return {
|
||||
"items": [
|
||||
{k: v for k, v in e.items() if k != "session_id"} for e in eintraege
|
||||
{k: v for k, v in e.items() if k not in ("session_id", "user_id")}
|
||||
for e in eintraege
|
||||
],
|
||||
# Damit die Oberfläche den eigenen Vorschlag hervorheben kann
|
||||
"mine": next(
|
||||
(e["id"] for e in eintraege if e["session_id"] == sid),
|
||||
(e["id"] for e in eintraege if users.gehoert_mir(e, sid, ich)),
|
||||
None,
|
||||
),
|
||||
}
|
||||
@@ -317,7 +319,9 @@ async def vorschlagen(
|
||||
# Ein zweiter Vorschlag ersetzt den ersten, statt abgelehnt zu werden
|
||||
eintrag = suggestions.ersetzen(sid, movie, users.name_von(sid))
|
||||
events.senden("suggestions")
|
||||
return {k: v for k, v in eintrag.items() if k != "session_id"}
|
||||
return {
|
||||
k: v for k, v in eintrag.items() if k not in ("session_id", "user_id")
|
||||
}
|
||||
|
||||
|
||||
@app.delete("/api/suggestions")
|
||||
|
||||
@@ -14,13 +14,15 @@ import sqlite3
|
||||
import uuid
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from . import users
|
||||
from .db import connect
|
||||
from .users import besitzer
|
||||
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,
|
||||
@@ -38,67 +40,11 @@ COOKIE = "filmabend_sid"
|
||||
DREH_DAUER = 4.2
|
||||
|
||||
|
||||
# Ein Vorschlag je Person. Früher stand das UNIQUE auf der Sitzung — dann
|
||||
# blockierte der Vorschlag des einen Namens den des nächsten, sobald beide
|
||||
# denselben Browser benutzten.
|
||||
INDIZES = """
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS suggestions_je_benutzer
|
||||
ON suggestions (user_id) WHERE user_id IS NOT NULL;
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS suggestions_je_sitzung
|
||||
ON suggestions (session_id) WHERE user_id IS NULL;
|
||||
"""
|
||||
|
||||
|
||||
def _unique_loesen(conn):
|
||||
"""Das alte UNIQUE steckt in der Spaltendefinition — Tabelle neu bauen."""
|
||||
sql = conn.execute(
|
||||
"SELECT sql FROM sqlite_master WHERE type='table' AND name='suggestions'"
|
||||
).fetchone()
|
||||
if not sql or "UNIQUE" not in sql["sql"]:
|
||||
return
|
||||
conn.executescript(
|
||||
"""
|
||||
ALTER TABLE suggestions RENAME TO suggestions_alt;
|
||||
CREATE TABLE 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
|
||||
);
|
||||
INSERT INTO suggestions
|
||||
(id, session_id, user_id, movie_id, title, year, poster_path,
|
||||
name, created_at)
|
||||
SELECT id, session_id, user_id, movie_id, title, year, poster_path,
|
||||
name, created_at FROM suggestions_alt;
|
||||
DROP TABLE suggestions_alt;
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def _spalte_user_id(conn, tabelle):
|
||||
"""Nachrüsten und aus der Sitzung füllen — einmalig je Datenbank."""
|
||||
spalten = {r[1] for r in conn.execute(f"PRAGMA table_info({tabelle})")}
|
||||
if "user_id" in spalten:
|
||||
return
|
||||
conn.execute(f"ALTER TABLE {tabelle} ADD COLUMN user_id INTEGER")
|
||||
conn.execute(
|
||||
f"UPDATE {tabelle} SET user_id = ("
|
||||
" SELECT user_id FROM user_sessions s WHERE s.session_id = "
|
||||
f" {tabelle}.session_id)"
|
||||
)
|
||||
|
||||
|
||||
def init():
|
||||
with connect() as conn:
|
||||
conn.executescript(SCHEMA)
|
||||
_spalte_user_id(conn, "suggestions")
|
||||
_unique_loesen(conn)
|
||||
conn.executescript(INDIZES)
|
||||
# Ein Vorschlag je Person
|
||||
users.je_person(conn, "suggestions", SCHEMA)
|
||||
|
||||
|
||||
def neue_session():
|
||||
@@ -123,10 +69,6 @@ def alle():
|
||||
]
|
||||
|
||||
|
||||
# Mit Namen zählt der Name, ohne Namen die Sitzung
|
||||
WEM = "(user_id = ? OR (? IS NULL AND user_id IS NULL AND session_id = ?))"
|
||||
|
||||
|
||||
def von_session(session_id):
|
||||
"""Der eigene Vorschlag — auch aus einem anderen Browser."""
|
||||
ich = besitzer(session_id)
|
||||
@@ -191,7 +133,9 @@ def ziehen():
|
||||
"winkel": winkel,
|
||||
"dauer": DREH_DAUER,
|
||||
"gewinner": {
|
||||
k: v for k, v in eintraege[treffer].items() if k != "session_id"
|
||||
k: v
|
||||
for k, v in eintraege[treffer].items()
|
||||
if k not in ("session_id", "user_id")
|
||||
},
|
||||
"anzahl": len(eintraege),
|
||||
}
|
||||
|
||||
@@ -128,6 +128,63 @@ def gehoert_mir(zeile, session_id, user_id):
|
||||
return zeile["session_id"] == session_id
|
||||
|
||||
|
||||
# Dasselbe als WHERE-Klausel. Parameter: (user_id, user_id, session_id)
|
||||
WEM = "(user_id = ? OR (? IS NULL AND user_id IS NULL AND session_id = ?))"
|
||||
|
||||
|
||||
def user_id_nachruesten(conn, tabelle):
|
||||
"""Spalte ergänzen und aus der Sitzung füllen — einmalig je Datenbank."""
|
||||
spalten = {r[1] for r in conn.execute(f"PRAGMA table_info({tabelle})")}
|
||||
if "user_id" in spalten:
|
||||
return
|
||||
conn.execute(f"ALTER TABLE {tabelle} ADD COLUMN user_id INTEGER")
|
||||
conn.execute(
|
||||
f"UPDATE {tabelle} SET user_id = ("
|
||||
" SELECT user_id FROM user_sessions s WHERE s.session_id = "
|
||||
f" {tabelle}.session_id)"
|
||||
)
|
||||
|
||||
|
||||
def je_person(conn, tabelle, schema, *schluessel):
|
||||
"""Eindeutig je Person statt je Sitzung.
|
||||
|
||||
Früher stand das UNIQUE auf der Sitzung — dann blockierte der Eintrag
|
||||
des einen Namens den des nächsten, sobald beide denselben Browser
|
||||
benutzten. Mit Namen zählt jetzt der Name, ohne Namen die Sitzung.
|
||||
|
||||
Das alte UNIQUE steckt in der Spaltendefinition und lässt sich nicht
|
||||
ändern — die Tabelle wird dafür nach `schema` neu gebaut. Hat ein Name
|
||||
aus mehreren Browsern doppelt eingetragen, bleibt der jüngste Eintrag.
|
||||
"""
|
||||
user_id_nachruesten(conn, tabelle)
|
||||
alt = conn.execute(
|
||||
"SELECT sql FROM sqlite_master WHERE type='table' AND name=?", (tabelle,)
|
||||
).fetchone()
|
||||
if alt and "UNIQUE" in alt["sql"]:
|
||||
spalten = ", ".join(
|
||||
r[1] for r in conn.execute(f"PRAGMA table_info({tabelle})")
|
||||
)
|
||||
conn.executescript(
|
||||
f"ALTER TABLE {tabelle} RENAME TO {tabelle}_alt;"
|
||||
f"{schema}"
|
||||
f"INSERT INTO {tabelle} ({spalten}) "
|
||||
f"SELECT {spalten} FROM {tabelle}_alt;"
|
||||
f"DROP TABLE {tabelle}_alt;"
|
||||
)
|
||||
mit_name = ", ".join((*schluessel, "user_id"))
|
||||
ohne_name = ", ".join((*schluessel, "session_id"))
|
||||
conn.execute(
|
||||
f"DELETE FROM {tabelle} WHERE user_id IS NOT NULL AND id NOT IN "
|
||||
f"(SELECT MAX(id) FROM {tabelle} GROUP BY {mit_name})"
|
||||
)
|
||||
conn.executescript(
|
||||
f"CREATE UNIQUE INDEX IF NOT EXISTS {tabelle}_je_benutzer "
|
||||
f"ON {tabelle} ({mit_name}) WHERE user_id IS NOT NULL;"
|
||||
f"CREATE UNIQUE INDEX IF NOT EXISTS {tabelle}_je_sitzung "
|
||||
f"ON {tabelle} ({ohne_name}) WHERE user_id IS NULL;"
|
||||
)
|
||||
|
||||
|
||||
def name_von(session_id):
|
||||
"""Nur der Name, für Vorschläge und Wertungen."""
|
||||
benutzer = aktuell(session_id)
|
||||
|
||||
@@ -10,8 +10,9 @@ auch wenn der Film später aus dem Katalog fällt.
|
||||
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from . import users
|
||||
from .db import connect
|
||||
from .users import besitzer, gehoert_mir
|
||||
from .users import WEM, besitzer, gehoert_mir
|
||||
|
||||
SCHEMA = """
|
||||
CREATE TABLE IF NOT EXISTS watched (
|
||||
@@ -23,38 +24,28 @@ CREATE TABLE IF NOT EXISTS watched (
|
||||
watched_at TEXT NOT NULL,
|
||||
hidden INTEGER NOT NULL DEFAULT 0
|
||||
);
|
||||
"""
|
||||
|
||||
RATINGS = """
|
||||
CREATE TABLE IF NOT EXISTS ratings (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
watched_id INTEGER NOT NULL,
|
||||
session_id TEXT NOT NULL,
|
||||
user_id INTEGER,
|
||||
stars INTEGER NOT NULL,
|
||||
name TEXT,
|
||||
created_at TEXT NOT NULL,
|
||||
UNIQUE (watched_id, session_id)
|
||||
created_at TEXT NOT NULL
|
||||
);
|
||||
"""
|
||||
|
||||
MAX_STERNE = 5
|
||||
|
||||
|
||||
def _spalte_user_id(conn, tabelle):
|
||||
"""Nachrüsten und aus der Sitzung füllen — einmalig je Datenbank."""
|
||||
spalten = {r[1] for r in conn.execute(f"PRAGMA table_info({tabelle})")}
|
||||
if "user_id" in spalten:
|
||||
return
|
||||
conn.execute(f"ALTER TABLE {tabelle} ADD COLUMN user_id INTEGER")
|
||||
conn.execute(
|
||||
f"UPDATE {tabelle} SET user_id = ("
|
||||
" SELECT user_id FROM user_sessions s WHERE s.session_id = "
|
||||
f" {tabelle}.session_id)"
|
||||
)
|
||||
|
||||
|
||||
def init():
|
||||
with connect() as conn:
|
||||
conn.executescript(SCHEMA)
|
||||
_spalte_user_id(conn, "ratings")
|
||||
conn.executescript(SCHEMA + RATINGS)
|
||||
# Eine Stimme je Person und Film
|
||||
users.je_person(conn, "ratings", RATINGS, "watched_id")
|
||||
# Die Tabelle stand vor der Spalte schon in bestehenden Datenbanken
|
||||
spalten = {r["name"] for r in conn.execute("PRAGMA table_info(watched)")}
|
||||
if "hidden" not in spalten:
|
||||
@@ -165,21 +156,16 @@ def bewerten(watched_id, session_id, stars, name=None):
|
||||
).fetchone()
|
||||
if not da:
|
||||
return None
|
||||
# Eine Stimme je Person — die eigene alte fliegt vorher raus.
|
||||
# Mit Namen zählt der Name, ohne Namen die Sitzung.
|
||||
# Eine Stimme je Person — die eigene alte fliegt vorher raus
|
||||
ich = besitzer(session_id)
|
||||
conn.execute(
|
||||
"DELETE FROM ratings WHERE watched_id = ? AND "
|
||||
"(user_id = ? OR (? IS NULL AND user_id IS NULL AND session_id = ?))",
|
||||
f"DELETE FROM ratings WHERE watched_id = ? AND {WEM}",
|
||||
(watched_id, ich, ich, session_id),
|
||||
)
|
||||
conn.execute(
|
||||
"INSERT INTO ratings "
|
||||
"(watched_id, session_id, user_id, stars, name, created_at) "
|
||||
"VALUES (?, ?, ?, ?, ?, ?) "
|
||||
"ON CONFLICT (watched_id, session_id) DO UPDATE SET "
|
||||
"user_id = excluded.user_id, stars = excluded.stars, "
|
||||
"name = excluded.name, created_at = excluded.created_at",
|
||||
"VALUES (?, ?, ?, ?, ?, ?)",
|
||||
(
|
||||
watched_id,
|
||||
session_id,
|
||||
|
||||
@@ -12,40 +12,29 @@ import json
|
||||
import sqlite3
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from . import users
|
||||
from .db import connect
|
||||
from .users import besitzer, gehoert_mir
|
||||
from .users import WEM, besitzer, gehoert_mir
|
||||
|
||||
SCHEMA = """
|
||||
CREATE TABLE IF NOT EXISTS wishlist (
|
||||
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,
|
||||
created_at TEXT NOT NULL,
|
||||
UNIQUE (session_id, movie_id)
|
||||
created_at TEXT NOT NULL
|
||||
);
|
||||
"""
|
||||
|
||||
|
||||
def _spalte_user_id(conn, tabelle):
|
||||
"""Nachrüsten und aus der Sitzung füllen — einmalig je Datenbank."""
|
||||
spalten = {r[1] for r in conn.execute(f"PRAGMA table_info({tabelle})")}
|
||||
if "user_id" in spalten:
|
||||
return
|
||||
conn.execute(f"ALTER TABLE {tabelle} ADD COLUMN user_id INTEGER")
|
||||
conn.execute(
|
||||
f"UPDATE {tabelle} SET user_id = ("
|
||||
" SELECT user_id FROM user_sessions s WHERE s.session_id = "
|
||||
f" {tabelle}.session_id)"
|
||||
)
|
||||
|
||||
|
||||
def init():
|
||||
with connect() as conn:
|
||||
conn.executescript(SCHEMA)
|
||||
_spalte_user_id(conn, "wishlist")
|
||||
# Jeder Film einmal je Person
|
||||
users.je_person(conn, "wishlist", SCHEMA, "movie_id")
|
||||
|
||||
|
||||
def alle(session_id):
|
||||
@@ -55,8 +44,7 @@ def alle(session_id):
|
||||
# Genres kommen aus dem Katalog. Ein Film, der nie darin stand,
|
||||
# hat keine — das Rad behandelt ihn dann als "nicht Horror".
|
||||
"SELECT w.*, u.name, m.genres FROM wishlist w "
|
||||
"LEFT JOIN user_sessions s ON s.session_id = w.session_id "
|
||||
"LEFT JOIN users u ON u.id = s.user_id "
|
||||
"LEFT JOIN users u ON u.id = w.user_id "
|
||||
"LEFT JOIN movies m ON m.id = w.movie_id "
|
||||
"ORDER BY w.created_at DESC"
|
||||
).fetchall()
|
||||
@@ -98,10 +86,6 @@ def merken(session_id, movie):
|
||||
return alle(session_id)
|
||||
|
||||
|
||||
# Mit Namen zählt der Name, ohne Namen die Sitzung
|
||||
WEM = "(user_id = ? OR (? IS NULL AND user_id IS NULL AND session_id = ?))"
|
||||
|
||||
|
||||
def vergessen(session_id, movie_id):
|
||||
ich = besitzer(session_id)
|
||||
with connect() as conn:
|
||||
|
||||
Reference in New Issue
Block a user