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.
This commit is contained in:
62
assessments/CX_AI_Diagnostic/diaglib/export.py
Normal file
62
assessments/CX_AI_Diagnostic/diaglib/export.py
Normal file
@@ -0,0 +1,62 @@
|
||||
"""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"))
|
||||
Reference in New Issue
Block a user