import os import io import re import json import streamlit as st from docx import Document from docx.shared import Cm, Pt, RGBColor from docx.enum.text import WD_ALIGN_PARAGRAPH, WD_BREAK from docx.enum.section import WD_SECTION from docx.oxml.ns import qn from docx.oxml import OxmlElement from pydantic import BaseModel from typing import List, Optional, Tuple from google import genai from google.genai import types # ===================================================== # CONFIGURAÇÕES GLOBAIS E ESTADO # ===================================================== st.set_page_config(page_title="Corretor TCC - UNIFAGOC", page_icon="⚖️", layout="wide") def reset_estado(chave): if chave in st.session_state: st.session_state[chave] = None # Inicialização do estado for key in ["resultado_auditoria", "doc_revisado_bytes", "doc_formatado_bytes", "relatorio_abnt"]: if key not in st.session_state: st.session_state[key] = None MAX_CHARS_PER_CHUNK = 15000 # Limite de segurança para a janela de contexto LATINISMOS = [ "habeas corpus", "data venia", "jus puniendi", "jus postulandi", "mens legis", "ratio decidendi", "obiter dictum", "in casu", "periculum in mora", "fumus boni iuris", "a quo", "ad quem", "ex tunc", "ex nunc", "erga omnes", "vacatio legis", "mutatis mutandis", "strictu sensu", "lato sensu", "sine qua non" ] # ===================================================== # ESQUEMAS PYDANTIC # ===================================================== class AuditoriaTCC(BaseModel): aderencia_percentual: int diagnostico_geral: str problema_identificado_no_projeto: str tcc_respondeu_ao_problema: bool justificativa_problema: str objetivos_especificos_identificados: List[str] objetivos_nao_cumpridos_no_tcc: List[str] desvios_de_tema_ou_linguica: List[str] indicios_texto_generico_ia: List[str] incoerencias_internas: List[str] nota_tema_problema: float nota_introducao_metodo: float nota_estruturacao: float nota_formatacao_abnt: float justificativa_notas_banca: str class ErroTexto(BaseModel): original: str corrigido: str motivo: str categoria: str gravidade: str contexto: str class ProblemaAcademico(BaseModel): trecho: str problema: str sugestao: str gravidade: str class RevisaoInteligente(BaseModel): correcoes: List[ErroTexto] problemas_academicos: List[ProblemaAcademico] termos_estrangeiros_para_italico: List[str] class ProblemaReferencia(BaseModel): original: str erros: List[str] class ListaReferencias(BaseModel): referencias: List[ProblemaReferencia] # Novos Esquemas para o Diagnóstico ABNT (ABA 3) class AlertaCorrecao(BaseModel): trecho_original: str como_deve_ficar: str justificativa_regra: str local_aproximado: str class RelatorioDiagnostico(BaseModel): erros_estrutura_abnt: List[AlertaCorrecao] erros_texto_juridico: List[AlertaCorrecao] erros_referencias: List[AlertaCorrecao] nota_geral_formatacao: int # ===================================================== # CLASSE REVISOR IA (Responsável por interações com Gemini) # ===================================================== class RevisorIA: def __init__(self): api_key = st.secrets.get("GEMINI_API_KEY") or os.environ.get("GEMINI_API_KEY") if not api_key: st.error("❌ ERRO: Chave GEMINI_API_KEY não configurada nos secrets.") st.stop() self.client = genai.Client(api_key=api_key) def auditar_tcc(self, texto_projeto: str, texto_tcc: str, fase_tcc: str) -> Optional[AuditoriaTCC]: regra_fase = ( "ATENÇÃO: O aluno está em TCC 1. NÃO penalize a ausência de Resumo e Conclusão." if "TCC 1" in fase_tcc else "O aluno está em TCC 2. Exija o trabalho completo." ) prompt = ( f"Como Professor Examinador da UNIFAGOC:\n{regra_fase}\n" f"=== PRÉ-PROJETO ===\n{texto_projeto}\n" f"=== TCC ===\n{texto_tcc}\n" "TAREFA 1: Avalie cruzamento de dados e fuga de tema.\n" "TAREFA 2: Verifique incoerências internas.\n" "TAREFA 3: Distribua 80 pontos conforme ficha UNIFAGOC." ) try: res = self.client.models.generate_content( model="gemini-3.5-flash", contents=prompt, config=types.GenerateContentConfig( temperature=0.0, response_mime_type="application/json", response_schema=AuditoriaTCC ) ) return AuditoriaTCC(**json.loads(res.text)) except Exception as e: st.error(f"Erro na auditoria: {e}") return None def revisar_texto(self, texto: str) -> Optional[RevisaoInteligente]: prompt = ( "Você é um Professor Revisor de TCCs de Direito da UNIFAGOC. " "IMPORTANTE: O ano atual é maio de 2026. Não aponte citações ou referências de 2024, 2025 ou 2026 como sendo do futuro. " "Analise o texto e corrija: ORTOGRAFIA, GRAMÁTICA, VÍCIOS JURÍDICOS, COERÊNCIA, ABNT. " f"Texto: {texto}" ) try: res = self.client.models.generate_content( model="gemini-3.5-flash", contents=prompt, config=types.GenerateContentConfig( temperature=0.1, response_mime_type="application/json", response_schema=RevisaoInteligente ) ) return RevisaoInteligente(**json.loads(res.text)) except Exception as e: st.error(f"Erro na revisão: {e}") return None def validar_referencias(self, referencias: List[str]) -> ListaReferencias: refs_str = '\n'.join(referencias) prompt = ( "Analise as referências abaixo **APENAS PARA IDENTIFICAR ERROS** (NÃO reescreva, NÃO invente dados!).\n" "IMPORTANTE: Estamos em maio de 2026. Obras de 2024, 2025 e 2026 estão perfeitamente válidas e não são obras do futuro.\n" "Para cada uma, retorne:\n" "- original: texto original\n" "- erros: lista de erros (ex: [\"título não em negrito\", \"autor não invertido\"])\n\n" f"Referências:\n{refs_str}" ) try: res = self.client.models.generate_content( model="gemini-3.5-flash", contents=prompt, config=types.GenerateContentConfig( temperature=0.0, response_mime_type="application/json", response_schema=ListaReferencias ) ) return ListaReferencias(**json.loads(res.text)) except Exception as e: st.error(f"Erro na validação de referências: {e}") return ListaReferencias(referencias=[]) def gerar_diagnostico_abnt(self, texto: str) -> Optional[RelatorioDiagnostico]: prompt = f""" Você é um Inspetor Acadêmico Implacável de TCCs de Direito da UNIFAGOC. Sua missão não é reescrever o texto, mas gerar um laudo diagnóstico rigoroso apontando os erros. IMPORTANTE: O ano atual é maio de 2026. Nunca afirme que referências ou dados de 2024 a 2026 são do futuro. Para evitar estourar o limite, foque nos 5 piores erros de cada categoria. Analise o texto abaixo e preencha o relatório: 1. ESTRUTURA E ABNT: Procure parágrafos com formatação incorreta, citações longas sem destaque visual, falta de itálico em latinismos, etc. 2. TEXTO JURÍDICO: Aponte erros de estilo, repetições viciosas, e inadequação acadêmica. 3. REFERÊNCIAS: Aponte exatamente qual referência está fora do padrão ABNT (ex: título sem destaque, autor minúsculo). Para cada erro, cite o trecho original exato, sugira como deve ficar, explique a regra de forma clara e indique a localização. Dê uma nota geral de saúde da formatação (0 a 100). TEXTO DO ALUNO: {texto} """ try: res = self.client.models.generate_content( model="gemini-3.5-flash", contents=prompt, config=types.GenerateContentConfig( temperature=0.1, response_mime_type="application/json", response_schema=RelatorioDiagnostico ) ) return RelatorioDiagnostico(**json.loads(res.text)) except Exception as e: st.error(f"Erro no diagnóstico: {e}") return None # ===================================================== # CLASSE FORMATADOR UNIFAGOC (Responsável por formatação DOCX) # ===================================================== class FormatadorUNIFAGOC: @staticmethod def aplicar_margens(doc: Document): """Aplica margens oficiais UNIFAGOC (A4: 21x29.7cm)""" for section in doc.sections: section.page_height = Cm(29.7) section.page_width = Cm(21.0) section.top_margin = Cm(3.0) section.left_margin = Cm(3.0) section.bottom_margin = Cm(2.0) section.right_margin = Cm(2.0) section.header_distance = Cm(0.5) section.footer_distance = Cm(0.5) @staticmethod def adicionar_paginacao(doc: Document): """Adiciona numeração de páginas no rodapé (centralizado) - XML correto""" for i, section in enumerate(doc.sections): if i == 0: # Ignora capa/folha de rosto continue footer = section.footer para = footer.paragraphs[0] if footer.paragraphs else footer.add_paragraph() para.alignment = WD_ALIGN_PARAGRAPH.CENTER # Adiciona campo PAGE via XML run = para.add_run() fldChar = OxmlElement('w:fldChar') fldChar.set(qn('w:fldCharType'), 'begin') run._r.append(fldChar) instrText = OxmlElement('w:instrText') instrText.set(qn('xml:space'), 'preserve') instrText.text = 'PAGE' run._r.append(instrText) fldChar = OxmlElement('w:fldChar') fldChar.set(qn('w:fldCharType'), 'end') run._r.append(fldChar) for run in para.runs: run.font.name = 'Calibri' run.font.size = Pt(10) @staticmethod def formatar_figuras_tabelas(doc: Document): """Formata figuras, tabelas e quadros""" for para in doc.paragraphs: texto = para.text.strip() if re.match(r'^(Figura|Tabela|Quadro)\s+\d+', texto, re.IGNORECASE): para.alignment = WD_ALIGN_PARAGRAPH.CENTER for run in para.runs: run.font.name = 'Calibri' run.font.size = Pt(12) run.bold = True # Formata legenda idx = doc.paragraphs.index(para) if idx + 1 < len(doc.paragraphs): next_para = doc.paragraphs[idx + 1] for run in next_para.runs: run.font.name = 'Calibri' run.font.size = Pt(11) # Formata tabelas internas for table in doc.tables: for row in table.rows: for cell in row.cells: for para in cell.paragraphs: para.alignment = WD_ALIGN_PARAGRAPH.LEFT para.paragraph_format.line_spacing = 1.0 for run in para.runs: run.font.name = 'Calibri' run.font.size = Pt(11) @staticmethod def formatar_imagens(doc: Document): """Centraliza imagens (inline shapes)""" for para in doc.paragraphs: if para._p.xpath('.//w:drawing') or para._p.xpath('.//w:pic'): para.alignment = WD_ALIGN_PARAGRAPH.CENTER @staticmethod def formatar_notas_rodape(doc: Document): """Formata notas de rodapé (¹, ², ³) representadas no texto principal""" for para in doc.paragraphs: if re.search(r'[\d\^]+$', para.text.strip()): for run in para.runs: run.font.size = Pt(8) run.font.superscript = True @staticmethod def substituir_texto_preservando_formato(para, original: str, corrigido: str) -> int: """ Substitui texto PRESERVANDO formatação. Tenta substituir dentro do próprio run primeiro para preservar hiperlinks e estilos mistos. """ texto_completo = "".join(run.text for run in para.runs) if original not in texto_completo: return 0 # Tentativa 1: Substituição cirúrgica no mesmo run (preserva 100% da formatação) for run in para.runs: if original in run.text: run.text = run.text.replace(original, corrigido, 1) return 1 # Tentativa 2: Fallback caso a palavra esteja quebrada entre runs diferentes pos = texto_completo.find(original) if pos == -1: return 0 antes = texto_completo[:pos] depois = texto_completo[pos + len(original):] estilo_base = para.style alinhamento = para.paragraph_format.alignment para.clear() if antes: para.add_run(antes) run_corrigido = para.add_run(corrigido) run_corrigido.font.name = 'Calibri' run_corrigido.font.size = Pt(12) if depois: para.add_run(depois) if estilo_base: para.style = estilo_base if alinhamento: para.paragraph_format.alignment = alinhamento return 1 @staticmethod def formatar_documento(doc: Document, revisor: RevisorIA) -> List[ProblemaReferencia]: """Função principal de formatação estrutural (ABNT UNIFAGOC)""" FormatadorUNIFAGOC.aplicar_margens(doc) FormatadorUNIFAGOC.adicionar_paginacao(doc) FormatadorUNIFAGOC.formatar_figuras_tabelas(doc) FormatadorUNIFAGOC.formatar_imagens(doc) FormatadorUNIFAGOC.formatar_notas_rodape(doc) for para in doc.paragraphs: texto = para.text.strip() if not texto: continue # --- REFERÊNCIAS --- if re.search(r"REFER[ÊE]NCIAS", texto, re.IGNORECASE): para.alignment = WD_ALIGN_PARAGRAPH.CENTER para.paragraph_format.space_before = Pt(12) para.paragraph_format.space_after = Pt(6) for run in para.runs: run.font.name = 'Calibri' run.font.size = Pt(12) run.bold = True continue # --- RESUMO --- if texto.upper() == "RESUMO": para.alignment = WD_ALIGN_PARAGRAPH.JUSTIFY para.paragraph_format.space_before = Pt(12) para.paragraph_format.space_after = Pt(0) para.paragraph_format.line_spacing = 1.5 for run in para.runs: run.font.name = 'Calibri' run.font.size = Pt(11) run.bold = True continue # --- PALAVRAS-CHAVE --- if texto.upper().startswith("PALAVRAS-CHAVE"): para.alignment = WD_ALIGN_PARAGRAPH.JUSTIFY para.paragraph_format.space_before = Pt(6) para.paragraph_format.space_after = Pt(0) para.paragraph_format.line_spacing = 1.5 for run in para.runs: run.font.name = 'Calibri' run.font.size = Pt(11) if "PALAVRAS-CHAVE" in run.text.upper(): run.bold = True continue # --- TÍTULOS E SEÇÕES --- nivel = FormatadorUNIFAGOC.detectar_nivel_secao(texto) if nivel > 0 or FormatadorUNIFAGOC.detectar_titulo_ou_subtitulo(texto) == "titulo": para.alignment = WD_ALIGN_PARAGRAPH.LEFT para.paragraph_format.space_before = Pt(12) para.paragraph_format.space_after = Pt(6) for run in para.runs: run.font.name = 'Calibri' run.font.size = Pt(12) run.bold = True if nivel == 3: run.italic = True if nivel == 2: para.paragraph_format.left_indent = Cm(0.5) continue # --- CITAÇÕES LONGAS --- if FormatadorUNIFAGOC.detectar_citacao_longa(para): para.paragraph_format.left_indent = Cm(4.0) para.paragraph_format.line_spacing = 1.0 para.paragraph_format.space_before = Pt(6) para.paragraph_format.space_after = Pt(6) for run in para.runs: run.font.name = 'Calibri' run.font.size = Pt(11) continue # --- LISTAS --- if FormatadorUNIFAGOC.detectar_lista(texto): para.paragraph_format.left_indent = Cm(1.25) para.paragraph_format.first_line_indent = Cm(0) para.paragraph_format.space_before = Pt(0) para.paragraph_format.space_after = Pt(0) para.paragraph_format.line_spacing = 1.5 for run in para.runs: run.font.name = 'Calibri' run.font.size = Pt(12) continue # --- CORPO DO TEXTO PADRÃO --- para.alignment = WD_ALIGN_PARAGRAPH.JUSTIFY para.paragraph_format.first_line_indent = Cm(1.25) para.paragraph_format.line_spacing = 1.5 para.paragraph_format.space_before = Pt(0) para.paragraph_format.space_after = Pt(0) for run in para.runs: run.font.name = 'Calibri' run.font.size = Pt(12) # 4. Valida referências extraídas no documento refs = FormatadorUNIFAGOC.extrair_referencias(doc) if refs: lista_refs = revisor.validar_referencias(refs) return lista_refs.referencias return [] @staticmethod def formatacao_cruzada_indices(para, texto_alvo: str, aplicar_italico: bool = False, aplicar_negrito: bool = False) -> int: aplicacoes = 0 if aplicar_italico: matches = list(re.finditer(r'\b' + re.escape(texto_alvo) + r'\b', para.text, re.IGNORECASE)) else: idx = para.text.find(texto_alvo) matches = [(idx, idx + len(texto_alvo))] if idx != -1 else [] for match in matches: pos_ini, pos_fim = match if isinstance(match, tuple) else (match.start(), match.end()) if pos_ini == -1: continue pos_atual = 0 for run in para.runs: fim_run = pos_atual + len(run.text) if pos_atual < pos_fim and fim_run > pos_ini: if aplicar_italico: run.italic = True if aplicar_negrito: run.bold = True aplicacoes += 1 pos_atual += len(run.text) return aplicacoes @staticmethod def detectar_nivel_secao(texto: str) -> int: match = re.match(r'^(\d+(?:\.\d+)*)\s+', texto) if not match: return 0 return match.group(1).count('.') + 1 @staticmethod def detectar_titulo_ou_subtitulo(texto: str) -> Optional[str]: SECOES_ESPECIAIS = ["INTRODUÇÃO", "CONSIDERAÇÕES FINAIS", "REFERÊNCIAS", "RESUMO", "PALAVRAS-CHAVE"] texto_upper = texto.upper() if any(sec in texto_upper for sec in SECOES_ESPECIAIS): return "titulo" if FormatadorUNIFAGOC.detectar_nivel_secao(texto) > 0: return "titulo" if texto.isupper() and len(texto) < 150 and not FormatadorUNIFAGOC.detectar_nivel_secao(texto): return "titulo" return None @staticmethod def detectar_citacao_longa(para) -> bool: if para.paragraph_format.left_indent and para.paragraph_format.left_indent >= Cm(3.5): return True texto = para.text.strip() if len(texto.split()) >= 40: return True return False @staticmethod def detectar_lista(texto: str) -> bool: return bool(re.match(r'^(\d+[\)\.]|[a-zA-Z][\)\.]|[IVXLCDM]+[\)\.]|[-–])\s', texto)) @staticmethod def extrair_referencias(doc: Document) -> List[str]: refs = [] em_refs = False for para in doc.paragraphs: texto = para.text.strip() if re.search(r"REFER[ÊE]NCIAS", texto, re.IGNORECASE): em_refs = True continue if em_refs and (len(texto) < 10 or texto.isupper()): break if em_refs: refs.append(texto) return refs @staticmethod def extrair_texto_docx(arquivo_upload) -> str: doc = Document(arquivo_upload) return "\n\n".join([para.text.strip() for para in doc.paragraphs if para.text.strip()]) @staticmethod def docx_to_bytes(doc: Document) -> bytes: byte_io = io.BytesIO() doc.save(byte_io) return byte_io.getvalue() @staticmethod def estimar_paginas(doc: Document) -> int: palavras_totais = sum( len(para.text.split()) for para in doc.paragraphs if not re.search(r"^(?:\d+\.?\s*)?REFER[ÊE]NCIAS", para.text, re.IGNORECASE) ) return max(1, palavras_totais // 350) # ===================================================== # CLASSE AUDITOR TCC (Responsável pela lógica da aplicação) # ===================================================== class AuditorTCC: def __init__(self, revisor: RevisorIA): self.revisor = revisor def auditar(self, arq_projeto, arq_tcc, fase_tcc: str) -> Optional[AuditoriaTCC]: texto_projeto = FormatadorUNIFAGOC.extrair_texto_docx(arq_projeto) texto_tcc = FormatadorUNIFAGOC.extrair_texto_docx(arq_tcc) return self.revisor.auditar_tcc(texto_projeto, texto_tcc, fase_tcc) def revisar(self, arq_rev) -> Tuple[Document, RevisaoInteligente, List[str]]: doc = Document(arq_rev) texto_completo = FormatadorUNIFAGOC.extrair_texto_docx(arq_rev) erros_locais = self.verificar_erros_locais(texto_completo) # Chunking seguro respeitando parágrafos chunks = self.dividir_em_chunks(texto_completo) resultados_revisao = [] for chunk in chunks: resultado = self.revisor.revisar_texto(chunk) if resultado: resultados_revisao.append(resultado) revisao_consolidada = self.consolidar_revisoes(resultados_revisao) self.aplicar_correcoes(doc, revisao_consolidada) return doc, revisao_consolidada, erros_locais def verificar_erros_locais(self, texto: str) -> List[str]: erros = [] if re.search(r"\s{2,}", texto): erros.append("Espaços duplos detectados no documento.") if re.search(r",\s*,", texto): erros.append("Vírgulas duplicadas detectadas (ex: ',,').") if re.search(r"[.!?]{2,}", texto) and not "..." in texto: erros.append("Pontuação repetida indevida.") if re.search(r"\b(\w+)\s+\1\b", texto, re.IGNORECASE): erros.append("Palavras repetidas sequencialmente.") if re.search(r"\([A-ZÀ-Ú\s]+,\s*\)", texto): erros.append("Citação ABNT com ano faltando.") return erros def dividir_em_chunks(self, texto: str) -> List[str]: """Divide texto em chunks respeitando a quebra de parágrafos""" paragrafos = texto.split('\n\n') chunks = [] chunk_atual = "" for p in paragrafos: if len(chunk_atual) + len(p) + 2 < MAX_CHARS_PER_CHUNK: chunk_atual += p + "\n\n" else: if chunk_atual: chunks.append(chunk_atual.strip()) chunk_atual = p + "\n\n" if chunk_atual: chunks.append(chunk_atual.strip()) return chunks def consolidar_revisoes(self, revisoes: List[RevisaoInteligente]) -> RevisaoInteligente: if not revisoes: return RevisaoInteligente(correcoes=[], problemas_academicos=[], termos_estrangeiros_para_italico=[]) todas_correcoes = [] todos_problemas = [] todos_termos = [] for revisao in revisoes: todas_correcoes.extend(revisao.correcoes) todos_problemas.extend(revisao.problemas_academicos) todos_termos.extend(revisao.termos_estrangeiros_para_italico) return RevisaoInteligente( correcoes=todas_correcoes, problemas_academicos=todos_problemas, termos_estrangeiros_para_italico=list(set(todos_termos)) ) def aplicar_correcoes(self, doc: Document, revisao: RevisaoInteligente): for para in doc.paragraphs: for correcao in revisao.correcoes: FormatadorUNIFAGOC.substituir_texto_preservando_formato(para, correcao.original, correcao.corrigido) for table in doc.tables: for row in table.rows: for cell in row.cells: for para in cell.paragraphs: for correcao in revisao.correcoes: FormatadorUNIFAGOC.substituir_texto_preservando_formato(para, correcao.original, correcao.corrigido) termos_para_italico = set(LATINISMOS + revisao.termos_estrangeiros_para_italico) for para in doc.paragraphs: for termo in termos_para_italico: FormatadorUNIFAGOC.formatacao_cruzada_indices(para, termo, aplicar_italico=True) for table in doc.tables: for row in table.rows: for cell in row.cells: for para in cell.paragraphs: for termo in termos_para_italico: FormatadorUNIFAGOC.formatacao_cruzada_indices(para, termo, aplicar_italico=True) # ===================================================== # INTERFACE STREAMLIT # ===================================================== st.title("⚖️ Assistente Acadêmico - Direito UNIFAGOC") aba_auditoria, aba_revisao, aba_abnt, aba_prompt, aba_feedback = st.tabs([ "🔍 Auditoria e Notas", "🧠 Revisão Inteligente", "📋 Diagnóstico ABNT", "🤖 Prompt Mestre", "👨‍🏫 Feedback Professor" ]) # Inicialização centralizada das classes revisor_ia = RevisorIA() auditor = AuditorTCC(revisor_ia) formatador = FormatadorUNIFAGOC() # ABA 1: AUDITORIA with aba_auditoria: st.markdown("Cruze o **Pré-Projeto** com o **TCC** para identificar fuga do tema e gerar a **Nota da Banca (80 pontos)**.") fase_tcc = st.radio("Fase atual:", ["TCC 1 (Apenas Introdução e Capítulo 1)", "TCC 2 (TCC Completo)"], horizontal=True) col1, col2 = st.columns(2) with col1: arq_projeto = st.file_uploader("1. Pré-Projeto (.docx)", type=["docx"], key="proj") with col2: arq_tcc = st.file_uploader("2. TCC (.docx)", type=["docx"], key="tcc_aud") if st.button("🔍 Iniciar Auditoria", type="primary"): if not arq_projeto or not arq_tcc: st.warning("⚠️ Envie os DOIS arquivos para realizar a comparação.") else: with st.status("Analisando coerência jurídica...", expanded=True) as status: resultado = auditor.auditar(arq_projeto, arq_tcc, fase_tcc) if resultado: st.session_state.resultado_auditoria = resultado status.update(label="✅ Avaliação concluída!", state="complete", expanded=False) else: status.update(label="❌ Falha na avaliação.", state="error") if st.session_state.resultado_auditoria: dados = st.session_state.resultado_auditoria st.header("🏆 Parecer Oficial da Banca UNIFAGOC") # 1. CÁLCULO DA NOTA COM FEEDBACK VISUAL nota_total = sum([ dados.nota_tema_problema, dados.nota_introducao_metodo, dados.nota_estruturacao, dados.nota_formatacao_abnt ]) # Se a nota for maior ou igual a 56 (70% de 80), fica verde. Se não, vermelho. if nota_total >= 56: st.success(f"### 🎉 NOTA FINAL ESTIMADA: {nota_total}/80 PONTOS") else: st.error(f"### ⚠️ NOTA FINAL ESTIMADA: {nota_total}/80 PONTOS (Risco de Reprovação)") st.info(f"**💡 Parecer Geral do Examinador:** {dados.justificativa_notas_banca}") st.divider() # 2. DISTRIBUIÇÃO DAS NOTAS (Métricas) st.subheader("📊 Distribuição de Pontos") c1, c2, c3, c4 = st.columns(4) c1.metric("Tema/Problema", f"{dados.nota_tema_problema}/20") c2.metric("Intro/Método", f"{dados.nota_introducao_metodo}/20") c3.metric("Estrutura/Linguagem", f"{dados.nota_estruturacao}/20") c4.metric("Formatação/ABNT", f"{dados.nota_formatacao_abnt}/20") st.divider() # 3. ANÁLISE DE ADERÊNCIA E CONTEÚDO (Verde vs Vermelho) st.subheader("🎯 Cruzamento: Projeto vs. TCC") # Barra de progresso visual para a aderência st.write(f"**Índice de Fidelidade ao Projeto:** {dados.aderencia_percentual}%") st.progress(dados.aderencia_percentual / 100) st.write("") # Espaçamento col_a, col_b = st.columns(2) with col_a: st.markdown("#### ❓ Resposta ao Problema") if dados.tcc_respondeu_ao_problema: st.success(f"**✅ TCC Respondeu:** {dados.justificativa_problema}") else: st.error(f"**❌ Fuga do Problema:** {dados.justificativa_problema}") st.markdown("#### 📋 Cumprimento de Objetivos") # Filtra os objetivos que foram cumpridos subtraindo os não cumpridos objetivos_cumpridos = [ obj for obj in dados.objetivos_especificos_identificados if obj not in dados.objetivos_nao_cumpridos_no_tcc ] # Exibe os Verdes (Cumpridos) if objetivos_cumpridos: for obj in objetivos_cumpridos: st.success(f"✅ **Atingido:** {obj}") # Exibe os Vermelhos (Falhos) if dados.objetivos_nao_cumpridos_no_tcc: for obj in dados.objetivos_nao_cumpridos_no_tcc: st.error(f"❌ **Faltou Abordar:** {obj}") elif not dados.objetivos_nao_cumpridos_no_tcc and objetivos_cumpridos: st.success("✨ **Perfeito!** Todos os objetivos específicos foram mapeados e cumpridos.") with col_b: st.markdown("#### 🚨 Filtro Anti-Embromação e Incoerências") teve_problema_grave = False if dados.incoerencias_internas: teve_problema_grave = True for incoerencia in dados.incoerencias_internas: st.error(f"**Incoerência Estrutural:** {incoerencia}") if dados.desvios_de_tema_ou_linguica: teve_problema_grave = True for desvio in dados.desvios_de_tema_ou_linguica: st.warning(f"**Fuga de Tema / Enrolação:** {desvio}") if dados.indicios_texto_generico_ia: teve_problema_grave = True for ia in dados.indicios_texto_generico_ia: st.error(f"**Indício de Texto Genérico/IA:** {ia}") if not teve_problema_grave: st.success("✅ **Excelente!** O texto está fluido, sem contradições internas, sem enrolação e com linguagem autêntica.") # ABA 2: REVISÃO INTELIGENTE with aba_revisao: st.markdown("Revisão de **Ortografia**, **Gramática**, **Coerência**, **Vícios Jurídicos** e **Anti-IA**.") arq_rev = st.file_uploader("Envie o TCC (.docx)", type=["docx"], key="revisao") if st.button("🧠 Realizar Revisão"): if arq_rev: with st.status("Processando revisão...", expanded=True) as status: doc_rev, revisao, erros_locais = auditor.revisar(arq_rev) for erro in erros_locais: st.warning(f"⚠️ {erro}") if revisao.problemas_academicos: st.subheader("🚨 Problemas de Redação Detectados") for prob in revisao.problemas_academicos: st.error(f"**[{prob.gravidade.upper()}] {prob.problema}**\n\nTrecho: '{prob.trecho}'\n\nSugestão: {prob.sugestao}") # --- NOVO BLOCO: Mostrando as alterações realizadas no texto --- if revisao.correcoes: st.subheader("📝 Registro de Alterações Automáticas") with st.expander("Ver detalhes das correções aplicadas ao texto"): for c in revisao.correcoes: st.markdown(f"❌ **Original:** `{c.original}`") st.markdown(f"✅ **Corrigido:** `{c.corrigido}`") st.caption(f"💡 *Motivo:* {c.motivo} | *Categoria:* {c.categoria}") st.divider() st.session_state.doc_revisado_bytes = formatador.docx_to_bytes(doc_rev) st.session_state.nome_arq_rev = arq_rev.name.replace('.docx', '') + "_Revisado.docx" status.update( label=f"✅ Revisão concluída: {len(revisao.correcoes)} correções automáticas registradas, {len(revisao.termos_estrangeiros_para_italico)} itálicos.", state="complete", expanded=False ) if st.session_state.doc_revisado_bytes: st.download_button( "⬇️ Baixar TCC Revisado", data=st.session_state.doc_revisado_bytes, file_name=st.session_state.nome_arq_rev, mime="application/vnd.openxmlformats-officedocument.wordprocessingml.document" ) # ABA 3: DIAGNÓSTICO ABNT (Painel Interativo de Laudos) with aba_abnt: st.markdown("Diagnóstico Rigoroso de **Estrutura e ABNT** (Inspetor Acadêmico UNIFAGOC).") arq_diag = st.file_uploader("Envie o TCC (.docx) para Laudo", type=["docx"], key="abnt_file") if st.button("📋 Gerar Laudo Diagnóstico"): if arq_diag: with st.status("Inspecionando documento...", expanded=True) as status: texto_completo = formatador.extrair_texto_docx(arq_diag) # Para evitar limite de output JSON, a IA foca nos principais erros do arquivo relatorio = revisor_ia.gerar_diagnostico_abnt(texto_completo) if relatorio: st.session_state.relatorio_abnt = relatorio status.update(label="✅ Laudo gerado com sucesso!", state="complete", expanded=False) else: status.update(label="❌ Falha na inspeção.", state="error") if st.session_state.get("relatorio_abnt"): relatorio = st.session_state.relatorio_abnt st.header("📋 Laudo de Revisão de Formatação") # Dashboard de Saúde ABNT if relatorio.nota_geral_formatacao >= 70: st.success(f"### Saúde da Formatação: {relatorio.nota_geral_formatacao}/100") else: st.error(f"### Saúde da Formatação: {relatorio.nota_geral_formatacao}/100") st.progress(relatorio.nota_geral_formatacao / 100) st.divider() # Função para renderizar os cards do Dashboard de Erros def renderizar_cards_erro(lista_erros, titulo, icone): st.subheader(f"{icone} {titulo}") if not lista_erros: st.success("✅ Nenhum erro encontrado nesta categoria!") for erro in lista_erros: with st.expander(f"📍 Local aproximado: {erro.local_aproximado}"): st.error(f"**Trecho com erro:**\n\n{erro.trecho_original}") st.success(f"**Como deve ficar:**\n\n{erro.como_deve_ficar}") st.info(f"**Regra UNIFAGOC aplicada:** {erro.justificativa_regra}") st.write("") # Espaçamento renderizar_cards_erro(relatorio.erros_estrutura_abnt, "Problemas de ABNT e Estrutura", "📐") renderizar_cards_erro(relatorio.erros_texto_juridico, "Problemas de Texto Jurídico", "✍️") renderizar_cards_erro(relatorio.erros_referencias, "Problemas nas Referências Bibliográficas", "📚") # ABA 4: PROMPT MESTRE with aba_prompt: st.markdown("### 🤖 Prompt Mestre - Padrão UNIFAGOC") prompt_pronto = """ Você é um revisor acadêmico especializado nas normas ABNT e nas diretrizes da UNIFAGOC. Sua tarefa é receber um texto bruto e formatá-lo EXATAMENTE conforme as especificações técnicas abaixo, sem omitir nenhuma regra, sem inventar informações, e sem alterar o conteúdo original (apenas a estrutura e a formatação). Siga cada item deste guia na ordem apresentada. ════════════════════════════════════════════════════════════ BLOCO 1 — CONFIGURAÇÃO DE PÁGINA ════════════════════════════════════════════════════════════ ▸ Tamanho do papel: A4 (210 × 297 mm) ▸ Margens: - Superior: 3 cm - Esquerda: 3 cm - Inferior: 2 cm - Direita: 2 cm ▸ Cabeçalho: posicionado a 0,5 cm da borda superior da página ▸ Rodapé: posicionado a 0,5 cm da borda inferior da página ▸ Orientação: retrato (portrait) ▸ Extensão total do artigo: entre 10 e 18 páginas ════════════════════════════════════════════════════════════ BLOCO 2 — NUMERAÇÃO DE PÁGINAS ════════════════════════════════════════════════════════════ ▸ Localização: cabeçalho (não no rodapé) ▸ Alinhamento: direita (ou conforme padrão do template) ▸ Formato: arábico (1, 2, 3...) ▸ Início da contagem: página 1 ▸ IMPORTANTE: a PRIMEIRA página NÃO exibe o número (cabeçalho da primeira página fica em branco). O número aparece a partir da 2ª página. ════════════════════════════════════════════════════════════ BLOCO 3 — FONTE PADRÃO DO DOCUMENTO ════════════════════════════════════════════════════════════ ▸ Fonte exclusiva em todo o documento: Calibri ▸ Tamanho padrão do corpo do texto: 12 pt ▸ Idioma configurado: Português (Brasil) ▸ NÃO usar Times New Roman, Arial ou qualquer outra fonte. ════════════════════════════════════════════════════════════ BLOCO 4 — ESTRUTURA OBRIGATÓRIA DO ARTIGO (nesta ordem) ════════════════════════════════════════════════════════════ O artigo deve conter, obrigatoriamente, os seguintes elementos na ordem abaixo: 1. TÍTULO (e subtítulo, se houver) 2. Nome(s) do(s) autor(es) com afiliações 3. RESUMO + Palavras-chave 4. INTRODUÇÃO 5. Seções de desenvolvimento (numeradas: 1. TÍTULO, 2. TÍTULO...) 6. CONSIDERAÇÕES FINAIS 7. REFERÊNCIAS ════════════════════════════════════════════════════════════ BLOCO 5 — TÍTULO DO ARTIGO ════════════════════════════════════════════════════════════ ▸ Fonte: Calibri 12 pt ▸ Estilo: negrito ▸ Alinhamento: centralizado ▸ Capitalização: MAIÚSCULAS (caixa alta) para o título principal ▸ Subtítulo (após dois-pontos): quando houver, em caixa baixa - Exemplo: "TÍTULO DO ARTIGO: subtítulo do artigo" ▸ Espaçamento antes do título: 0 pt ▸ Espaçamento depois do título: 0 pt ▸ Entrelinhamento: simples (1,0) ▸ Recuos esquerdo e direito: 0 cm ════════════════════════════════════════════════════════════ BLOCO 6 — AUTORES E AFILIAÇÕES ════════════════════════════════════════════════════════════ ▸ FORMATO DO NOME DOS AUTORES: - SOBRENOME, Nome (em negrito, Calibri 12 pt) - Alinhamento: centralizado - Separar autores com ponto e vírgula ( ; ) - Após cada nome, inserir letra sobrescrita (a, b, c...) identificando a afiliação correspondente - Espaçamento antes: 12 pt | Espaçamento depois: 6 pt - Exemplo: SILVA, João a ; SOUZA, Maria b ▸ FORMATO DAS AFILIAÇÕES (logo abaixo dos nomes): - Uma linha por autor, com a letra sobrescrita correspondente (a, b, c...) - Conteúdo: Titulação – Principal atividade profissional – Instituição – E-mail - Fonte: Calibri 12 pt, sem negrito - Alinhamento: centralizado - Cada afiliação em parágrafo separado ▸ NOTA DE RODAPÉ (TCC Direito – opcional): - Pode ser inserida nota de rodapé ao nome do(s) autor(es) com "Informação adicional sobre o(a) autor(a) (opcional)." - Numeração por algarismos arábicos ════════════════════════════════════════════════════════════ BLOCO 7 — RESUMO ════════════════════════════════════════════════════════════ ▸ Rótulo "RESUMO": Calibri 12 pt, negrito ▸ Texto do resumo: Calibri 11 pt, itálico ▸ Alinhamento: justificado (recuos esquerdo e direito = 0 cm) ▸ Estrutura: parágrafo ÚNICO (sem quebras internas) ▸ Extensão: máximo 150 palavras ▸ Conteúdo obrigatório no resumo: 1. Problema de pesquisa 2. Objetivo 3. Método(s) utilizado(s) 4. Resultados/conclusões mais importantes ▸ Entrelinhamento do resumo: simples ▸ PALAVRAS-CHAVE (logo abaixo do resumo, no mesmo bloco): - Rótulo "Palavras-chave:" em Calibri 12 pt, negrito - Seguido das palavras-chave em Calibri 12 pt, sem negrito - Quantidade: de 3 a 5 palavras-chave - Separação: ponto ( . ) entre cada palavra-chave - Capitalização: primeira letra de cada palavra em MAIÚSCULA (as demais letras em minúscula) - Formato: Palavras-chave: Direito. Previdência. Reforma Constitucional. - Espaçamento antes das palavras-chave: 6 pt (após o resumo) ════════════════════════════════════════════════════════════ BLOCO 8 — TÍTULOS DAS SEÇÕES E SUBSEÇÕES ════════════════════════════════════════════════════════════ ▸ SEÇÕES PRIMÁRIAS (INTRODUÇÃO, 1. TÍTULO, CONSIDERAÇÕES FINAIS, REFERÊNCIAS): - Fonte: Calibri 12 pt - Estilo: negrito - Capitalização: CAIXA ALTA (todas em maiúsculas) - Numeração: INTRODUÇÃO e CONSIDERAÇÕES FINAIS sem número; seções de desenvolvimento com número arábico seguido de ponto (ex: 1. TÍTULO, 2. TÍTULO, 3. TÍTULO...) - Alinhamento: justificado à esquerda (sem recuo) - Espaçamento antes: 0 pt - Espaçamento depois: aproximadamente 6 pt - Recuos esquerdo e direito: 0 cm ▸ SEÇÕES SECUNDÁRIAS (subseções): - Fonte: Calibri 12 pt - Estilo: negrito - Capitalização: caixa baixa (apenas a primeira letra em maiúscula) - Numeração: 1.1 Título, 1.2 Título... - Alinhamento: justificado à esquerda (sem recuo) ▸ SEÇÕES TERCIÁRIAS (sub-subseções): - Fonte: Calibri 12 pt - Estilo: negrito + itálico - Capitalização: caixa baixa - Numeração: 1.1.1 Título, 1.1.2 Título... - Alinhamento: justificado à esquerda (sem recuo) ════════════════════════════════════════════════════════════ BLOCO 9 — CORPO DO TEXTO (parágrafos das seções) ════════════════════════════════════════════════════════════ ▸ Fonte: Calibri 12 pt ▸ Alinhamento: justificado (margens esquerda e direita alinhadas) ▸ Recuo de primeira linha (parágrafo): 1,25 cm ▸ Recuo esquerdo do parágrafo: 0 cm ▸ Recuo direito do parágrafo: 0 cm ▸ Entrelinhamento: 1,5 (150%) ▸ Espaçamento antes do parágrafo: 0 pt ▸ Espaçamento depois do parágrafo: 0 pt ════════════════════════════════════════════════════════════ BLOCO 10 — CITAÇÕES ════════════════════════════════════════════════════════════ ▸ SISTEMA DE CITAÇÃO: AUTOR-data, conforme NBR 10520 (ABNT, 2002) - Exemplos: (SILVA, 2020); Silva (2020); (SILVA, 2020, p. 45) ▸ CITAÇÕES CURTAS (até 4 linhas): - Inseridas no corpo do texto - Entre aspas duplas (" ") - Mesma fonte e tamanho do parágrafo (Calibri 12) - Chamada da fonte ao final, entre parênteses: (AUTOR, ano, p. x) ▸ CITAÇÕES LONGAS (mais de 4 linhas — citação em bloco): - Parágrafo separado do texto principal - Fonte: Calibri 11 pt (um ponto menor que o corpo do texto) - Entrelinhamento: SIMPLES (1,0) - Alinhamento: justificado - Recuo SOMENTE da margem ESQUERDA: 4 cm (a partir da margem esquerda da página, que já é 3 cm — portanto, 4 cm adicionais) - Recuo direito: 0 cm (sem recuo à direita) - SEM aspas na citação em bloco - Espaçamento antes e depois da citação em bloco: 1 linha em branco - A chamada da fonte deve vir ao final, após o ponto final: (AUTOR, ano, p. x) - NÃO usar recuo de primeira linha neste parágrafo ════════════════════════════════════════════════════════════ BLOCO 11 — FIGURAS, QUADROS E TABELAS ════════════════════════════════════════════════════════════ ▸ Posicionamento: centralizados na página, próximos à sua citação no texto ▸ Numeração: sequencial por tipo (Figura 1, Figura 2; Quadro 1, Quadro 2; Tabela 1...) ▸ TÍTULO (acima da figura/quadro/tabela): - Formato: "Figura X – Descrição do título" - Fonte: Calibri 12 pt - Alinhamento: centralizado - O rótulo "Figura X" em negrito; o texto do título sem negrito ▸ LEGENDA / FONTE (abaixo da figura/quadro/tabela): - Formato: "Fonte: AUTOR (ano)." ou "Fonte: elaborado pelo(a) autor(a)." - Fonte: Calibri 11 pt - Alinhamento: centralizado ▸ ESPAÇAMENTOS ao redor de figuras/quadros/tabelas: - Entre o corpo do texto e o TÍTULO: 6 pt - Entre o título e a figura/quadro/tabela: 3 pt - Entre a figura/quadro/tabela e a legenda/fonte: 3 pt - Entre a legenda/fonte e o parágrafo seguinte: 6 pt ════════════════════════════════════════════════════════════ BLOCO 12 — REFERÊNCIAS ════════════════════════════════════════════════════════════ ▸ Título da seção: "REFERÊNCIAS" (Calibri 12, negrito, caixa alta, sem numeração) ▸ Norma: ABNT NBR 6023:2018 ▸ Fonte das referências: Calibri 11 pt ▸ Entrelinhamento: simples (1,0) ▸ Espaçamento entre referências: 6 pt após cada referência (espaçamento depois) ▸ Alinhamento: à ESQUERDA (justificado à esquerda, sem recuo) ▸ Ordenação: ordem ALFABÉTICA pelo sobrenome do primeiro autor ▸ NÃO numerar as referências ▸ Incluir APENAS as referências efetivamente citadas no texto ▸ DESTAQUE EM NEGRITO: o título do livro, periódico ou veículo de publicação deve estar em NEGRITO (e somente ele, não o restante da referência) - Livro: SILVA, João. **Título do Livro**. 2. ed. São Paulo: Editora, 2020. - Artigo: SOUZA, M. Título do artigo. **Nome da Revista**, v. 2, n. 1, 2021. - Legislação: BRASIL. Lei nº X. **Diário Oficial da União**, Brasília, 2020. - Jurisprudência: ACÓRDÃO XXXXXRS. Relator: Nome. **Diário da Justiça**, data. ▸ REGRAS DE INTEGRIDADE: Nunca inventar, modificar ou completar referências, leis, jurisprudências ou doutrinas não fornecidas no texto original. ▸ EXEMPLOS DE FORMATAÇÃO POR TIPO: • Livro: SOBRENOME, Nome. **Título em negrito**: subtítulo. ed. Cidade: Editora, ano. • Artigo em periódico: SOBRENOME, Nome. Título do artigo. **Nome do Periódico**, cidade, v. X, n. X, p. XX-XX, mês abrev. ano. Disponível em: URL. Acesso em: dia mês. ano. • Dissertação/Tese: SOBRENOME, Nome. **Título da dissertação**. ano. X f. Dissertação (Mestrado em ...) – Universidade, Cidade. Disponível em: URL. Acesso em: data. • Legislação: BRASIL. [Decreto-Lei / Lei] nº X, de data. Ementa. **Diário Oficial da União**, Brasília, data. Disponível em: URL. Acesso em: data. • Jurisprudência: TRIBUNAL/ACÓRDÃO nº X. Relator: Nome. Câmara/Turma. Data de Julgamento: data. **Nome do Diário**, data. • Documento eletrônico/site: SOBRENOME, Nome ou INSTITUIÇÃO. Ano (se houver). Título. **Veículo/Site**. Disponível em: URL. Acesso em: mês. ano. ════════════════════════════════════════════════════════════ BLOCO 13 — TERMOS ESTRANGEIROS E ITÁLICO ════════════════════════════════════════════════════════════ ▸ Todo termo em língua estrangeira deve ser formatado em ITÁLICO - Exemplos: habeas corpus, fake news, data mining, marketing, in verbis, ex officio, ad hoc, ab initio, stricto sensu, lato sensu ▸ Nomes de obras, filmes, jornais e revistas citados no texto: itálico ▸ Palavras usadas com sentido especial ou neologismos: itálico ou aspas simples ════════════════════════════════════════════════════════════ BLOCO 14 — ALINHAMENTOS — RESUMO RÁPIDO ════════════════════════════════════════════════════════════ Elemento │ Alinhamento ──────────────────────────────────┼────────────────────────── Título do artigo │ Centralizado Autores e afiliações │ Centralizado RESUMO (texto) │ Justificado Palavras-chave │ Justificado à esquerda Títulos de seções (todos níveis) │ Justificado à esquerda Corpo do texto (parágrafos) │ Justificado (ambos lados) Citações longas (em bloco) │ Justificado Títulos de figuras/tabelas │ Centralizado Legendas/Fontes de figuras │ Centralizado REFERÊNCIAS (título) │ Centralizado ou esquerda* Referências (entradas) │ Esquerda (sem recuo) * O título "REFERÊNCIAS" pode ser centralizado conforme o template 2025, mas as entradas da lista são sempre alinhadas à esquerda. ════════════════════════════════════════════════════════════ BLOCO 15 — TAMANHOS DE FONTE — RESUMO RÁPIDO ════════════════════════════════════════════════════════════ Elemento │ Tamanho │ Estilo ──────────────────────────────────┼─────────┼──────────────────── Título do artigo │ 12 pt │ Negrito, MAIÚSCULA Autores │ 12 pt │ Negrito Afiliações dos autores │ 12 pt │ Normal Rótulo "RESUMO" │ 12 pt │ Negrito Texto do resumo │ 11 pt │ Itálico Rótulo "Palavras-chave:" │ 12 pt │ Negrito Palavras-chave (termos) │ 12 pt │ Normal Títulos de seções primárias │ 12 pt │ Negrito, MAIÚSCULA Títulos de seções secundárias │ 12 pt │ Negrito Títulos de seções terciárias │ 12 pt │ Negrito + Itálico Corpo do texto (parágrafos) │ 12 pt │ Normal Citações longas (bloco) │ 11 pt │ Normal Títulos de figuras/tabelas │ 12 pt │ "Figura X" em negrito Legendas/Fontes de figuras │ 11 pt │ Normal Entradas de referências │ 11 pt │ Título da obra em negrito Notas de rodapé │ 10 pt │ Normal (padrão) ════════════════════════════════════════════════════════════ BLOCO 16 — ENTRELINHAMENTOS — RESUMO RÁPIDO ════════════════════════════════════════════════════════════ Elemento │ Entrelinhamento ──────────────────────────────────┼────────────────── Corpo do texto (parágrafos) │ 1,5 (150%) Resumo │ Simples (1,0) Citações longas (bloco) │ Simples (1,0) Referências (cada entrada) │ Simples (1,0) Títulos de seções │ Simples (1,0) ════════════════════════════════════════════════════════════ BLOCO 17 — REGRAS DE INTEGRIDADE E CONDUTA ════════════════════════════════════════════════════════════ ▸ NÃO inventar, modificar ou criar: leis, artigos de lei, jurisprudências, doutrinas, autores ou referências que não estejam explicitamente no texto. ▸ NÃO alterar o conteúdo do texto — apenas a estrutura e a formatação. ▸ NÃO remover seções, parágrafos ou informações do original. ▸ SE o texto original não tiver resumo, palavras-chave ou considerações finais, sinalize que o autor precisa providenciá-los — não os crie. ▸ SE alguma referência estiver incompleta no original, mantenha-a como está e sinalize a incompletude para o autor. ════════════════════════════════════════════════════════════ BLOCO 18 — PROCESSO PASSO A PASSO (execute nesta ordem) ════════════════════════════════════════════════════════════ PASSO 1 — LEIA o texto completo antes de qualquer ação. PASSO 2 — CONFIGURE a página: A4, margens (sup 3 cm, esq 3 cm, inf 2 cm, dir 2 cm), cabeçalho/rodapé a 0,5 cm. PASSO 3 — CONFIGURE a numeração de página no cabeçalho (direita), iniciando na página 1, sem número na primeira página. PASSO 4 — FORMATE o TÍTULO: Calibri 14, negrito, centralizado, maiúsculas; subtítulo (se houver) em minúsculas. PASSO 5 — FORMATE os AUTORES e AFILIAÇÕES com letras sobrescritas, centralizado, Calibri 12, negrito nos nomes. PASSO 6 — FORMATE o RESUMO: rótulo em negrito, texto em Calibri 11 itálico, parágrafo único, máx. 150 palavras, justificado. Logo abaixo: "Palavras-chave:" negrito + 3-5 termos separados por ponto, primeira letra de cada palavra em maiúscula. PASSO 7 — VERIFIQUE a estrutura: o texto possui INTRODUÇÃO, seções numeradas (1, 2, 3...), CONSIDERAÇÕES FINAIS e REFERÊNCIAS. Se faltarem, sinalize ao autor. PASSO 8 — FORMATE todos os TÍTULOS DE SEÇÃO: Primárias → Calibri 12, negrito, MAIÚSCULA, sem recuo. Secundárias → Calibri 12, negrito, caixa baixa. Terciárias → Calibri 12, negrito + itálico, caixa baixa. PASSO 9 — FORMATE o CORPO DO TEXTO: Calibri 12, justificado, recuo de primeira linha 1,25 cm, entrelinhamento 1,5. PASSO 10 — IDENTIFIQUE e FORMATE as CITAÇÕES: Curtas (≤ 4 linhas) → no texto, entre aspas duplas. Longas (> 4 linhas) → bloco separado, Calibri 11, recuo esquerdo 4 cm, espaçamento simples, sem aspas. PASSO 11 — COLOQUE os TERMOS ESTRANGEIROS em itálico. PASSO 12 — FORMATE as REFERÊNCIAS: Calibri 11, espaçamento simples + 6 pt entre cada referência, alinhamento à esquerda, ordem alfabética, título da obra em negrito, sem numeração. PASSO 13 — REVISE TODO O DOCUMENTO verificando: □ Fonte Calibri em todo o documento (nenhuma exceção) □ Margens corretas (3/3/2/2 cm) □ Numeração de página no cabeçalho (sem número na 1ª pág.) □ Entrelinhamento 1,5 no corpo; simples no resumo e referências □ Termos estrangeiros em itálico □ Títulos de obras nas referências em negrito □ Referências em ordem alfabética □ Extensão entre 10 e 18 páginas ════════════════════════════════════════════════════════════ BLOCO 19 — DIFERENÇAS ENTRE OS DOIS TEMPLATES ════════════════════════════════════════════════════════════ ▸ TEMPLATE TCC DIREITO 2025: - Destinado a artigos do curso de Direito - Admite nota de rodapé junto ao nome dos autores com "Informação adicional sobre Autor/a (opcional)." - Numeração de notas de rodapé: algarismos arábicos ▸ TEMPLATE TRABALHO DE CURSO 2026: - Destinado a trabalhos de curso em geral (não exclusivo a Direito) - SEM nota de rodapé de informações adicionais dos autores - Estrutura de seções idêntica ao TCC Direito 2025 ▸ AMBOS OS TEMPLATES são idênticos em: - Configuração de página, margens, fontes, tamanhos, espaçamentos, entrelinhamentos, citações, referências e estrutura geral. ════════════════════════════════════════════════════════════ EXEMPLO DE ESTRUTURA FINAL ESPERADA ════════════════════════════════════════════════════════════ [CABEÇALHO com número de página — exceto na 1ª página] TÍTULO DO ARTIGO: subtítulo quando houver ← centralizado, Calibri 14, negrito, maiúsculas SOBRENOME, Nome a ; SOBRENOME, Nome b ← centralizado, Calibri 12, negrito a Titulação – Função – Instituição – email@email.com b Titulação – Função – Instituição – email@email.com ← centralizado, Calibri 12 RESUMO Texto do resumo em itálico, parágrafo único, máximo 150 palavras, contendo o problema, objetivo, método e resultados. [Calibri 11, itálico] Palavras-chave: Palavra Um. Palavra Dois. Palavra Três. INTRODUÇÃO ← Calibri 12, negrito, maiúsculas, sem recuo Texto do parágrafo com recuo de 1,25 cm na primeira linha... [Calibri 12, justificado, entrelinhamento 1,5] 1. TÍTULO DA SEÇÃO Texto do parágrafo... Citação curta: conforme autor, "texto da citação" (AUTOR, ano, p. x). Citação longa (em bloco): [4 cm de recuo à esquerda, Calibri 11, espaçamento simples] Texto da citação com mais de quatro linhas vai aqui, sem aspas, recuado 4 cm da margem esquerda, em Calibri tamanho 11, com espaçamento simples. (AUTOR, ano, p. x) 2. TÍTULO DA SEGUNDA SEÇÃO 2.1 Título de subseção em caixa baixa ← Calibri 12, negrito 2.1.1 Título de sub-subseção em caixa baixa ← Calibri 12, negrito + itálico CONSIDERAÇÕES FINAIS REFERÊNCIAS ← Calibri 11, negrito, maiúsculas SOBRENOME, Nome. **Título da Obra em Negrito**. ed. Cidade: Editora, ano. ← Calibri 11, esquerda, 6 pt após cada entrada --- AGORA APLIQUE TODAS ESTAS REGRAS AO TEXTO FORNECIDO ABAIXO: [COLE O TEXTO AQUI] """ st.code(prompt_pronto, language="markdown") # ABA 5: FEEDBACK with aba_feedback: st.markdown("### 👨‍🏫 Feedback do Professor Examinador") arq_feedback = st.file_uploader("Envie o TCC (.docx)", type=["docx"], key="feedback") if st.button("📊 Gerar Relatório"): if arq_feedback: with st.spinner("Analisando estilo acadêmico..."): texto = formatador.extrair_texto_docx(arq_feedback) prompt_fb = ( "Analise o TCC: ESTRUTURA, CITAÇÕES, PONTOS FORTES/ATENÇÃO, FORMAÇÃO. " "ATENÇÃO: Estamos em maio de 2026. Publicações de 2024, 2025 ou 2026 estão no prazo e NÃO pertencem ao futuro. " f"Texto: {texto}" ) try: res = revisor_ia.client.models.generate_content( model="gemini-3.5-flash", contents=prompt_fb, config=types.GenerateContentConfig(temperature=0.2) ) st.write(res.text) except Exception as e: st.error(f"❌ Erro: {e}")