Spaces:
Sleeping
Sleeping
Commit
·
33f14fa
1
Parent(s):
a68e0ce
feat: adicionar destaque de referências regex no texto extraído
Browse filesCo-authored-by: aider (anthropic/claude-sonnet-4-20250514) <aider@aider.chat>
app.py
CHANGED
|
@@ -197,16 +197,113 @@ def extract_references_with_regex(text):
|
|
| 197 |
except Exception as e:
|
| 198 |
return [{"error": f"Erro na extração por regex: {str(e)}"}]
|
| 199 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 200 |
def process_pdf(pdf_file, model_name):
|
| 201 |
"""Função principal que processa o PDF e retorna resultados"""
|
| 202 |
if pdf_file is None:
|
| 203 |
-
return {"error": "Nenhum arquivo enviado"}, pd.DataFrame(), pd.DataFrame(), "❌ Nenhum arquivo enviado", ""
|
| 204 |
|
| 205 |
# Extrair texto do PDF
|
| 206 |
text, metadata = extract_pdf_text(pdf_file)
|
| 207 |
|
| 208 |
if text is None:
|
| 209 |
-
return metadata, pd.DataFrame(), pd.DataFrame(), "❌ Erro ao processar PDF", ""
|
| 210 |
|
| 211 |
# Adicionar modelo selecionado aos metadados
|
| 212 |
metadata["modelo_usado"] = model_name
|
|
@@ -219,6 +316,9 @@ def process_pdf(pdf_file, model_name):
|
|
| 219 |
# Extrair referências com Regex
|
| 220 |
regex_references = extract_references_with_regex(text)
|
| 221 |
|
|
|
|
|
|
|
|
|
|
| 222 |
# Converter para DataFrames
|
| 223 |
if llm_references and not any("error" in ref for ref in llm_references):
|
| 224 |
llm_df = pd.DataFrame(llm_references)
|
|
@@ -236,7 +336,7 @@ def process_pdf(pdf_file, model_name):
|
|
| 236 |
|
| 237 |
status = f"📊 **Resultados da Extração:**\n- LLM ({model_name}): {llm_count} referências\n- Regex: {regex_count} referências"
|
| 238 |
|
| 239 |
-
return metadata, llm_df, regex_df, status,
|
| 240 |
|
| 241 |
def create_interface():
|
| 242 |
"""Cria a interface Gradio"""
|
|
@@ -272,13 +372,9 @@ def create_interface():
|
|
| 272 |
with gr.Column():
|
| 273 |
metadata_output = gr.JSON(label="📋 Metadados do Artigo")
|
| 274 |
with gr.Column():
|
| 275 |
-
extracted_text_output = gr.
|
| 276 |
-
label="📄 Texto Extraído
|
| 277 |
-
|
| 278 |
-
max_lines=20,
|
| 279 |
-
show_copy_button=True,
|
| 280 |
-
placeholder="O texto extraído do PDF aparecerá aqui...",
|
| 281 |
-
interactive=False
|
| 282 |
)
|
| 283 |
|
| 284 |
with gr.Row():
|
|
|
|
| 197 |
except Exception as e:
|
| 198 |
return [{"error": f"Erro na extração por regex: {str(e)}"}]
|
| 199 |
|
| 200 |
+
def create_highlighted_text(text, regex_references):
|
| 201 |
+
"""Cria HTML com texto destacado onde foram encontradas referências por regex"""
|
| 202 |
+
try:
|
| 203 |
+
# Encontrar a seção de referências
|
| 204 |
+
references_section = ""
|
| 205 |
+
section_start = 0
|
| 206 |
+
|
| 207 |
+
# Padrões para identificar início da seção de referências
|
| 208 |
+
ref_patterns = [
|
| 209 |
+
r'(?i)references?\s*\n',
|
| 210 |
+
r'(?i)bibliography\s*\n',
|
| 211 |
+
r'(?i)literatura\s+citada\s*\n',
|
| 212 |
+
r'(?i)referências\s+bibliográficas\s*\n'
|
| 213 |
+
]
|
| 214 |
+
|
| 215 |
+
for pattern in ref_patterns:
|
| 216 |
+
match = re.search(pattern, text)
|
| 217 |
+
if match:
|
| 218 |
+
section_start = match.start()
|
| 219 |
+
references_section = text[match.end():]
|
| 220 |
+
break
|
| 221 |
+
|
| 222 |
+
if not references_section:
|
| 223 |
+
# Se não encontrou seção específica, usar últimos 30% do texto
|
| 224 |
+
section_start = int(len(text) * 0.7)
|
| 225 |
+
references_section = text[section_start:]
|
| 226 |
+
|
| 227 |
+
# Criar HTML base
|
| 228 |
+
html_text = text.replace('\n', '<br>')
|
| 229 |
+
|
| 230 |
+
# Cores para diferentes tipos de matches
|
| 231 |
+
colors = ['#ffeb3b', '#4caf50', '#2196f3', '#ff9800', '#9c27b0']
|
| 232 |
+
|
| 233 |
+
# Padrões para destacar
|
| 234 |
+
patterns = [
|
| 235 |
+
(r'([A-Z][^.]*?)\.\s*\((\d{4})\)\.\s*([^.]+)\.\s*([^.]+?)(?:\.|$)', 'Padrão básico'),
|
| 236 |
+
(r'\[\d+\]\s*([A-Z][^.]*?)\.\s*\((\d{4})\)\.\s*([^.]+)\.\s*([^.]+?)(?:\.|$)', 'Padrão numerado'),
|
| 237 |
+
(r'([A-Z][A-Za-z\s,&]+)\s+\((\d{4})\)[.,]\s*([^.]+)[.,]\s*([^.]+?)(?:\.|$)', 'Padrão alternativo'),
|
| 238 |
+
(r'(?i)references?\s*\n', 'Seção de referências'),
|
| 239 |
+
(r'(?i)bibliography\s*\n', 'Bibliografia')
|
| 240 |
+
]
|
| 241 |
+
|
| 242 |
+
# Aplicar destaques
|
| 243 |
+
for i, (pattern, description) in enumerate(patterns):
|
| 244 |
+
color = colors[i % len(colors)]
|
| 245 |
+
|
| 246 |
+
# Encontrar matches no texto da seção de referências
|
| 247 |
+
section_html = references_section.replace('\n', '<br>')
|
| 248 |
+
matches = list(re.finditer(pattern, references_section, re.MULTILINE | re.DOTALL))
|
| 249 |
+
|
| 250 |
+
# Destacar matches (processar de trás para frente para não afetar posições)
|
| 251 |
+
for match in reversed(matches):
|
| 252 |
+
start, end = match.span()
|
| 253 |
+
matched_text = references_section[start:end]
|
| 254 |
+
highlighted = f'<span style="background-color: {color}; padding: 2px; border-radius: 3px;" title="{description}">{matched_text.replace(chr(10), "<br>")}</span>'
|
| 255 |
+
|
| 256 |
+
# Calcular posição no texto completo
|
| 257 |
+
full_start = section_start + start
|
| 258 |
+
full_end = section_start + end
|
| 259 |
+
|
| 260 |
+
# Substituir no HTML completo
|
| 261 |
+
before = html_text[:full_start].replace('\n', '<br>')
|
| 262 |
+
after = html_text[full_end:].replace('\n', '<br>')
|
| 263 |
+
html_text = before + highlighted + after
|
| 264 |
+
|
| 265 |
+
# Criar HTML final com estilo
|
| 266 |
+
styled_html = f"""
|
| 267 |
+
<div style="
|
| 268 |
+
font-family: 'Courier New', monospace;
|
| 269 |
+
font-size: 12px;
|
| 270 |
+
line-height: 1.4;
|
| 271 |
+
max-height: 400px;
|
| 272 |
+
overflow-y: auto;
|
| 273 |
+
padding: 15px;
|
| 274 |
+
border: 1px solid #ddd;
|
| 275 |
+
border-radius: 5px;
|
| 276 |
+
background-color: #fafafa;
|
| 277 |
+
white-space: pre-wrap;
|
| 278 |
+
">
|
| 279 |
+
<div style="margin-bottom: 10px; font-weight: bold; color: #333;">
|
| 280 |
+
📄 Texto Extraído com Destaques das Referências
|
| 281 |
+
</div>
|
| 282 |
+
<div style="margin-bottom: 15px; font-size: 11px; color: #666;">
|
| 283 |
+
<span style="background-color: #ffeb3b; padding: 2px;">■</span> Padrão básico
|
| 284 |
+
<span style="background-color: #4caf50; padding: 2px;">■</span> Padrão numerado
|
| 285 |
+
<span style="background-color: #2196f3; padding: 2px;">■</span> Padrão alternativo
|
| 286 |
+
<span style="background-color: #ff9800; padding: 2px;">■</span> Seção referências
|
| 287 |
+
</div>
|
| 288 |
+
{html_text}
|
| 289 |
+
</div>
|
| 290 |
+
"""
|
| 291 |
+
|
| 292 |
+
return styled_html
|
| 293 |
+
|
| 294 |
+
except Exception as e:
|
| 295 |
+
return f"<div style='color: red;'>Erro ao criar texto destacado: {str(e)}</div>"
|
| 296 |
+
|
| 297 |
def process_pdf(pdf_file, model_name):
|
| 298 |
"""Função principal que processa o PDF e retorna resultados"""
|
| 299 |
if pdf_file is None:
|
| 300 |
+
return {"error": "Nenhum arquivo enviado"}, pd.DataFrame(), pd.DataFrame(), "❌ Nenhum arquivo enviado", "<div>Nenhum texto para exibir</div>"
|
| 301 |
|
| 302 |
# Extrair texto do PDF
|
| 303 |
text, metadata = extract_pdf_text(pdf_file)
|
| 304 |
|
| 305 |
if text is None:
|
| 306 |
+
return metadata, pd.DataFrame(), pd.DataFrame(), "❌ Erro ao processar PDF", "<div style='color: red;'>Erro ao extrair texto</div>"
|
| 307 |
|
| 308 |
# Adicionar modelo selecionado aos metadados
|
| 309 |
metadata["modelo_usado"] = model_name
|
|
|
|
| 316 |
# Extrair referências com Regex
|
| 317 |
regex_references = extract_references_with_regex(text)
|
| 318 |
|
| 319 |
+
# Criar HTML com destaques
|
| 320 |
+
highlighted_html = create_highlighted_text(text, regex_references)
|
| 321 |
+
|
| 322 |
# Converter para DataFrames
|
| 323 |
if llm_references and not any("error" in ref for ref in llm_references):
|
| 324 |
llm_df = pd.DataFrame(llm_references)
|
|
|
|
| 336 |
|
| 337 |
status = f"📊 **Resultados da Extração:**\n- LLM ({model_name}): {llm_count} referências\n- Regex: {regex_count} referências"
|
| 338 |
|
| 339 |
+
return metadata, llm_df, regex_df, status, highlighted_html
|
| 340 |
|
| 341 |
def create_interface():
|
| 342 |
"""Cria a interface Gradio"""
|
|
|
|
| 372 |
with gr.Column():
|
| 373 |
metadata_output = gr.JSON(label="📋 Metadados do Artigo")
|
| 374 |
with gr.Column():
|
| 375 |
+
extracted_text_output = gr.HTML(
|
| 376 |
+
label="📄 Texto Extraído com Destaques",
|
| 377 |
+
show_copy_button=True
|
|
|
|
|
|
|
|
|
|
|
|
|
| 378 |
)
|
| 379 |
|
| 380 |
with gr.Row():
|