feat: add backfill API endpoint for historical data collection
All checks were successful
Deploy to Production / deploy (push) Successful in 1m12s
All checks were successful
Deploy to Production / deploy (push) Successful in 1m12s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
9b4d678995
commit
98d8c1115e
@ -25,6 +25,7 @@ from app.services.collectors import (
|
|||||||
ETFCollector,
|
ETFCollector,
|
||||||
ETFPriceCollector,
|
ETFPriceCollector,
|
||||||
)
|
)
|
||||||
|
from jobs.collection_job import run_backfill
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -71,6 +72,24 @@ def _start_background_collection(collector_cls, **kwargs):
|
|||||||
thread.start()
|
thread.start()
|
||||||
|
|
||||||
|
|
||||||
|
def _run_backfill_background(start_year: int):
|
||||||
|
"""Run backfill in a background thread."""
|
||||||
|
try:
|
||||||
|
run_backfill(start_year=start_year)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error("Background backfill failed: %s", e)
|
||||||
|
|
||||||
|
|
||||||
|
def run_backfill_background(start_year: int):
|
||||||
|
"""Start backfill in a daemon thread."""
|
||||||
|
thread = threading.Thread(
|
||||||
|
target=_run_backfill_background,
|
||||||
|
args=(start_year,),
|
||||||
|
daemon=True,
|
||||||
|
)
|
||||||
|
thread.start()
|
||||||
|
|
||||||
|
|
||||||
@router.post("/collect/stocks", response_model=CollectResponse)
|
@router.post("/collect/stocks", response_model=CollectResponse)
|
||||||
async def collect_stocks(
|
async def collect_stocks(
|
||||||
current_user: CurrentUser,
|
current_user: CurrentUser,
|
||||||
@ -132,6 +151,16 @@ async def collect_etf_prices(
|
|||||||
return CollectResponse(message="ETF price collection started")
|
return CollectResponse(message="ETF price collection started")
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/collect/backfill", response_model=CollectResponse)
|
||||||
|
async def collect_backfill(
|
||||||
|
current_user: CurrentUser,
|
||||||
|
start_year: int = Query(2000, ge=1990, le=2026, description="Start year for backfill"),
|
||||||
|
):
|
||||||
|
"""Backfill historical price data from start_year to today (runs in background)."""
|
||||||
|
run_backfill_background(start_year)
|
||||||
|
return CollectResponse(message=f"Backfill started from {start_year}")
|
||||||
|
|
||||||
|
|
||||||
@router.get("/collect/status", response_model=List[JobLogResponse])
|
@router.get("/collect/status", response_model=List[JobLogResponse])
|
||||||
async def get_collection_status(
|
async def get_collection_status(
|
||||||
current_user: CurrentUser,
|
current_user: CurrentUser,
|
||||||
|
|||||||
@ -157,3 +157,21 @@ def test_scheduler_has_daily_collection_job():
|
|||||||
trigger = jobs["daily_collection"].trigger
|
trigger = jobs["daily_collection"].trigger
|
||||||
trigger_str = str(trigger)
|
trigger_str = str(trigger)
|
||||||
assert "18" in trigger_str # hour=18
|
assert "18" in trigger_str # hour=18
|
||||||
|
|
||||||
|
|
||||||
|
def test_backfill_api_endpoint(client, admin_auth_headers):
|
||||||
|
"""POST /api/admin/collect/backfill should trigger backfill."""
|
||||||
|
with patch("app.api.admin.run_backfill_background") as mock_backfill:
|
||||||
|
response = client.post(
|
||||||
|
"/api/admin/collect/backfill?start_year=2020",
|
||||||
|
headers=admin_auth_headers,
|
||||||
|
)
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert "backfill" in response.json()["message"].lower()
|
||||||
|
mock_backfill.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
def test_backfill_api_requires_auth(client):
|
||||||
|
"""POST /api/admin/collect/backfill should require authentication."""
|
||||||
|
response = client.post("/api/admin/collect/backfill")
|
||||||
|
assert response.status_code == 401
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user