Fix score refresh, add granular fetch and live job status
Deploy / lint (push) Successful in 6s
Deploy / test (push) Successful in 35s
Deploy / deploy (push) Successful in 22s

Scores never updated ("101d ago"): get_score only recomputes stale/
missing dimensions, but nothing marked them stale on new data, and there
was no scheduled scoring job.
- Fetch endpoint force-recomputes dimensions + composite.
- Scheduled scan (scan_all_tickers) refreshes scores per ticker, so
  scores stay current globally, not just on manual fetch.

Granular fetch: /ingestion/fetch accepts a sources filter; the freshness
bar gets a per-row refresh button (OHLCV/Sentiment/Fundamentals fetch
that provider only — marked paid; S/R/Scores recompute for free). Header
button is now "Fetch All".

Job visibility: GET /jobs/running (any user) + sidebar live indicator
showing running scheduled jobs with progress, polled every 10s.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-14 13:10:15 +02:00
parent 3aebfd72d3
commit 316226096b
10 changed files with 296 additions and 94 deletions
+11 -2
View File
@@ -13,8 +13,17 @@ export interface FetchDataResult {
sources: Record<string, IngestionSourceResult>;
}
export function fetchData(symbol: string) {
/** Provider sources that cost an API call/quota. */
export type FetchSource = 'ohlcv' | 'sentiment' | 'fundamentals';
/** Source selector: omit → fetch all; array → those providers; 'recompute' → derived only (free). */
export type FetchSelector = FetchSource[] | 'recompute';
export function fetchData(symbol: string, sources?: FetchSelector) {
let sourcesParam: string | undefined;
if (sources === 'recompute') sourcesParam = 'recompute';
else if (sources && sources.length) sourcesParam = sources.join(',');
const params = sourcesParam ? { sources: sourcesParam } : undefined;
return apiClient
.post<FetchDataResult>(`ingestion/fetch/${symbol}`)
.post<FetchDataResult>(`ingestion/fetch/${symbol}`, null, { params })
.then((r) => r.data);
}
+16
View File
@@ -0,0 +1,16 @@
import apiClient from './client';
export interface RunningJob {
name: string;
label: string;
progress_pct: number | null;
processed: number | null;
total: number | null;
current_ticker: string | null;
}
export function getRunningJobs() {
return apiClient
.get<{ running: RunningJob[] }>('jobs/running')
.then((r) => r.data);
}