Add Query parameter validation with regex patterns for date fields (YYYYMMDD format) and numeric constraints for limit parameter. Update JobLogResponse to use datetime types instead of strings for proper date serialization. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
129 lines
3.9 KiB
Python
129 lines
3.9 KiB
Python
"""
|
|
Admin API for data collection management.
|
|
"""
|
|
from typing import List
|
|
from datetime import datetime
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
from sqlalchemy.orm import Session
|
|
from pydantic import BaseModel
|
|
|
|
from app.core.database import get_db
|
|
from app.api.deps import CurrentUser
|
|
from app.models.stock import JobLog
|
|
from app.services.collectors import (
|
|
StockCollector,
|
|
SectorCollector,
|
|
PriceCollector,
|
|
ValuationCollector,
|
|
)
|
|
|
|
router = APIRouter(prefix="/api/admin", tags=["admin"])
|
|
|
|
|
|
class JobLogResponse(BaseModel):
|
|
id: int
|
|
job_name: str
|
|
status: str
|
|
started_at: datetime
|
|
finished_at: datetime | None
|
|
records_count: int | None
|
|
error_msg: str | None
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class CollectResponse(BaseModel):
|
|
message: str
|
|
job_id: int
|
|
|
|
|
|
@router.post("/collect/stocks", response_model=CollectResponse)
|
|
async def collect_stocks(
|
|
current_user: CurrentUser,
|
|
db: Session = Depends(get_db),
|
|
biz_day: str = Query(None, regex=r"^\d{8}$", description="Business day in YYYYMMDD format"),
|
|
):
|
|
"""Collect stock master data from KRX."""
|
|
try:
|
|
collector = StockCollector(db, biz_day=biz_day)
|
|
job_log = collector.run()
|
|
return CollectResponse(
|
|
message=f"Stock collection completed: {job_log.records_count} records",
|
|
job_id=job_log.id,
|
|
)
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
@router.post("/collect/sectors", response_model=CollectResponse)
|
|
async def collect_sectors(
|
|
current_user: CurrentUser,
|
|
db: Session = Depends(get_db),
|
|
biz_day: str = Query(None, regex=r"^\d{8}$", description="Business day in YYYYMMDD format"),
|
|
):
|
|
"""Collect sector classification data from WISEindex."""
|
|
try:
|
|
collector = SectorCollector(db, biz_day=biz_day)
|
|
job_log = collector.run()
|
|
return CollectResponse(
|
|
message=f"Sector collection completed: {job_log.records_count} records",
|
|
job_id=job_log.id,
|
|
)
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
@router.post("/collect/prices", response_model=CollectResponse)
|
|
async def collect_prices(
|
|
current_user: CurrentUser,
|
|
db: Session = Depends(get_db),
|
|
start_date: str = Query(None, regex=r"^\d{8}$", description="Start date in YYYYMMDD format"),
|
|
end_date: str = Query(None, regex=r"^\d{8}$", description="End date in YYYYMMDD format"),
|
|
):
|
|
"""Collect price data using pykrx."""
|
|
try:
|
|
collector = PriceCollector(db, start_date=start_date, end_date=end_date)
|
|
job_log = collector.run()
|
|
return CollectResponse(
|
|
message=f"Price collection completed: {job_log.records_count} records",
|
|
job_id=job_log.id,
|
|
)
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
@router.post("/collect/valuations", response_model=CollectResponse)
|
|
async def collect_valuations(
|
|
current_user: CurrentUser,
|
|
db: Session = Depends(get_db),
|
|
biz_day: str = Query(None, regex=r"^\d{8}$", description="Business day in YYYYMMDD format"),
|
|
):
|
|
"""Collect valuation data from KRX."""
|
|
try:
|
|
collector = ValuationCollector(db, biz_day=biz_day)
|
|
job_log = collector.run()
|
|
return CollectResponse(
|
|
message=f"Valuation collection completed: {job_log.records_count} records",
|
|
job_id=job_log.id,
|
|
)
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
@router.get("/collect/status", response_model=List[JobLogResponse])
|
|
async def get_collection_status(
|
|
current_user: CurrentUser,
|
|
db: Session = Depends(get_db),
|
|
limit: int = Query(20, gt=0, le=100, description="Number of records to return"),
|
|
):
|
|
"""Get recent job execution status."""
|
|
jobs = (
|
|
db.query(JobLog)
|
|
.order_by(JobLog.started_at.desc())
|
|
.limit(limit)
|
|
.all()
|
|
)
|
|
return jobs
|