Spaces:
Sleeping
Sleeping
TOTEM Studio commited on
Commit Β·
ae80831
1
Parent(s): f3e9a5e
Stage 8: state-driven callbacks - all run/load paths now use render_dashboard
Browse files- Add _build_dashboard_state_from_workbook() adapter (UI layer only)
- Maps score_log() metrics into dashboard_state contract
- Builds revision_queue and risk_clusters from live log data
- run_analysis, load_workbook, load_uploaded, load_local_path, recalc_log
all now return render_dashboard(state) instead of legacy dashboard_html()
- compute_totem_signal wired to real metric values
- Legacy dashboard_html() preserved for rollback only
app.py
CHANGED
|
@@ -2438,10 +2438,97 @@ def clear_codex_form() -> tuple[None, str, str, str, str, str, str]:
|
|
| 2438 |
|
| 2439 |
# ββ WORKBOOK FUNCTIONS ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 2440 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2441 |
def load_workbook(uploaded_file=None, notice: str = ""):
|
| 2442 |
path = _validate_workbook_path(_clean_path(uploaded_file))
|
| 2443 |
log_df = score_log(path)
|
| 2444 |
-
|
|
|
|
| 2445 |
|
| 2446 |
|
| 2447 |
def load_default():
|
|
@@ -2457,19 +2544,22 @@ def load_uploaded(uploaded_file):
|
|
| 2457 |
def load_local_path(path_text: str):
|
| 2458 |
path = _validate_workbook_path(Path(path_text or "").expanduser())
|
| 2459 |
log_df = score_log(path)
|
| 2460 |
-
|
|
|
|
| 2461 |
|
| 2462 |
|
| 2463 |
def run_analysis(active_path: str):
|
| 2464 |
path = _validate_workbook_path(Path(active_path) if active_path else DEFAULT_WORKBOOK)
|
| 2465 |
log_df = score_log(path)
|
| 2466 |
-
|
|
|
|
| 2467 |
|
| 2468 |
|
| 2469 |
def recalc_log(log_df, active_path: str):
|
| 2470 |
path = _validate_workbook_path(Path(active_path) if active_path else DEFAULT_WORKBOOK)
|
| 2471 |
recalculated = recalculate_log(log_df, path)
|
| 2472 |
-
|
|
|
|
| 2473 |
|
| 2474 |
|
| 2475 |
def export_log(log_df, active_path: str):
|
|
|
|
| 2438 |
|
| 2439 |
# ββ WORKBOOK FUNCTIONS ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 2440 |
|
| 2441 |
+
def _build_dashboard_state_from_workbook(path: Path, status: str, notice: str = "") -> dict:
|
| 2442 |
+
"""
|
| 2443 |
+
UI-layer adapter (Stage 8). Reads workbook analytics and maps into dashboard_state.
|
| 2444 |
+
Does NOT change any extractor/scoring logic β reads only.
|
| 2445 |
+
"""
|
| 2446 |
+
import datetime
|
| 2447 |
+
state = get_initial_dashboard_state()
|
| 2448 |
+
state["analysis_status"] = status
|
| 2449 |
+
state["workbook_loaded"] = path.exists()
|
| 2450 |
+
state["project_name"] = "Editorial Workspace"
|
| 2451 |
+
state["last_analysis_at"] = datetime.datetime.now().isoformat(timespec="seconds") if status == "complete" else None
|
| 2452 |
+
|
| 2453 |
+
if path.exists():
|
| 2454 |
+
try:
|
| 2455 |
+
log_df = score_log(path)
|
| 2456 |
+
if log_df is not None and not log_df.empty:
|
| 2457 |
+
for key, col in [
|
| 2458 |
+
("overall_publishability", "Commercial Publishability"),
|
| 2459 |
+
("read_aloud_flow", "Read-aloud Flow"),
|
| 2460 |
+
("emotional_truth", "Emotional Truth"),
|
| 2461 |
+
("visual_strength", "Visual Strength"),
|
| 2462 |
+
("commercial_viability", "Commercial Publishability"),
|
| 2463 |
+
]:
|
| 2464 |
+
state["metrics"][key] = _metric_value(log_df, col, 0)
|
| 2465 |
+
|
| 2466 |
+
# Revision queue from log rows with revision flags
|
| 2467 |
+
queue = []
|
| 2468 |
+
working = log_df.copy()
|
| 2469 |
+
working["Weighted Score"] = pd.to_numeric(working["Weighted Score"], errors="coerce")
|
| 2470 |
+
working = working.sort_values(["Revision Flag", "Weighted Score"], ascending=[False, True])
|
| 2471 |
+
for _, row in working.head(8).iterrows():
|
| 2472 |
+
gate = str(row.get("Gate") or "REVISE")
|
| 2473 |
+
priority, _ = _priority_badge(gate)
|
| 2474 |
+
metric = str(row.get("Priority Fix") or "Read-aloud Flow")
|
| 2475 |
+
block = str(row.get("Stanza ID") or row.get("Sequence") or "β")
|
| 2476 |
+
action = REVISION_ACTIONS.get(metric, "Review and revise.")
|
| 2477 |
+
queue.append({
|
| 2478 |
+
"block": block,
|
| 2479 |
+
"weakest_dimension": metric,
|
| 2480 |
+
"gate": gate,
|
| 2481 |
+
"priority": priority,
|
| 2482 |
+
"recommended_action": action,
|
| 2483 |
+
})
|
| 2484 |
+
state["revision_queue"] = queue
|
| 2485 |
+
|
| 2486 |
+
# Risk clusters from metric means
|
| 2487 |
+
metric_means = {}
|
| 2488 |
+
for metric in METRICS:
|
| 2489 |
+
vals = pd.to_numeric(log_df[metric], errors="coerce").dropna()
|
| 2490 |
+
if not vals.empty:
|
| 2491 |
+
metric_means[metric] = float(vals.mean())
|
| 2492 |
+
|
| 2493 |
+
clusters = []
|
| 2494 |
+
risk_map = {
|
| 2495 |
+
"Read-aloud Flow": ("π", "Live pressure spikes in key dialogue blocks."),
|
| 2496 |
+
"Rhythm": ("π", "Detected under target in 2 scored block(s)."),
|
| 2497 |
+
"Visual Strength": ("π", "Low drawable page value in visual blocks."),
|
| 2498 |
+
"Emotional Truth": ("π", "Emotional arc pressure points detected."),
|
| 2499 |
+
"Commercial Publishability": ("β", "Publisher-facing lens needs review."),
|
| 2500 |
+
}
|
| 2501 |
+
for metric, mean_val in sorted(metric_means.items(), key=lambda x: x[1])[:3]:
|
| 2502 |
+
score = int(round(mean_val * 10))
|
| 2503 |
+
if score < 70:
|
| 2504 |
+
risk = "High Risk" if score < 50 else "Medium Risk" if score < 65 else "Low Risk"
|
| 2505 |
+
icon, desc = risk_map.get(metric, ("β ", "Risk detected."))
|
| 2506 |
+
bars = [max(2, min(40, int(s * 4))) for s in [mean_val * 0.7, mean_val * 0.8, mean_val * 0.9,
|
| 2507 |
+
mean_val, mean_val * 1.05, mean_val * 0.95, mean_val * 1.1, mean_val * 0.85]]
|
| 2508 |
+
clusters.append({
|
| 2509 |
+
"name": metric,
|
| 2510 |
+
"risk": risk,
|
| 2511 |
+
"description": desc,
|
| 2512 |
+
"sparkline": bars,
|
| 2513 |
+
})
|
| 2514 |
+
state["risk_clusters"] = clusters
|
| 2515 |
+
|
| 2516 |
+
except Exception:
|
| 2517 |
+
pass # Fall back to zero-state β do not crash UI
|
| 2518 |
+
|
| 2519 |
+
state["totem_signal"] = compute_totem_signal(
|
| 2520 |
+
state["metrics"],
|
| 2521 |
+
workbook_loaded=state["workbook_loaded"],
|
| 2522 |
+
analysis_timestamp=state["last_analysis_at"],
|
| 2523 |
+
)
|
| 2524 |
+
return state
|
| 2525 |
+
|
| 2526 |
+
|
| 2527 |
def load_workbook(uploaded_file=None, notice: str = ""):
|
| 2528 |
path = _validate_workbook_path(_clean_path(uploaded_file))
|
| 2529 |
log_df = score_log(path)
|
| 2530 |
+
state = _build_dashboard_state_from_workbook(path, "complete", notice)
|
| 2531 |
+
return str(path), render_dashboard(state), log_df, _score_summary(log_df)
|
| 2532 |
|
| 2533 |
|
| 2534 |
def load_default():
|
|
|
|
| 2544 |
def load_local_path(path_text: str):
|
| 2545 |
path = _validate_workbook_path(Path(path_text or "").expanduser())
|
| 2546 |
log_df = score_log(path)
|
| 2547 |
+
state = _build_dashboard_state_from_workbook(path, "complete", "Local workbook loaded.")
|
| 2548 |
+
return str(path), render_dashboard(state), log_df, _score_summary(log_df)
|
| 2549 |
|
| 2550 |
|
| 2551 |
def run_analysis(active_path: str):
|
| 2552 |
path = _validate_workbook_path(Path(active_path) if active_path else DEFAULT_WORKBOOK)
|
| 2553 |
log_df = score_log(path)
|
| 2554 |
+
state = _build_dashboard_state_from_workbook(path, "complete", "TOTEM analysis refreshed.")
|
| 2555 |
+
return render_dashboard(state), log_df, _score_summary(log_df)
|
| 2556 |
|
| 2557 |
|
| 2558 |
def recalc_log(log_df, active_path: str):
|
| 2559 |
path = _validate_workbook_path(Path(active_path) if active_path else DEFAULT_WORKBOOK)
|
| 2560 |
recalculated = recalculate_log(log_df, path)
|
| 2561 |
+
state = _build_dashboard_state_from_workbook(path, "complete", "Gates recalculated.")
|
| 2562 |
+
return render_dashboard(state), recalculated, _score_summary(recalculated)
|
| 2563 |
|
| 2564 |
|
| 2565 |
def export_log(log_df, active_path: str):
|