feat: add backfill API endpoint for historical data collection
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:
zephyrdark 2026-02-14 00:35:31 +09:00
parent 9b4d678995
commit 98d8c1115e
2 changed files with 47 additions and 0 deletions

View File

@ -25,6 +25,7 @@ from app.services.collectors import (
ETFCollector,
ETFPriceCollector,
)
from jobs.collection_job import run_backfill
logger = logging.getLogger(__name__)
@ -71,6 +72,24 @@ def _start_background_collection(collector_cls, **kwargs):
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)
async def collect_stocks(
current_user: CurrentUser,
@ -132,6 +151,16 @@ async def collect_etf_prices(
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])
async def get_collection_status(
current_user: CurrentUser,

View File

@ -157,3 +157,21 @@ def test_scheduler_has_daily_collection_job():
trigger = jobs["daily_collection"].trigger
trigger_str = str(trigger)
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