Backend: gemeinsame Helfer in common.py
- Eine ValidationError-Klasse statt zwei (links.py, projects.py), ein Fehler-Handler in main.py - clean_text() ersetzt vier fast gleiche Text-Prüfungen (Kanban, Haushalt, Reisen, Artikel); Fehlermeldungen nennen jetzt überall die Höchstlänge - Zeitstempel (now_iso, iso_ago, iso_after) und Kontenliste (user_list) an einer Stelle; ungenutzte Importe entfernt - Tests für die Helfer und für einheitliche 400-Fehler aus allen Bereichen Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d28e6b2e4b
commit
4aba48ce51
14 files changed
+117
-86
No files matched your search
@@ -0,0 +1,48 @@
|
||||
"""Gemeinsame Helfer fürs Backend: Eingabefehler, Texte prüfen, Zeitstempel, Kontenliste."""
|
||||
import re
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
|
||||
class ValidationError(ValueError):
|
||||
"""Ungültige Eingabe; main.py macht daraus HTTP 400 mit {"error": Meldung}."""
|
||||
|
||||
|
||||
def clean_text(value, field: str, max_len: int, *, required: bool = True, collapse: bool = True) -> str:
|
||||
"""Text säubern und prüfen. collapse: Leerraum (auch Zeilenumbrüche) zu einem Leerzeichen zusammenfassen."""
|
||||
if not isinstance(value, str):
|
||||
value = ""
|
||||
v = re.sub(r"\s+", " ", value).strip() if collapse else value.strip()
|
||||
if required and not v:
|
||||
raise ValidationError(f"{field} fehlt.")
|
||||
if len(v) > max_len:
|
||||
raise ValidationError(f"{field} ist zu lang (max. {max_len} Zeichen).")
|
||||
return v
|
||||
|
||||
|
||||
# ---------- Zeitstempel: ISO 8601 in UTC mit Millisekunden, z. B. 2026-10-15T08:57:47.533Z ----------
|
||||
# Einheitliches Format, damit Vergleiche als Text in SQLite stimmen (done_at <= ? usw.).
|
||||
|
||||
|
||||
def iso(moment: datetime) -> str:
|
||||
return moment.astimezone(timezone.utc).isoformat(timespec="milliseconds").replace("+00:00", "Z")
|
||||
|
||||
|
||||
def now_iso() -> str:
|
||||
return iso(datetime.now(timezone.utc))
|
||||
|
||||
|
||||
def iso_ago(delta: timedelta) -> str:
|
||||
return iso(datetime.now(timezone.utc) - delta)
|
||||
|
||||
|
||||
def iso_after(value: str, delta: timedelta) -> str:
|
||||
return iso(datetime.fromisoformat(value.replace("Z", "+00:00")) + delta)
|
||||
|
||||
|
||||
# ---------- Konten ----------
|
||||
|
||||
|
||||
def user_list(conn) -> list:
|
||||
"""Alle Konten für Auswahlfelder (Bearbeiter, Freigaben), alphabetisch."""
|
||||
rows = conn.execute("SELECT id, display_name FROM users ORDER BY display_name COLLATE NOCASE").fetchall()
|
||||
return [{"id": u["id"], "name": u["display_name"]} for u in rows]
|
||||
Reference in new issue
Block a user