""" Presentation Renderer Service (MVP) - Accepts SlideSpec and brand theme reference - Persists render requests (placeholder for PPTX rendering) """ from fastapi import FastAPI, HTTPException from pydantic import BaseModel from typing import Any, Dict, Optional from datetime import datetime import json import logging import os import uuid from pathlib import Path import httpx logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) DATA_DIR = Path(os.getenv("PRESENTATION_DATA", "/data/presentations")) BRAND_REGISTRY_URL = os.getenv("BRAND_REGISTRY_URL", "http://brand-registry:9210").rstrip("/") app = FastAPI( title="Presentation Renderer Service", description="Renders SlideSpec into PPTX/PDF (MVP placeholder)", version="0.1.0" ) class RenderRequest(BaseModel): brand_id: str theme_version: str slidespec: Dict[str, Any] output: Optional[str] = "pptx" class RenderResponse(BaseModel): render_id: str status: str created_at: str theme_version: str brand_id: str def _ensure_dirs() -> None: (DATA_DIR / "requests").mkdir(parents=True, exist_ok=True) async def _fetch_theme(brand_id: str, theme_version: str) -> Dict[str, Any]: url = f"{BRAND_REGISTRY_URL}/brands/{brand_id}/themes/{theme_version}" async with httpx.AsyncClient(timeout=10.0) as client: resp = await client.get(url) if resp.status_code != 200: raise HTTPException(status_code=502, detail="Theme not found in registry") return resp.json() @app.get("/") async def root() -> Dict[str, Any]: _ensure_dirs() return { "service": "presentation-renderer", "status": "running", "registry": BRAND_REGISTRY_URL, "version": "0.1.0" } @app.get("/health") async def health() -> Dict[str, Any]: return {"status": "healthy"} @app.post("/present/render", response_model=RenderResponse) async def render_presentation(req: RenderRequest) -> RenderResponse: _ensure_dirs() theme = await _fetch_theme(req.brand_id, req.theme_version) render_id = uuid.uuid4().hex created_at = datetime.utcnow().isoformat() + "Z" payload = { "render_id": render_id, "created_at": created_at, "brand_id": req.brand_id, "theme_version": req.theme_version, "theme": theme, "slidespec": req.slidespec, "output": req.output } (DATA_DIR / "requests" / f"{render_id}.json").write_text( json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8" ) logger.info("Render request stored: %s", render_id) return RenderResponse( render_id=render_id, status="accepted", created_at=created_at, theme_version=req.theme_version, brand_id=req.brand_id )