solventum-image-metadata/backend/app/api/templates.py
SamoilenkoVadym 5370f43345 fix(critical): fix upload errors, template selection, and add template apply endpoint
Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
2026-02-09 18:27:35 +00:00

198 lines
6.1 KiB
Python

"""
Templates API Endpoints
Handles template CRUD operations and application.
"""
from fastapi import APIRouter, Depends, HTTPException, Request, status
from sqlalchemy.ext.asyncio import AsyncSession
from typing import List
from app.core.auth import get_current_user_id
from app.core.database import get_db, AuditLogRepository
from app.services.metadata_service import get_metadata_service, MetadataService
from app.models.file import (
TemplateCreate,
TemplateResponse,
TemplateApply,
TemplatePreview
)
router = APIRouter()
@router.get("/", response_model=List[TemplateResponse])
async def list_templates(
metadata_service: MetadataService = Depends(get_metadata_service),
user_id: int = Depends(get_current_user_id)
):
"""List all available templates."""
templates = metadata_service.template_manager.list_templates()
return [TemplateResponse(**template) for template in templates]
@router.post("/", status_code=status.HTTP_201_CREATED)
async def create_template(
template_data: TemplateCreate,
db: AsyncSession = Depends(get_db),
metadata_service: MetadataService = Depends(get_metadata_service),
user_id: int = Depends(get_current_user_id)
):
"""Create a new template."""
template = {
"name": template_data.name,
"title": template_data.title,
"subject": template_data.subject,
"keywords": template_data.keywords,
"description": template_data.description
}
metadata_service.template_manager.save_template(template)
await AuditLogRepository.log_action(
db,
user_id=user_id,
action="template_create",
details=f"Created template: {template_data.name}"
)
return {"success": True, "message": "Template created", "template": template}
@router.get("/{template_name}", response_model=TemplateResponse)
async def get_template(
template_name: str,
metadata_service: MetadataService = Depends(get_metadata_service),
user_id: int = Depends(get_current_user_id)
):
"""Get template by name."""
template = metadata_service.template_manager.load_template(template_name)
if not template:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Template '{template_name}' not found"
)
return TemplateResponse(**template)
@router.delete("/{template_name}")
async def delete_template(
template_name: str,
db: AsyncSession = Depends(get_db),
metadata_service: MetadataService = Depends(get_metadata_service),
user_id: int = Depends(get_current_user_id)
):
"""Delete template."""
success = metadata_service.template_manager.delete_template(template_name)
if not success:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Template '{template_name}' not found"
)
await AuditLogRepository.log_action(
db,
user_id=user_id,
action="template_delete",
details=f"Deleted template: {template_name}"
)
return {"success": True, "message": "Template deleted"}
@router.post("/preview")
async def preview_template(
preview_data: TemplatePreview,
metadata_service: MetadataService = Depends(get_metadata_service),
user_id: int = Depends(get_current_user_id)
):
"""Preview template output."""
template = {
"title": preview_data.title,
"subject": preview_data.subject,
"keywords": preview_data.keywords
}
result = metadata_service.template_manager.apply_template(
template=template,
filename=preview_data.sample_filename,
user="user",
custom_vars=preview_data.custom_vars or {}
)
return {"preview": result}
@router.post("/apply")
async def apply_template(
apply_data: TemplateApply,
request: Request,
db: AsyncSession = Depends(get_db),
metadata_service: MetadataService = Depends(get_metadata_service),
user_id: int = Depends(get_current_user_id)
):
"""
Apply template to files in session with variable substitution.
Loads template, applies to each file with variable substitution,
updates session with suggested metadata.
"""
# Load template
template = metadata_service.template_manager.load_template(apply_data.template_name)
if not template:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Template '{apply_data.template_name}' not found"
)
# Get file session from Redis
redis = request.app.state.redis
file_session = await redis.get_file_session(apply_data.session_id)
if not file_session:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Session not found or expired"
)
files = file_session.get("files", [])
results = []
# Apply template to each selected file
for file_index in apply_data.file_indices:
if file_index >= len(files):
results.append({"index": file_index, "success": False, "error": "Invalid file index"})
continue
file_info = files[file_index]
filename = file_info.get("filename", "")
# Apply template with variable substitution
try:
metadata = metadata_service.template_manager.apply_template(
template=template,
filename=filename,
user=f"user_{user_id}",
custom_vars=apply_data.custom_vars or {}
)
# Update file's suggested metadata in session
file_info["suggested_metadata"] = metadata
results.append({"index": file_index, "success": True, "metadata": metadata})
except Exception as e:
results.append({"index": file_index, "success": False, "error": str(e)})
# Update session with modified files
file_session["files"] = files
await redis.update_file_session(apply_data.session_id, file_session)
# Log action
await AuditLogRepository.log_action(
db,
user_id=user_id,
action="template_apply",
details=f"Applied template '{apply_data.template_name}' to {len(apply_data.file_indices)} files"
)
return {"success": True, "results": results}