diff --git a/backend/routers/causal_lab.py b/backend/routers/causal_lab.py index c3044c0..57fcb68 100644 --- a/backend/routers/causal_lab.py +++ b/backend/routers/causal_lab.py @@ -1462,7 +1462,10 @@ def refresh_auto_analyses(): "pred_keys": list(json.loads(pred).keys()) if pred not in (None, "", "{}", "null") else [], }) - rows = [r for r in all_rows if (r["actual_json"] or "") in (None, "", "{}", "null")] + _empty = (None, "", "{}", "null", "[]") + rows = [r for r in all_rows if + (r["actual_json"] or "") in _empty or + (r["prediction_json"] or "") in _empty] print(f"[refresh] {len(rows)} analyses with empty actual_json out of {len(all_rows)} total", flush=True) conn.close() diff --git a/frontend/src/pages/InstrumentDashboard.tsx b/frontend/src/pages/InstrumentDashboard.tsx index 20212d9..57025e9 100644 --- a/frontend/src/pages/InstrumentDashboard.tsx +++ b/frontend/src/pages/InstrumentDashboard.tsx @@ -1014,16 +1014,19 @@ function CausalFrise({ // ── ExplanationScore ────────────────────────────────────────────────────────── function ExplanationScore({ - events, templates, causalInsts, onRefreshDone, + events, templates, causalInsts, onRefreshDone, onDebugResult, debugInfo, }: { events: SnapshotEvent[] templates: CausalTemplate[] causalInsts: string[] onRefreshDone?: () => void + // eslint-disable-next-line @typescript-eslint/no-explicit-any + onDebugResult?: (data: any) => void + // eslint-disable-next-line @typescript-eslint/no-explicit-any + debugInfo?: any }) { const [refreshing, setRefreshing] = useState(false) const [refreshDone, setRefreshDone] = useState(false) - const [debugInfo, setDebugInfo] = useState(null) // For each linked event, compute the comprehension score and collect scored ones const scored: number[] = [] @@ -1087,17 +1090,17 @@ function ExplanationScore({ : globalScore >= 70 ? 'bg-emerald-500' : globalScore >= 40 ? 'bg-amber-500' : 'bg-red-500' const handleRefresh = async () => { - setDebugInfo(null) + onDebugResult?.(null) setRefreshing(true) try { const res = await api.post('/causal-lab/auto-analyze/refresh') const data = res.data - setDebugInfo(data) + onDebugResult?.(data) setRefreshDone(true) onRefreshDone?.() setTimeout(() => setRefreshDone(false), 8000) } catch (err: any) { - setDebugInfo({ error: String(err) }) + onDebugResult?.({ error: String(err) }) } finally { setRefreshing(false) } @@ -1193,6 +1196,7 @@ export default function InstrumentDashboard() { const [snapshot, setSnapshot] = useState(null) const [narrative, setNarrative] = useState('') const [loading, setLoading] = useState(false) + const [refreshDebug, setRefreshDebug] = useState(null) const [loadingNarr, setLoadingNarr] = useState(false) const [selectorOpen, setSelectorOpen] = useState(false) const [selectedDate, setSelectedDate] = useState(null) @@ -1226,6 +1230,13 @@ export default function InstrumentDashboard() { .finally(() => setLoading(false)) }, [instrumentId, period]) + // Silent re-fetch (no loading spinner) — used after Recalculer so ExplanationScore keeps its state + const fetchSnapshotSilent = useCallback(() => { + api.get(`/instruments/${instrumentId}/snapshot?period=${period}`) + .then(r => { setSnapshot(r.data) }) + .catch(() => {}) + }, [instrumentId, period]) + useEffect(() => { setSnapshot(null) setNarrative('') @@ -1606,7 +1617,9 @@ export default function InstrumentDashboard() { events={snapshot.events} templates={templates} causalInsts={causalInsts} - onRefreshDone={fetchSnapshot} + onRefreshDone={fetchSnapshotSilent} + onDebugResult={setRefreshDebug} + debugInfo={refreshDebug} /> )