Files
proxmox-api-simulator/app/compatibility.py
T
2026-07-12 23:45:55 +03:00

100 lines
3.2 KiB
Python

"""Evidence-based compatibility accounting and reporting."""
from __future__ import annotations
from dataclasses import dataclass
from app.contracts.model import Snapshot
MethodKey = tuple[str, str]
@dataclass(frozen=True, slots=True)
class CompatibilityReport:
source_version: str
declared: frozenset[MethodKey]
schema_only: frozenset[MethodKey]
implemented: frozenset[MethodKey]
observed: frozenset[MethodKey]
verified: frozenset[MethodKey]
def as_json(self) -> dict[str, object]:
levels = {
"declared": self.declared,
"schema_only": self.schema_only,
"implemented": self.implemented,
"observed": self.observed,
"verified": self.verified,
}
total = len(self.declared)
return {
"source_version": self.source_version,
"total_declared": total,
"levels": {
name: {
"count": len(methods),
"score": len(methods) / total if total else 1.0,
"methods": [f"{verb} {path}" for path, verb in sorted(methods)],
}
for name, methods in levels.items()
},
"groups": self._groups(),
}
def _groups(self) -> dict[str, dict[str, int]]:
groups: dict[str, dict[str, int]] = {}
for path, verb in self.declared:
group = path.strip("/").split("/", 1)[0] or "root"
counters = groups.setdefault(group, {"declared": 0, "implemented": 0, "verified": 0})
counters["declared"] += 1
counters["implemented"] += int((path, verb) in self.implemented)
counters["verified"] += int((path, verb) in self.verified)
return dict(sorted(groups.items()))
def as_markdown(self) -> str:
levels = {
"declared": self.declared,
"schema_only": self.schema_only,
"implemented": self.implemented,
"observed": self.observed,
"verified": self.verified,
}
total = len(self.declared)
lines = [
"# Compatibility report",
"",
"| Level | Count | Score |",
"|---|---:|---:|",
]
for name, methods in levels.items():
score = len(methods) / total if total else 1.0
lines.append(f"| {name} | {len(methods)} | {score:.2%} |")
return "\n".join(lines)
def build_report(
snapshot: Snapshot,
*,
implemented: frozenset[MethodKey] = frozenset(),
observed: frozenset[MethodKey] = frozenset(),
verified: frozenset[MethodKey] = frozenset(),
) -> CompatibilityReport:
declared = frozenset(
(path.path, method.verb) for path in snapshot.paths for method in path.methods
)
for name, evidence in {
"implemented": implemented,
"observed": observed,
"verified": verified,
}.items():
if not evidence <= declared:
raise ValueError(f"{name} evidence references undeclared methods")
return CompatibilityReport(
source_version=snapshot.source_version,
declared=declared,
schema_only=declared - implemented,
implemented=implemented,
observed=observed,
verified=verified,
)