Browse Source

Fix #809 full text search (#980)

* Inicia a pesquisa textual

* Melhora o front-end da pesquisa textual

* Bug fix

* Ajusta alguns detalhes e cria botão de acesso a pesquisa

* Adiciona signals para atualizar o index

* Inicia a pesquisa textual

* Melhora o front-end da pesquisa textual

* Bug fix

* Ajusta alguns detalhes e cria botão de acesso a pesquisa

* Adiciona signals para atualizar o index

* Insere ao README o comando de indexação

* Atualiza versão do Haystack

* Remove pacote haystack antigo
Coloca url de search no padrão
pull/978/head
eduardocalil 8 years ago
committed by Edward
parent
commit
fb0753d090
  1. 1
      .gitignore
  2. 3
      docs/instacao31.rst
  3. 3
      requirements/requirements.txt
  4. 58
      sapl/base/search_indexes.py
  5. 13
      sapl/base/templatetags/common_tags.py
  6. 2
      sapl/base/urls.py
  7. 3
      sapl/materia/apps.py
  8. 29
      sapl/materia/signals.py
  9. 11
      sapl/settings.py
  10. 4
      sapl/templates/materia/materialegislativa_filter.html
  11. 7
      sapl/templates/search/indexes/materia/documentoacessorio_text.txt
  12. 7
      sapl/templates/search/indexes/materia/materialegislativa_text.txt
  13. 95
      sapl/templates/search/search.html

1
.gitignore

@ -92,3 +92,4 @@ collected_static
bower bower
bower_components bower_components
media media
whoosh/

3
docs/instacao31.rst

@ -187,6 +187,9 @@ Copie a chave que aparecerá, edite o arquivo .env e altere o valor do parâmetr
./manage.py collectstatic --noinput ./manage.py collectstatic --noinput
* Preparar o ambiente para indexação de arquivos::
./manage.py rebuild_index
* Subir o servidor do django:: * Subir o servidor do django::
./manage.py runserver 0.0.0.0:8001 ./manage.py runserver 0.0.0.0:8001

3
requirements/requirements.txt

@ -1,4 +1,5 @@
dj-database-url==0.4.1 dj-database-url==0.4.1
django-haystack==2.6.0
django>=1.9,<1.10 django>=1.9,<1.10
# TODO O django-admin-bootstrapped 2.5.7 não inseriu a mudança que permite # TODO O django-admin-bootstrapped 2.5.7 não inseriu a mudança que permite
# a compatibilidade com Django 1.9+. A linha abaixo será mudada quando uma # a compatibilidade com Django 1.9+. A linha abaixo será mudada quando uma
@ -25,7 +26,9 @@ python-decouple==3.0
pytz==2016.4 pytz==2016.4
pyyaml==3.11 pyyaml==3.11
rtyaml==0.0.3 rtyaml==0.0.3
textract==1.5.0
unipath==1.1 unipath==1.1
python-magic==0.4.12 python-magic==0.4.12
gunicorn==19.6.0 gunicorn==19.6.0
django-reversion==2.0.8 django-reversion==2.0.8
whoosh==2.7.4

58
sapl/base/search_indexes.py

@ -0,0 +1,58 @@
import textract
from haystack import indexes
from sapl.materia.models import DocumentoAcessorio, MateriaLegislativa
from django.template import Context, loader
class DocumentoAcessorioIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
filename = 'arquivo'
model = DocumentoAcessorio
template_name = 'materia/documentoacessorio_text.txt'
def get_model(self):
return self.model
def index_queryset(self, using=None):
return self.get_model().objects.all()
def prepare(self, obj):
if not self.filename or not self.model or not self.template_name:
raise Exception
data = super(DocumentoAcessorioIndex, self).prepare(obj)
arquivo = getattr(obj, self.filename)
if arquivo:
try:
arquivo.open()
except OSError:
return self.prepared_data
extracted_data = textract.process(
arquivo.path).decode(
'utf-8').replace('\n', ' ')
extracted_data = extracted_data.replace('\t', ' ')
# Now we'll finally perform the template processing to render the
# text field with *all* of our metadata visible for templating:
t = loader.select_template((
'search/indexes/' + self.template_name, ))
data['text'] = t.render(Context({'object': obj,
'extracted': extracted_data}))
return data
return self.prepared_data
class MateriaLegislativaIndex(DocumentoAcessorioIndex):
text = indexes.CharField(document=True, use_template=True)
filename = 'texto_original'
model = MateriaLegislativa
template_name = 'materia/materialegislativa_text.txt'

13
sapl/base/templatetags/common_tags.py

@ -2,6 +2,7 @@ from compressor.utils import get_class
from django import template from django import template
from sapl.base.models import AppConfig from sapl.base.models import AppConfig
from sapl.materia.models import DocumentoAcessorio, MateriaLegislativa
from sapl.parlamentares.models import Filiacao from sapl.parlamentares.models import Filiacao
register = template.Library() register = template.Library()
@ -124,7 +125,7 @@ def url(value):
@register.filter @register.filter
def cronometro_to_seconds(value): def cronometro_to_seconds(value):
if not AppConfig.attr('cronometro_' + value): if not AppConfig.attr('cronometro_' + value):
return 0 return 0
@ -137,3 +138,13 @@ def cronometro_to_seconds(value):
@register.filter @register.filter
def to_list_pk(object_list): def to_list_pk(object_list):
return [o.pk for o in object_list] return [o.pk for o in object_list]
@register.filter
def search_get_model(object):
if type(object) == MateriaLegislativa:
return 'm'
elif type(object) == DocumentoAcessorio:
return 'd'
return None

2
sapl/base/urls.py

@ -99,4 +99,6 @@ urlpatterns = [
name='login'), name='login'),
url(r'^logout/$', views.logout, {'next_page': '/login'}, name='logout'), url(r'^logout/$', views.logout, {'next_page': '/login'}, name='logout'),
url(r'^sistema/search/', include('haystack.urls')),
] + recuperar_senha ] + recuperar_senha

3
sapl/materia/apps.py

@ -6,3 +6,6 @@ class AppConfig(apps.AppConfig):
name = 'sapl.materia' name = 'sapl.materia'
label = 'materia' label = 'materia'
verbose_name = _('Matéria') verbose_name = _('Matéria')
def ready(self):
from . import signals

29
sapl/materia/signals.py

@ -0,0 +1,29 @@
from django.db.models.signals import post_delete, post_save
from sapl.settings import PROJECT_DIR
from subprocess import PIPE, call
from threading import Thread
from .models import MateriaLegislativa, DocumentoAcessorio
class UpdateIndexCommand(Thread):
def run(self):
call([PROJECT_DIR.child('manage.py'), 'update_index'],
stdout=PIPE)
def save_texto(sender, instance, **kwargs):
update_index = UpdateIndexCommand()
update_index.start()
def delete_texto(sender, instance, **kwargs):
update_index = UpdateIndexCommand()
update_index.start()
post_save.connect(save_texto, sender=MateriaLegislativa)
post_save.connect(save_texto, sender=DocumentoAcessorio)
post_delete.connect(delete_texto, sender=MateriaLegislativa)
post_delete.connect(delete_texto, sender=DocumentoAcessorio)

11
sapl/settings.py

@ -76,12 +76,23 @@ INSTALLED_APPS = (
'crispy_forms', 'crispy_forms',
'easy_thumbnails', 'easy_thumbnails',
'floppyforms', 'floppyforms',
'haystack',
'sass_processor', 'sass_processor',
'rest_framework', 'rest_framework',
'reversion', 'reversion',
'whoosh',
) + SAPL_APPS ) + SAPL_APPS
HAYSTACK_CONNECTIONS = {
'default': {
'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
'PATH': PROJECT_DIR.child('whoosh'),
},
}
if DEBUG: if DEBUG:
INSTALLED_APPS += ('debug_toolbar', 'rest_framework_docs',) INSTALLED_APPS += ('debug_toolbar', 'rest_framework_docs',)

4
sapl/templates/materia/materialegislativa_filter.html

@ -4,6 +4,10 @@
{% block actions %} {% block actions %}
<div class="actions btn-group pull-right" role="group"> <div class="actions btn-group pull-right" role="group">
<a href="{% url 'sapl.base:haystack_search' %}" class="btn btn-default">
Pesquisa Textual
</a>
{% if perms.materia %} {% if perms.materia %}
<a href="{% url 'sapl.materia:materialegislativa_create' %}" class="btn btn-default"> <a href="{% url 'sapl.materia:materialegislativa_create' %}" class="btn btn-default">
{% blocktrans with verbose_name=view.verbose_name %} Adicionar Matéria Legislativa {% endblocktrans %} {% blocktrans with verbose_name=view.verbose_name %} Adicionar Matéria Legislativa {% endblocktrans %}

7
sapl/templates/search/indexes/materia/documentoacessorio_text.txt

@ -0,0 +1,7 @@
{% for k, v in extracted.metadata.items %}
{% for val in v %}
{{ k }}: {{ val|safe }}
{% endfor %}
{% endfor %}
{{ extracted|striptags|safe }}

7
sapl/templates/search/indexes/materia/materialegislativa_text.txt

@ -0,0 +1,7 @@
{% for k, v in extracted.metadata.items %}
{% for val in v %}
{{ k }}: {{ val|safe }}
{% endfor %}
{% endfor %}
{{ extracted|striptags|safe }}

95
sapl/templates/search/search.html

@ -0,0 +1,95 @@
{% extends 'crud/form.html' %}
{% load crispy_forms_tags %}
{% load common_tags %}
{% block base_content %}
<h1><legend>Pesquisa Textual</legend></h1>
</br>
<form method="get" action=".">
<div class="row container-detail clearfix">
<div class="row-fluid">
<div class="col-md-8">
{{ form.q|as_crispy_field }}
</div>
</div>
<div class="row-fluid">
<div class="col-md-8">
</br>
<h4> Em quais tipos de documento deseja pesquisar?</h4>
</div>
</div>
<div class="row-fluid">
<div class="col-md-8">
{{ form.models }}
</div>
</div>
<div class="row-fluid">
<div class="col-md-12">
<input class="btn btn-primary pull-right" type="submit" value="Pesquisar">
</div>
</div>
</div>
</br>
{% if query %}
<table class="table table-striped table-bordered">
<thead class="thead-default">
<tr><td><h3>Resultados</h3></td></tr>
</thead>
{% for result in page.object_list %}
<tr>
<td>
{% if result.object|search_get_model == 'm' %}
<p>
<strong>Matéria Legislativa: </strong> <a href="{% url 'sapl.materia:materialegislativa_detail' result.object.pk %}">{{ result.object }}</a></br>
{% if result.object.texto_original %}
<strong>Texto Original:</strong> <a href="{{result.object.texto_original.url}}"> Clique aqui </a></br>
{% else %}
<strong>O texto desta matéria foi removido recentemente. Em breve ela sairá desta listagem.</strong></br>
{% endif %}
</p>
{% elif result.object|search_get_model == 'd' %}
<p>
<strong> Documento Acessório: </strong><a href="{% url 'sapl.materia:documentoacessorio_detail' result.object.pk %}">{{ result.object }}</a></br>
{% if result.object.arquivo %}
<strong>Texto Original:</strong> <a href="{{result.object.arquivo.url}}"> Clique aqui </a></br>
{% else %}
<strong>O texto deste documento foi removido recentemente. Em breve ele sairá desta listagem.</strong></br>
{% endif %}
</p>
{% endif %}
</td>
</tr>
{% empty %}
<h3> Nenhum texto encontrado! </h3>
<tr>
<td>
</td>
</tr>
{% endfor %}
</table>
{% if page.has_previous or page.has_next %}
<div>
{% if page.has_previous %}<a href="?q={{ query }}&amp;page={{ page.previous_page_number }}">{% endif %}&laquo; Previous{% if page.has_previous %}</a>{% endif %}
|
{% if page.has_next %}<a href="?q={{ query }}&amp;page={{ page.next_page_number }}">{% endif %}Next &raquo;{% if page.has_next %}</a>{% endif %}
</div>
{% endif %}
{% else %}
{# Show some example queries to run, maybe query syntax, something else? #}
{% endif %}
</form>
{% endblock %}
Loading…
Cancel
Save