- POST /api/sources/health/run (202, bei laufendem Run 409) stoesst den Lauf als BackgroundTask an: eigene DB-Connection, run_health_checks + generate_suggestions aus den geteilten Diensten, exakt nach dem Vorbild des Monitor-Nachtjobs daily_source_health_check. Kein Code-Duplikat. - GET /api/sources/health/run-status liefert den In-Memory-Status (health_run.py, pure Funktionen mit Doppelstart-Sperre, unit-getestet). Nach einem Neustart ist der Status idle, ein partieller Stand wird vom naechsten Lauf archiviert und ueberschrieben (wie beim Nachtjob). - Frontend: Knopf Jetzt pruefen + Zuletzt geprueft in der Stats-Leiste der Quellenliste, Polling alle 4 s, Abschluss-Toast mit geprueft/ Probleme/neue Vorschlaege, danach Liste + Aufgaben-Badge neu laden. Beim Oeffnen des Reiters wird ein laufender Run aus anderer Session erkannt und weiterverfolgt. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
48 Zeilen
1.4 KiB
Python
48 Zeilen
1.4 KiB
Python
"""In-Memory-Status für den manuellen Health-Run des Portals.
|
|
|
|
Pure Funktionen ohne DB und IO, damit die Zustandsübergänge unit-testbar sind.
|
|
Der Status lebt nur im Prozess. Nach einem Neustart ist er wieder idle, ein
|
|
dabei abgebrochener Lauf hinterlässt höchstens einen partiellen Stand in
|
|
source_health_checks, den der nächste Lauf archiviert und überschreibt
|
|
(identisch zum Verhalten des Monitor-Nachtjobs um 04:00 Uhr).
|
|
"""
|
|
from datetime import datetime
|
|
|
|
|
|
def new_state() -> dict:
|
|
return {
|
|
"running": False,
|
|
"started_at": None,
|
|
"finished_at": None,
|
|
"result": None,
|
|
"error": None,
|
|
}
|
|
|
|
|
|
def _now_iso(now=None) -> str:
|
|
return now if now is not None else datetime.now().isoformat(timespec="seconds")
|
|
|
|
|
|
def try_start(state: dict, now=None) -> bool:
|
|
"""Setzt den Lauf auf running. False, wenn schon einer läuft."""
|
|
if state.get("running"):
|
|
return False
|
|
state["running"] = True
|
|
state["started_at"] = _now_iso(now)
|
|
state["finished_at"] = None
|
|
state["result"] = None
|
|
state["error"] = None
|
|
return True
|
|
|
|
|
|
def mark_finished(state: dict, result: dict, now=None) -> None:
|
|
state["running"] = False
|
|
state["finished_at"] = _now_iso(now)
|
|
state["result"] = result
|
|
|
|
|
|
def mark_failed(state: dict, error: str, now=None) -> None:
|
|
state["running"] = False
|
|
state["finished_at"] = _now_iso(now)
|
|
state["error"] = error
|