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:
guyg1474andClaude Opus 5.5 committed 2026-10-15 18:10:43 +02:00
1 parent d28e6b2e4b
commit 4aba48ce51
14 files changed
+117 -86

No files matched your search

+5 -16
View File
@@ -7,13 +7,12 @@ Wer eine Liste anlegt, legt fest, ob alle Konten sie sehen oder nur ausgewählte
Vorschläge angeboten – "milch" wird dabei automatisch zum bereits bekannten "Milch".
"""
import math
import re
from fastapi import APIRouter, Body, Depends, HTTPException
from .auth import current_user
from .db import now_iso, transaction
from .links import ValidationError
from .common import ValidationError, clean_text, now_iso, user_list
from .db import transaction
router = APIRouter()
@@ -67,12 +66,7 @@ def _quantity(value, unit: str = "stk"):
def _clean_name(value) -> str:
name = re.sub(r"\s+", " ", value).strip() if isinstance(value, str) else ""
if not name:
raise ValidationError("Produktname fehlt.")
if len(name) > MAX_NAME:
raise ValidationError(f"Produktname ist zu lang (max. {MAX_NAME} Zeichen).")
return name
return clean_text(value, "Produktname", MAX_NAME)
def _key(name: str) -> str:
@@ -168,11 +162,7 @@ def _shopping_list(conn, row, user: dict) -> dict:
def _list_fields(conn, data: dict, user: dict, owner_id) -> tuple:
"""Name, "für alle" und Mitglieder prüfen. Der Ersteller sieht seine Liste immer und steht nicht bei den Mitgliedern."""
name = re.sub(r"\s+", " ", data.get("name")).strip() if isinstance(data.get("name"), str) else ""
if not name:
raise ValidationError("Name der Liste fehlt.")
if len(name) > MAX_LIST_NAME:
raise ValidationError(f"Name ist zu lang (max. {MAX_LIST_NAME} Zeichen).")
name = clean_text(data.get("name"), "Name der Liste", MAX_LIST_NAME)
everyone = data.get("everyone", True)
if not isinstance(everyone, bool):
raise ValidationError("Ungültige Sichtbarkeit.")
@@ -211,11 +201,10 @@ def _state(conn, user: dict) -> dict:
).fetchall()
# Häufig genutzte Produkte zuerst vorschlagen
products = conn.execute("SELECT name, uses FROM products").fetchall()
users = conn.execute("SELECT id, display_name FROM users ORDER BY display_name COLLATE NOCASE").fetchall()
return {
"items": [_item(r) for r in sorted(items, key=lambda r: _sort_key(r["name"]))],
"lists": [_shopping_list(conn, r, user) for r in lists],
"users": [{"id": u["id"], "name": u["display_name"]} for u in users],
"users": user_list(conn),
"products": [r["name"] for r in sorted(products, key=lambda r: (-r["uses"], _sort_key(r["name"])))],
}