ppt-tool/backend/utils/export_utils.py
Vadym Samoilenko c97841f6d1 Phase 6: Export & Polish — brand export, client dashboard, retention, analytics
- Brand-enforced export pipeline (PPTX/PDF with auto brand fonts/colors/logo)
- Client library dashboard with two-level navigation (client grid → detail tabs)
- Data retention service with ARQ cron jobs (daily cleanup + weekly purge)
- Brand-adaptive UI theme via CSS custom properties (dynamic per client)
- Analytics dashboard with overview, usage, quality, and performance metrics

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 16:41:58 +00:00

90 lines
3.3 KiB
Python

import os
import uuid
from typing import Literal, Optional
import aiohttp
from fastapi import HTTPException
from pathvalidate import sanitize_filename
from sqlalchemy.ext.asyncio import AsyncSession
from models.pptx_models import PptxPresentationModel
from models.presentation_and_path import PresentationAndPath
from services.pptx_presentation_creator import PptxPresentationCreator
from services.temp_file_service import TEMP_FILE_SERVICE
from utils.asset_directory_utils import get_exports_directory
async def export_presentation(
presentation_id: uuid.UUID,
title: str,
export_as: Literal["pptx", "pdf"],
client_id: Optional[uuid.UUID] = None,
session: Optional[AsyncSession] = None,
) -> PresentationAndPath:
if export_as == "pptx":
# Get the converted PPTX model from the Next.js service
async with aiohttp.ClientSession() as http:
async with http.get(
f"http://localhost/api/presentation_to_pptx_model?id={presentation_id}"
) as response:
if response.status != 200:
error_text = await response.text()
print(f"Failed to get PPTX model: {error_text}")
raise HTTPException(
status_code=500,
detail="Failed to convert presentation to PPTX model",
)
pptx_model_data = await response.json()
# Create PPTX file using the converted model
pptx_model = PptxPresentationModel(**pptx_model_data)
# Apply brand enforcement if client has brand config
if client_id and session:
try:
from services.brand_enforcement_service import BrandEnforcementService
from models.sql.brand_config import BrandConfigModel
from sqlalchemy import select as sa_select
stmt = sa_select(BrandConfigModel).where(
BrandConfigModel.client_id == client_id
)
result = await session.execute(stmt)
brand = result.scalar_one_or_none()
if brand:
enforcer = BrandEnforcementService()
pptx_model = enforcer.enforce_on_pptx_model(pptx_model, brand)
except Exception as e:
print(f"Brand enforcement skipped: {e}")
temp_dir = TEMP_FILE_SERVICE.create_temp_dir()
pptx_creator = PptxPresentationCreator(pptx_model, temp_dir)
await pptx_creator.create_ppt()
export_directory = get_exports_directory()
pptx_path = os.path.join(
export_directory,
f"{sanitize_filename(title or str(uuid.uuid4()))}.pptx",
)
pptx_creator.save(pptx_path)
return PresentationAndPath(
presentation_id=presentation_id,
path=pptx_path,
)
else:
async with aiohttp.ClientSession() as http:
async with http.post(
"http://localhost/api/export-as-pdf",
json={
"id": str(presentation_id),
"title": sanitize_filename(title or str(uuid.uuid4())),
},
) as response:
response_json = await response.json()
return PresentationAndPath(
presentation_id=presentation_id,
path=response_json["path"],
)