"""Admin API: construct static telegram parsers from a sample post.""" from __future__ import annotations from typing import Any from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel, Field from sqlalchemy.orm import Session from contracts.heuristic_profile import HeuristicProfile from contracts.sources import TelegramSourceConfig from ..database import get_db from ..models import ParseJob from ..schemas import ParseJobRead from ..services.jobs import enqueue_job from ..services import parser_builder as builder router = APIRouter(prefix="/admin/parser-builder", tags=["parser-builder"]) class GenerateRequest(BaseModel): sample_post: str = Field(min_length=1) class GenerateResponse(BaseModel): profile: dict[str, Any] class PreviewRequest(BaseModel): sample_post: str = Field(min_length=1) profile: dict[str, Any] class PreviewResponse(BaseModel): fields: dict[str, str] class CreateProfileJobRequest(BaseModel): channel: str = Field(min_length=1) profile: dict[str, Any] sample_post: str | None = None limit: int = Field(default=100, ge=1, le=1000) interval_seconds: int = Field(default=3600, ge=60, le=604800) is_active: bool = True @router.get("/target-fields") def target_fields(): return {"fields": builder.get_target_fields()} @router.post("/generate", response_model=GenerateResponse) async def generate_profile(payload: GenerateRequest): if not builder.deepseek_enabled(): raise HTTPException( status_code=503, detail="DEEPSEEK_API_KEY is not set on ca-api. Add it to .env for parser generation.", ) try: profile = await builder.generate_profile(payload.sample_post) except ValueError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc except Exception as exc: raise HTTPException( status_code=502, detail=f"DeepSeek generate failed: {exc}", ) from exc return GenerateResponse(profile=profile.model_dump()) @router.post("/preview", response_model=PreviewResponse) def preview_profile(payload: PreviewRequest): try: HeuristicProfile.model_validate(payload.profile) fields = builder.preview_with_profile(payload.sample_post, payload.profile) except Exception as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc return PreviewResponse(fields=fields) @router.post("/jobs", response_model=ParseJobRead, status_code=201) def create_profile_job(payload: CreateProfileJobRequest, db: Session = Depends(get_db)): try: cfg = TelegramSourceConfig( channel=payload.channel.strip(), limit=payload.limit, extract_mode="profile", heuristic_profile=payload.profile, sample_post=payload.sample_post, ) except Exception as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc job = ParseJob( source_type="telegram", source_config=cfg.model_dump(), interval_seconds=payload.interval_seconds, is_active=payload.is_active, status="queued", ) db.add(job) db.commit() db.refresh(job) enqueue_job(job.id, job.source_type, job.source_config) return job