94 lines
3.1 KiB
Python
94 lines
3.1 KiB
Python
"""Flattened PDF export endpoint."""
|
|
|
|
import json
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
from fastapi.responses import Response
|
|
from sqlalchemy import select
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.auth.dependencies import get_current_user, verify_csrf
|
|
from app.config import settings
|
|
from app.db import get_db
|
|
from app.models.annotation_state import AnnotationState
|
|
from app.models.document import Document
|
|
from app.models.version import Version
|
|
from app.schemas.export import ExportRequest
|
|
from app.services.export.renderer import export_annotations
|
|
from app.services.storage import safe_filename
|
|
|
|
router = APIRouter(
|
|
prefix="/documents",
|
|
tags=["export"],
|
|
dependencies=[Depends(get_current_user)],
|
|
)
|
|
|
|
|
|
def _document_or_404(document_id: str, db: Session) -> Document:
|
|
document = db.scalar(
|
|
select(Document).where(Document.id == document_id, Document.deleted_at.is_(None))
|
|
)
|
|
if not document:
|
|
raise HTTPException(status_code=404, detail="Document not found")
|
|
return document
|
|
|
|
|
|
def _state_data(document_id: str, db: Session) -> list[dict[str, object]]:
|
|
state = db.scalar(select(AnnotationState).where(AnnotationState.document_id == document_id))
|
|
if not state:
|
|
return []
|
|
try:
|
|
data = json.loads(state.data)
|
|
except json.JSONDecodeError:
|
|
return []
|
|
return data if isinstance(data, list) else []
|
|
|
|
|
|
@router.post("/{document_id}/export", dependencies=[Depends(verify_csrf)])
|
|
def export_document(
|
|
document_id: str,
|
|
request: ExportRequest,
|
|
db: Session = Depends(get_db),
|
|
) -> Response:
|
|
"""Export the working layer or a selected version as a downloadable PDF."""
|
|
document = _document_or_404(document_id, db)
|
|
pdf_path = settings.PDF_STORAGE_PATH / f"{document.id}.pdf"
|
|
if not pdf_path.is_file():
|
|
raise HTTPException(status_code=404, detail="Original PDF is missing")
|
|
|
|
if not request.flatten:
|
|
content = pdf_path.read_bytes()
|
|
elif request.versionId:
|
|
version = db.scalar(
|
|
select(Version).where(
|
|
Version.id == request.versionId,
|
|
Version.document_id == document_id,
|
|
)
|
|
)
|
|
if not version:
|
|
raise HTTPException(status_code=404, detail="Version not found")
|
|
try:
|
|
version_data = json.loads(version.data)
|
|
except json.JSONDecodeError:
|
|
version_data = []
|
|
content = export_annotations(
|
|
pdf_path,
|
|
version_data if isinstance(version_data, list) else [],
|
|
settings.PDF_STORAGE_PATH / "assets" / document.id,
|
|
)
|
|
else:
|
|
content = export_annotations(
|
|
pdf_path,
|
|
_state_data(document_id, db),
|
|
settings.PDF_STORAGE_PATH / "assets" / document.id,
|
|
)
|
|
|
|
filename = safe_filename(document.original_filename)
|
|
if not filename.lower().endswith(".pdf"):
|
|
filename = f"{filename}.pdf"
|
|
filename = filename.replace('"', "'").replace("\r", "").replace("\n", "")
|
|
return Response(
|
|
content=content,
|
|
media_type="application/pdf",
|
|
headers={"Content-Disposition": f'attachment; filename="{filename}"'},
|
|
)
|