CoolFace
Apppublic

internationalscholarsprogram/handbook-engine

sourceHugging Faceupdated 5mo agoView on Hugging Face
0likes
routes.py215 linesDownload Raw Back to api
1"""API router — handbook endpoints.2 3Exposes REST endpoints that the PHP application calls over HTTP.4"""5 6from __future__ import annotations7 8import logging9from typing import Any10 11from fastapi import APIRouter, HTTPException, Query12from fastapi.responses import HTMLResponse, Response13 14from app.schemas.handbook import (15    ErrorResponse,16    FontDiagnosticsResponse,17    GlobalSectionsResponse,18    HandbookRequest,19    HealthResponse,20    SectionItem,21    UniversitySectionsResponse,22    UniversityPayload,23)24 25logger = logging.getLogger(__name__)26 27router = APIRouter()28 29 30# ── Root / HF health probe ──31 32@router.get("/", tags=["system"])33async def root():34    """Root endpoint — HF Spaces probes this URL for health checks."""35    return {"status": "ok"}36 37 38# ── Health check ──39 40@router.get("/health", response_model=HealthResponse, tags=["system"])41async def health_check():42    """Health check endpoint."""43    from app.core.config import get_settings44    settings = get_settings()45    return HealthResponse(46        status="ok",47        service=settings.app_name,48        version=settings.app_version,49    )50 51 52# ── Font diagnostics ──53 54@router.get("/diagnostics/fonts", tags=["system"])55async def font_diagnostics():56    """Font diagnostics endpoint. Mirrors PHP font_diagnostics.php."""57    from app.core.fonts import font_diagnostics as _diag58    try:59        result = _diag()60        return result61    except Exception as exc:62        raise HTTPException(status_code=500, detail=str(exc))63 64 65# ── Global sections (proxy/fetch) ──66 67@router.get("/api/v1/sections/global", tags=["sections"])68async def get_global_sections(catalog_id: int = Query(0, description="Catalog ID filter")):69    """Fetch global handbook sections from the upstream API.70 71    Returns normalised section data identical to what the PHP code produces.72    """73    from app.services.data_fetcher import fetch_global_sections74 75    try:76        sections = await fetch_global_sections(catalog_id)77        return {78            "ok": True,79            "general_sections": sections,80            "count": len(sections),81        }82    except Exception as exc:83        logger.exception("Failed to fetch global sections")84        raise HTTPException(status_code=502, detail=str(exc))85 86 87# ── University sections (proxy/fetch) ──88 89@router.get("/api/v1/sections/universities", tags=["sections"])90async def get_university_sections():91    """Fetch university handbook sections from the upstream API."""92    from app.services.data_fetcher import fetch_university_sections93 94    try:95        by_uni = await fetch_university_sections()96        return {97            "ok": True,98            "universities": by_uni,99            "count": len(by_uni),100        }101    except Exception as exc:102        logger.exception("Failed to fetch university sections")103        raise HTTPException(status_code=502, detail=str(exc))104 105 106# ── Generate handbook (HTML or PDF) ──107 108@router.get("/api/v1/handbook/pdf", tags=["handbook"])109async def generate_handbook_pdf_get(110    catalog_id: int = Query(0),111    include_inactive_programs: bool = Query(False),112    debug: bool = Query(False),113):114    """Generate the ISP Handbook as a PDF download (GET for easy PHP integration)."""115    from app.services.pdf_service import generate_handbook_pdf116 117    try:118        pdf_bytes = await generate_handbook_pdf(119            catalog_id=catalog_id,120            include_inactive_programs=include_inactive_programs,121            debug=debug,122        )123        return Response(124            content=pdf_bytes,125            media_type="application/pdf",126            headers={127                "Content-Disposition": 'attachment; filename="ISP_Handbook.pdf"',128                "Cache-Control": "private, max-age=0, must-revalidate",129            },130        )131    except Exception as exc:132        logger.exception("PDF generation failed")133        raise HTTPException(status_code=500, detail=str(exc))134 135 136@router.post("/api/v1/handbook/pdf", tags=["handbook"])137async def generate_handbook_pdf_post(request: HandbookRequest):138    """Generate the ISP Handbook as a PDF download (POST with body)."""139    from app.services.pdf_service import generate_handbook_pdf140 141    try:142        pdf_bytes = await generate_handbook_pdf(143            catalog_id=request.catalog_id,144            include_inactive_programs=request.include_inactive_programs,145            debug=request.debug,146        )147        return Response(148            content=pdf_bytes,149            media_type="application/pdf",150            headers={151                "Content-Disposition": 'attachment; filename="ISP_Handbook.pdf"',152                "Cache-Control": "private, max-age=0, must-revalidate",153            },154        )155    except Exception as exc:156        logger.exception("PDF generation failed")157        raise HTTPException(status_code=500, detail=str(exc))158 159 160@router.get("/api/v1/handbook/html", tags=["handbook"])161async def generate_handbook_html_get(162    catalog_id: int = Query(0),163    include_inactive_programs: bool = Query(False),164    debug: bool = Query(False),165):166    """Generate the ISP Handbook as raw HTML (useful for preview/debugging)."""167    from app.services.pdf_service import generate_handbook_html168 169    try:170        html = await generate_handbook_html(171            catalog_id=catalog_id,172            include_inactive_programs=include_inactive_programs,173            debug=debug,174        )175        return HTMLResponse(content=html)176    except Exception as exc:177        logger.exception("HTML generation failed")178        raise HTTPException(status_code=500, detail=str(exc))179 180 181@router.post("/api/v1/handbook/render", tags=["handbook"])182async def render_handbook(request: HandbookRequest):183    """Generate handbook in the requested format (pdf or html)."""184    if request.output_format == "html":185        from app.services.pdf_service import generate_handbook_html186        try:187            html = await generate_handbook_html(188                catalog_id=request.catalog_id,189                include_inactive_programs=request.include_inactive_programs,190                debug=request.debug,191            )192            return HTMLResponse(content=html)193        except Exception as exc:194            logger.exception("HTML generation failed")195            raise HTTPException(status_code=500, detail=str(exc))196    else:197        from app.services.pdf_service import generate_handbook_pdf198        try:199            pdf_bytes = await generate_handbook_pdf(200                catalog_id=request.catalog_id,201                include_inactive_programs=request.include_inactive_programs,202                debug=request.debug,203            )204            return Response(205                content=pdf_bytes,206                media_type="application/pdf",207                headers={208                    "Content-Disposition": 'attachment; filename="ISP_Handbook.pdf"',209                    "Cache-Control": "private, max-age=0, must-revalidate",210                },211            )212        except Exception as exc:213            logger.exception("PDF generation failed")214            raise HTTPException(status_code=500, detail=str(exc))215