feat: Laientauglichkeit — Auto-Modus, Klartext-Ausgabe, einfachere Doku
Ziel: ein einziger täglicher Befehl genügt, jede Meldung ist auch ohne IT-Wissen verständlich. Auto-Modus (ein Cron-Job genügt für alles): - data/state.json merkt sich, wann die Wochen-Prüfungen zuletzt liefen (baseline.py: load_state/save_state/is_check_due/mark_check_run) - `scan` führt fällige Zusatzprüfungen (Tarnung/externe Links/Dateien) automatisch mit aus; Gesamt-Ampel = schlechtestes Teilergebnis; EIN kombinierter Report + EIN Alert - neuer Config-Abschnitt periodic_checks (Default: wöchentlich, an) Klartext (scanner/plain.py): - Ampel 🟢/🟡/🔴 + Sätze ohne Fachbegriffe, aus den strukturierten Befunden - Report: Klartext oben, "Technische Details (für Ihren Dienstleister)" unten - Terminal-Ausgabe und E-Mail (alerter.py) ebenso umgestellt Komfort: - approve --all/--rebuild legt die Datei-Überwachung automatisch mit an (Opt-out: --skip-assets); approve-assets bleibt für den Sonderfall - status zeigt Ampel zuoberst + wann die Wochen-Prüfungen zuletzt liefen - Onboarding-Text in einfacher Sprache, wenn noch kein Vergleichsstand existiert Doku: README + BEDIENUNGSANLEITUNG mit "In 3 Schritten"-Einstieg. Tests: test_state.py, test_plain.py (Fachbegriff-Assertion), Auto-Asset- Baseline-Test. 165 Tests grün. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
54ff1a6060
commit
bed20db8bd
11 changed files with 856 additions and 202 deletions
|
|
@ -233,6 +233,50 @@ class BaselineManager:
|
|||
)
|
||||
logger.info("Asset hashes saved: %d entries", len(hashes))
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Run-state (drives auto-scheduling of the weekly checks)
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
@property
|
||||
def _state_path(self) -> Path:
|
||||
return self.data_dir / "state.json"
|
||||
|
||||
def load_state(self) -> dict:
|
||||
"""Load the run-state file. Returns {'last_runs': {}} if none exists."""
|
||||
if not self._state_path.exists():
|
||||
return {"last_runs": {}}
|
||||
try:
|
||||
data = json.loads(self._state_path.read_text(encoding="utf-8"))
|
||||
except (json.JSONDecodeError, ValueError):
|
||||
return {"last_runs": {}}
|
||||
data.setdefault("last_runs", {})
|
||||
return data
|
||||
|
||||
def save_state(self, state: dict) -> None:
|
||||
"""Persist the run-state file."""
|
||||
self.data_dir.mkdir(parents=True, exist_ok=True)
|
||||
self._state_path.write_text(
|
||||
json.dumps(state, indent=2, ensure_ascii=False), encoding="utf-8"
|
||||
)
|
||||
|
||||
def is_check_due(self, name: str, interval_days: int) -> bool:
|
||||
"""True if a periodic check has never run or last ran >= interval_days ago."""
|
||||
last = self.load_state().get("last_runs", {}).get(name)
|
||||
if not last:
|
||||
return True
|
||||
try:
|
||||
last_dt = datetime.fromisoformat(last)
|
||||
except ValueError:
|
||||
return True
|
||||
age = datetime.now(timezone.utc) - last_dt
|
||||
return age.total_seconds() >= interval_days * 86400
|
||||
|
||||
def mark_check_run(self, name: str) -> None:
|
||||
"""Record that a periodic check just ran (now, UTC)."""
|
||||
state = self.load_state()
|
||||
state["last_runs"][name] = datetime.now(timezone.utc).isoformat()
|
||||
self.save_state(state)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Internal helpers
|
||||
# ------------------------------------------------------------------
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue