update
This commit is contained in:
@@ -7,6 +7,7 @@ Jeder darf aufschreiben, was er sich wünscht. Alle sehen alle Wünsche,
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from .db import connect
|
||||
from .users import besitzer, gehoert_mir
|
||||
|
||||
SCHEMA = """
|
||||
CREATE TABLE IF NOT EXISTS features (
|
||||
@@ -20,9 +21,23 @@ 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")
|
||||
|
||||
|
||||
def alle(session_id):
|
||||
@@ -34,10 +49,15 @@ def alle(session_id):
|
||||
"LEFT JOIN users u ON u.id = s.user_id "
|
||||
"ORDER BY f.created_at DESC"
|
||||
).fetchall()
|
||||
ich = besitzer(session_id)
|
||||
return [
|
||||
{
|
||||
**{k: v for k, v in dict(r).items() if k != "session_id"},
|
||||
"mine": r["session_id"] == session_id,
|
||||
**{
|
||||
k: v
|
||||
for k, v in dict(r).items()
|
||||
if k not in ("session_id", "user_id")
|
||||
},
|
||||
"mine": gehoert_mir(r, session_id, ich),
|
||||
}
|
||||
for r in rows
|
||||
]
|
||||
@@ -50,9 +70,11 @@ def anlegen(session_id, text):
|
||||
return None
|
||||
with connect() as conn:
|
||||
conn.execute(
|
||||
"INSERT INTO features (session_id, text, created_at) VALUES (?, ?, ?)",
|
||||
"INSERT INTO features (session_id, user_id, text, created_at) "
|
||||
"VALUES (?, ?, ?, ?)",
|
||||
(
|
||||
session_id,
|
||||
besitzer(session_id),
|
||||
text,
|
||||
datetime.now(timezone.utc).isoformat(timespec="seconds"),
|
||||
),
|
||||
|
||||
@@ -15,11 +15,12 @@ import uuid
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from .db import connect
|
||||
from .users import besitzer
|
||||
|
||||
SCHEMA = """
|
||||
CREATE TABLE IF NOT EXISTS suggestions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
session_id TEXT NOT NULL UNIQUE,
|
||||
session_id TEXT NOT NULL,
|
||||
movie_id INTEGER NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
year INTEGER,
|
||||
@@ -37,9 +38,67 @@ 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)
|
||||
|
||||
|
||||
def neue_session():
|
||||
@@ -49,15 +108,31 @@ def neue_session():
|
||||
def alle():
|
||||
with connect() as conn:
|
||||
rows = conn.execute(
|
||||
"SELECT * FROM suggestions ORDER BY created_at ASC"
|
||||
# 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 [dict(r) for r in rows]
|
||||
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
|
||||
]
|
||||
|
||||
|
||||
# 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)
|
||||
with connect() as conn:
|
||||
row = conn.execute(
|
||||
"SELECT * FROM suggestions WHERE session_id = ?", (session_id,)
|
||||
f"SELECT * FROM suggestions WHERE {WEM}", (ich, ich, session_id)
|
||||
).fetchone()
|
||||
return dict(row) if row else None
|
||||
|
||||
@@ -66,6 +141,7 @@ 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"),
|
||||
@@ -77,9 +153,10 @@ def anlegen(session_id, movie, name=None):
|
||||
with connect() as conn:
|
||||
conn.execute(
|
||||
"INSERT INTO suggestions "
|
||||
"(session_id, movie_id, title, year, poster_path, name, created_at) "
|
||||
"VALUES (:session_id, :movie_id, :title, :year, :poster_path, "
|
||||
":name, :created_at)",
|
||||
"(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:
|
||||
@@ -89,8 +166,7 @@ def anlegen(session_id, movie, name=None):
|
||||
|
||||
def ersetzen(session_id, movie, name=None):
|
||||
"""Wer schon vorgeschlagen hat, darf seinen Vorschlag ändern."""
|
||||
with connect() as conn:
|
||||
conn.execute("DELETE FROM suggestions WHERE session_id = ?", (session_id,))
|
||||
loeschen(session_id)
|
||||
return anlegen(session_id, movie, name)
|
||||
|
||||
|
||||
@@ -122,9 +198,10 @@ def ziehen():
|
||||
|
||||
|
||||
def loeschen(session_id):
|
||||
ich = besitzer(session_id)
|
||||
with connect() as conn:
|
||||
return conn.execute(
|
||||
"DELETE FROM suggestions WHERE session_id = ?", (session_id,)
|
||||
f"DELETE FROM suggestions WHERE {WEM}", (ich, ich, session_id)
|
||||
).rowcount
|
||||
|
||||
|
||||
|
||||
@@ -106,6 +106,28 @@ def aktuell(session_id):
|
||||
return dict(row) if row else None
|
||||
|
||||
|
||||
def besitzer(session_id):
|
||||
"""Die user_id dieser Sitzung, oder None ohne gewählten Namen.
|
||||
|
||||
Sammlungen, Vorschläge und Wertungen werden damit beschriftet. Ein
|
||||
Namenswechsel im selben Browser lässt die alten Einträge dann in Ruhe —
|
||||
sie gehören weiter dem Namen, unter dem sie entstanden sind.
|
||||
"""
|
||||
with connect() as conn:
|
||||
row = conn.execute(
|
||||
"SELECT user_id FROM user_sessions WHERE session_id = ?",
|
||||
(session_id,),
|
||||
).fetchone()
|
||||
return row["user_id"] if row else None
|
||||
|
||||
|
||||
def gehoert_mir(zeile, session_id, user_id):
|
||||
"""Ein Eintrag gehört mir, wenn der Name passt — sonst die Sitzung."""
|
||||
if zeile["user_id"] is not None or user_id is not None:
|
||||
return zeile["user_id"] == user_id
|
||||
return zeile["session_id"] == session_id
|
||||
|
||||
|
||||
def name_von(session_id):
|
||||
"""Nur der Name, für Vorschläge und Wertungen."""
|
||||
benutzer = aktuell(session_id)
|
||||
|
||||
@@ -11,6 +11,7 @@ auch wenn der Film später aus dem Katalog fällt.
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from .db import connect
|
||||
from .users import besitzer, gehoert_mir
|
||||
|
||||
SCHEMA = """
|
||||
CREATE TABLE IF NOT EXISTS watched (
|
||||
@@ -37,9 +38,23 @@ CREATE TABLE IF NOT EXISTS ratings (
|
||||
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")
|
||||
# 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:
|
||||
@@ -59,10 +74,11 @@ def alle(session_id):
|
||||
"SELECT * FROM watched ORDER BY watched_at DESC, id DESC"
|
||||
).fetchall()
|
||||
stimmen = conn.execute(
|
||||
"SELECT id, watched_id, session_id, stars, name FROM ratings "
|
||||
"SELECT id, watched_id, session_id, user_id, stars, name FROM ratings "
|
||||
"ORDER BY created_at ASC"
|
||||
).fetchall()
|
||||
|
||||
ich = besitzer(session_id)
|
||||
je_film = {}
|
||||
for s in stimmen:
|
||||
je_film.setdefault(s["watched_id"], []).append(s)
|
||||
@@ -81,7 +97,11 @@ def alle(session_id):
|
||||
"average": round(sum(werte) / len(werte), 1) if werte else None,
|
||||
"count": len(werte),
|
||||
"mine": next(
|
||||
(s["stars"] for s in eigene if s["session_id"] == session_id),
|
||||
(
|
||||
s["stars"]
|
||||
for s in eigene
|
||||
if gehoert_mir(s, session_id, ich)
|
||||
),
|
||||
None,
|
||||
),
|
||||
}
|
||||
@@ -145,17 +165,25 @@ def bewerten(watched_id, session_id, stars, name=None):
|
||||
).fetchone()
|
||||
if not da:
|
||||
return None
|
||||
# Eine Stimme je Sitzung — eine neue Bewertung ersetzt die alte
|
||||
# Eine Stimme je Person — die eigene alte fliegt vorher raus.
|
||||
# Mit Namen zählt der Name, ohne Namen die Sitzung.
|
||||
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 = ?))",
|
||||
(watched_id, ich, ich, session_id),
|
||||
)
|
||||
conn.execute(
|
||||
"INSERT INTO ratings "
|
||||
"(watched_id, session_id, stars, name, created_at) "
|
||||
"VALUES (?, ?, ?, ?, ?) "
|
||||
"(watched_id, session_id, user_id, stars, name, created_at) "
|
||||
"VALUES (?, ?, ?, ?, ?, ?) "
|
||||
"ON CONFLICT (watched_id, session_id) DO UPDATE SET "
|
||||
"stars = excluded.stars, name = excluded.name, "
|
||||
"created_at = excluded.created_at",
|
||||
"user_id = excluded.user_id, stars = excluded.stars, "
|
||||
"name = excluded.name, created_at = excluded.created_at",
|
||||
(
|
||||
watched_id,
|
||||
session_id,
|
||||
ich,
|
||||
stars,
|
||||
(name or "").strip()[:40] or None,
|
||||
_jetzt(),
|
||||
|
||||
@@ -8,10 +8,12 @@ Titel und Poster kommen mit in die Zeile. Ein gemerkter Film soll lesbar
|
||||
bleiben, auch wenn er nie im Katalog stand oder Netflix ihn abgibt.
|
||||
"""
|
||||
|
||||
import json
|
||||
import sqlite3
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from .db import connect
|
||||
from .users import besitzer, gehoert_mir
|
||||
|
||||
SCHEMA = """
|
||||
CREATE TABLE IF NOT EXISTS wishlist (
|
||||
@@ -27,24 +29,47 @@ CREATE TABLE IF NOT EXISTS wishlist (
|
||||
"""
|
||||
|
||||
|
||||
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")
|
||||
|
||||
|
||||
def alle(session_id):
|
||||
"""Alle Sammlungen, jede mit dem Namen ihres Besitzers."""
|
||||
with connect() as conn:
|
||||
rows = conn.execute(
|
||||
"SELECT w.*, u.name FROM wishlist w "
|
||||
# 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 movies m ON m.id = w.movie_id "
|
||||
"ORDER BY w.created_at DESC"
|
||||
).fetchall()
|
||||
ich = besitzer(session_id)
|
||||
return [
|
||||
{
|
||||
**{k: v for k, v in dict(r).items() if k != "session_id"},
|
||||
"mine": r["session_id"] == session_id,
|
||||
**{
|
||||
k: v
|
||||
for k, v in dict(r).items()
|
||||
if k not in ("session_id", "user_id", "genres")
|
||||
},
|
||||
"genres": json.loads(r["genres"]) if r["genres"] else [],
|
||||
"mine": gehoert_mir(r, session_id, ich),
|
||||
}
|
||||
for r in rows
|
||||
]
|
||||
@@ -56,10 +81,11 @@ def merken(session_id, movie):
|
||||
with connect() as conn:
|
||||
conn.execute(
|
||||
"INSERT INTO wishlist "
|
||||
"(session_id, movie_id, title, year, poster_path, created_at) "
|
||||
"VALUES (?, ?, ?, ?, ?, ?)",
|
||||
"(session_id, user_id, movie_id, title, year, poster_path, "
|
||||
"created_at) VALUES (?, ?, ?, ?, ?, ?, ?)",
|
||||
(
|
||||
session_id,
|
||||
besitzer(session_id),
|
||||
movie["id"],
|
||||
movie["title"],
|
||||
movie.get("year"),
|
||||
@@ -72,20 +98,27 @@ 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:
|
||||
return conn.execute(
|
||||
"DELETE FROM wishlist WHERE session_id = ? AND movie_id = ?",
|
||||
(session_id, movie_id),
|
||||
f"DELETE FROM wishlist WHERE {WEM} AND movie_id = ?",
|
||||
(ich, ich, session_id, movie_id),
|
||||
).rowcount
|
||||
|
||||
|
||||
def ids(session_id):
|
||||
"""movie_ids der gemerkten Filme, für die Detailansicht."""
|
||||
ich = besitzer(session_id)
|
||||
with connect() as conn:
|
||||
return [
|
||||
r["movie_id"]
|
||||
for r in conn.execute(
|
||||
"SELECT movie_id FROM wishlist WHERE session_id = ?", (session_id,)
|
||||
f"SELECT movie_id FROM wishlist WHERE {WEM}",
|
||||
(ich, ich, session_id),
|
||||
)
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user