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. 187
      sapl/base/search_indexes.py
  2. 16
      sapl/compilacao/views.py
  3. 5
      sapl/materia/views.py
  4. 6
      sapl/templates/search/search.html

187
sapl/base/search_indexes.py

@ -3,33 +3,36 @@ import os.path
import re import re
import string 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 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 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.materia.models import DocumentoAcessorio, MateriaLegislativa
from sapl.norma.models import NormaJuridica from sapl.norma.models import NormaJuridica
from sapl.settings import BASE_DIR, SOLR_URL from sapl.settings import BASE_DIR, SOLR_URL
logger = logging.getLogger(BASE_DIR.name)
logger = logging.getLogger(BASE_DIR.name)
class DocumentoAcessorioIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
filename = 'arquivo' class TextExtractField(CharField):
model = DocumentoAcessorio
template_name = 'materia/documentoacessorio_text.txt'
def get_model(self): def __init__(self, **kwargs):
return self.model super().__init__(**kwargs)
assert self.model_attr
def index_queryset(self, using=None):
return self.get_model().objects.all()
def get_updated_field(self): if not isinstance(self.model_attr, (list, tuple)):
return 'data_ultima_atualizacao' self.model_attr = (self.model_attr, )
def solr_extraction(self, arquivo): def solr_extraction(self, arquivo):
extracted_data = self._get_backend(None).extract_file_contents( extracted_data = self._get_backend(None).extract_file_contents(
@ -59,71 +62,109 @@ class DocumentoAcessorioIndex(indexes.SearchIndex, indexes.Indexable):
print(msg) print(msg)
logger.error(msg) logger.error(msg)
def prepare(self, obj): def file_extractor(self, arquivo):
if not self.filename or not self.model or not self.template_name: if not os.path.exists(arquivo.path) or \
raise Exception not os.path.splitext(arquivo.path)[1][:1]:
return ''
data = super(DocumentoAcessorioIndex, self).prepare(obj)
# Em ambiente de produção utiliza-se o SOLR
arquivo = getattr(obj, self.filename) if SOLR_URL:
try:
if arquivo: return self.solr_extraction(arquivo)
if not os.path.exists(arquivo.path): except Exception:
return self.prepared_data self.print_error(arquivo)
if not os.path.splitext(arquivo.path)[1][:1]: # Em ambiente de DEV utiliza-se o Whoosh
return self.prepared_data # Como ele não possui extração, faz-se uso do textract
else:
# Em ambiente de produção utiliza-se o SOLR try:
if SOLR_URL: return self.whoosh_extraction(arquivo)
try: except ExtensionNotSupported as e:
extracted_data = self.solr_extraction(arquivo) print(str(e))
except Exception: logger.error(str(e))
self.print_error(arquivo) except Exception:
return self.prepared_data self.print_error(arquivo)
return ''
# Em ambiente de DEV utiliza-se o Whoosh
# Como ele não possui extração, faz-se uso do textract def ta_extractor(self, value):
else: r = []
try: for ta in value.filter(privacidade__in=[
extracted_data = self.whoosh_extraction(arquivo) STATUS_TA_PUBLIC,
except ExtensionNotSupported as e: STATUS_TA_IMMUTABLE_PUBLIC]):
print(str(e)) dispositivos = Dispositivo.objects.filter(
logger.error(str(e)) Q(ta=ta) | Q(ta_publicado=ta)
return self.prepared_data ).order_by(
except Exception: 'ordem'
self.print_error(arquivo) ).annotate(
return self.prepared_data rotulo_texto=Concat(
F('rotulo'), Value(' '), F('texto'),
# Now we'll finally perform the template processing to render the output_field=TextField(),
# text field with *all* of our metadata visible for templating: )
t = loader.select_template(( ).values_list(
'search/indexes/' + self.template_name, )) 'rotulo_texto', flat=True)
data['text'] = t.render({'object': obj, r += list(filter(lambda x: x.strip(), dispositivos))
'extracted': extracted_data}) return ' '.join(r)
return data def extract_data(self, obj):
return self.prepared_data data = ''
for attr, func in self.model_attr:
if not hasattr(obj, attr) or not hasattr(self, func):
raise Exception
value = getattr(obj, attr)
if not value:
continue
data += getattr(self, func)(value)
return 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)
return t.render({'object': obj,
'extracted': self.extract_data(obj)})
class DocumentoAcessorioIndex(SearchIndex, Indexable):
model = DocumentoAcessorio
text = TextExtractField(
document=True, use_template=True,
model_attr=(('arquivo', 'file_extractor'), )
)
class MateriaLegislativaIndex(DocumentoAcessorioIndex): def get_model(self):
text = indexes.CharField(document=True, use_template=True) return self.model
filename = 'texto_original' def index_queryset(self, using=None):
model = MateriaLegislativa return self.get_model().objects.all()
template_name = 'materia/materialegislativa_text.txt'
def get_updated_field(self): def get_updated_field(self):
return 'data_ultima_atualizacao' return 'data_ultima_atualizacao'
class NormaJuridicaIndex(DocumentoAcessorioIndex): class NormaJuridicaIndex(DocumentoAcessorioIndex):
text = indexes.CharField(document=True, use_template=True)
filename = 'texto_integral'
model = NormaJuridica 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 collections import OrderedDict
from datetime import timedelta from datetime import timedelta
import logging
import sys
from braces.views import FormMessagesMixin from braces.views import FormMessagesMixin
from django import forms 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.shortcuts import get_object_or_404, redirect
from django.utils.dateparse import parse_date from django.utils.dateparse import parse_date
from django.utils.encoding import force_text 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 string_concat
from django.utils.translation import ugettext_lazy as _
from django.views.generic.base import TemplateView from django.views.generic.base import TemplateView
from django.views.generic.detail import DetailView from django.views.generic.detail import DetailView
from django.views.generic.edit import (CreateView, DeleteView, FormView, 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.crud.base import Crud, CrudListView, make_pagination
from sapl.settings import BASE_DIR from sapl.settings import BASE_DIR
TipoNotaCrud = Crud.build(TipoNota, 'tipo_nota') TipoNotaCrud = Crud.build(TipoNota, 'tipo_nota')
TipoVideCrud = Crud.build(TipoVide, 'tipo_vide') TipoVideCrud = Crud.build(TipoVide, 'tipo_vide')
TipoPublicacaoCrud = Crud.build(TipoPublicacao, 'tipo_publicacao') TipoPublicacaoCrud = Crud.build(TipoPublicacao, 'tipo_publicacao')
@ -1157,10 +1158,14 @@ class TextEditView(CompMixin, TemplateView):
self.object.save() self.object.save()
messages.success(request, _( messages.success(request, _(
'Texto Articulado desbloqueado com sucesso.')) 'Texto Articulado desbloqueado com sucesso.'))
if self.object.content_object:
self.object.content_object.save()
else: else:
if 'lock' in request.GET: 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( notificacoes = self.get_notificacoes(
object_list=self.object.dispositivos_set.all(), object_list=self.object.dispositivos_set.all(),
type_notificacoes=['danger', ]) type_notificacoes=['danger', ])
@ -1183,6 +1188,9 @@ class TextEditView(CompMixin, TemplateView):
messages.success(request, _( messages.success(request, _(
'Texto Articulado bloqueado com sucesso.')) 'Texto Articulado bloqueado com sucesso.'))
if self.object.content_object:
self.object.content_object.save()
return redirect(to=reverse_lazy( return redirect(to=reverse_lazy(
'sapl.compilacao:ta_text', kwargs={ 'sapl.compilacao:ta_text', kwargs={
'ta_id': self.object.id})) 'ta_id': self.object.id}))

5
sapl/materia/views.py

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

6
sapl/templates/search/search.html

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

Loading…
Cancel
Save