Browse Source

adiciona Textos Articulados na pesquisa textual (#1594)

* adiciona Textos Articulados na pesquisa textual

* separa extrações em funções individuais

* apl sugestões de rev e outras ideias surgidas a partir daí
pull/1607/head
Leandro Roberto da Silva 7 years ago
committed by Edward
parent
commit
dc5666155f
  1. 145
      sapl/base/search_indexes.py
  2. 16
      sapl/compilacao/views.py
  3. 5
      sapl/materia/views.py
  4. 6
      sapl/templates/search/search.html

145
sapl/base/search_indexes.py

@ -3,33 +3,36 @@ import os.path
import re
import string
import textract
from django.db.models import Q, F, Value
from django.db.models.fields import TextField
from django.db.models.fields.files import FieldFile
from django.db.models.functions import Concat
from django.template import loader
from haystack import indexes
from haystack.constants import Indexable
from haystack.fields import CharField
from haystack.indexes import SearchIndex
from haystack.utils import get_model_ct_tuple
from textract.exceptions import ExtensionNotSupported
import textract
from sapl.compilacao.models import TextoArticulado, Dispositivo,\
STATUS_TA_PUBLIC, STATUS_TA_IMMUTABLE_PUBLIC
from sapl.materia.models import DocumentoAcessorio, MateriaLegislativa
from sapl.norma.models import NormaJuridica
from sapl.settings import BASE_DIR, SOLR_URL
logger = logging.getLogger(BASE_DIR.name)
class DocumentoAcessorioIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
class TextExtractField(CharField):
filename = 'arquivo'
model = DocumentoAcessorio
template_name = 'materia/documentoacessorio_text.txt'
def __init__(self, **kwargs):
super().__init__(**kwargs)
assert self.model_attr
def get_model(self):
return self.model
def index_queryset(self, using=None):
return self.get_model().objects.all()
def get_updated_field(self):
return 'data_ultima_atualizacao'
if not isinstance(self.model_attr, (list, tuple)):
self.model_attr = (self.model_attr, )
def solr_extraction(self, arquivo):
extracted_data = self._get_backend(None).extract_file_contents(
@ -59,71 +62,109 @@ class DocumentoAcessorioIndex(indexes.SearchIndex, indexes.Indexable):
print(msg)
logger.error(msg)
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:
if not os.path.exists(arquivo.path):
return self.prepared_data
if not os.path.splitext(arquivo.path)[1][:1]:
return self.prepared_data
def file_extractor(self, arquivo):
if not os.path.exists(arquivo.path) or \
not os.path.splitext(arquivo.path)[1][:1]:
return ''
# Em ambiente de produção utiliza-se o SOLR
if SOLR_URL:
try:
extracted_data = self.solr_extraction(arquivo)
return self.solr_extraction(arquivo)
except Exception:
self.print_error(arquivo)
return self.prepared_data
# Em ambiente de DEV utiliza-se o Whoosh
# Como ele não possui extração, faz-se uso do textract
else:
try:
extracted_data = self.whoosh_extraction(arquivo)
return self.whoosh_extraction(arquivo)
except ExtensionNotSupported as e:
print(str(e))
logger.error(str(e))
return self.prepared_data
except Exception:
self.print_error(arquivo)
return self.prepared_data
return ''
def ta_extractor(self, value):
r = []
for ta in value.filter(privacidade__in=[
STATUS_TA_PUBLIC,
STATUS_TA_IMMUTABLE_PUBLIC]):
dispositivos = Dispositivo.objects.filter(
Q(ta=ta) | Q(ta_publicado=ta)
).order_by(
'ordem'
).annotate(
rotulo_texto=Concat(
F('rotulo'), Value(' '), F('texto'),
output_field=TextField(),
)
).values_list(
'rotulo_texto', flat=True)
r += list(filter(lambda x: x.strip(), dispositivos))
return ' '.join(r)
def extract_data(self, obj):
data = ''
for attr, func in self.model_attr:
if not hasattr(obj, attr) or not hasattr(self, func):
raise Exception
# 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({'object': obj,
'extracted': extracted_data})
value = getattr(obj, attr)
if not value:
continue
data += getattr(self, func)(value)
return data
return self.prepared_data
def prepare_template(self, obj):
app_label, model_name = get_model_ct_tuple(obj)
template_names = ['search/indexes/%s/%s_%s.txt' %
(app_label, model_name, self.instance_name)]
t = loader.select_template(template_names)
class MateriaLegislativaIndex(DocumentoAcessorioIndex):
text = indexes.CharField(document=True, use_template=True)
return t.render({'object': obj,
'extracted': self.extract_data(obj)})
filename = 'texto_original'
model = MateriaLegislativa
template_name = 'materia/materialegislativa_text.txt'
class DocumentoAcessorioIndex(SearchIndex, Indexable):
model = DocumentoAcessorio
text = TextExtractField(
document=True, use_template=True,
model_attr=(('arquivo', 'file_extractor'), )
)
def get_model(self):
return self.model
def index_queryset(self, using=None):
return self.get_model().objects.all()
def get_updated_field(self):
return 'data_ultima_atualizacao'
class NormaJuridicaIndex(DocumentoAcessorioIndex):
text = indexes.CharField(document=True, use_template=True)
filename = 'texto_integral'
model = NormaJuridica
template_name = 'norma/normajuridica_text.txt'
text = TextExtractField(
document=True, use_template=True,
model_attr=(
('texto_integral', 'file_extractor'),
('texto_articulado', 'ta_extractor')
)
)
def get_updated_field(self):
return 'data_ultima_atualizacao'
class MateriaLegislativaIndex(DocumentoAcessorioIndex):
model = MateriaLegislativa
text = TextExtractField(
document=True, use_template=True,
model_attr=(
('texto_original', 'file_extractor'),
('texto_articulado', 'ta_extractor')
)
)

16
sapl/compilacao/views.py

@ -1,7 +1,7 @@
import logging
import sys
from collections import OrderedDict
from datetime import timedelta
import logging
import sys
from braces.views import FormMessagesMixin
from django import forms
@ -19,8 +19,8 @@ from django.http.response import (HttpResponse, HttpResponseRedirect,
from django.shortcuts import get_object_or_404, redirect
from django.utils.dateparse import parse_date
from django.utils.encoding import force_text
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import string_concat
from django.utils.translation import ugettext_lazy as _
from django.views.generic.base import TemplateView
from django.views.generic.detail import DetailView
from django.views.generic.edit import (CreateView, DeleteView, FormView,
@ -50,6 +50,7 @@ from sapl.compilacao.utils import (DISPOSITIVO_SELECT_RELATED,
from sapl.crud.base import Crud, CrudListView, make_pagination
from sapl.settings import BASE_DIR
TipoNotaCrud = Crud.build(TipoNota, 'tipo_nota')
TipoVideCrud = Crud.build(TipoVide, 'tipo_vide')
TipoPublicacaoCrud = Crud.build(TipoPublicacao, 'tipo_publicacao')
@ -1157,10 +1158,14 @@ class TextEditView(CompMixin, TemplateView):
self.object.save()
messages.success(request, _(
'Texto Articulado desbloqueado com sucesso.'))
if self.object.content_object:
self.object.content_object.save()
else:
if 'lock' in request.GET:
# TODO - implementar logging de ação de usuário
# TODO - implementar logging de ação de usuário
notificacoes = self.get_notificacoes(
object_list=self.object.dispositivos_set.all(),
type_notificacoes=['danger', ])
@ -1183,6 +1188,9 @@ class TextEditView(CompMixin, TemplateView):
messages.success(request, _(
'Texto Articulado bloqueado com sucesso.'))
if self.object.content_object:
self.object.content_object.save()
return redirect(to=reverse_lazy(
'sapl.compilacao:ta_text', kwargs={
'ta_id': self.object.id}))

5
sapl/materia/views.py

@ -2,7 +2,6 @@ from datetime import datetime
from random import choice
from string import ascii_letters, digits
import weasyprint
from crispy_forms.helper import FormHelper
from crispy_forms.layout import HTML
from django.contrib import messages
@ -20,8 +19,8 @@ from django.views.generic import CreateView, ListView, TemplateView, UpdateView
from django.views.generic.base import RedirectView
from django.views.generic.edit import FormView
from django_filters.views import FilterView
import weasyprint
import sapl
from sapl.base.models import Autor, CasaLegislativa
from sapl.comissoes.models import Comissao, Participacao
from sapl.compilacao.models import (STATUS_TA_IMMUTABLE_RESTRICT,
@ -42,6 +41,7 @@ from sapl.protocoloadm.models import Protocolo
from sapl.utils import (TURNO_TRAMITACAO_CHOICES, YES_NO_CHOICES, autor_label,
autor_modal, gerar_hash_arquivo, get_base_url,
montar_row_autor, show_results_filter_set)
import sapl
from .email_utils import do_envia_email_confirmacao
from .forms import (AcessorioEmLoteFilterSet, AcompanhamentoMateriaForm,
@ -62,6 +62,7 @@ from .models import (AcompanhamentoMateria, Anexada, AssuntoMateria, Autoria,
TipoProposicao, Tramitacao, UnidadeTramitacao)
from .signals import tramitacao_signal
AssuntoMateriaCrud = Crud.build(AssuntoMateria, 'assunto_materia')
OrigemCrud = Crud.build(Origem, '')

6
sapl/templates/search/search.html

@ -54,6 +54,9 @@
{% if result.object.texto_original %}
<strong>Texto Original:</strong> <a href="{{result.object.texto_original.url}}"> Clique aqui </a></br>
{% endif %}
{% if result.object.texto_articulado.first %}
<strong>Texto Articulado:</strong> <a href="{% url 'sapl.materia:materia_ta' result.object.pk %}"> Clique aqui </a></br>
{% else %}
<strong>O texto desta matéria foi removido recentemente. Em breve ela sairá desta listagem.</strong></br>
{% endif %}
@ -74,6 +77,9 @@
<strong> Norma Jurídica: </strong><a href="{% url 'sapl.norma:normajuridica_detail' result.object.pk %}">{{ result.object }}</a></br>
{% if result.object.texto_integral %}
<strong>Texto Original:</strong> <a href="{{result.object.texto_integral.url}}"> Clique aqui </a></br>
{% endif %}
{% if result.object.texto_articulado.first %}
<strong>Texto Articulado:</strong> <a href="{% url 'sapl.norma:norma_ta' result.object.pk %}"> Clique aqui </a></br>
{% else %}
<strong>O texto desta norma foi removido recentemente. Em breve ela sairá desta listagem.</strong></br>
{% endif %}

Loading…
Cancel
Save