Files
palladium/assessments/CX_AI_Diagnostic/diaglib/export.py
Robert Helewka a967f73d09 feat: add master notebook library scaffolding and review tooling
Add CLAUDE.md defining the Palladium master notebook conventions and
Red Panda Approval criteria, plus a review-notebook slash command for
LLM-driven notebook review.

Expand .gitignore to block client/engagement documents and generated
exports, keeping masters client-clean while allowing text/image sources.

Normalize slider widget numeric values from floats to integers in
notebook JSON.
2026-07-31 16:16:07 +00:00

63 lines
2.3 KiB
Python

"""Structured engagement exports — the JSON is the source-of-truth artifact.
``exports/{engagement_id}.json`` — the full Engagement, serialized.
``exports/{engagement_id}.csv`` — one row per competency, for
cross-engagement spreadsheet analysis (build spec §8).
"""
from __future__ import annotations
import json
from pathlib import Path
import pandas as pd
from .models import DiagnosticConfig, Engagement
CSV_COLUMNS = [
"engagement_id", "client_name", "industry", "workshop_date",
"competency_id", "dimension", "score", "evidence",
"is_foundational", "is_binding_constraint",
]
def engagement_json(engagement: Engagement) -> str:
return json.dumps(engagement.model_dump(mode="json"), indent=2,
ensure_ascii=False)
def scores_dataframe(engagement: Engagement,
config: DiagnosticConfig) -> pd.DataFrame:
binding = set(engagement.computed_value.binding_constraints
if engagement.computed_value else [])
foundational = set(config.foundational_competencies)
rows = [{
"engagement_id": engagement.engagement_id,
"client_name": engagement.client_name,
"industry": engagement.industry_config,
"workshop_date": engagement.workshop_date.isoformat(),
"competency_id": s.competency_id,
"dimension": s.dimension,
"score": s.score,
"evidence": s.evidence,
"is_foundational": s.competency_id in foundational,
"is_binding_constraint": s.competency_id in binding,
} for s in engagement.scores]
return pd.DataFrame(rows, columns=CSV_COLUMNS)
def write_exports(engagement: Engagement, config: DiagnosticConfig,
exports_dir: Path) -> tuple[Path, Path]:
"""Write both artifacts; returns ``(json_path, csv_path)``."""
exports_dir.mkdir(parents=True, exist_ok=True)
json_path = exports_dir / f"{engagement.engagement_id}.json"
csv_path = exports_dir / f"{engagement.engagement_id}.csv"
json_path.write_text(engagement_json(engagement), encoding="utf-8")
scores_dataframe(engagement, config).to_csv(csv_path, index=False)
return json_path, csv_path
def load_engagement(json_path: Path) -> Engagement:
"""Reload a saved engagement for review (acceptance §10 nice-to-have)."""
return Engagement.model_validate_json(json_path.read_text(encoding="utf-8"))