GabrielGD commited on
Commit
aa10b77
·
verified ·
1 Parent(s): 021c093

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +149 -76
app.py CHANGED
@@ -24,6 +24,21 @@ class AuditoriaTCC(BaseModel):
24
  objetivos_nao_cumpridos_no_tcc: list[str]
25
  desvios_de_tema_ou_linguica: list[str]
26
  indicios_texto_generico_ia: list[str]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
  # ─── AUTENTICAÇÃO GEMINI ──────────────────────────────────────────────────
29
  api_key = os.environ.get("GEMINI_API_KEY")
@@ -59,40 +74,32 @@ def docx_to_bytes(doc: Document) -> bytes:
59
  return byte_io.getvalue()
60
 
61
  def estimar_paginas(doc: Document) -> int:
62
- """Estima o número de páginas com base na contagem de palavras do corpo do texto."""
63
  palavras_totales = 0
64
  for para in doc.paragraphs:
65
- if re.search(r"^(?:\d+\.?\s*)?REFER[ÊE]NCIAS", para.text, re.IGNORECASE):
66
- break
67
  palavras_totales += len(para.text.split())
68
  return max(1, palavras_totales // 350)
69
 
70
  def aplicar_formatacao_estrutural_unifagoc(doc: Document):
71
- """Varre o documento inteiro e força o template estrutural físico da UNIFAGOC."""
72
- # 1. Aplicar Margens Oficiais (3cm sup/esq, 2cm inf/dir)
73
  for section in doc.sections:
74
- section.page_height = Cm(29.7) # A4
75
- section.page_width = Cm(21.0) # A4
76
  section.top_margin = Cm(3.0)
77
  section.left_margin = Cm(3.0)
78
  section.bottom_margin = Cm(2.0)
79
  section.right_margin = Cm(2.0)
80
 
81
- em_referencias = False
82
- em_resumo = False
83
 
84
- # 2. Varrer Parágrafos para Fonte, Alinhamento e Tamanho
85
  for para in doc.paragraphs:
86
  texto = para.text.strip()
87
  if not texto: continue
88
 
89
  texto_upper = texto.upper()
90
-
91
  if texto_upper.startswith("RESUMO"): em_resumo = True
92
  elif texto_upper.startswith("INTRODUÇÃO"): em_resumo = False
93
  elif re.search(r"^(?:\d+\.?\s*)?REFER[ÊE]NCIAS", texto, re.IGNORECASE): em_referencias = True
94
 
95
- # Alinhamento Base e Recuos
96
  if em_referencias:
97
  para.alignment = WD_ALIGN_PARAGRAPH.LEFT
98
  para.paragraph_format.first_line_indent = Cm(0)
@@ -107,10 +114,8 @@ def aplicar_formatacao_estrutural_unifagoc(doc: Document):
107
  para.alignment = WD_ALIGN_PARAGRAPH.JUSTIFY
108
  para.paragraph_format.first_line_indent = Cm(1.25)
109
 
110
- # Forçar Fonte e Tamanho
111
  for run in para.runs:
112
  run.font.name = 'Calibri'
113
-
114
  if em_resumo:
115
  run.font.size = Pt(11)
116
  if texto_upper != "RESUMO" and not texto_upper.startswith("PALAVRAS-CHAVE"):
@@ -118,46 +123,85 @@ def aplicar_formatacao_estrutural_unifagoc(doc: Document):
118
  elif em_referencias:
119
  run.font.size = Pt(11)
120
  elif texto.isupper() and len(texto) < 100:
121
- if "TULO" in texto_upper or "DIREITO" in texto_upper:
122
- run.font.size = Pt(14)
123
- else:
124
- run.font.size = Pt(12)
125
  else:
126
  run.font.size = Pt(12)
127
 
128
  # ─── INTERFACE PRINCIPAL (ABAS) ───────────────────────────────────────────
129
  st.title("⚖️ Assistente Acadêmico - Direito UNIFAGOC")
130
- aba_auditoria, aba_typos, aba_abnt = st.tabs(["🔍 Auditoria de Conteúdo", "📝 Correção Ortográfica", "📐 Formatação Total ABNT"])
131
 
132
  # ==============================================================================
133
- # ABA 1: AUDITORIA
134
  # ==============================================================================
135
  with aba_auditoria:
136
- st.markdown("Cruze o **Pré-Projeto** com o **TCC** para identificar fuga do tema e objetivos não cumpridos.")
137
  fase_tcc = st.radio("Selecione a fase atual do trabalho:", ["TCC 1 (Apenas Introdução e Capítulo 1)", "TCC 2 (TCC Completo com Resumo e Conclusão)"], horizontal=True)
138
 
139
  col1, col2 = st.columns(2)
140
  with col1: arq_projeto = st.file_uploader("1. Envie o Pré-Projeto (.docx)", type=["docx"], key="proj")
141
  with col2: arq_tcc = st.file_uploader("2. Envie os Capítulos do TCC (.docx)", type=["docx"], key="tcc_aud")
142
 
143
- if st.button("🔍 Iniciar Auditoria Cruzada", type="primary"):
144
  if not arq_projeto or not arq_tcc:
145
  st.warning("⚠️ Envie os DOIS arquivos para realizar a comparação.")
146
  else:
147
- with st.spinner("Avaliando argumentos jurídicos e metodológicos..."):
148
  texto_projeto = extrair_texto_docx(arq_projeto)
149
  texto_tcc = extrair_texto_docx(arq_tcc)
 
 
 
 
150
 
151
- regra_fase = "ATENÇÃO MÁXIMA: O aluno está na disciplina TCC 1. Ele DEVE entregar apenas a Introdução e o Capítulo 1. NÃO penalize e NÃO aponte como erro a ausência de Resumo, Conclusão ou capítulos posteriores." if "TCC 1" in fase_tcc else "O aluno está no TCC 2. O trabalho deve estar completo (Resumo, Introdução, Desenvolvimento, Conclusão e Referências)."
152
- prompt = f"Você é um Professor Examinador de Direito da UNIFAGOC.\n{regra_fase}\n\n=== PRÉ-PROJETO ===\n{texto_projeto}\n\n=== TCC ===\n{texto_tcc}\nIdentifique o Problema, Objetivos e procure por fugas de tema genéricas."
 
 
 
 
 
 
 
 
 
 
 
 
153
  config = types.GenerateContentConfig(temperature=0.0, response_mime_type="application/json", response_schema=AuditoriaTCC)
154
 
155
  try:
156
  resposta = client.models.generate_content(model="gemini-3.5-flash", contents=prompt, config=config)
157
  dados = json.loads(resposta.text)
158
- st.success(f"Auditoria concluída para os parâmetros do {fase_tcc.split(' ')[0]}!")
159
- st.metric(label="Índice de Aderência", value=f"{dados.get('aderencia_percentual')}%")
160
- st.info(dados.get("diagnostico_geral"))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
  c1, c2 = st.columns(2)
162
  with c1:
163
  st.subheader("🎯 Problema de Pesquisa")
@@ -169,77 +213,108 @@ with aba_auditoria:
169
  for f in dados.get("objetivos_nao_cumpridos_no_tcc", []): st.error(f"Faltou: {f}")
170
  if not dados.get("objetivos_nao_cumpridos_no_tcc"): st.success("✅ Objetivos abordados.")
171
  st.subheader("🚨 Filtro Anti-Embromação")
172
- for fuga in dados.get("desvios_de_tema_ou_linguica", []): st.warning(f"Fuga: {fuga}")
173
  for ia in dados.get("indicios_texto_generico_ia", []): st.error(f"Texto Genérico/IA: {ia}")
174
  except Exception as e:
175
- st.error(f"Erro: {e}")
 
176
 
177
  # ==============================================================================
178
- # ABA 2: CORREÇÃO DE ORTOGRAFIA
179
  # ==============================================================================
180
- with aba_typos:
181
- st.markdown("Identifica e corrige automaticamente palavras duplicadas, erros ortográficos e gramaticais.")
182
- arq_typos = st.file_uploader("Envie o TCC (.docx) para correção gramatical", type=["docx"], key="typos")
183
- if st.button("📝 Corrigir Ortografia"):
184
- if arq_typos:
185
- with st.spinner("Caçando erros de digitação..."):
186
- texto = extrair_texto_docx(arq_typos)
187
- prompt_typos = f"Analise o texto e identifique erros OBJETIVOS (ortografia, gramática, palavras duplicadas). NÃO aponte termos em inglês ou jargões jurídicos.\nTEXTO: {texto[:20000]}\nRetorne um JSON: {{\"erros\": [{{\"original\": \"trecho errado\", \"corrigido\": \"trecho certo\", \"motivo\": \"explicacao\"}}]}}"
 
 
 
 
 
 
188
  try:
189
- res = client.models.generate_content(model="gemini-3.5-flash", contents=prompt_typos, config=types.GenerateContentConfig(temperature=0.1, response_mime_type="application/json"))
190
- erros = json.loads(res.text).get("erros", [])
191
- if not erros: st.success("🎉 Nenhum erro encontrado! O texto está limpo.")
 
 
 
 
 
 
 
 
192
  else:
193
- st.warning(f"{len(erros)} erro(s) encontrado(s). Aplicando correções...")
194
- doc_typos = Document(arq_typos)
195
- aplicadas = 0
196
- for e in erros:
197
- orig, corr = e.get("original", ""), e.get("corrigido", "")
198
- st.write(f"❌ `{orig}` ➔ ✅ `{corr}` (*{e.get('motivo')}*)")
199
- for para in doc_typos.paragraphs:
200
- if orig in para.text:
201
- for run in para.runs:
202
- if orig in run.text:
203
- run.text = run.text.replace(orig, corr, 1)
204
- aplicadas += 1
205
- break
206
- if aplicadas > 0:
207
- st.success(f"{aplicadas} correções aplicadas no documento!")
208
- st.download_button("⬇️ Baixar Documento Corrigido (.docx)", data=docx_to_bytes(doc_typos), file_name="TCC_Sem_Erros.docx", mime="application/vnd.openxmlformats-officedocument.wordprocessingml.document")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
  except Exception as e:
210
- st.error(f"Erro ao processar: {e}")
 
211
 
212
  # ==============================================================================
213
  # ABA 3: FORMATAÇÃO TOTAL E REFERÊNCIAS ABNT
214
  # ==============================================================================
215
  with aba_abnt:
216
  st.markdown("Varre todo o documento forçando a fonte **Calibri**, Margens **3x2**, Alinhamento e Espaçamentos do edital, além de aplicar destaques ABNT nas Referências.")
217
- st.warning("⏱️ **Aviso de Sobrecarga:** Esta ferramenta utiliza inteligência artificial. Se muitos alunos tentarem formatar ao mesmo tempo, aguarde 60 segundos e tente novamente.")
218
 
219
- arq_abnt = st.file_uploader("Envie o TCC (.docx) para formatar o layout", type=["docx"], key="abnt")
220
 
221
  if st.button("📐 Formatar Layout e Referências"):
222
  if arq_abnt:
223
  with st.spinner("Padronizando fontes, margens e referências... Isso pode levar um minuto."):
224
  doc_abnt = Document(arq_abnt)
225
 
226
- # Estimativa de Páginas
227
  paginas_estimadas = estimar_paginas(doc_abnt)
228
- if paginas_estimadas > 18:
229
- st.error(f"🚨 **Atenção:** O corpo do seu TCC possui aproximadamente {paginas_estimadas} páginas. O limite máximo permitido pela banca (sem contar as referências) é de 18 páginas. Revise a extensão do seu texto.")
230
- else:
231
- st.info(f"📄 Extensão estimada: {paginas_estimadas} páginas (dentro do limite de 18).")
232
 
233
- # Aplica as regras estruturais
234
  aplicar_formatacao_estrutural_unifagoc(doc_abnt)
235
  st.success("✅ Margens (3x2) ajustadas!")
236
  st.success("✅ Fonte Calibri, tamanhos (11, 12 e 14) e alinhamentos aplicados perfeitamente!")
237
 
238
- # Analisa referências com a IA
239
  referencias = extrair_referencias_docx(doc_abnt)
240
  if not referencias:
241
  st.warning("Aviso: Nenhuma seção com o título 'REFERÊNCIAS' foi localizada para revisão de negrito.")
242
- st.download_button("⬇️ Baixar TCC Formatado (Sem Correção de Referências)", data=docx_to_bytes(doc_abnt), file_name="TCC_Padrao_UNIFAGOC.docx", mime="application/vnd.openxmlformats-officedocument.wordprocessingml.document")
243
  else:
244
  texto_refs = "\n".join(f"{i+1}. {texto}" for i, (_, texto) in enumerate(referencias))
245
  prompt_abnt = f"Verifique as referências abaixo seguindo a ABNT NBR 6023:2018. Retorne um JSON:\n{{\"problemas\": [{{\"numero\": int, \"erros\": [\"erro\"], \"elemento_negrito\": \"palavra destaque\"}}]}}\nREFERÊNCIAS:\n{texto_refs}"
@@ -252,9 +327,8 @@ with aba_abnt:
252
  )
253
  problemas = json.loads(res.text).get("problemas", [])
254
 
255
- def aplicar_negrito(doc, refs_problematicas):
256
- em_refs = False
257
- ref_idx = 0
258
  for para in doc.paragraphs:
259
  texto = para.text.strip()
260
  if not texto: continue
@@ -277,11 +351,10 @@ with aba_abnt:
277
  pos_atual += len(run.text)
278
  ref_idx += 1
279
 
280
- aplicar_negrito(doc_abnt, problemas)
281
 
282
  if problemas:
283
- for p in problemas:
284
- st.write(f"⚠️ Referência #{p['numero']} ajustada: {', '.join(p['erros'])}")
285
  st.success("✅ Formatação de destaque (negrito) nas referências aplicada!")
286
 
287
  st.download_button(
@@ -291,5 +364,5 @@ with aba_abnt:
291
  mime="application/vnd.openxmlformats-officedocument.wordprocessingml.document"
292
  )
293
  except Exception as e:
294
- if "429" in str(e): st.error("❌ A API está sobrecarregada no momento (muitos alunos usando). Por favor, aguarde 1 minuto e tente novamente.")
295
  else: st.error(f"Erro na verificação pela IA: {e}")
 
24
  objetivos_nao_cumpridos_no_tcc: list[str]
25
  desvios_de_tema_ou_linguica: list[str]
26
  indicios_texto_generico_ia: list[str]
27
+ # NOVOS CAMPOS: Ficha de Avaliação UNIFAGOC (80 Pontos Textuais)
28
+ nota_tema_problema: float
29
+ nota_introducao_metodo: float
30
+ nota_estruturacao: float
31
+ nota_formatacao_abnt: float
32
+ justificativa_notas_banca: str
33
+
34
+ class ErroTexto(BaseModel):
35
+ original: str
36
+ corrigido: str
37
+ motivo: str
38
+
39
+ class RevisaoInteligente(BaseModel):
40
+ correcoes: list[ErroTexto]
41
+ termos_estrangeiros_para_italico: list[str]
42
 
43
  # ─── AUTENTICAÇÃO GEMINI ──────────────────────────────────────────────────
44
  api_key = os.environ.get("GEMINI_API_KEY")
 
74
  return byte_io.getvalue()
75
 
76
  def estimar_paginas(doc: Document) -> int:
 
77
  palavras_totales = 0
78
  for para in doc.paragraphs:
79
+ if re.search(r"^(?:\d+\.?\s*)?REFER[ÊE]NCIAS", para.text, re.IGNORECASE): break
 
80
  palavras_totales += len(para.text.split())
81
  return max(1, palavras_totales // 350)
82
 
83
  def aplicar_formatacao_estrutural_unifagoc(doc: Document):
 
 
84
  for section in doc.sections:
85
+ section.page_height = Cm(29.7)
86
+ section.page_width = Cm(21.0)
87
  section.top_margin = Cm(3.0)
88
  section.left_margin = Cm(3.0)
89
  section.bottom_margin = Cm(2.0)
90
  section.right_margin = Cm(2.0)
91
 
92
+ em_referencias, em_resumo = False, False
 
93
 
 
94
  for para in doc.paragraphs:
95
  texto = para.text.strip()
96
  if not texto: continue
97
 
98
  texto_upper = texto.upper()
 
99
  if texto_upper.startswith("RESUMO"): em_resumo = True
100
  elif texto_upper.startswith("INTRODUÇÃO"): em_resumo = False
101
  elif re.search(r"^(?:\d+\.?\s*)?REFER[ÊE]NCIAS", texto, re.IGNORECASE): em_referencias = True
102
 
 
103
  if em_referencias:
104
  para.alignment = WD_ALIGN_PARAGRAPH.LEFT
105
  para.paragraph_format.first_line_indent = Cm(0)
 
114
  para.alignment = WD_ALIGN_PARAGRAPH.JUSTIFY
115
  para.paragraph_format.first_line_indent = Cm(1.25)
116
 
 
117
  for run in para.runs:
118
  run.font.name = 'Calibri'
 
119
  if em_resumo:
120
  run.font.size = Pt(11)
121
  if texto_upper != "RESUMO" and not texto_upper.startswith("PALAVRAS-CHAVE"):
 
123
  elif em_referencias:
124
  run.font.size = Pt(11)
125
  elif texto.isupper() and len(texto) < 100:
126
+ if "TULO" in texto_upper or "DIREITO" in texto_upper: run.font.size = Pt(14)
127
+ else: run.font.size = Pt(12)
 
 
128
  else:
129
  run.font.size = Pt(12)
130
 
131
  # ─── INTERFACE PRINCIPAL (ABAS) ───────────────────────────────────────────
132
  st.title("⚖️ Assistente Acadêmico - Direito UNIFAGOC")
133
+ aba_auditoria, aba_revisao, aba_abnt = st.tabs(["🔍 Auditoria de Conteúdo e Notas", "🧠 Revisão Textual Inteligente", "📐 Formatação Total ABNT"])
134
 
135
  # ==============================================================================
136
+ # ABA 1: AUDITORIA E PONTUAÇÃO UNIFAGOC
137
  # ==============================================================================
138
  with aba_auditoria:
139
+ st.markdown("Cruze o **Pré-Projeto** com o **TCC** para identificar fuga do tema e gerar a **Nota da Banca (80 pontos)**.")
140
  fase_tcc = st.radio("Selecione a fase atual do trabalho:", ["TCC 1 (Apenas Introdução e Capítulo 1)", "TCC 2 (TCC Completo com Resumo e Conclusão)"], horizontal=True)
141
 
142
  col1, col2 = st.columns(2)
143
  with col1: arq_projeto = st.file_uploader("1. Envie o Pré-Projeto (.docx)", type=["docx"], key="proj")
144
  with col2: arq_tcc = st.file_uploader("2. Envie os Capítulos do TCC (.docx)", type=["docx"], key="tcc_aud")
145
 
146
+ if st.button("🔍 Iniciar Auditoria e Avaliação", type="primary"):
147
  if not arq_projeto or not arq_tcc:
148
  st.warning("⚠️ Envie os DOIS arquivos para realizar a comparação.")
149
  else:
150
+ with st.spinner("Avaliando argumentos e calculando notas da banca..."):
151
  texto_projeto = extrair_texto_docx(arq_projeto)
152
  texto_tcc = extrair_texto_docx(arq_tcc)
153
+ regra_fase = "ATENÇÃO: O aluno está em TCC 1 (Apenas Introdução e Capítulo 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. O trabalho deve estar completo (Resumo, Introdução, Desenvolvimento, Conclusão e Referências)."
154
+
155
+ prompt = f"""Você é um Professor Examinador de Direito da UNIFAGOC.
156
+ {regra_fase}
157
 
158
+ === PRÉ-PROJETO ===
159
+ {texto_projeto}
160
+
161
+ === TCC ===
162
+ {texto_tcc}
163
+
164
+ TAREFA 1: Identifique o Problema, Objetivos e procure por fugas de tema genéricas.
165
+ TAREFA 2: Avalie o TCC conforme os 80 pontos da FICHA DE AVALIAÇÃO DA UNIFAGOC:
166
+ - Tema, Delimitação do Problema e Objeto (0 a 20 pts)
167
+ - Introdução e Método (0 a 20 pts)
168
+ - Estruturação e Desenvolvimento (0 a 20 pts)
169
+ - Formatação e ABNT (0 a 20 pts)
170
+ Seja rigoroso e acadêmico na atribuição das notas.
171
+ """
172
  config = types.GenerateContentConfig(temperature=0.0, response_mime_type="application/json", response_schema=AuditoriaTCC)
173
 
174
  try:
175
  resposta = client.models.generate_content(model="gemini-3.5-flash", contents=prompt, config=config)
176
  dados = json.loads(resposta.text)
177
+
178
+ st.success(f"Auditoria e Avaliação concluídas para {fase_tcc.split(' ')[0]}!")
179
+
180
+ # --- NOVO BLOCO DE NOTAS DA BANCA ---
181
+ st.header("🏆 Avaliação da Banca UNIFAGOC")
182
+ st.markdown("*Distribuição de 80 pontos textuais (20 pts de apresentação não inclusos).*")
183
+
184
+ nota_total = sum([
185
+ dados.get('nota_tema_problema', 0),
186
+ dados.get('nota_introducao_metodo', 0),
187
+ dados.get('nota_estruturacao', 0),
188
+ dados.get('nota_formatacao_abnt', 0)
189
+ ])
190
+
191
+ c_nota1, c_nota2, c_nota3, c_nota4 = st.columns(4)
192
+ c_nota1.metric("Tema/Problema", f"{dados.get('nota_tema_problema')}/20")
193
+ c_nota2.metric("Intro/Método", f"{dados.get('nota_introducao_metodo')}/20")
194
+ c_nota3.metric("Estrutura/Linguagem", f"{dados.get('nota_estruturacao')}/20")
195
+ c_nota4.metric("Formatação/ABNT", f"{dados.get('nota_formatacao_abnt')}/20")
196
+
197
+ st.info(f"**NOTA TEXTUAL FINAL: {nota_total}/80 pontos** \n*Justificativa da Banca:* {dados.get('justificativa_notas_banca')}")
198
+
199
+ st.divider()
200
+ # --- FIM DO BLOCO DE NOTAS ---
201
+
202
+ st.metric(label="Índice de Aderência ao Projeto", value=f"{dados.get('aderencia_percentual')}%")
203
+ st.write(f"**Diagnóstico:** {dados.get('diagnostico_geral')}")
204
+
205
  c1, c2 = st.columns(2)
206
  with c1:
207
  st.subheader("🎯 Problema de Pesquisa")
 
213
  for f in dados.get("objetivos_nao_cumpridos_no_tcc", []): st.error(f"Faltou: {f}")
214
  if not dados.get("objetivos_nao_cumpridos_no_tcc"): st.success("✅ Objetivos abordados.")
215
  st.subheader("🚨 Filtro Anti-Embromação")
216
+ for fuga in dados.get("desvios_de_tema_ou_linguica", []): st.warning(f"Fuga de Tema: {fuga}")
217
  for ia in dados.get("indicios_texto_generico_ia", []): st.error(f"Texto Genérico/IA: {ia}")
218
  except Exception as e:
219
+ if "429" in str(e): st.error("❌ A API está sobrecarregada. Aguarde 1 minuto.")
220
+ else: st.error(f"Erro: {e}")
221
 
222
  # ==============================================================================
223
+ # ABA 2: REVISÃO TEXTUAL INTELIGENTE (CAIXA ALTA E ITÁLICO)
224
  # ==============================================================================
225
+ with aba_revisao:
226
+ st.markdown("A IA analisa o contexto para corrigir **Ortografia**, parágrafos indevidamente em **CAIXA ALTA** e aplica **Itálico** em termos estrangeiros/latinos.")
227
+ arq_revisao = st.file_uploader("Envie o TCC (.docx) para revisão inteligente", type=["docx"], key="rev")
228
+
229
+ if st.button("🧠 Realizar Revisão Inteligente"):
230
+ if arq_revisao:
231
+ with st.spinner("Lendo o documento para identificar jargões, caixa alta e erros... Isso pode levar um tempo."):
232
+ texto = extrair_texto_docx(arq_revisao)
233
+ prompt_rev = f"""Analise o texto acadêmico de Direito abaixo e identifique:
234
+ 1. Erros ortográficos e gramaticais.
235
+ 2. Trechos do corpo do texto que estão indevidamente em CAIXA ALTA (retorne a versão corrigida). Ignore títulos.
236
+ 3. Termos estrangeiros ou jargões em latim (ex: habeas corpus, fake news) que necessitam de itálico.
237
+ TEXTO: {texto[:25000]}"""
238
+
239
  try:
240
+ res = client.models.generate_content(
241
+ model="gemini-3.5-flash",
242
+ contents=prompt_rev,
243
+ config=types.GenerateContentConfig(temperature=0.1, response_mime_type="application/json", response_schema=RevisaoInteligente)
244
+ )
245
+ dados_rev = json.loads(res.text)
246
+ correcoes = dados_rev.get("correcoes", [])
247
+ italicos = dados_rev.get("termos_estrangeiros_para_italico", [])
248
+
249
+ if not correcoes and not italicos:
250
+ st.success("🎉 O texto está perfeitamente revisado e sem jargões para formatar.")
251
  else:
252
+ doc_rev = Document(arq_revisao)
253
+ aplicadas_texto = 0
254
+ aplicadas_italico = 0
255
+
256
+ if correcoes:
257
+ st.subheader("📝 Correções de Texto e Caixa Alta")
258
+ for c in correcoes:
259
+ orig, corr = c.get("original", ""), c.get("corrigido", "")
260
+ st.write(f"❌ `{orig}` ➔ ✅ `{corr}` (*{c.get('motivo')}*)")
261
+ for para in doc_rev.paragraphs:
262
+ if orig in para.text:
263
+ for run in para.runs:
264
+ if orig in run.text:
265
+ run.text = run.text.replace(orig, corr, 1)
266
+ aplicadas_texto += 1
267
+ break
268
+
269
+ if italicos:
270
+ st.subheader("🪄 Termos Estrangeiros (Itálico Aplicado)")
271
+ st.write(", ".join([f"*{i}*" for i in italicos]))
272
+ for termo in italicos:
273
+ for para in doc_rev.paragraphs:
274
+ matches = list(re.finditer(r'\b' + re.escape(termo) + r'\b', para.text, re.IGNORECASE))
275
+ for match in matches:
276
+ pos_ini = match.start()
277
+ pos_fim = match.end()
278
+ pos_atual = 0
279
+ for run in para.runs:
280
+ fim_run = pos_atual + len(run.text)
281
+ if pos_atual < pos_fim and fim_run > pos_ini:
282
+ run.italic = True
283
+ aplicadas_italico += 1
284
+ pos_atual += len(run.text)
285
+
286
+ st.success(f"✅ {aplicadas_texto} correções de texto e {aplicadas_italico} itálicos aplicados!")
287
+ st.download_button("⬇️ Baixar Documento Revisado (.docx)", data=docx_to_bytes(doc_rev), file_name="TCC_Revisado_Inteligente.docx", mime="application/vnd.openxmlformats-officedocument.wordprocessingml.document")
288
  except Exception as e:
289
+ if "429" in str(e): st.error("❌ A API está sobrecarregada. Aguarde 1 minuto.")
290
+ else: st.error(f"Erro ao processar: {e}")
291
 
292
  # ==============================================================================
293
  # ABA 3: FORMATAÇÃO TOTAL E REFERÊNCIAS ABNT
294
  # ==============================================================================
295
  with aba_abnt:
296
  st.markdown("Varre todo o documento forçando a fonte **Calibri**, Margens **3x2**, Alinhamento e Espaçamentos do edital, além de aplicar destaques ABNT nas Referências.")
297
+ st.warning("⏱️ **Aviso de Sobrecarga:** Se muitos alunos tentarem formatar ao mesmo tempo, aguarde 60 segundos.")
298
 
299
+ arq_abnt = st.file_uploader("Envie o TCC (.docx) para formatar o layout estrutural", type=["docx"], key="abnt")
300
 
301
  if st.button("📐 Formatar Layout e Referências"):
302
  if arq_abnt:
303
  with st.spinner("Padronizando fontes, margens e referências... Isso pode levar um minuto."):
304
  doc_abnt = Document(arq_abnt)
305
 
 
306
  paginas_estimadas = estimar_paginas(doc_abnt)
307
+ if paginas_estimadas > 18: st.error(f"🚨 **Atenção:** Seu TCC tem aprox. {paginas_estimadas} páginas. O limite da banca é 18.")
308
+ else: st.info(f"📄 Extensão estimada: {paginas_estimadas} páginas.")
 
 
309
 
 
310
  aplicar_formatacao_estrutural_unifagoc(doc_abnt)
311
  st.success("✅ Margens (3x2) ajustadas!")
312
  st.success("✅ Fonte Calibri, tamanhos (11, 12 e 14) e alinhamentos aplicados perfeitamente!")
313
 
 
314
  referencias = extrair_referencias_docx(doc_abnt)
315
  if not referencias:
316
  st.warning("Aviso: Nenhuma seção com o título 'REFERÊNCIAS' foi localizada para revisão de negrito.")
317
+ st.download_button("⬇️ Baixar TCC Formatado (Sem Referências)", data=docx_to_bytes(doc_abnt), file_name="TCC_Padrao_UNIFAGOC.docx", mime="application/vnd.openxmlformats-officedocument.wordprocessingml.document")
318
  else:
319
  texto_refs = "\n".join(f"{i+1}. {texto}" for i, (_, texto) in enumerate(referencias))
320
  prompt_abnt = f"Verifique as referências abaixo seguindo a ABNT NBR 6023:2018. Retorne um JSON:\n{{\"problemas\": [{{\"numero\": int, \"erros\": [\"erro\"], \"elemento_negrito\": \"palavra destaque\"}}]}}\nREFERÊNCIAS:\n{texto_refs}"
 
327
  )
328
  problemas = json.loads(res.text).get("problemas", [])
329
 
330
+ def aplicar_negrito_ref(doc, refs_problematicas):
331
+ em_refs, ref_idx = False, 0
 
332
  for para in doc.paragraphs:
333
  texto = para.text.strip()
334
  if not texto: continue
 
351
  pos_atual += len(run.text)
352
  ref_idx += 1
353
 
354
+ aplicar_negrito_ref(doc_abnt, problemas)
355
 
356
  if problemas:
357
+ for p in problemas: st.write(f"⚠️ Referência #{p['numero']} ajustada: {', '.join(p['erros'])}")
 
358
  st.success("✅ Formatação de destaque (negrito) nas referências aplicada!")
359
 
360
  st.download_button(
 
364
  mime="application/vnd.openxmlformats-officedocument.wordprocessingml.document"
365
  )
366
  except Exception as e:
367
+ if "429" in str(e): st.error("❌ A API está sobrecarregada. Aguarde 1 minuto.")
368
  else: st.error(f"Erro na verificação pela IA: {e}")