ppt-tool/backend/models/sql/presentation.py
Vadym Samoilenko 69a8829750 Phase 3: Bug fixes, feature enhancements, and polish
P0 Critical: presentation isolation (client scoping), storage super_admin fix,
template selection in worker, IMAGE_PROVIDERS list fix.

P1 High: template layout management UI (delete/filter/bulk), slide-based parsing
mode, LLM model listing & connection test, settings persistence to DB (Fernet
encryption), logout button.

P2 Polish: storage improvements (master deck files, per-client breakdown, bulk
delete, hard purge, client selector), image generation error visibility
(__image_error__ badge), hamster wheel loading animation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 12:58:52 +00:00

105 lines
4.2 KiB
Python

from datetime import datetime
from typing import List, Optional
import uuid
from sqlalchemy import JSON, Column, DateTime, ForeignKey, String
from sqlmodel import Boolean, Field, SQLModel
from models.presentation_layout import PresentationLayoutModel
from models.presentation_outline_model import PresentationOutlineModel
from models.presentation_structure_model import PresentationStructureModel
from utils.datetime_utils import get_current_utc_datetime
class PresentationModel(SQLModel, table=True):
__tablename__ = "presentations"
id: uuid.UUID = Field(primary_key=True, default_factory=uuid.uuid4)
content: str
n_slides: int
language: str
title: Optional[str] = None
file_paths: Optional[List[str]] = Field(sa_column=Column(JSON), default=None)
outlines: Optional[dict] = Field(sa_column=Column(JSON), default=None)
created_at: datetime = Field(
sa_column=Column(
DateTime(timezone=True), nullable=False, default=get_current_utc_datetime
),
)
updated_at: datetime = Field(
sa_column=Column(
DateTime(timezone=True),
nullable=False,
default=get_current_utc_datetime,
onupdate=get_current_utc_datetime,
),
)
layout: Optional[dict] = Field(sa_column=Column(JSON), default=None)
structure: Optional[dict] = Field(sa_column=Column(JSON), default=None)
instructions: Optional[str] = Field(sa_column=Column(String), default=None)
tone: Optional[str] = Field(sa_column=Column(String), default=None)
verbosity: Optional[str] = Field(sa_column=Column(String), default=None)
include_table_of_contents: bool = Field(sa_column=Column(Boolean), default=False)
include_title_slide: bool = Field(sa_column=Column(Boolean), default=True)
web_search: bool = Field(sa_column=Column(Boolean), default=False)
# Multi-tenant fields (all nullable for backward compat with existing data)
owner_id: Optional[uuid.UUID] = Field(
sa_column=Column(ForeignKey("users.id"), nullable=True), default=None
)
client_id: Optional[uuid.UUID] = Field(
sa_column=Column(ForeignKey("clients.id"), nullable=True), default=None
)
master_deck_id: Optional[uuid.UUID] = Field(
sa_column=Column(ForeignKey("master_decks.id"), nullable=True), default=None
)
status: str = Field(sa_column=Column(String, default="draft")) # draft, in_review, approved
review_comment: Optional[str] = Field(
sa_column=Column(String, nullable=True), default=None
)
template_name: Optional[str] = Field(
sa_column=Column(String, nullable=True), default=None
) # e.g. "general", "modern", "custom-{uuid}"
source_type: Optional[str] = Field(
sa_column=Column(String, nullable=True), default=None
) # brief, url, manual
is_saved: bool = Field(sa_column=Column(Boolean, default=False))
deleted_at: Optional[datetime] = Field(
sa_column=Column(DateTime(timezone=True), nullable=True), default=None
)
def get_new_presentation(self):
return PresentationModel(
id=uuid.uuid4(),
content=self.content,
n_slides=self.n_slides,
language=self.language,
title=self.title,
file_paths=self.file_paths,
outlines=self.outlines,
layout=self.layout,
structure=self.structure,
instructions=self.instructions,
tone=self.tone,
verbosity=self.verbosity,
include_table_of_contents=self.include_table_of_contents,
include_title_slide=self.include_title_slide,
)
def get_presentation_outline(self):
if not self.outlines:
return None
return PresentationOutlineModel(**self.outlines)
def get_layout(self):
return PresentationLayoutModel(**self.layout)
def set_layout(self, layout: PresentationLayoutModel):
self.layout = layout.model_dump()
def get_structure(self):
if not self.structure:
return None
return PresentationStructureModel(**self.structure)
def set_structure(self, structure: PresentationStructureModel):
self.structure = structure.model_dump()