diff --git a/requirements/requirements.txt b/requirements/requirements.txt index f48f8e0e1..ae3972fcd 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -29,7 +29,7 @@ Pillow==6.2.2 gunicorn==19.9.0 more-itertools==8.2.0 pysolr==3.6.0 - +PyPDF4==1.27.0 pyoai==2.5.0 git+https://github.com/interlegis/trml2pdf diff --git a/sapl/materia/urls.py b/sapl/materia/urls.py index 2fe9145de..05733599c 100644 --- a/sapl/materia/urls.py +++ b/sapl/materia/urls.py @@ -27,7 +27,7 @@ from sapl.materia.views import (AcompanhamentoConfirmarView, proposicao_texto, recuperar_materia, ExcluirTramitacaoEmLoteView, RetornarProposicao, MateriaPesquisaSimplesView, - DespachoInicialMultiCreateView) + DespachoInicialMultiCreateView, get_zip_docacessorios, get_pdf_docacessorios) from sapl.norma.views import NormaPesquisaSimplesView from sapl.protocoloadm.views import ( FichaPesquisaAdmView, FichaSelecionaAdmView) @@ -118,6 +118,10 @@ urlpatterns_materia = [ name='tramitacao_em_lote'), url(r'^materia/excluir-tramitacao-em-lote', ExcluirTramitacaoEmLoteView.as_view(), name='excluir_tramitacao_em_lote'), + url(r'^materia/docacessorio/zip/(?P\d+)$', get_zip_docacessorios, + name='compress_docacessorios'), + url(r'^materia/docacessorio/pdf/(?P\d+)$', get_pdf_docacessorios, + name='merge_docacessorios') ] diff --git a/sapl/materia/views.py b/sapl/materia/views.py index 7cb3372d0..db28dd2c3 100644 --- a/sapl/materia/views.py +++ b/sapl/materia/views.py @@ -6,11 +6,15 @@ import sapl import shutil import tempfile import weasyprint +import time from crispy_forms.layout import HTML from datetime import datetime from random import choice from string import ascii_letters, digits +from datetime import datetime +from PyPDF4 import PdfFileReader, PdfFileMerger +import zipfile from django.conf import settings 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, get_client_ip, get_mime_type_from_file_extension, lista_anexados, 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, AnexadaEmLoteFilterSet, AdicionarVariasAutoriasFilterSet, @@ -2725,3 +2729,127 @@ class TipoMateriaCrud(CrudAux): self.object.save() 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 + diff --git a/sapl/templates/materia/documentoacessorio_list.html b/sapl/templates/materia/documentoacessorio_list.html new file mode 100644 index 000000000..08bae0819 --- /dev/null +++ b/sapl/templates/materia/documentoacessorio_list.html @@ -0,0 +1,13 @@ +{% extends "crud/list.html" %} +{% load i18n %} +{% block base_content %} + {{ block.super }} +
+ + +
+{% endblock %} \ No newline at end of file diff --git a/sapl/utils.py b/sapl/utils.py index ded879806..da74e086b 100644 --- a/sapl/utils.py +++ b/sapl/utils.py @@ -7,7 +7,8 @@ import magic import os import re import unicodedata - +import platform +import tempfile from crispy_forms.layout import Button, HTML from easy_thumbnails import source_generators from floppyforms import ClearableFileInput @@ -1061,3 +1062,6 @@ class OverwriteStorage(FileSystemStorage): if self.exists(name): os.remove(os.path.join(settings.MEDIA_ROOT, name)) return name + +def get_tempfile_dir(): + return '/tmp' if platform.system() == 'Darwin' else tempfile.gettempdir()