From 332d5ad743e8ee5a8e0bb54791c1cfa8025e7dd5 Mon Sep 17 00:00:00 2001 From: OpenSquared Date: Thu, 25 Jun 2026 21:36:12 +0200 Subject: [PATCH] fix: evaluated field on event list + abort race condition - Backend: add evaluated subquery column to list SELECT so each event returns evaluated=1/0 (was missing, causing all events to appear as unevaluated regardless of filter) - Frontend: AbortController cancels the previous in-flight fetch when a new load fires, preventing stale results from overwriting current filter state Co-Authored-By: Claude Sonnet 4.6 --- backend/routers/market_events.py | 10 ++++++++-- frontend/src/pages/MarketEvents.tsx | 22 ++++++++++++++++------ 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/backend/routers/market_events.py b/backend/routers/market_events.py index 680bbeb..fc07fef 100644 --- a/backend/routers/market_events.py +++ b/backend/routers/market_events.py @@ -171,7 +171,13 @@ def list_events( # When joining with instrument_impacts an event can appear multiple times — use GROUP BY group_sql = "GROUP BY me.id" if instrument else "" - # Extra column: best instrument score for display + # Extra columns: evaluated flag + optional instrument score + evaluated_col = ( + ", (CASE WHEN EXISTS (" + "SELECT 1 FROM instrument_impacts _ei " + "WHERE _ei.source_type='event' AND _ei.source_id=me.id" + ") THEN 1 ELSE 0 END) as evaluated" + ) inst_col = ( ", MAX(COALESCE(ii.adjusted_score, ii.impact_score)) as inst_score" ", ii.direction as inst_direction" @@ -181,7 +187,7 @@ def list_events( SELECT COUNT(DISTINCT me.id) FROM market_events me {join_clause} {where_sql} """ data_sql = f""" - SELECT me.* {inst_col} + SELECT me.* {evaluated_col} {inst_col} FROM market_events me {join_clause} {where_sql} {group_sql} {order_sql} LIMIT ? OFFSET ? diff --git a/frontend/src/pages/MarketEvents.tsx b/frontend/src/pages/MarketEvents.tsx index 48adb9f..c356a49 100644 --- a/frontend/src/pages/MarketEvents.tsx +++ b/frontend/src/pages/MarketEvents.tsx @@ -1173,9 +1173,15 @@ export default function MarketEvents() { setDateTo(to) }, []) - const debounceRef = useRef | null>(null) + const debounceRef = useRef | null>(null) + const abortRef = useRef(null) const load = useCallback(async () => { + // Cancel any in-flight fetch + abortRef.current?.abort() + abortRef.current = new AbortController() + const signal = abortRef.current.signal + setLoading(true) const params = new URLSearchParams() if (search) params.set('search', search) @@ -1193,11 +1199,15 @@ export default function MarketEvents() { params.set('sort_by', instTicker ? 'instrument_score' : sortBy) params.set('sort_dir', sortDir) params.set('limit', '500') - const r = await fetch(`/api/market-events?${params}`) - if (r.ok) { - const d = await r.json() - setEvents(d.events) - setTotal(d.total) + try { + const r = await fetch(`/api/market-events?${params}`, { signal }) + if (r.ok) { + const d = await r.json() + setEvents(d.events) + setTotal(d.total) + } + } catch (e: any) { + if (e.name === 'AbortError') return // superseded by a newer load } setLoading(false) }, [search, category, level, minScore, dateFrom, dateTo, evaluated,