|
|
|
|
|
|
|
|
import pandas as pd |
|
|
import re |
|
|
import os |
|
|
import requests |
|
|
import matplotlib.font_manager as fm |
|
|
from pydantic import BaseModel, Field, ConfigDict |
|
|
from typing import List, Tuple, Optional |
|
|
from fpdf import FPDF |
|
|
from datetime import datetime |
|
|
|
|
|
|
|
|
class Historico(BaseModel): |
|
|
entradas: List[Tuple[str, str]] = [] |
|
|
|
|
|
def adicionar(self, pergunta: str, resposta: str): |
|
|
if pergunta and resposta: |
|
|
self.entradas.append((pergunta, resposta)) |
|
|
|
|
|
|
|
|
def descricao_colunas(df: pd.DataFrame) -> str: |
|
|
descricao = "\n".join([f"`{col}`: {str(df[col].dtype)}" for col in df.columns]) |
|
|
return "Colunas do DataFrame:\n" + descricao |
|
|
|
|
|
def limpar_codigo_pandas(codigo: str) -> str: |
|
|
codigo = re.sub(r'```(?:python)?\n?', '', codigo) |
|
|
codigo = re.sub(r'```', '', codigo) |
|
|
linhas = [linha.strip() for linha in codigo.split('\n') if linha.strip()] |
|
|
codigo_filtrado = [l for l in linhas if not (l.startswith('#') or 'resposta:' in l.lower())] |
|
|
return codigo_filtrado[-1] if codigo_filtrado else "" |
|
|
|
|
|
|
|
|
FONT_PATH = "" |
|
|
def setup_font(): |
|
|
global FONT_PATH |
|
|
FONT_NAME_TO_FIND = "DejaVu Sans" |
|
|
try: |
|
|
FONT_PATH = fm.findfont(FONT_NAME_TO_FIND, fallback_to_default=True) |
|
|
if not os.path.exists(FONT_PATH): raise FileNotFoundError |
|
|
print(f"✅ Fonte encontrada localmente: {FONT_PATH}") |
|
|
except Exception: |
|
|
print(f"⚠️ Fonte '{FONT_NAME_TO_FIND}' não encontrada. Baixando alternativa (Roboto)...") |
|
|
FONT_FILE_NAME = "Roboto-Regular.ttf" |
|
|
FONT_URL = "https://github.com/google/fonts/raw/main/ofl/roboto/Roboto-Regular.ttf" |
|
|
if not os.path.exists(FONT_FILE_NAME): |
|
|
try: |
|
|
response = requests.get(FONT_URL) |
|
|
response.raise_for_status() |
|
|
with open(FONT_FILE_NAME, "wb") as f: f.write(response.content) |
|
|
FONT_PATH = FONT_FILE_NAME |
|
|
print(f"✅ Fonte alternativa baixada!") |
|
|
except Exception as e: |
|
|
print(f"❌ FALHA CRÍTICA ao baixar fonte: {e}") |
|
|
FONT_PATH = "" |
|
|
else: |
|
|
FONT_PATH = FONT_FILE_NAME |
|
|
print(f"✅ Fonte alternativa já existe.") |
|
|
return FONT_PATH |
|
|
|
|
|
|
|
|
def gerar_pdf_report(historico: Historico, font_path: str): |
|
|
if not historico or not historico.entradas: |
|
|
print("⚠️ Tentativa de gerar PDF com histórico vazio.") |
|
|
return None |
|
|
if not font_path or not os.path.exists(font_path): |
|
|
print(f"❌ Erro ao gerar PDF: Arquivo da fonte não encontrado em '{font_path}'.") |
|
|
return None |
|
|
|
|
|
try: |
|
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") |
|
|
caminho_pdf = f"relatorio_consultas_{timestamp}.pdf" |
|
|
pdf = FPDF() |
|
|
pdf.add_page() |
|
|
pdf.add_font('CustomFont', '', font_path, uni=True) |
|
|
pdf.set_font('CustomFont', '', 16) |
|
|
pdf.cell(0, 10, "Relatório de Consultas ao CSV", ln=True, align="C") |
|
|
pdf.ln(10) |
|
|
|
|
|
for i, (pergunta, resposta) in enumerate(historico.entradas, 1): |
|
|
pdf.set_font('CustomFont', '', 12) |
|
|
pdf.multi_cell(0, 8, f"Pergunta {i}: {pergunta}") |
|
|
pdf.ln(2) |
|
|
pdf.set_font('CustomFont', '', 10) |
|
|
pdf.multi_cell(0, 6, f"Resposta: {resposta}") |
|
|
pdf.ln(8) |
|
|
pdf.cell(0, 0, '', 'T'); pdf.ln(8) |
|
|
|
|
|
pdf.output(caminho_pdf) |
|
|
print(f"✅ PDF gerado: {caminho_pdf}") |
|
|
return caminho_pdf |
|
|
except Exception as e: |
|
|
print(f"❌ Erro crítico ao gerar PDF: {e}") |
|
|
return None |