From dc5666155f0b90fd55cc64ff942f7d6b5de0df86 Mon Sep 17 00:00:00 2001
From: Leandro Roberto da Silva
Date: Tue, 14 Nov 2017 10:50:17 -0200
Subject: [PATCH] adiciona Textos Articulados na pesquisa textual (#1594)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* 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í
---
sapl/base/search_indexes.py | 187 ++++++++++++++++++------------
sapl/compilacao/views.py | 16 ++-
sapl/materia/views.py | 5 +-
sapl/templates/search/search.html | 8 +-
4 files changed, 136 insertions(+), 80 deletions(-)
diff --git a/sapl/base/search_indexes.py b/sapl/base/search_indexes.py
index cee126dd2..e925de8c8 100644
--- a/sapl/base/search_indexes.py
+++ b/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)
+logger = logging.getLogger(BASE_DIR.name)
-class DocumentoAcessorioIndex(indexes.SearchIndex, indexes.Indexable):
- text = indexes.CharField(document=True, use_template=True)
- filename = 'arquivo'
- model = DocumentoAcessorio
- template_name = 'materia/documentoacessorio_text.txt'
+class TextExtractField(CharField):
- def get_model(self):
- return self.model
-
- def index_queryset(self, using=None):
- return self.get_model().objects.all()
+ def __init__(self, **kwargs):
+ super().__init__(**kwargs)
+ assert self.model_attr
- 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
-
- # Em ambiente de produção utiliza-se o SOLR
- if SOLR_URL:
- try:
- extracted_data = 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)
- 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
-
- # 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})
-
- return data
-
- 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:
+ return self.solr_extraction(arquivo)
+ except Exception:
+ self.print_error(arquivo)
+
+ # Em ambiente de DEV utiliza-se o Whoosh
+ # Como ele não possui extração, faz-se uso do textract
+ else:
+ try:
+ return self.whoosh_extraction(arquivo)
+ except ExtensionNotSupported as e:
+ print(str(e))
+ logger.error(str(e))
+ except Exception:
+ self.print_error(arquivo)
+ 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
+
+ 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):
- text = indexes.CharField(document=True, use_template=True)
+ def get_model(self):
+ return self.model
- filename = 'texto_original'
- model = MateriaLegislativa
- template_name = 'materia/materialegislativa_text.txt'
+ 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')
+ )
+ )
diff --git a/sapl/compilacao/views.py b/sapl/compilacao/views.py
index 4dfad8e99..9a763f3b2 100644
--- a/sapl/compilacao/views.py
+++ b/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}))
diff --git a/sapl/materia/views.py b/sapl/materia/views.py
index 05120f616..d478a8589 100644
--- a/sapl/materia/views.py
+++ b/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, '')
diff --git a/sapl/templates/search/search.html b/sapl/templates/search/search.html
index 3ea67cc86..e3b430f40 100644
--- a/sapl/templates/search/search.html
+++ b/sapl/templates/search/search.html
@@ -54,11 +54,14 @@
{% if result.object.texto_original %}
Texto Original: Clique aqui
+ {% endif %}
+ {% if result.object.texto_articulado.first %}
+ Texto Articulado: Clique aqui
{% else %}
O texto desta matéria foi removido recentemente. Em breve ela sairá desta listagem.
{% endif %}
-
+
{% elif result.object|search_get_model == 'd' %}
Documento Acessório: {{ result.object }}
@@ -74,6 +77,9 @@
Norma Jurídica: {{ result.object }}
{% if result.object.texto_integral %}
Texto Original: Clique aqui
+ {% endif %}
+ {% if result.object.texto_articulado.first %}
+ Texto Articulado: Clique aqui
{% else %}
O texto desta norma foi removido recentemente. Em breve ela sairá desta listagem.
{% endif %}