diff --git a/backend/app/api/__init__.py b/backend/app/api/__init__.py index f260b90..10e58b6 100644 --- a/backend/app/api/__init__.py +++ b/backend/app/api/__init__.py @@ -1,3 +1,4 @@ from app.api.auth import router as auth_router +from app.api.admin import router as admin_router -__all__ = ["auth_router"] +__all__ = ["auth_router", "admin_router"] diff --git a/backend/app/api/admin.py b/backend/app/api/admin.py new file mode 100644 index 0000000..2586ea0 --- /dev/null +++ b/backend/app/api/admin.py @@ -0,0 +1,127 @@ +""" +Admin API for data collection management. +""" +from typing import List + +from fastapi import APIRouter, Depends, HTTPException +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: str + finished_at: str | 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 = None, +): + """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 = None, +): + """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 = None, + end_date: str = None, +): + """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 = None, +): + """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 = 20, +): + """Get recent job execution status.""" + jobs = ( + db.query(JobLog) + .order_by(JobLog.started_at.desc()) + .limit(limit) + .all() + ) + return jobs diff --git a/backend/app/main.py b/backend/app/main.py index 4b35f96..143977e 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -4,7 +4,7 @@ Galaxy-PO Backend API from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware -from app.api import auth_router +from app.api import auth_router, admin_router app = FastAPI( title="Galaxy-PO API", @@ -22,6 +22,7 @@ app.add_middleware( # Include routers app.include_router(auth_router) +app.include_router(admin_router) @app.get("/health")