feat(pdf): adicionar exportação de ficha do consultor em PDF

- Novo endpoint GET /api/v1/consultor/{id}/pdf para download
- Serviço PDFService usando WeasyPrint para geração
- Template HTML com layout padrão governo federal
- Botão de exportar PDF no card e modal de dados brutos
This commit is contained in:
Frederico Castro
2025-12-18 22:43:42 -03:00
parent 9d93e42a12
commit 47f0a80f3f
13 changed files with 1607 additions and 9 deletions

View File

@@ -1,6 +1,9 @@
import asyncio
from io import BytesIO
from datetime import datetime
from fastapi import APIRouter, Depends, HTTPException, Query
from fastapi.responses import StreamingResponse
from typing import Optional, List
from ...application.use_cases.obter_ranking import ObterRankingUseCase
@@ -365,3 +368,45 @@ async def obter_consultor_raw(
return documento
except RuntimeError as e:
raise HTTPException(status_code=500, detail=str(e))
@router.get("/consultor/{id_pessoa}/pdf")
async def exportar_ficha_pdf(
id_pessoa: int,
repository: ConsultorRepositoryImpl = Depends(get_repository),
es_client: ElasticsearchClient = Depends(get_es_client),
store = Depends(get_ranking_store),
):
from ...application.services.pdf_service import PDFService
use_case = ObterConsultorUseCase(repository=repository)
rank = None
if store.is_ready():
found = store.get_by_id(id_pessoa)
rank = found.posicao if found else None
consultor = await use_case.executar(id_pessoa=id_pessoa, rank=rank)
if not consultor:
raise HTTPException(status_code=404, detail=f"Consultor {id_pessoa} não encontrado")
try:
pdf_service = PDFService()
raw_documento = await es_client.buscar_documento_completo(id_pessoa)
raw_source = raw_documento.get("_source") if raw_documento else {}
pdf_bytes = pdf_service.gerar_ficha_consultor(consultor, raw_source)
except Exception as e:
import traceback
traceback.print_exc()
raise HTTPException(status_code=500, detail=f"Erro ao gerar PDF: {str(e)}")
nome_sanitizado = "".join(c if c.isalnum() or c in " -_" else "_" for c in consultor.nome)
nome_arquivo = f"ficha_consultor_{id_pessoa}_{nome_sanitizado[:30]}_{datetime.now().strftime('%Y%m%d')}.pdf"
return StreamingResponse(
BytesIO(pdf_bytes),
media_type="application/pdf",
headers={
"Content-Disposition": f'attachment; filename="{nome_arquivo}"'
}
)