ppt-tool/backend/api/v1/ppt/endpoints/template_codegen.py
Vadym Samoilenko 587f5ef6e1 Add 3 sandbox features: diagrams, mermaid, and template code-gen
Feature 1 — PPTX from Template (Code-Gen):
- backend/services/template_codegen_service.py: analyze PPTX, strip slides,
  Gemini code-gen + subprocess exec (60s timeout, auto-retry on error)
- backend/api/v1/ppt/endpoints/template_codegen.py: POST /template-codegen/generate
  (multipart: presentation_id + template_file + custom_prompt, rate-limited 3/min)
- frontend/components/TemplateCodegenExport.tsx: drag-drop modal
- Header.tsx: "Export from Template" option in export dropdown

Feature 2 — Diagrams in Slides:
- backend/models/diagram_data.py: DiagramData / FlowStep / BarChartItem models
- generate_slide_content.py: optional __diagram__ + __mermaid__ fields in LLM schema
- DiagramRenderer.tsx: pure React flowchart / bar chart / pie chart (no deps)
- SlideRenderer.tsx: chart elements render DiagramRenderer/MermaidRenderer;
  floating overlay fallback when no chart element exists in JSON layout
- V1ContentRender.tsx: diagram/mermaid overlay on built-in template slides
- generate-pptx/route.ts: addDiagramToSlide() — bar/pie via pptxgenjs addChart(),
  flowchart via addShape()+addText(), mermaid via /api/mermaid-to-image

Feature 3 — Mermaid Diagrams:
- MermaidRenderer.tsx: dynamic import mermaid@11, useEffect render, error fallback
- frontend/app/api/mermaid-to-image/route.ts: Puppeteer renders Mermaid to PNG → base64

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 18:47:31 +00:00

70 lines
2.4 KiB
Python

import os
import tempfile
import uuid
from typing import Optional
from fastapi import APIRouter, Depends, File, Form, HTTPException, UploadFile
from fastapi.responses import FileResponse
from api.middlewares.rate_limit_middleware import limiter
from models.sql.user import UserModel
from services.template_codegen_service import generate_pptx_from_template
from utils.auth_dependencies import get_current_user
from fastapi import Request
TEMPLATE_CODEGEN_ROUTER = APIRouter(prefix="/template-codegen", tags=["Template CodeGen"])
@TEMPLATE_CODEGEN_ROUTER.post("/generate")
@limiter.limit("3/minute")
async def generate_from_template(
request: Request,
presentation_id: str = Form(...),
template_file: UploadFile = File(...),
custom_prompt: Optional[str] = Form(None),
_current_user: UserModel = Depends(get_current_user),
):
"""
Generate a populated PPTX from a branded template file and existing presentation content.
Accepts: multipart/form-data with presentation_id, template_file (.pptx), custom_prompt (optional)
Returns: PPTX file download
"""
# Validate file type
filename = template_file.filename or ""
if not filename.lower().endswith(".pptx"):
raise HTTPException(status_code=400, detail="Template file must be a .pptx file")
# Save uploaded template to temp
tmp_dir = os.environ.get("TEMP_DIRECTORY", tempfile.gettempdir())
template_path = os.path.join(tmp_dir, f"template_{uuid.uuid4().hex}.pptx")
output_path = os.path.join(tmp_dir, f"output_{uuid.uuid4().hex}.pptx")
try:
content = await template_file.read()
with open(template_path, "wb") as f:
f.write(content)
result = await generate_pptx_from_template(
template_path=template_path,
presentation_id=presentation_id,
custom_prompt=custom_prompt or "",
output_path=output_path,
)
return FileResponse(
path=result["output_path"],
media_type="application/vnd.openxmlformats-officedocument.presentationml.presentation",
filename="presentation-from-template.pptx",
background=None,
)
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
except RuntimeError as e:
raise HTTPException(status_code=500, detail=str(e))
finally:
# Clean up template file
try:
os.unlink(template_path)
except Exception:
pass