from datetime import date from fastapi import APIRouter, BackgroundTasks, Depends, Request from sqlalchemy import desc, select from sqlalchemy.orm import Session from app.config import Settings, get_settings from app.db import SessionLocal, get_db from app.models import CrawlRun, Employee from app.security import require_admin from app.services.admin_data import employee_display_payload, list_employees_page, run_detail_payload, run_payload, stats_payload from app.services.crawl_control import get_running_run, run_crawl_if_idle from app.version import BACKEND_VERSION, FRONTEND_VERSION router = APIRouter(prefix="/api") @router.get("/health") def health() -> dict: return {"status": "ok", "backend_version": BACKEND_VERSION, "frontend_version": FRONTEND_VERSION} @router.get("/employees") def list_employees( request: Request, status: str | None = None, q: str | None = None, started_from: date | None = None, started_to: date | None = None, has_email: bool | None = None, sort: str = "full_name", direction: str = "asc", limit: int = 50, offset: int = 0, db: Session = Depends(get_db), settings: Settings = Depends(get_settings), ) -> dict: require_admin(request, settings) return list_employees_page( db, status=status, q=q, started_from=started_from, started_to=started_to, has_email=has_email, sort=sort, direction=direction, limit=limit, offset=offset, ) @router.get("/employees/{employee_id}") def get_employee( employee_id: int, request: Request, db: Session = Depends(get_db), settings: Settings = Depends(get_settings), ) -> dict: require_admin(request, settings) employee = db.get(Employee, employee_id) if not employee: return {"error": "not_found"} return _employee_detail(employee) @router.get("/crawl-runs") def list_crawl_runs( request: Request, limit: int = 20, db: Session = Depends(get_db), settings: Settings = Depends(get_settings), ) -> dict: require_admin(request, settings) runs = db.scalars(select(CrawlRun).order_by(desc(CrawlRun.started_at)).limit(limit)).all() return {"items": [run_payload(run) for run in runs]} @router.get("/crawl-runs/latest") def latest_crawl_run( request: Request, db: Session = Depends(get_db), settings: Settings = Depends(get_settings), ) -> dict: require_admin(request, settings) running = get_running_run(db) latest = db.scalar(select(CrawlRun).order_by(desc(CrawlRun.started_at)).limit(1)) return {"running": run_payload(running), "latest": run_payload(latest)} @router.get("/crawl-runs/{run_id}") def get_crawl_run( run_id: int, request: Request, db: Session = Depends(get_db), settings: Settings = Depends(get_settings), ) -> dict: require_admin(request, settings) run = db.get(CrawlRun, run_id) if not run: return {"error": "not_found"} return run_detail_payload(db, run) or {"error": "not_found"} @router.post("/crawl-runs") def trigger_crawl( request: Request, background_tasks: BackgroundTasks, db: Session = Depends(get_db), settings: Settings = Depends(get_settings), ) -> dict: require_admin(request, settings) running = get_running_run(db) if running: return {"status": "already_running", "run": run_payload(running)} def _crawl() -> None: with SessionLocal() as db: run_crawl_if_idle(db, settings) background_tasks.add_task(_crawl) return {"status": "scheduled"} @router.get("/stats") def stats( request: Request, db: Session = Depends(get_db), settings: Settings = Depends(get_settings), ) -> dict: require_admin(request, settings) return stats_payload(db) def _employee_summary(employee: Employee) -> dict: return employee_display_payload(employee) def _employee_detail(employee: Employee) -> dict: data = _employee_summary(employee) data["current_data"] = employee.current_data data["tabs"] = [{"title": tab.title, "href": tab.href, "data_index": tab.data_index} for tab in employee.tabs] return data def _run_summary(run: CrawlRun) -> dict: return run_payload(run) or {}