Browse Source

Permitir o download de documentos acessórios em matéria legislativa (#3139)

* Fix #3127

* Adiciona merger de PDF

* Adiciona pypdf4 em requirements.txt

* Adicionando mensagem de erro caso não tenha documento acessório

* Subido algumas recomendações(logs, exceptions, localização de imports)

* Mudando maneira de pegar o diretorio tmp

* Concertando problema de css

* Arrumando mensagem de erro para quando todos os documentos acessorios não tem pdf cadastrados

* Generalizando tmp file para utils.py

* Adicionando logs de info e quebrando linhas grandes

Co-authored-by: eribeiro <edwardr@senado.leg.br>
Co-authored-by: ulysses <ulysses3353@gmail.com>
pull/3367/head
Edward 5 years ago
committed by eribeiro
parent
commit
1c24c16843
  1. 2
      requirements/requirements.txt
  2. 7
      sapl/materia/urls.py
  3. 130
      sapl/materia/views.py
  4. 13
      sapl/templates/materia/documentoacessorio_list.html
  5. 8
      sapl/utils.py

2
requirements/requirements.txt

@ -37,7 +37,7 @@ Whoosh==2.7.4
more-itertools==8.2.0 more-itertools==8.2.0
pysolr==3.6.0 pysolr==3.6.0
PyPDF4==1.27.0
pyoai==2.5.0 pyoai==2.5.0
daphne==2.2.5 daphne==2.2.5

7
sapl/materia/urls.py

@ -28,7 +28,8 @@ from sapl.materia.views import (AcompanhamentoConfirmarView,
ExcluirTramitacaoEmLoteView, RetornarProposicao, ExcluirTramitacaoEmLoteView, RetornarProposicao,
MateriaPesquisaSimplesView, MateriaPesquisaSimplesView,
DespachoInicialMultiCreateView, DespachoInicialMultiCreateView,
TipoTurnoTramitacaoCrud) TipoTurnoTramitacaoCrud,
get_zip_docacessorios, get_pdf_docacessorios)
from sapl.norma.views import NormaPesquisaSimplesView from sapl.norma.views import NormaPesquisaSimplesView
from sapl.protocoloadm.views import ( from sapl.protocoloadm.views import (
FichaPesquisaAdmView, FichaSelecionaAdmView) FichaPesquisaAdmView, FichaSelecionaAdmView)
@ -121,6 +122,10 @@ urlpatterns_materia = [
name='tramitacao_em_lote'), name='tramitacao_em_lote'),
url(r'^materia/excluir-tramitacao-em-lote', ExcluirTramitacaoEmLoteView.as_view(), url(r'^materia/excluir-tramitacao-em-lote', ExcluirTramitacaoEmLoteView.as_view(),
name='excluir_tramitacao_em_lote'), name='excluir_tramitacao_em_lote'),
url(r'^materia/docacessorio/zip/(?P<pk>\d+)$', get_zip_docacessorios,
name='compress_docacessorios'),
url(r'^materia/docacessorio/pdf/(?P<pk>\d+)$', get_pdf_docacessorios,
name='merge_docacessorios')
] ]

130
sapl/materia/views.py

@ -6,11 +6,15 @@ import sapl
import shutil import shutil
import tempfile import tempfile
import weasyprint import weasyprint
import time
from crispy_forms.layout import HTML from crispy_forms.layout import HTML
from datetime import datetime from datetime import datetime
from random import choice from random import choice
from string import ascii_letters, digits from string import ascii_letters, digits
from datetime import datetime
from PyPDF4 import PdfFileReader, PdfFileMerger
import zipfile
from django.conf import settings from django.conf import settings
from django.contrib import messages from django.contrib import messages
@ -52,7 +56,7 @@ from sapl.settings import MAX_DOC_UPLOAD_SIZE, MEDIA_ROOT
from sapl.utils import (autor_label, autor_modal, gerar_hash_arquivo, get_base_url, from sapl.utils import (autor_label, autor_modal, gerar_hash_arquivo, get_base_url,
get_client_ip, get_mime_type_from_file_extension, lista_anexados, get_client_ip, get_mime_type_from_file_extension, lista_anexados,
mail_service_configured, montar_row_autor, SEPARADOR_HASH_PROPOSICAO, mail_service_configured, montar_row_autor, SEPARADOR_HASH_PROPOSICAO,
show_results_filter_set, YES_NO_CHOICES) show_results_filter_set, YES_NO_CHOICES,get_tempfile_dir)
from .forms import (AcessorioEmLoteFilterSet, AcompanhamentoMateriaForm, from .forms import (AcessorioEmLoteFilterSet, AcompanhamentoMateriaForm,
AnexadaEmLoteFilterSet, AdicionarVariasAutoriasFilterSet, AnexadaEmLoteFilterSet, AdicionarVariasAutoriasFilterSet,
@ -2749,3 +2753,127 @@ class TipoMateriaCrud(CrudAux):
self.object.save() self.object.save()
return fv return fv
def create_zip_docacessorios(materia):
logger = logging.getLogger(__name__)
docs = materia.documentoacessorio_set.\
all().values_list('arquivo', flat=True)
if not docs:
return None, None
docs_path = [os.path.join(MEDIA_ROOT, i) for i in docs]
if not docs_path:
raise FileNotFoundError("Não há arquivos PDF cadastrados em documentos acessorios.")
logger.info("Gerando compilado PDF de documentos acessorios com {} documentos".format(docs_path))
zipfilename = '{}/mat_{}_{}_docacessorios.zip'.format(
get_tempfile_dir(),
materia.pk,
time.mktime(datetime.now().timetuple()))
with zipfile.ZipFile(zipfilename, 'w', zipfile.ZIP_DEFLATED) as zipf:
for f in docs_path:
zipf.write(f, f.split(os.sep)[-1])
external_name = "mat_{}_{}_docacessorios.zip".format(materia.numero, materia.ano)
return external_name, zipfilename
def get_zip_docacessorios(request, pk):
logger = logging.getLogger(__name__)
username = request.user.username
materia = get_object_or_404(MateriaLegislativa, pk=pk)
try:
external_name, zipfilename = create_zip_docacessorios(materia)
logger.info("user= {}. Gerou o zip compilado de documento acessorios")
except FileNotFoundError:
logger.error("user= {}.Não há arquivos cadastrados".format(username))
msg=_('Não há arquivos cadastrados nesses documentos acessórios.')
messages.add_message(request, messages.ERROR, msg)
return redirect(reverse('sapl.materia:documentoacessorio_list',
kwargs={'pk': pk}))
except Exception as e:
logger.error("user={}. Um erro inesperado ocorreu na criação do pdf de documentos acessorios: {}"
.format(username,str(e)))
msg=_('Um erro inesperado ocorreu. Entre em contato com o suporte do SAPL.')
messages.add_message(request, messages.ERROR, msg)
return redirect(reverse('sapl.materia:documentoacessorio_list',
kwargs={'pk': pk}))
if not zipfilename:
msg=_('Não há nenhum documento acessório cadastrado.')
messages.add_message(request, messages.ERROR, msg)
return redirect(reverse('sapl.materia:documentoacessorio_list',
kwargs={'pk': pk}))
with open(os.path.join(get_tempfile_dir(), zipfilename), 'rb') as f:
data = f.read()
response = HttpResponse(data, content_type='application/zip')
response['Content-Disposition'] = ('attachment; filename="%s"'
% external_name)
return response
def create_pdf_docacessorios(materia):
logger = logging.getLogger(__name__)
docs = materia.documentoacessorio_set. \
all().values_list('arquivo', flat=True)
if not docs:
return None, None
# TODO: o for-comprehension abaixo filtra os arquivos não PDF.
# TODO: o que fazer com os arquivos não PDF? converter? ignorar?
docs_path = [os.path.join(MEDIA_ROOT, i) for i in docs if i.lower().endswith('pdf')]
if not docs_path:
raise FileNotFoundError("Não há arquivos PDF cadastrados em documentos acessorios.")
logger.info("Gerando compilado PDF de documentos acessorios com {} documentos"
.format(docs_path))
merged_pdf = '{}/mat_{}_{}_docacessorios.pdf'.format(
get_tempfile_dir(),
materia.pk,
time.mktime(datetime.now().timetuple()))
merger = PdfFileMerger()
for f in docs_path:
merger.append(fileobj=f)
merger.write(fileobj=open(merged_pdf, "wb"))
merger.close()
external_name = "mat_{}_{}_docacessorios.pdf".format(materia.numero, materia.ano)
return external_name, merged_pdf
def get_pdf_docacessorios(request, pk):
materia = get_object_or_404(MateriaLegislativa, pk=pk)
logger = logging.getLogger(__name__)
username = request.user.username
try:
external_name, pdffilename = create_pdf_docacessorios(materia)
logger.info("user= {}. Gerou o pdf compilado de documento acessorios")
except FileNotFoundError:
logger.error("user= {}.Não há arquivos cadastrados".format(username))
msg=_('Não há arquivos cadastrados nesses documentos acessórios.')
messages.add_message(request, messages.ERROR, msg)
return redirect(reverse('sapl.materia:documentoacessorio_list',
kwargs={'pk': pk}))
except Exception as e:
logger.error("user= {}.Um erro inesperado ocorreu na criação do pdf de documentos acessorios: {}"
.format(username,str(e)))
msg=_('Um erro inesperado ocorreu. Entre em contato com o suporte do SAPL.')
messages.add_message(request, messages.ERROR, msg)
return redirect(reverse('sapl.materia:documentoacessorio_list',
kwargs={'pk': pk}))
if not pdffilename:
msg=_('Não há nenhum documento acessório PDF cadastrado.')
messages.add_message(request, messages.ERROR, msg)
return redirect(reverse('sapl.materia:documentoacessorio_list',
kwargs={'pk': pk}))
with open(os.path.join(get_tempfile_dir(), pdffilename), 'rb') as f:
data = f.read()
response = HttpResponse(data, content_type='application/pdf')
response['Content-Disposition'] = ('attachment; filename="%s"'
% external_name)
return response

13
sapl/templates/materia/documentoacessorio_list.html

@ -0,0 +1,13 @@
{% extends "crud/list.html" %}
{% load i18n %}
{% block base_content %}
{{ block.super }}
<div style="display:flex;padding-left: 600px;padding-top: 10px;">
<div class="actions btn-group float-right" role="group">
<a href="{% url 'sapl.materia:merge_docacessorios' root_pk %}" class="btn btn-outline-primary">{% trans 'Baixar documentos como PDF único' %}</a>
</div>
<div class="actions btn-group float-right" role="group">
<a href="{% url 'sapl.materia:compress_docacessorios' root_pk %}" class="btn btn-outline-primary">{% trans 'Baixar documentos compactados' %}</a>
</div>
</div>
{% endblock %}

8
sapl/utils.py

@ -7,7 +7,8 @@ import magic
import os import os
import re import re
import unicodedata import unicodedata
import platform
import tempfile
from crispy_forms.layout import Button, HTML from crispy_forms.layout import Button, HTML
from easy_thumbnails import source_generators from easy_thumbnails import source_generators
from floppyforms import ClearableFileInput from floppyforms import ClearableFileInput
@ -1092,3 +1093,8 @@ def verifica_afastamento_parlamentar(parlamentar, data_inicio, data_fim=None):
data_fim__gte=data_inicio).exists() data_fim__gte=data_inicio).exists()
return existe_afastamento return existe_afastamento
def get_tempfile_dir():
return '/tmp' if platform.system() == 'Darwin' else tempfile.gettempdir()

Loading…
Cancel
Save