Explorer
/opt/struktur/worker-watcher/src/worker_watcher/reports.py
← Zurück ↓ Download
import json
from typing import Any

from .timeutil import iso, utc_now
from .watcher import Watcher


def _row(row: Any) -> dict[str, Any]:
    result = {key: row[key] for key in row.keys()}  # noqa: SIM118 - sqlite Row exposes column names here
    for key in ("evidence_json", "metadata_json"):
        if result.get(key):
            try:
                result[key] = json.loads(result[key])
            except json.JSONDecodeError:
                pass
    return result


def report_data(watcher: Watcher) -> dict[str, Any]:
    return {"created_at": iso(utc_now()), "watcher": watcher.health(),
            "jobs": [_row(row) for row in watcher.repository.status_rows()],
            "workers": [_row(row) for row in watcher.repository.worker_rows()],
            "agents": [_row(row) for row in watcher.repository.agent_rows()],
            "collectors": [_row(row) for row in watcher.repository.collector_rows()],
            "remote_events": [_row(row) for row in watcher.repository.remote_event_rows()],
            "events": [_row(row) for row in watcher.repository.events()]}


def render_json(watcher: Watcher) -> str:
    return json.dumps(report_data(watcher), ensure_ascii=False, indent=2, default=str)


def render_markdown(watcher: Watcher) -> str:
    data = report_data(watcher)
    health = data["watcher"]
    lines = ["# Worker Watcher Report", "", f"Erstellt: `{data['created_at']}`", f"Watcher: **{health['watcher_status']}**", "",
             "## Job-Summen", "", f"- healthy: {health['jobs_healthy']}", f"- degraded: {health['jobs_degraded']}",
             f"- failed: {health['jobs_failed']}", f"- stale: {health['jobs_stale']}", f"- unknown: {health['jobs_unknown']}", "",
             "## Aktuelle Jobs", "", "| Job | Instanz | Operational | Health | Observation | Severity |", "|---|---|---|---|---|---|"]
    for job in data["jobs"]:
        lines.append(f"| {job['job_key']} | {job['instance_key']} | {job['operational_state']} | {job['health_status']} | {job['observation_status']} | {job['severity']} |")
    lines.extend(["", "## Collector-Läufe", "", "| Collector | Erfolg | Fehler |", "|---|---:|---|"])
    for collector in data["collectors"][:20]:
        lines.append(f"| {collector['collector_name']} | {bool(collector['success'])} | {collector.get('error_message') or ''} |")
    lines.extend(["", "## Ausführungsorte", "", "| Agent | Umgebung | Host | Status |", "|---|---|---|---|"])
    for agent in data["agents"]:
        lines.append(f"| {agent['agent_key']} | {agent['environment']} | {agent['host']} | {agent['status']} |")
    return "\n".join(lines) + "\n"