Add artifact.logging_module/src/artifact_logging_module/core.py
This commit is contained in:
commit
ab46cf8336
1 changed files with 112 additions and 0 deletions
112
artifact.logging_module/src/artifact_logging_module/core.py
Normal file
112
artifact.logging_module/src/artifact_logging_module/core.py
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
import json
|
||||
import os
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Optional, List, Dict, Any
|
||||
|
||||
__all__ = ["LogEntry", "log_decision", "generate_report"]
|
||||
|
||||
|
||||
class LogEntry:
|
||||
"""Repräsentiert einen einzelnen Logeintrag einer CI-Entscheidung."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
timestamp: datetime,
|
||||
decision_type: str,
|
||||
reason_code: str,
|
||||
additional_info: Optional[str] = None,
|
||||
) -> None:
|
||||
self.timestamp = timestamp
|
||||
self.decision_type = decision_type
|
||||
self.reason_code = reason_code
|
||||
self.additional_info = additional_info or ""
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
return {
|
||||
"timestamp": self.timestamp.isoformat(),
|
||||
"decision_type": self.decision_type,
|
||||
"reason_code": self.reason_code,
|
||||
"additional_info": self.additional_info,
|
||||
}
|
||||
|
||||
|
||||
_LOG_PATH = Path("data/ci_decisions.json")
|
||||
|
||||
|
||||
class _LogError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def _validate_decision(decision_type: str, reason_code: str) -> None:
|
||||
valid_types = {"PASS", "WARN", "UNKNOWN"}
|
||||
assert isinstance(decision_type, str) and decision_type in valid_types, (
|
||||
f"Ungültiger decision_type: {decision_type}. Erwartet: {valid_types}."
|
||||
)
|
||||
assert isinstance(reason_code, str) and reason_code.strip(), (
|
||||
"reason_code muss ein nicht-leerer String sein."
|
||||
)
|
||||
|
||||
|
||||
def _load_existing_logs(path: Path) -> List[Dict[str, Any]]:
|
||||
if not path.exists():
|
||||
return []
|
||||
try:
|
||||
with path.open("r", encoding="utf-8") as f:
|
||||
return json.load(f)
|
||||
except (json.JSONDecodeError, OSError) as e:
|
||||
raise _LogError(f"Fehler beim Laden der Logdatei: {e}") from e
|
||||
|
||||
|
||||
def _write_logs(path: Path, logs: List[Dict[str, Any]]) -> None:
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
try:
|
||||
with path.open("w", encoding="utf-8") as f:
|
||||
json.dump(logs, f, ensure_ascii=False, indent=2)
|
||||
except OSError as e:
|
||||
raise _LogError(f"Fehler beim Schreiben der Logdatei: {e}") from e
|
||||
|
||||
|
||||
def log_decision(decision_type: str, reason_code: str) -> None:
|
||||
"""Protokolliert eine CI-Entscheidung in data/ci_decisions.json."""
|
||||
|
||||
_validate_decision(decision_type, reason_code)
|
||||
|
||||
entry = LogEntry(
|
||||
timestamp=datetime.utcnow(),
|
||||
decision_type=decision_type,
|
||||
reason_code=reason_code,
|
||||
)
|
||||
|
||||
logs = _load_existing_logs(_LOG_PATH)
|
||||
logs.append(entry.to_dict())
|
||||
_write_logs(_LOG_PATH, logs)
|
||||
|
||||
|
||||
def generate_report() -> str:
|
||||
"""Erzeugt eine textuelle Zusammenfassung der Entscheidungsstatistik."""
|
||||
|
||||
logs = _load_existing_logs(_LOG_PATH)
|
||||
if not logs:
|
||||
return "Keine Logeinträge gefunden."
|
||||
|
||||
stats = {"PASS": 0, "WARN": 0, "UNKNOWN": 0}
|
||||
for e in logs:
|
||||
dt = e.get("decision_type")
|
||||
if dt in stats:
|
||||
stats[dt] += 1
|
||||
else:
|
||||
stats["UNKNOWN"] += 1
|
||||
|
||||
total = sum(stats.values()) or 1
|
||||
warn_rate = (stats["WARN"] / total) * 100
|
||||
|
||||
report_lines = [
|
||||
"CI Decisions Summary Report",
|
||||
"============================",
|
||||
f"Total Entries: {total}",
|
||||
f"PASS: {stats['PASS']}",
|
||||
f"WARN: {stats['WARN']} ({warn_rate:.2f}%)",
|
||||
f"UNKNOWN:{stats['UNKNOWN']}",
|
||||
]
|
||||
return "\n".join(report_lines)
|
||||
Loading…
Reference in a new issue