From 817f39a42c408ce70a7dd507dc2b8748fc564c12 Mon Sep 17 00:00:00 2001 From: team3 Date: Wed, 30 Sep 2026 14:53:23 +0200 Subject: [PATCH] update --- backend/app/main.py | 26 +++++ backend/app/punkte.py | 20 ++++ backend/tests/test_info.py | 22 ++++ frontend/src/App.vue | 4 + frontend/src/components/InfoTab.vue | 114 +++++++++++++++++++ frontend/src/composables/useEinstellungen.js | 1 + 6 files changed, 187 insertions(+) create mode 100644 backend/tests/test_info.py create mode 100644 frontend/src/components/InfoTab.vue diff --git a/backend/app/main.py b/backend/app/main.py index c0967cc..95d4d95 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -686,6 +686,32 @@ async def film_holen(movie_id: int): return movie +# ── Info: Bezahlen und Regeln ─────────────────────────────────────────── +INFO_MAX = 8000 + + +@app.get("/api/info") +def info(): + """Ein Markdown-Text des Hosts: PayPal, IBAN, Regeln, was man wissen + muss. Solange keiner da ist, steht die Vorlage mit den Punkteregeln.""" + return {"text": db.get_meta("info_text") or punkte.vorlage()} + + +@app.put("/api/info") +def info_setzen( + response: Response, + text: str = Body(..., embed=True), + filmabend_sid: str | None = Cookie(None), +): + sid = sitzung(response, filmabend_sid) + if not users.ist_host(sid): + raise HTTPException(status_code=403, detail="Nur für den Host") + with db.connect() as conn: + db.set_meta(conn, "info_text", text.strip()[:INFO_MAX]) + events.senden("info") + return {"text": db.get_meta("info_text")} + + @app.get("/api/features") def wuensche(response: Response, filmabend_sid: str | None = Cookie(None)): sid = sitzung(response, filmabend_sid) diff --git a/backend/app/punkte.py b/backend/app/punkte.py index 749d40c..5b21028 100644 --- a/backend/app/punkte.py +++ b/backend/app/punkte.py @@ -20,6 +20,15 @@ KOMMENTAR = 5 ANTWORT = 2 HERZ = 1 +# Für den Info-Reiter: was zählt, mit der Zahl — aus denselben Konstanten +REGELN = ( + ("Beim Filmabend dabei", SCHAUEN), + ("Film mit Sternen bewertet", BEWERTEN), + ("Kommentar geschrieben", KOMMENTAR), + ("Auf einen Kommentar geantwortet", ANTWORT), + ("Herz vergeben", HERZ), +) + ZAEHLUNGEN = ( (SCHAUEN, "SELECT user_id, COUNT(*) AS n FROM watched_dabei GROUP BY user_id"), ( @@ -46,6 +55,17 @@ ZAEHLUNGEN = ( ) +def vorlage(): + """Der Info-Text, solange der Host keinen eigenen geschrieben hat — + die Regeln mit ihren Zahlen und die ersten Levelstufen, als Markdown.""" + zeilen = ["## Erfahrung", ""] + zeilen += [f"- {was}: **+{p}**" for was, p in REGELN] + zeilen += ["", "Je Film zählt jede Art nur einmal.", "", "## Level", ""] + stufen = ", ".join(f"Level {stufe} ab {stufe * (stufe - 1) // 2}" for stufe in range(2, 8)) + zeilen.append(f"{stufen} … Jedes weitere Level kostet einen Punkt mehr als das davor.") + return "\n".join(zeilen) + + def level(punkte): """Größtes L mit L·(L−1)/2 ≤ Punkte. Null Punkte sind Level 1.""" stufe = (1 + isqrt(1 + 8 * punkte)) // 2 diff --git a/backend/tests/test_info.py b/backend/tests/test_info.py new file mode 100644 index 0000000..7e166bb --- /dev/null +++ b/backend/tests/test_info.py @@ -0,0 +1,22 @@ +"""Der Info-Reiter: ein Markdown-Text des Hosts, vorbelegt mit den Regeln.""" + +from app import punkte + + +def test_vorlage_traegt_die_regeln_aus_dem_code(client): + text = client.get("/api/info").json()["text"] + assert text == punkte.vorlage() + assert f"- Beim Filmabend dabei: **+{punkte.SCHAUEN}**" in text + assert f"- Herz vergeben: **+{punkte.HERZ}**" in text + # Dreieckszahlen: Level 2 ab 1, Level 3 ab 3, Level 4 ab 6 + assert "Level 2 ab 1, Level 3 ab 3, Level 4 ab 6" in text + + +def test_text_schreibt_nur_der_host(client, zuschauer, hostmodus): + assert client.put("/api/info", json={"text": "PayPal: x"}).status_code == 403 + hostmodus(zuschauer) + assert zuschauer.put("/api/info", json={"text": " PayPal: x "}).json()["text"] == "PayPal: x" + assert client.get("/api/info").json()["text"] == "PayPal: x" + # Leer gespeichert: wieder die Vorlage + zuschauer.put("/api/info", json={"text": ""}) + assert client.get("/api/info").json()["text"] == punkte.vorlage() diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 1e403ef..eabf933 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -13,6 +13,7 @@ import NamensWahl from './components/NamensWahl.vue' import FilmLogin from './components/FilmLogin.vue' import VerwaltungTab from './components/VerwaltungTab.vue' import WuenscheTab from './components/WuenscheTab.vue' +import InfoTab from './components/InfoTab.vue' import SearchTab from './components/SearchTab.vue' import StreamTab from './components/StreamTab.vue' import WatchedTab from './components/WatchedTab.vue' @@ -150,6 +151,7 @@ const tabs = computed(() => [ label: 'Wünsche', count: wuensche.value.filter((w) => !w.done_at).length, }, + { id: 'info', label: 'Info', count: null }, ...(host.value || angemeldet.value ? [{ id: 'verwaltung', label: 'Verwaltung', count: null }] : []), @@ -430,6 +432,8 @@ onMounted(load) @changed="ladeWuensche" /> + + diff --git a/frontend/src/components/InfoTab.vue b/frontend/src/components/InfoTab.vue new file mode 100644 index 0000000..81c1799 --- /dev/null +++ b/frontend/src/components/InfoTab.vue @@ -0,0 +1,114 @@ + + + + + diff --git a/frontend/src/composables/useEinstellungen.js b/frontend/src/composables/useEinstellungen.js index fa84e37..200887e 100644 --- a/frontend/src/composables/useEinstellungen.js +++ b/frontend/src/composables/useEinstellungen.js @@ -35,6 +35,7 @@ const TAB_IDS = [ 'merkliste', 'watched', 'wuensche', + 'info', 'verwaltung', 'stream', 'clips',