import io
import re
import random
import streamlit as st
from PyPDF2 import PdfReader, PdfWriter
from reportlab.lib.pagesizes import A4
from reportlab.pdfgen import canvas
from reportlab.lib.units import cm
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, KeepTogether
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
import mistune
#==========================novos
AREA_ATUACAO = ["Eduação Básica", "Educação Superior", "Saúde", "Assistência Social", "Misto", "Desconhecido" ]
def traduz_area_atuacao(area):
if area == "educacao_basica": return AREA_ATUACAO[0]
elif area == "educacao_superior": return AREA_ATUACAO[1]
elif area == "saude": return AREA_ATUACAO[2]
elif area == "assistencia_social": return AREA_ATUACAO[3]
elif area == "misto": return AREA_ATUACAO[4]
return AREA_ATUACAO[5]
STATUS = ["Presente", "Parcial", "Ausente", "Pedente", "Não Aplicável", "Atendido com Ressalvas"]
def traduz_status(status):
if status == "presente": return STATUS[0]
elif status == "parcial": return STATUS[1]
elif status == "ausente": return STATUS[2]
elif status == "pendente": return STATUS[3]
elif status == "atendido_com_ressalvas": return STATUS[5]
return STATUS[4]
def traduz_status_item(status):
if status == "presente": return 0
elif status == "parcial": return 1
elif status == "ausente": return 2
elif status == "pendente": return 3
elif status == "atendido_com_ressalvas": 5
return 4
def traduz_item_status(status):
if status == 0: return "presente"
elif status == 1: return "parcial"
elif status == 2: return "ausente"
elif status == 3: return "pendente"
elif status == 5 : return "atendido_com_ressalvas"
return "naoaplicavel"
def gerarStatusGrupo(gg, result):
contagem = [0, 0, 0, 0, 0, 0]
tami = len(result["nota_tecnica"]["checklists"]["checklists"][gg]["itens"])
for i in range(tami):
pos = traduz_status_item(result["nota_tecnica"]["checklists"]["checklists"][gg]["itens"][i]["status"])
contagem[pos] = contagem[pos] + 1
if contagem[0] == 2 and tami == 3: return traduz_item_status(1)
if contagem[0] == 1 and tami == 2: return traduz_item_status(1)
maior = -1
pred = 0
for i in range(len(contagem)):
if contagem[i] > maior:
maior = contagem[i]
pred = i
if contagem[i] == tami :
return traduz_item_status(i)
return traduz_item_status(pred)
#================antigos
def traduz_categoria(cat):
if cat == "CAT_UCP": return CATEGORIAS[0]
elif cat == "CAT_UE": return CATEGORIAS[1]
elif cat == "CAT_DOM": return CATEGORIAS[2]
return None
# Opções de classificação/temas/itens
CATEGORIAS = [
"Bens de Uso Comum do Povo",
"Bens de Uso Especial",
"Bens Dominicais ou Dominiais",
]
def traduz_tema(tema):
if tema == "USO_IRREGULAR": return TEMAS[0]
elif tema == "INTERVENCAO_SEM_AUTORIZACAO": return TEMAS[1]
elif tema == "DESCARACTERIZACAO": return TEMAS[2]
elif tema == "POSSE_OCUPACAO_ILICITA": return TEMAS[3]
elif tema == "INADIMPLENCIA_FORO_TAXA": return TEMAS[4]
return None
TEMAS = [
"Uso Irregular",
"Intervenção sem Autorização",
"Descaracterização",
"Posse Ocupação Ilícita",
"Inadimplência",
]
def traduz_legislacao(legislacao):
return legislacao
LEGISLACAO = [
"IN SPU nº nº 23/2020",
"Decreto-Lei nº 2.398/87, Art. 6º",
"Lei nº 9.636/98, Art. 11",
"Código Civil (Arts. 98-103)",
"Lei nº 9.636/98, Art. 10º e Art. 47º, §1º",
"Decreto-Lei nº 9.760/46, Art. 2º (para Terrenos de Marinha)",
]
def traduz_sancoes(sancoes):
if sancoes == "EMBARGO": return SANCOES[0]
elif sancoes == "MULTA_MENSAL": return SANCOES[1]
elif sancoes == "DEMOLICAO_REMOCAO": return SANCOES[2]
elif sancoes == "DESOCUPACAO": return SANCOES[3]
elif sancoes == "INDENIZACAO": return SANCOES[4]
elif sancoes == "PODER_DE_POLICIA_AUTOEXECUTORIEDADE": return SANCOES[5]
return SANCOES[6]
SANCOES = [
"Embargo",
"Multa Mensal",
"Demolição e/ou Remoção",
"Desocupação",
"Idenização",
"Poder de Policia/Autoexecutoriedade",
"Outras Sanções",
]
def extract_text_from_pdf(file_bytes: bytes) -> str:
"""Extrai texto simples do PDF enviado."""
try:
reader = PdfReader(io.BytesIO(file_bytes))
texts = []
for page in reader.pages:
try:
texts.append(page.extract_text() or "")
except Exception:
pass
return "\n".join(texts).strip()
except Exception as e:
st.warning(f"Não foi possível extrair texto do PDF: {e}")
return ""
def _draw_wrapped_text(c, text, x, y, max_width, line_height=14):
"""Desenha texto com wrap básico no PDF reportlab."""
from reportlab.pdfbase.pdfmetrics import stringWidth
words = text.split()
line = ""
for w in words:
test = f"{line} {w}".strip()
if stringWidth(test, "Helvetica", 10) <= max_width:
line = test
else:
c.drawString(x, y, line)
y -= line_height
line = w
if line:
c.drawString(x, y, line)
y -= line_height
return y
def build_report_pdf(summary_md: str, selections: dict) -> bytes:
"""Gera um PDF com o 'laudo de conformidade' contendo o resumo e as escolhas."""
buffer = io.BytesIO()
doc = SimpleDocTemplate(
buffer, pagesize=A4,
rightMargin=2*cm, leftMargin=2*cm,
topMargin=2*cm, bottomMargin=2*cm
)
styles = getSampleStyleSheet()
h1 = ParagraphStyle(
'H1', parent=styles['Heading1'],
fontName='Helvetica-Bold', fontSize=14,
spaceBefore=12, spaceAfter=8, leading=18
)
h2 = ParagraphStyle(
'H2', parent=styles['Heading2'],
fontName='Helvetica-Bold', fontSize=12,
spaceBefore=10, spaceAfter=6, leading=16
)
normal = ParagraphStyle(
'Normal', parent=styles['Normal'],
fontName='Helvetica', fontSize=10,
leading=14, spaceAfter=4
)
bullet = ParagraphStyle(
'Bullet', parent=normal,
leftIndent=20, bulletIndent=10,
spaceAfter=3
)
subitem = ParagraphStyle(
'SubItem', parent=normal,
leftIndent=35, spaceAfter=3
)
story = []
story.append(Paragraph("Laudo de Conformidade", h1))
story.append(Spacer(1, 8))
story.append(Paragraph("Resumo da Nota Técnica (Markdown renderizado):", h2))
story.append(Spacer(1, 6))
lines = [line.rstrip() for line in summary_md.splitlines()]
i = 0
while i < len(lines):
line = lines[i]
if not line.strip():
story.append(Spacer(1, 4))
i += 1
continue
if line.startswith("# ") and not line.startswith("## "):
text = line[2:].strip()
text = re.sub(r"\*\*(.*?)\*\*", r"\1", text)
story.append(Paragraph(text, h1))
story.append(Spacer(1, 6))
elif line.startswith("## "):
text = line[3:].strip()
text = re.sub(r"\*\*(.*?)\*\*", r"\1", text)
story.append(Paragraph(text, h2))
story.append(Spacer(1, 4))
elif line.startswith("- "):
# Item principal
item_text = line[2:].strip()
item_text = re.sub(r"\*\*(.*?)\*\*", r"\1", item_text)
story.append(Paragraph(item_text, bullet, bulletText="•"))
i += 1
sub_story = []
while i < len(lines) and lines[i].strip() and not lines[i].strip().startswith(("- ", "#", "##")):
sub_text = lines[i].strip()
sub_text = re.sub(r"\*\*(.*?)\*\*", r"\1", sub_text)
sub_story.append(Paragraph(sub_text, subitem))
i += 1
if sub_story:
story.append(KeepTogether(sub_story))
continue
else:
text = line.strip()
text = re.sub(r"\*\*(.*?)\*\*", r"\1", text)
story.append(Paragraph(text, normal))
i += 1
doc.build(story)
buffer.seek(0)
return buffer.read()
def merge_with_original(report_pdf_bytes: bytes, original_pdf_bytes: bytes) -> bytes:
"""Anexa o PDF original ao final do relatório gerado."""
writer = PdfWriter()
rep_reader = PdfReader(io.BytesIO(report_pdf_bytes))
for p in rep_reader.pages:
writer.add_page(p)
try:
orig_reader = PdfReader(io.BytesIO(original_pdf_bytes))
for p in orig_reader.pages:
writer.add_page(p)
except Exception:
# Se falhar, apenas retorna o report
pass
out = io.BytesIO()
writer.write(out)
out.seek(0)
return out.read()
def randomized_defaults():
"""Gera seleções aleatórias para demonstração do fluxo."""
categoria = random.choice(CATEGORIAS)
# de 1 a 3 temas únicos
temas = random.sample(TEMAS, k=random.randint(1, min(3, len(TEMAS))))
# legis e sanções: 1 a 3 itens
legislacao = random.sample(LEGISLACAO, k=random.randint(1, min(3, len(LEGISLACAO))))
sancoes = random.sample(SANCOES, k=random.randint(1, min(3, len(SANCOES))))
return categoria, temas, legislacao, sancoes
def build_report_pdf2(summary_md: str, selections: dict) -> bytes:
"""
Gera o Laudo de Conformidade com formatação perfeita do Markdown.
"""
buffer = io.BytesIO()
doc = SimpleDocTemplate(
buffer, pagesize=A4,
rightMargin=2*cm, leftMargin=2*cm,
topMargin=2*cm, bottomMargin=2*cm
)
styles = getSampleStyleSheet()
h1 = ParagraphStyle(
'H1', parent=styles['Heading1'],
fontName='Helvetica-Bold', fontSize=14,
spaceBefore=12, spaceAfter=8, leading=18
)
h2 = ParagraphStyle(
'H2', parent=styles['Heading2'],
fontName='Helvetica-Bold', fontSize=12,
spaceBefore=10, spaceAfter=6, leading=16
)
normal = ParagraphStyle(
'Normal', parent=styles['Normal'],
fontName='Helvetica', fontSize=10,
leading=14, spaceAfter=4
)
bullet = ParagraphStyle(
'Bullet', parent=normal,
leftIndent=20, bulletIndent=10,
spaceAfter=3
)
subitem = ParagraphStyle(
'SubItem', parent=normal,
leftIndent=35, spaceAfter=3
)
story = []
#if st.session_state["instancia2"] == True:
# story.append(Paragraph("Nota Técnica 2a Instância", h1))
#else:
# story.append(Paragraph("Nota Técnica 1a Instância", h1))
#story.append(Spacer(1, 8))
#story.append(Paragraph("Resumo da Nota Técnica (Markdown renderizado):", h2))
#story.append(Spacer(1, 6))
lines = [line.rstrip() for line in summary_md.splitlines()]
i = 0
while i < len(lines):
line = lines[i]
if not line.strip():
story.append(Spacer(1, 4))
i += 1
continue
if line.startswith("# ") and not line.startswith("## "):
text = line[2:].strip()
text = re.sub(r"\*\*(.*?)\*\*", r"\1", text)
story.append(Paragraph(text, h1))
story.append(Spacer(1, 6))
elif line.startswith("## "):
text = line[3:].strip()
text = re.sub(r"\*\*(.*?)\*\*", r"\1", text)
story.append(Paragraph(text, h2))
story.append(Spacer(1, 4))
elif line.startswith("- "):
# Item principal
item_text = line[2:].strip()
item_text = re.sub(r"\*\*(.*?)\*\*", r"\1", item_text)
story.append(Paragraph(item_text, bullet, bulletText="•"))
i += 1
sub_story = []
while i < len(lines) and lines[i].strip() and not lines[i].strip().startswith(("- ", "#", "##")):
sub_text = lines[i].strip()
sub_text = re.sub(r"\*\*(.*?)\*\*", r"\1", sub_text)
sub_story.append(Paragraph(sub_text, subitem))
i += 1
if sub_story:
story.append(KeepTogether(sub_story))
continue
else:
text = line.strip()
text = re.sub(r"\*\*(.*?)\*\*", r"\1", text)
story.append(Paragraph(text, normal))
i += 1
story.append(Spacer(1, 12))
#story.append(Paragraph("Seleções do Wizard", h2))
#story.append(Spacer(1, 6))
for key, value in selections.items():
title = key.replace("_", " ").title()
story.append(Paragraph(f"{title}:", normal))
if isinstance(value, list):
text = ", ".join(value) if value else "(nenhum)"
else:
text = str(value) if value else "(nenhum)"
story.append(Paragraph(text, normal))
story.append(Spacer(1, 6))
doc.build(story)
buffer.seek(0)
return buffer.read()