221 lines
7.0 KiB
Python
221 lines
7.0 KiB
Python
"""Was der Seite noch fehlt.
|
|
|
|
Jeder darf aufschreiben, was er sich wünscht, und für Wünsche anderer
|
|
stimmen — eine Stimme je Person. Ergänzungen hängen als Anmerkungen unter
|
|
dem Wunsch, damit der ursprüngliche Text stehen bleibt und die Diskussion
|
|
nachvollziehbar ist. Der Host hakt ab, was umgesetzt ist: der Wunsch bleibt
|
|
sichtbar, rutscht aber ans Ende.
|
|
|
|
Ändern darf jeder nur die eigenen, wie bei den Filmsammlungen.
|
|
"""
|
|
|
|
from datetime import datetime, timezone
|
|
|
|
from . import users
|
|
from .db import connect
|
|
from .users import WEM, besitzer, gehoert_mir
|
|
|
|
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,
|
|
done_at TEXT
|
|
);
|
|
CREATE TABLE IF NOT EXISTS feature_notes (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
feature_id INTEGER NOT NULL,
|
|
session_id TEXT NOT NULL,
|
|
user_id INTEGER,
|
|
text TEXT NOT NULL,
|
|
created_at TEXT NOT NULL
|
|
);
|
|
"""
|
|
|
|
# Eigenes Statement, weil users.je_person() die Tabelle daraus neu bauen
|
|
# könnte — hier ohne UNIQUE in der Spaltendefinition, die Eindeutigkeit
|
|
# kommt aus den Indizes je Person.
|
|
VOTES_SCHEMA = """
|
|
CREATE TABLE IF NOT EXISTS feature_votes (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
feature_id INTEGER NOT NULL,
|
|
session_id TEXT NOT NULL,
|
|
user_id INTEGER,
|
|
created_at TEXT NOT NULL
|
|
);
|
|
"""
|
|
|
|
MAX_LAENGE = 500
|
|
|
|
|
|
def _jetzt():
|
|
return datetime.now(timezone.utc).isoformat(timespec="seconds")
|
|
|
|
|
|
def init():
|
|
with connect() as conn:
|
|
conn.executescript(SCHEMA)
|
|
conn.executescript(VOTES_SCHEMA)
|
|
users.user_id_nachruesten(conn, "features")
|
|
# Die Tabelle stand vor der Spalte schon in bestehenden Datenbanken
|
|
spalten = {r[1] for r in conn.execute("PRAGMA table_info(features)")}
|
|
if "done_at" not in spalten:
|
|
conn.execute("ALTER TABLE features ADD COLUMN done_at TEXT")
|
|
# Eine Stimme je Person und Wunsch
|
|
users.je_person(conn, "feature_votes", VOTES_SCHEMA, "feature_id")
|
|
|
|
|
|
def alle(session_id):
|
|
"""Alle Wünsche: offene nach Stimmen, erledigte dahinter.
|
|
|
|
Jeder Wunsch bringt den Namen des Verfassers, die Stimmenzahl, die
|
|
eigene Stimme und seine Anmerkungen mit.
|
|
"""
|
|
ich = besitzer(session_id)
|
|
with connect() as conn:
|
|
rows = conn.execute(
|
|
"SELECT f.*, u.name, "
|
|
" (SELECT COUNT(*) FROM feature_votes v WHERE v.feature_id = f.id) "
|
|
" AS votes "
|
|
"FROM features f "
|
|
"LEFT JOIN users u ON u.id = f.user_id "
|
|
"ORDER BY f.done_at IS NOT NULL, votes DESC, f.created_at DESC"
|
|
).fetchall()
|
|
meine_stimmen = {
|
|
r["feature_id"]
|
|
for r in conn.execute(
|
|
f"SELECT feature_id FROM feature_votes WHERE {WEM}",
|
|
(ich, ich, session_id),
|
|
)
|
|
}
|
|
notizen = conn.execute(
|
|
"SELECT n.*, u.name FROM feature_notes n "
|
|
"LEFT JOIN users u ON u.id = n.user_id "
|
|
"ORDER BY n.created_at ASC"
|
|
).fetchall()
|
|
|
|
je_wunsch = {}
|
|
for n in notizen:
|
|
je_wunsch.setdefault(n["feature_id"], []).append(
|
|
{
|
|
"id": n["id"],
|
|
"text": n["text"],
|
|
"name": n["name"],
|
|
"created_at": n["created_at"],
|
|
"mine": gehoert_mir(n, session_id, ich),
|
|
}
|
|
)
|
|
|
|
return [
|
|
{
|
|
**{
|
|
k: v
|
|
for k, v in dict(r).items()
|
|
if k not in ("session_id", "user_id")
|
|
},
|
|
"mine": gehoert_mir(r, session_id, ich),
|
|
"voted": r["id"] in meine_stimmen,
|
|
"notes": je_wunsch.get(r["id"], []),
|
|
}
|
|
for r in rows
|
|
]
|
|
|
|
|
|
def anlegen(session_id, text):
|
|
"""Gibt None zurück, wenn nichts dasteht."""
|
|
text = (text or "").strip()[:MAX_LAENGE]
|
|
if not text:
|
|
return None
|
|
with connect() as conn:
|
|
conn.execute(
|
|
"INSERT INTO features (session_id, user_id, text, created_at) "
|
|
"VALUES (?, ?, ?, ?)",
|
|
(session_id, besitzer(session_id), text, _jetzt()),
|
|
)
|
|
return alle(session_id)
|
|
|
|
|
|
def aendern(feature_id, text):
|
|
"""Der Host räumt auf: Tippfehler, Dopplungen, unklare Formulierungen."""
|
|
text = (text or "").strip()[:MAX_LAENGE]
|
|
if not text:
|
|
return 0
|
|
with connect() as conn:
|
|
return conn.execute(
|
|
"UPDATE features SET text = ? WHERE id = ?", (text, feature_id)
|
|
).rowcount
|
|
|
|
|
|
def erledigen(feature_id, erledigt):
|
|
"""Der Host hakt ab — oder holt einen Wunsch wieder hervor."""
|
|
with connect() as conn:
|
|
return conn.execute(
|
|
"UPDATE features SET done_at = ? WHERE id = ?",
|
|
(_jetzt() if erledigt else None, feature_id),
|
|
).rowcount
|
|
|
|
|
|
def abstimmen(feature_id, session_id):
|
|
"""Stimme setzen oder zurücknehmen. Gibt None zurück, wenn es den
|
|
Wunsch nicht gibt, sonst ob die Stimme jetzt steht."""
|
|
ich = besitzer(session_id)
|
|
with connect() as conn:
|
|
weg = conn.execute(
|
|
f"DELETE FROM feature_votes WHERE feature_id = ? AND {WEM}",
|
|
(feature_id, ich, ich, session_id),
|
|
).rowcount
|
|
if weg:
|
|
return False
|
|
da = conn.execute(
|
|
"SELECT 1 FROM features WHERE id = ?", (feature_id,)
|
|
).fetchone()
|
|
if not da:
|
|
return None
|
|
conn.execute(
|
|
"INSERT INTO feature_votes (feature_id, session_id, user_id, created_at) "
|
|
"VALUES (?, ?, ?, ?)",
|
|
(feature_id, session_id, ich, _jetzt()),
|
|
)
|
|
return True
|
|
|
|
|
|
def anmerken(feature_id, session_id, text):
|
|
"""Eine Ergänzung unter den Wunsch hängen."""
|
|
text = (text or "").strip()[:MAX_LAENGE]
|
|
if not text:
|
|
return False
|
|
with connect() as conn:
|
|
da = conn.execute(
|
|
"SELECT 1 FROM features WHERE id = ?", (feature_id,)
|
|
).fetchone()
|
|
if not da:
|
|
return False
|
|
conn.execute(
|
|
"INSERT INTO feature_notes "
|
|
"(feature_id, session_id, user_id, text, created_at) "
|
|
"VALUES (?, ?, ?, ?, ?)",
|
|
(feature_id, session_id, besitzer(session_id), text, _jetzt()),
|
|
)
|
|
return True
|
|
|
|
|
|
def anmerkung_loeschen(note_id):
|
|
"""Ohne Sitzungsbindung — den Knopf zeigt die Oberfläche nur dem Host."""
|
|
with connect() as conn:
|
|
return conn.execute(
|
|
"DELETE FROM feature_notes WHERE id = ?", (note_id,)
|
|
).rowcount
|
|
|
|
|
|
def loeschen(feature_id):
|
|
"""Ohne Sitzungsbindung — den Knopf zeigt die Oberfläche nur dem
|
|
Verfasser und dem Host. Stimmen und Anmerkungen gehen mit."""
|
|
with connect() as conn:
|
|
conn.execute("DELETE FROM feature_votes WHERE feature_id = ?", (feature_id,))
|
|
conn.execute("DELETE FROM feature_notes WHERE feature_id = ?", (feature_id,))
|
|
return conn.execute(
|
|
"DELETE FROM features WHERE id = ?", (feature_id,)
|
|
).rowcount
|