Changes committed
This commit is contained in:
223
docengine/app/api/v1/templates.py
Normal file
223
docengine/app/api/v1/templates.py
Normal file
@@ -0,0 +1,223 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||
from fastapi.responses import FileResponse
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.database import get_db
|
||||
from app.core.dependencies import CurrentUser
|
||||
from app.core.logging_config import get_logger
|
||||
from app.repositories.document_repository import DocumentRepository, TemplateMatchRepository
|
||||
from app.repositories.template_repository import TemplateRepository
|
||||
from app.schemas.common import PaginatedResponse, SuccessResponse
|
||||
from app.schemas.document import TemplateMatchRequest, TemplateMatchResponse
|
||||
from app.schemas.template import (
|
||||
TemplateListResponse,
|
||||
TemplateRenderRequest,
|
||||
TemplateRenderResponse,
|
||||
TemplateResponse,
|
||||
)
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
router = APIRouter(prefix="/templates", tags=["Templates"])
|
||||
|
||||
|
||||
@router.get(
|
||||
"",
|
||||
response_model=PaginatedResponse[TemplateListResponse],
|
||||
summary="List Templates",
|
||||
description="List all active templates with pagination.",
|
||||
)
|
||||
def list_templates(
|
||||
page: int = Query(default=1, ge=1),
|
||||
page_size: int = Query(default=20, ge=1, le=100),
|
||||
current_user: CurrentUser = None,
|
||||
db: Session = Depends(get_db),
|
||||
) -> PaginatedResponse[TemplateListResponse]:
|
||||
"""List all active templates."""
|
||||
template_repo = TemplateRepository(db)
|
||||
offset = (page - 1) * page_size
|
||||
|
||||
templates = template_repo.get_active_templates(offset=offset, limit=page_size)
|
||||
total = template_repo.count_active()
|
||||
|
||||
items = [TemplateListResponse.model_validate(t) for t in templates]
|
||||
return PaginatedResponse.create(
|
||||
items=items,
|
||||
total=total,
|
||||
page=page,
|
||||
page_size=page_size,
|
||||
)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/{template_id}",
|
||||
response_model=TemplateResponse,
|
||||
summary="Get Template",
|
||||
description="Retrieve a template by ID with all its components.",
|
||||
)
|
||||
def get_template(
|
||||
template_id: uuid.UUID,
|
||||
current_user: CurrentUser = None,
|
||||
db: Session = Depends(get_db),
|
||||
) -> TemplateResponse:
|
||||
"""Get a template by ID."""
|
||||
template_repo = TemplateRepository(db)
|
||||
template = template_repo.get_by_id(template_id)
|
||||
if not template:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"Template '{template_id}' not found",
|
||||
)
|
||||
return TemplateResponse.model_validate(template)
|
||||
|
||||
|
||||
@router.delete(
|
||||
"/{template_id}",
|
||||
response_model=SuccessResponse,
|
||||
summary="Delete Template",
|
||||
description="Soft-delete a template by deactivating it.",
|
||||
)
|
||||
def delete_template(
|
||||
template_id: uuid.UUID,
|
||||
current_user: CurrentUser = None,
|
||||
db: Session = Depends(get_db),
|
||||
) -> SuccessResponse:
|
||||
"""Soft-delete a template."""
|
||||
template_repo = TemplateRepository(db)
|
||||
template = template_repo.get_by_id(template_id)
|
||||
if not template:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"Template '{template_id}' not found",
|
||||
)
|
||||
|
||||
template_repo.deactivate_template(template_id)
|
||||
db.commit()
|
||||
|
||||
return SuccessResponse(message=f"Template '{template_id}' deactivated successfully")
|
||||
|
||||
|
||||
@router.post(
|
||||
"/match",
|
||||
response_model=list[TemplateMatchResponse],
|
||||
summary="Match Document to Templates",
|
||||
description="Match a document against existing templates and return ranked results.",
|
||||
)
|
||||
def match_template(
|
||||
payload: TemplateMatchRequest,
|
||||
current_user: CurrentUser = None,
|
||||
db: Session = Depends(get_db),
|
||||
) -> list[TemplateMatchResponse]:
|
||||
"""Match a document against existing templates."""
|
||||
doc_repo = DocumentRepository(db)
|
||||
document = doc_repo.get_by_id(payload.document_id)
|
||||
if not document:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"Document '{payload.document_id}' not found",
|
||||
)
|
||||
|
||||
if document.status != "completed":
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=f"Document must be in 'completed' status. Current status: '{document.status}'",
|
||||
)
|
||||
|
||||
# Perform template matching
|
||||
from app.services.matching_service import MatchingService
|
||||
matching_service = MatchingService(db)
|
||||
matches = matching_service.match_document(
|
||||
document_id=payload.document_id,
|
||||
min_confidence=payload.min_confidence,
|
||||
max_results=payload.max_results,
|
||||
)
|
||||
db.commit()
|
||||
|
||||
results = []
|
||||
for match in matches:
|
||||
resp = TemplateMatchResponse(
|
||||
id=match.id,
|
||||
document_id=match.document_id,
|
||||
format_id=match.format_id,
|
||||
confidence_score=match.confidence_score,
|
||||
match_details=match.match_details,
|
||||
selected=match.selected,
|
||||
template_name=match.template.name if match.template else None,
|
||||
created_at=match.created_at,
|
||||
)
|
||||
results.append(resp)
|
||||
|
||||
return results
|
||||
|
||||
|
||||
@router.post(
|
||||
"/render",
|
||||
response_model=TemplateRenderResponse,
|
||||
summary="Render Template to PDF",
|
||||
description="Generate a PDF from a stored template with supplied data.",
|
||||
)
|
||||
def render_template(
|
||||
payload: TemplateRenderRequest,
|
||||
current_user: CurrentUser = None,
|
||||
db: Session = Depends(get_db),
|
||||
) -> TemplateRenderResponse:
|
||||
"""Render a template to PDF."""
|
||||
template_repo = TemplateRepository(db)
|
||||
template = template_repo.get_by_id(payload.template_id)
|
||||
if not template:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"Template '{payload.template_id}' not found",
|
||||
)
|
||||
|
||||
if not template.is_active:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Template is deactivated",
|
||||
)
|
||||
|
||||
from app.services.reconstruction_service import ReconstructionService
|
||||
reconstruction_service = ReconstructionService(db)
|
||||
result = reconstruction_service.render_template(
|
||||
template=template,
|
||||
data=payload.data,
|
||||
output_filename=payload.output_filename,
|
||||
images=payload.images,
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@router.get(
|
||||
"/{template_id}/download",
|
||||
summary="Download Rendered PDF",
|
||||
description="Download a previously rendered PDF.",
|
||||
)
|
||||
def download_rendered_pdf(
|
||||
template_id: uuid.UUID,
|
||||
filename: str = Query(..., description="Filename of the rendered PDF"),
|
||||
current_user: CurrentUser = None,
|
||||
db: Session = Depends(get_db),
|
||||
) -> FileResponse:
|
||||
"""Download a rendered PDF."""
|
||||
from app.storage.provider import get_storage_provider
|
||||
|
||||
storage = get_storage_provider()
|
||||
storage_path = f"rendered/{filename}"
|
||||
|
||||
if not storage.file_exists(storage_path):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"Rendered PDF '{filename}' not found",
|
||||
)
|
||||
|
||||
absolute_path = storage.get_absolute_path(storage_path)
|
||||
return FileResponse(
|
||||
path=absolute_path,
|
||||
media_type="application/pdf",
|
||||
filename=filename,
|
||||
)
|
||||
Reference in New Issue
Block a user