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()