fix: preserve debug info across snapshot re-fetch + widen refresh filter

- refreshDebug state lives in parent (InstrumentDashboard) so it survives
  fetchSnapshotSilent re-render (ExplanationScore no longer loses debugInfo
  when onRefreshDone triggers setLoading → unmount)
- fetchSnapshotSilent: re-fetches snapshot without setLoading(true) so
  ExplanationScore stays mounted with its state
- Refresh filter now catches rows where prediction_json is empty even if
  actual_json is populated, and handles '[]' / null variants

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
OpenSquared
2026-06-29 23:20:17 +02:00
parent c73bedd7d0
commit 05236c31f3
2 changed files with 23 additions and 7 deletions

View File

@@ -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()

View File

@@ -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<any>(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<Snapshot | null>(null)
const [narrative, setNarrative] = useState('')
const [loading, setLoading] = useState(false)
const [refreshDebug, setRefreshDebug] = useState<any>(null)
const [loadingNarr, setLoadingNarr] = useState(false)
const [selectorOpen, setSelectorOpen] = useState(false)
const [selectedDate, setSelectedDate] = useState<string | null>(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}
/>
</div>
)