feature: improve admin directory and crawl progress
This commit is contained in:
84
app/api.py
84
app/api.py
@@ -1,12 +1,15 @@
|
||||
from datetime import date
|
||||
|
||||
from fastapi import APIRouter, BackgroundTasks, Depends, Request
|
||||
from sqlalchemy import desc, or_, select
|
||||
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.crawler import run_crawl
|
||||
from app.services.admin_data import employee_display_payload, list_employees_page, 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")
|
||||
@@ -22,20 +25,29 @@ 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)
|
||||
stmt = select(Employee)
|
||||
if status:
|
||||
stmt = stmt.where(Employee.status == status)
|
||||
if q:
|
||||
pattern = f"%{q}%"
|
||||
stmt = stmt.where(or_(Employee.full_name.ilike(pattern), Employee.canonical_url.ilike(pattern)))
|
||||
employees = db.scalars(stmt.order_by(Employee.full_name).limit(limit).offset(offset)).all()
|
||||
return {"items": [_employee_summary(item) for item in employees], "limit": limit, "offset": offset}
|
||||
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}")
|
||||
@@ -61,34 +73,53 @@ def list_crawl_runs(
|
||||
) -> dict:
|
||||
require_admin(request, settings)
|
||||
runs = db.scalars(select(CrawlRun).order_by(desc(CrawlRun.started_at)).limit(limit)).all()
|
||||
return {"items": [_run_summary(run) for run in runs]}
|
||||
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.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(db, settings)
|
||||
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 {
|
||||
"id": employee.id,
|
||||
"full_name": employee.full_name,
|
||||
"status": employee.status,
|
||||
"canonical_url": employee.canonical_url,
|
||||
"last_seen_at": employee.last_seen_at.isoformat() if employee.last_seen_at else None,
|
||||
"dismissed_at": employee.dismissed_at.isoformat() if employee.dismissed_at else None,
|
||||
}
|
||||
return employee_display_payload(employee)
|
||||
|
||||
|
||||
def _employee_detail(employee: Employee) -> dict:
|
||||
@@ -99,15 +130,4 @@ def _employee_detail(employee: Employee) -> dict:
|
||||
|
||||
|
||||
def _run_summary(run: CrawlRun) -> dict:
|
||||
return {
|
||||
"id": run.id,
|
||||
"source_url": run.source_url,
|
||||
"status": run.status,
|
||||
"started_at": run.started_at.isoformat() if run.started_at else None,
|
||||
"finished_at": run.finished_at.isoformat() if run.finished_at else None,
|
||||
"found_count": run.found_count,
|
||||
"parsed_count": run.parsed_count,
|
||||
"error_count": run.error_count,
|
||||
"dismissed_count": run.dismissed_count,
|
||||
"message": run.message,
|
||||
}
|
||||
return run_payload(run) or {}
|
||||
|
||||
Reference in New Issue
Block a user