316226096b
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>
38 lines
1.3 KiB
Python
38 lines
1.3 KiB
Python
"""Lightweight job status for any authenticated user.
|
|
|
|
The admin Jobs page has full control; this exposes only which scheduled jobs
|
|
are currently running (name + progress) so the UI can show a live activity
|
|
indicator without admin rights.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from fastapi import APIRouter, Depends
|
|
|
|
from app.dependencies import require_access
|
|
from app.schemas.common import APIEnvelope
|
|
from app.services.admin_service import JOB_LABELS
|
|
|
|
router = APIRouter(tags=["jobs"])
|
|
|
|
|
|
@router.get("/jobs/running", response_model=APIEnvelope)
|
|
async def list_running_jobs(_user=Depends(require_access)) -> APIEnvelope:
|
|
"""Return scheduled jobs that are currently running, with progress."""
|
|
from app.scheduler import get_job_runtime_snapshot
|
|
|
|
snapshot = get_job_runtime_snapshot()
|
|
running = []
|
|
for name, meta in snapshot.items():
|
|
if meta.get("running"):
|
|
running.append({
|
|
"name": name,
|
|
"label": JOB_LABELS.get(name, name),
|
|
"progress_pct": meta.get("progress_pct"),
|
|
"processed": meta.get("processed"),
|
|
"total": meta.get("total"),
|
|
"current_ticker": meta.get("current_ticker"),
|
|
})
|
|
running.sort(key=lambda j: j["name"])
|
|
return APIEnvelope(status="success", data={"running": running})
|