presenton/servers/fastapi/api/routers/presentation/handlers/generate_presentation.py
2025-06-23 15:13:04 +05:45

190 lines
7.2 KiB
Python

from typing import List
import uuid, aiohttp
from fastapi import HTTPException
from api.models import LogMetadata
from api.routers.presentation.handlers.export_as_pptx import ExportAsPptxHandler
from api.routers.presentation.handlers.upload_files import UploadFilesHandler
from api.routers.presentation.mixins.fetch_assets_on_generation import (
FetchAssetsOnPresentationGenerationMixin,
)
from api.routers.presentation.models import (
ExportAsRequest,
GeneratePresentationRequest,
PresentationAndPath,
PresentationPathAndEditPath,
)
from api.services.database import get_sql_session
from api.services.instances import TEMP_FILE_SERVICE
from api.services.logging import LoggingService
from api.sql_models import PresentationSqlModel, SlideSqlModel
from api.utils.utils import get_presentation_dir, is_ollama_selected
from document_processor.loader import DocumentsLoader
from ppt_config_generator.document_summary_generator import generate_document_summary
from ppt_config_generator.models import PresentationMarkdownModel
from ppt_config_generator.ppt_outlines_generator import generate_ppt_content
from ppt_generator.generator import generate_presentation
from ppt_generator.models.llm_models import (
LLM_CONTENT_TYPE_MAPPING,
LLMPresentationModel,
)
from langchain_core.output_parsers import JsonOutputParser
from ppt_generator.models.slide_model import SlideModel
output_parser = JsonOutputParser(pydantic_object=LLMPresentationModel)
class GeneratePresentationHandler(FetchAssetsOnPresentationGenerationMixin):
def __init__(self, presentation_id: str, data: GeneratePresentationRequest):
self.session = str(uuid.uuid4())
self.presentation_id = presentation_id
self.data = data
self.temp_dir = TEMP_FILE_SERVICE.create_temp_dir()
self.presentation_dir = get_presentation_dir(self.presentation_id)
def __del__(self):
TEMP_FILE_SERVICE.cleanup_temp_dir(self.temp_dir)
async def post(self, logging_service: LoggingService, log_metadata: LogMetadata):
if is_ollama_selected():
raise HTTPException(
status_code=400,
detail="Ollama is not currently supported for this endpoint",
)
documents_and_images_path = await UploadFilesHandler(
documents=self.data.documents,
images=None,
).post(logging_service, log_metadata)
summary = None
if documents_and_images_path.documents:
documents_loader = DocumentsLoader(documents_and_images_path.documents)
await documents_loader.load_documents(self.temp_dir)
print("-" * 40)
print("Generating Document Summary")
summary = await generate_document_summary(documents_loader.documents)
print("-" * 40)
print("Generating PPT Outline")
presentation_content = await generate_ppt_content(
self.data.prompt,
self.data.n_slides,
self.data.language,
summary,
)
print("-" * 40)
print("Generating Presentation")
presentation_text = (
await generate_presentation(
PresentationMarkdownModel(
title=presentation_content.title,
slides=presentation_content.slides,
notes=presentation_content.notes,
)
)
).content
print("-" * 40)
print("Parsing Presentation")
presentation_json = output_parser.parse(presentation_text)
slide_models: List[SlideModel] = []
for i, slide in enumerate(presentation_json["slides"]):
slide["index"] = i
slide["presentation"] = self.presentation_id
slide["content"] = (
LLM_CONTENT_TYPE_MAPPING[slide["type"]](**slide["content"])
.to_content()
.model_dump(mode="json")
)
slide_model = SlideModel(**slide)
slide_models.append(slide_model)
print("-" * 40)
print("Fetching Theme Colors")
async with aiohttp.ClientSession() as session:
async with session.get(
f"http://localhost/api/get-theme-from-name?theme={self.data.theme.value}",
) as response:
self.theme = await response.json()
print("-" * 40)
print("Fetching Slide Assets")
async for result in self.fetch_slide_assets(slide_models):
print(result)
slide_sql_models = [
SlideSqlModel(**each.model_dump(mode="json")) for each in slide_models
]
presentation = PresentationSqlModel(
id=self.presentation_id,
prompt=self.data.prompt,
n_slides=self.data.n_slides,
language=self.data.language,
summary=summary,
theme=self.theme,
title=presentation_content.title,
outlines=[each.model_dump() for each in presentation_content.slides],
notes=presentation_content.notes,
)
with get_sql_session() as sql_session:
sql_session.add(presentation)
sql_session.add_all(slide_sql_models)
sql_session.commit()
for each in slide_sql_models:
sql_session.refresh(each)
if self.data.export_as == "pptx":
print("-" * 40)
print("Fetching Slide Metadata for Export")
async with aiohttp.ClientSession() as session:
async with session.post(
f"http://localhost/api/slide-metadata",
json={
"url": f"http://localhost/presentation?id={self.presentation_id}",
"theme": self.theme["name"],
"customColors": self.theme["colors"],
},
) as response:
export_request_body = await response.json()
print("-" * 40)
print("Exporting Presentation")
export_request_body["presentation_id"] = self.presentation_id
export_request = ExportAsRequest(**export_request_body)
presentation_and_path = await ExportAsPptxHandler(export_request).post(
logging_service, log_metadata
)
else:
print("-" * 40)
print("Exporting Presentation as PDF")
async with aiohttp.ClientSession() as session:
async with session.post(
f"http://localhost/api/export-as-pdf",
json={
"url": f"http://localhost/pdf-maker?id={self.presentation_id}",
"title": presentation_content.title,
},
) as response:
response_json = await response.json()
presentation_and_path = PresentationAndPath(
presentation_id=self.presentation_id,
path=response_json["path"].replace("app", "static"),
)
presentation_and_path.path = presentation_and_path.path.replace("app", "static")
return PresentationPathAndEditPath(
**presentation_and_path.model_dump(),
edit_path=f"/presentation?id={self.presentation_id}",
)