arruma o processo de busca textual nos diarios

This commit is contained in:
root
2025-03-14 17:36:14 +01:00
parent 8d1f6feeaf
commit f2e5cd73b7
15 changed files with 650 additions and 645 deletions

View File

@ -0,0 +1,124 @@
{% extends "base.html" %}
{% block content %}
<div class="container">
<h1>Busca de Diários Oficiais</h1>
<form method="GET" action="{% url 'search_diarios' %}" class="mb-4">
<div class="input-group">
<input type="text" name="q" class="form-control" value="{{ query }}" placeholder="Digite sua busca...">
<button type="submit" class="btn btn-primary">Buscar</button>
</div>
</form>
{% if query %}
<div class="mb-3">
<h2>Resultados para "{{ query }}"</h2>
<p>Encontrados {{ total }} resultados</p>
</div>
{% if results %}
<div class="search-results">
{% for result in results %}
<div class="card mb-3">
<div class="card-header">
<h5>{{ result.tipo }} nº {{ result.numero }}</h5>
<p class="text-muted">Data: {{ result.data }}</p>
</div>
<div class="card-body">
{% if result.highlight %}
<div class="highlight-section mb-3">
<h6>Destaques:</h6>
<div class="highlight-content">{{ result.highlight|safe }}</div>
</div>
{% endif %}
{% if result.highlighted_pages %}
<div class="highlighted-pages">
<h6>Páginas com o termo buscado:</h6>
<div class="accordion" id="pagesAccordion{{ result.id }}">
{% for page in result.highlighted_pages %}
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse"
data-bs-target="#page{{ result.id }}_{{ page.number }}">
Página {{ page.number }}
</button>
</h2>
<div id="page{{ result.id }}_{{ page.number }}" class="accordion-collapse collapse"
data-bs-parent="#pagesAccordion{{ result.id }}">
<div class="accordion-body">
{{ page.content|safe }}
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
<div class="mt-3">
<a href="{{ result.link }}" target="_blank" class="btn btn-sm btn-outline-primary">
Ver Diário Online
</a>
<a href="{% url 'diario_detail' result.id %}" class="btn btn-sm btn-outline-secondary">
Ver Detalhes
</a>
</div>
</div>
</div>
{% endfor %}
</div>
<!-- Paginação -->
{% if total_pages > 1 %}
<nav aria-label="Paginação">
<ul class="pagination">
{% if page > 1 %}
<li class="page-item">
<a class="page-link" href="?q={{ query }}&page={{ page|add:'-1' }}&size={{ size }}">Anterior</a>
</li>
{% endif %}
{% for i in total_pages|ljust:"5" %}
{% if i > 0 and i <= total_pages %}
<li class="page-item {% if i == page %}active{% endif %}">
<a class="page-link" href="?q={{ query }}&page={{ i }}&size={{ size }}">{{ i }}</a>
</li>
{% endif %}
{% endfor %}
{% if page < total_pages %}
<li class="page-item">
<a class="page-link" href="?q={{ query }}&page={{ page|add:'1' }}&size={{ size }}">Próxima</a>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
{% else %}
<div class="alert alert-info">
Nenhum resultado encontrado para a sua busca.
</div>
{% endif %}
{% endif %}
</div>
<style>
.highlight-content em {
background-color: #ffeeba;
font-style: normal;
padding: 2px;
border-radius: 2px;
}
.accordion-body em {
background-color: #ffeeba;
font-style: normal;
padding: 2px;
border-radius: 2px;
}
</style>
{% endblock %}

View File

@ -0,0 +1,173 @@
{% extends "base.html" %}
{% load static %}
{% block title %}Busca de Diários Oficiais{% endblock %}
{% block content %}
<div class="container mt-4">
<h1 class="mb-4">Busca de Diários Oficiais</h1>
<div class="card mb-4">
<div class="card-body">
<form method="get" action="{% url 'diario-search' %}">
<div class="row g-3">
<div class="col-md-9">
<label for="q" class="form-label">Buscar por:</label>
<input type="text" id="q" name="q" value="{{ query }}"
class="form-control"
placeholder="Digite palavras-chave, frases ou utilize operadores AND, OR, NOT">
</div>
<div class="col-md-3 d-flex align-items-end">
<button type="submit" class="btn btn-primary w-100">
<i class="bi bi-search"></i> Buscar
</button>
</div>
</div>
<div class="mt-3">
<a class="btn btn-link p-0" data-bs-toggle="collapse" href="#advancedOptions" role="button">
Opções avançadas
</a>
</div>
<div class="collapse" id="advancedOptions">
<div class="row g-3 mt-2">
<div class="col-md-4">
<label class="form-label">Tipos de Diário:</label>
<div class="border rounded p-2" style="max-height: 200px; overflow-y: auto;">
{% for tipo in tipos_disponiveis %}
<div class="form-check">
<input class="form-check-input" type="checkbox"
id="tipo_{{ tipo.id }}" name="tipos" value="{{ tipo.id }}"
{% if tipos_selecionados and tipo.id|stringformat:"i" in tipos_selecionados %}checked{% endif %}>
<label class="form-check-label" for="tipo_{{ tipo.id }}">
{{ tipo.nome }}
</label>
</div>
{% endfor %}
</div>
</div>
<div class="col-md-4">
<label for="data_inicio" class="form-label">Data Inicial:</label>
<input type="date" id="data_inicio" name="data_inicio"
value="{{ data_inicio }}" class="form-control">
</div>
<div class="col-md-4">
<label for="data_fim" class="form-label">Data Final:</label>
<input type="date" id="data_fim" name="data_fim"
value="{{ data_fim }}" class="form-control">
</div>
<div class="col-md-6">
<label for="fuzziness" class="form-label">Tolerância a erros:</label>
<select id="fuzziness" name="fuzziness" class="form-select">
<option value="0" {% if fuzziness == 0 %}selected{% endif %}>Sem tolerância</option>
<option value="1" {% if fuzziness == 1 %}selected{% endif %}>Baixa tolerância</option>
<option value="2" {% if fuzziness == 2 %}selected{% endif %}>Alta tolerância</option>
</select>
</div>
<div class="col-md-6 d-flex align-items-end">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="highlight"
name="highlight" value="true" {% if highlight %}checked{% endif %}>
<label class="form-check-label" for="highlight">
Destacar termos encontrados
</label>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
{% if query %}
<div class="mt-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>Resultados da busca</h2>
<span class="badge bg-primary">{{ total }} resultado(s)</span>
</div>
{% if results %}
{% for result in results %}
<div class="card mb-3">
<div class="card-body">
<h5 class="card-title">
<a href="{% url 'diario-detail' result.id %}?q={{ query|urlencode }}"
class="text-decoration-none">
{{ result.tipo_nome }} nº {{ result.numero }} - {{ result.data|date:"d/m/Y" }}
</a>
</h5>
{% if result.highlights %}
<div class="card-text mt-2">
{% for highlight in result.highlights %}
<p class="mb-1">...{{ highlight|safe }}...</p>
{% endfor %}
</div>
{% endif %}
<div class="mt-3 text-muted small">
<span class="me-3">
<i class="bi bi-star-fill text-warning"></i> Relevância: {{ result.score|floatformat:2 }}
</span>
{% if result.link %}
<a href="{{ result.link }}" target="_blank" class="text-decoration-none">
<i class="bi bi-box-arrow-up-right"></i> Ver original
</a>
{% endif %}
</div>
</div>
</div>
{% endfor %}
{% if pages > 1 %}
<nav aria-label="Page navigation">
<ul class="pagination justify-content-center">
{% if page > 1 %}
<li class="page-item">
<a class="page-link"
href="?q={{ query }}&page={{ page|add:'-1' }}&highlight={{ highlight|lower }}&fuzziness={{ fuzziness }}{% for tipo in tipos_selecionados %}&tipos={{ tipo }}{% endfor %}{% if data_inicio %}&data_inicio={{ data_inicio }}{% endif %}{% if data_fim %}&data_fim={{ data_fim }}{% endif %}">
Anterior
</a>
</li>
{% endif %}
{% for i in page_range %}
<li class="page-item {% if i == page %}active{% endif %}">
<a class="page-link"
href="?q={{ query }}&page={{ i }}&highlight={{ highlight|lower }}&fuzziness={{ fuzziness }}{% for tipo in tipos_selecionados %}&tipos={{ tipo }}{% endfor %}{% if data_inicio %}&data_inicio={{ data_inicio }}{% endif %}{% if data_fim %}&data_fim={{ data_fim }}{% endif %}">
{{ i }}
</a>
</li>
{% endfor %}
{% if page < pages %}
<li class="page-item">
<a class="page-link"
href="?q={{ query }}&page={{ page|add:'1' }}&highlight={{ highlight|lower }}&fuzziness={{ fuzziness }}{% for tipo in tipos_selecionados %}&tipos={{ tipo }}{% endfor %}{% if data_inicio %}&data_inicio={{ data_inicio }}{% endif %}{% if data_fim %}&data_fim={{ data_fim }}{% endif %}">
Próxima
</a>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
{% else %}
<div class="alert alert-warning text-center">
<h4 class="alert-heading">Nenhum resultado encontrado</h4>
<p>Não encontramos resultados para "{{ query }}". Tente ajustar seus termos de busca.</p>
</div>
{% endif %}
</div>
{% else %}
<div class="text-center py-5 bg-light rounded">
<p class="lead text-muted">Digite um termo de busca para encontrar diários oficiais</p>
</div>
{% endif %}
</div>
{% endblock %}

View File

@ -1,330 +0,0 @@
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% if query %}{{ query }} - {% endif %}Pesquisa de Documentos</title>
<!-- Bootstrap 5 CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Fonte personalizada -->
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
<!-- Ícones do Bootstrap -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.1/font/bootstrap-icons.css">
<style>
body {
font-family: 'Roboto', Arial, sans-serif;
background-color: #f8f9fa;
color: #202124;
}
.search-container {
max-width: 650px;
margin: 0 auto;
}
.main-container {
max-width: 650px;
margin: 0 auto;
}
.search-box {
border-radius: 24px;
border: 1px solid #dfe1e5;
box-shadow: none;
height: 44px;
padding-left: 20px;
}
.search-box:focus {
box-shadow: 0 1px 6px rgba(32,33,36,.28);
border-color: rgba(223,225,229,0);
}
.search-button {
border-radius: 24px;
margin-left: 10px;
}
.result-item {
padding: 20px 0;
border-bottom: 1px solid #e0e0e0;
}
.result-item:last-child {
border-bottom: none;
}
.pdf-link {
color: #e74c3c;
margin-left: 10px;
font-size: 0.8em;
text-decoration: none;
}
.result-title {
display: flex;
align-items: center;
justify-content: space-between;
color: #1a0dab;
font-weight: 500;
margin-bottom: 5px;
font-size: 18px;
}
.result-title a {
text-decoration: none;
}
.result-title a:hover {
text-decoration: underline;
}
.result-content {
color: #4d5156;
font-size: 14px;
margin-bottom: 5px;
line-height: 1.58;
}
.result-meta {
color: #70757a;
font-size: 12px;
}
.suggestion {
color: #1a0dab;
text-decoration: none;
}
.suggestion:hover {
text-decoration: underline;
}
mark {
background-color: #ffffc2;
padding: 0;
font-weight: bold;
}
.related-term {
color: #70757a;
font-size: 14px;
margin-right: 10px;
}
.pagination-link {
color: #1a0dab;
padding: 0 10px;
text-decoration: none;
}
.pagination-link.active {
color: #202124;
font-weight: bold;
}
.pagination-link:hover {
text-decoration: underline;
}
.search-stats {
color: #70757a;
font-size: 14px;
margin-bottom: 15px;
}
.header {
padding: 20px 0;
background-color: white;
border-bottom: 1px solid #dfe1e5;
}
.badge-exact-match {
background-color: #e8f0fe;
color: #1a73e8;
border: 1px solid #d2e3fc;
font-weight: normal;
}
.search-tip {
font-size: 13px;
color: #70757a;
margin-top: 5px;
}
</style>
<div class="position-relative">
<input type="text" name="q" class="form-control search-box"
id="searchInput" autocomplete="off">
<div id="suggestionsBox" class="position-absolute w-100 bg-white shadow"></div>
</div>
<script>
// JavaScript para sugestões em tempo real
document.getElementById('searchInput').addEventListener('input', function(e) {
const query = e.target.value;
if(query.length > 2) {
fetch(`/diarios/spellcheck/?q=${encodeURIComponent(query)}`)
.then(response => response.json())
.then(data => {
const suggestionsBox = document.getElementById('suggestionsBox');
suggestionsBox.innerHTML = data.suggestions.map(sug =>
`<div class="suggestion-item p-2 border-bottom cursor-pointer">
${sug}
</div>`
).join('');
});
}
});
// Clique na sugestão
document.getElementById('suggestionsBox').addEventListener('click', function(e) {
if(e.target.classList.contains('suggestion-item')) {
document.getElementById('searchInput').value = e.target.textContent;
this.innerHTML = '';
}
});
</script>
</head>
<body>
<!-- Cabeçalho com barra de pesquisa -->
<header class="header">
<div class="container">
<div class="row align-items-center">
<div class="col-auto">
<a href="/" class="text-decoration-none">
<h3 class="mb-0 text-primary"><i class="bi bi-search"></i> BuscaDocs</h3>
</a>
</div>
<div class="col">
<form action="{% url 'search_view' %}" method="get" class="d-flex">
<input type="text" name="q" class="form-control search-box" value="{{ query }}" placeholder="Pesquisar documentos..." aria-label="Pesquisar">
<button class="btn btn-primary search-button" type="submit"><i class="bi bi-search"></i></button>
</form>
<div class="search-tip">
Use aspas duplas para buscar frases exatas, ex: "documento oficial"
</div>
</div>
</div>
</div>
</header>
<div class="container py-4 main-container">
{% if query %}
<!-- Estatísticas da busca -->
<div class="search-stats">
{% if total_hits > 0 %}
<p>Cerca de {{ total_hits }} resultados encontrados para "{{ query }}"</p>
{% else %}
<p>Nenhum resultado encontrado para "{{ query }}"</p>
{% endif %}
</div>
<!-- Correção ortográfica -->
{% if spelling_correction %}
<div class="mb-4">
<p>Você quis dizer: <a href="?q={{ spelling_correction|urlencode }}" class="suggestion">{{ spelling_correction }}</a>?</p>
</div>
{% endif %}
<!-- Sugestões de termos -->
{% if suggestions %}
<div class="mb-4">
<p>Talvez você esteja procurando por:
{% for suggestion in suggestions %}
<a href="?q={{ suggestion|urlencode }}" class="suggestion me-2">{{ suggestion }}</a>{% if not forloop.last %}, {% endif %}
{% endfor %}
</p>
</div>
{% endif %}
<!-- Resultados da busca -->
{% if results %}
<div class="results-container">
{% for result in results %}
<div class="result-item">
<div class="d-flex gap-2 mb-2">
{% if result.is_exact_match %}
<span class="badge badge-exact-match">Correspondência exata</span>
{% endif %}
{% if result.is_related %}
<span class="badge bg-secondary">Termo relacionado</span>
{% endif %}
</div>
<h5 class="result-title">
<a href="{{ result.pdf_url }}" target="_blank">{{ result.highlighted_title|safe }}</a>
<a href="{{ result.pdf_url }}" target="_blank" class="pdf-link" title="Abrir PDF completo">
<i class="bi bi-file-pdf"></i>
</a>
</h5>
<div class="result-content">{{ result.highlighted_content|safe }}</div>
<div class="result-meta">
<i class="bi bi-calendar-date"></i> {{ result.uploaded_at|date:"d/m/Y" }}
{% if result.matching_pages %}
<span class="ms-3">
<i class="bi bi-file-earmark-text"></i> Páginas encontradas:
{% for page in result.matching_pages %}
<a href="{{ result.pdf_url }}#page={{ page }}" target="_blank" class="badge bg-light text-dark">{{ page }}</a>
{% endfor %}
</span>
{% endif %}
</div>
</div>
{% endfor %}
</div>
<!-- Paginação -->
{% if total_pages > 1 %}
<nav aria-label="Paginação de resultados" class="my-4">
<div class="d-flex justify-content-center">
{% if page > 1 %}
<a href="?q={{ query|urlencode }}&page={{ page|add:'-1' }}" class="pagination-link">
<i class="bi bi-chevron-left"></i> Anterior
</a>
{% endif %}
{% for p in page_range %}
<a href="?q={{ query|urlencode }}&page={{ p }}" class="pagination-link {% if p == page %}active{% endif %}">
{{ p }}
</a>
{% endfor %}
{% if page < total_pages %}
<a href="?q={{ query|urlencode }}&page={{ page|add:'1' }}" class="pagination-link">
Próxima <i class="bi bi-chevron-right"></i>
</a>
{% endif %}
</div>
</nav>
{% endif %}
{% else %}
<div class="alert alert-info" role="alert">
<i class="bi bi-info-circle-fill me-2"></i>
Nenhum documento corresponde aos termos de pesquisa. Tente usar palavras-chave diferentes ou mais gerais.
{% if has_exact_phrases %}
<p class="mt-2 mb-0">Você pesquisou por frases exatas. Tente remover as aspas para uma busca mais ampla.</p>
{% endif %}
</div>
{% endif %}
{% else %}
<!-- Página inicial de pesquisa -->
<div class="text-center py-5">
<h1 class="display-4 mb-4 text-primary"><i class="bi bi-search"></i> BuscaDocs</h1>
<div class="search-container mb-4">
<form action="{% url 'search_view' %}" method="get">
<div class="input-group mb-3">
<input type="text" name="q" class="form-control search-box py-3" placeholder="Pesquisar documentos..." aria-label="Pesquisar">
<button class="btn btn-primary search-button px-4" type="submit">
<i class="bi bi-search"></i> Pesquisar
</button>
</div>
</form>
<p class="text-muted">Pesquise em nossa biblioteca de documentos digitalizados</p>
<div class="mt-3 text-start p-3 border rounded bg-light">
<h5>Dicas de pesquisa:</h5>
<ul class="mb-0">
<li>Use <strong>aspas duplas</strong> para buscar frases exatas: <code>"documento oficial"</code></li>
<li>Tente usar sinônimos se não encontrar resultados</li>
<li>Seja específico para encontrar documentos relevantes</li>
</ul>
</div>
</div>
</div>
{% endif %}
</div>
<!-- Footer -->
<footer class="bg-light py-3 mt-5">
<div class="container text-center">
<p class="text-muted mb-0">© 2025 BuscaDocs - Sistema de Pesquisa de Documentos</p>
</div>
</footer>
<!-- Bootstrap JS Bundle with Popper -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<!-- Script para sugestões em tempo real (opcional) -->
<script>
document.addEventListener('DOMContentLoaded', function() {
const searchInput = document.querySelector('input[name="q"]');
searchInput.focus();
});
</script>
</body>
</html>