"""Assemblage déterministe du rapport d'audit 5D à partir des contrôles. Verdict global : FAIL — au moins un contrôle FAIL (défaut réel à corriger). PASS_WITH_OPEN_ITEMS — zéro FAIL, mais des paramètres réglementaires restent à confirmer (A_CONFIRMER) → backlog honnête, pas un échec. PASS — tous les contrôles PASS. Aucune date/horodatage (déterminisme du build · gate CI reproductible). """ from __future__ import annotations from typing import Any from . import controls from .artifacts import load_artifacts REPORT_NAME = "OTO QA · Audit 5D Conformité" REPORT_VERSION = "1.0" _PASS, _FAIL, _AC = controls.PASS, controls.FAIL, controls.A_CONFIRMER def _dimension_status(statuts: list[str]) -> str: if _FAIL in statuts: return _FAIL if _AC in statuts: return "PASS_WITH_OPEN_ITEMS" return _PASS def build(spec: dict[str, Any]) -> dict[str, Any]: art = load_artifacts() results = controls.run_all(art, spec) # Synthèse par dimension (ordre canonique de DIMENSIONS). dims = [] for d in controls.DIMENSIONS: rs = [r for r in results if r["dimension"] == d["id"]] statuts = [r["statut"] for r in rs] dims.append({ "id": d["id"], "titre": d["titre"], "reference": d["reference"], "controls_total": len(rs), "pass": statuts.count(_PASS), "fail": statuts.count(_FAIL), "a_confirmer": statuts.count(_AC), "statut": _dimension_status(statuts), }) # Open items = tout A_CONFIRMER, avec son propriétaire métier. open_items = [{ "control": r["id"], "dimension": r["dimension"], "detail": r["detail"], "owner": r["owner"], } for r in results if r["statut"] == _AC] all_statuts = [r["statut"] for r in results] totals = { "controls": len(results), "pass": all_statuts.count(_PASS), "fail": all_statuts.count(_FAIL), "a_confirmer": all_statuts.count(_AC), } if totals["fail"]: verdict = "FAIL" elif totals["a_confirmer"]: verdict = "PASS_WITH_OPEN_ITEMS" else: verdict = "PASS" # On n'expose pas la fonction (`fn`) : le rapport reste sérialisable JSON. public_results = [{k: v for k, v in r.items() if k != "fn"} for r in results] return { "audit": REPORT_NAME, "version": REPORT_VERSION, "reference_cadre": spec.get("reference_cadre", ""), "dimensions": dims, "controls": public_results, "open_items": open_items, "totals": totals, "verdict": verdict, "notes": spec.get("notes", []), }