From 4c485a5bb729e32e12c8fad0c2dfb1ab3ece4375 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 4 Oct 2024 15:20:01 -0300 Subject: [PATCH] =?UTF-8?q?Implementa=20relat=C3=B3rios=20CSV,=20XLSX=20e?= =?UTF-8?q?=20JSON=20para=20pauta=20de=20sess=C3=A3o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sapl/sessao/views.py | 21 +- sapl/templates/crud/format_options.html | 6 + .../templates/sessao/pauta_sessao_detail.html | 8 + sapl/utils.py | 213 ++++++++++++++++-- 4 files changed, 221 insertions(+), 27 deletions(-) diff --git a/sapl/sessao/views.py b/sapl/sessao/views.py index c4b092f52..1603631f6 100755 --- a/sapl/sessao/views.py +++ b/sapl/sessao/views.py @@ -46,7 +46,7 @@ from sapl.sessao.models import Correspondencia from sapl.settings import TIME_ZONE from sapl.utils import show_results_filter_set, remover_acentos, get_client_ip,\ - MultiFormatOutputMixin + MultiFormatOutputMixin, PautaMultiFormatOutputMixin from .forms import (AdicionarVariasMateriasFilterSet, BancadaForm, ExpedienteForm, JustificativaAusenciaForm, OcorrenciaSessaoForm, ListMateriaForm, @@ -3809,10 +3809,27 @@ def get(self, request, *args, **kwargs): reverse('sapl.sessao:pauta_sessao_detail', kwargs={'pk': sessao.pk})) -class PautaSessaoDetailView(DetailView): +class PautaSessaoDetailView(PautaMultiFormatOutputMixin, DetailView): template_name = "sessao/pauta_sessao_detail.html" model = SessaoPlenaria + queryset_values_for_formats = False + + fields_base_report = [ + [('id', 'ID'), ('titulo', 'Matéria'), ('autor', 'Autor'), ('ementa', 'Ementa'), ('situacao', 'Situação')], + [('id', 'ID'), ('titulo', 'Matéria'), ('autor', 'Autor'), ('ementa', 'Ementa'), ('situacao', 'Situação')] + ] + fields_report = { + 'csv': fields_base_report, + 'xlsx': fields_base_report, + 'json': fields_base_report, + } + + item_context = [ + ('materia_expediente', 'Matérias do Expediente'), + ('materias_ordem', 'Matérias da Ordem do Dia') + ] + def get(self, request, *args, **kwargs): from sapl.relatorios.views import relatorio_pauta_sessao_weasy # Evitar import ciclico diff --git a/sapl/templates/crud/format_options.html b/sapl/templates/crud/format_options.html index eb3bdd18d..1bd43c155 100644 --- a/sapl/templates/crud/format_options.html +++ b/sapl/templates/crud/format_options.html @@ -3,8 +3,14 @@
+ {% if sessao %} + + + + {% else %} + {% endif %}
diff --git a/sapl/templates/sessao/pauta_sessao_detail.html b/sapl/templates/sessao/pauta_sessao_detail.html index 92ad35a6b..a4eb7bec7 100644 --- a/sapl/templates/sessao/pauta_sessao_detail.html +++ b/sapl/templates/sessao/pauta_sessao_detail.html @@ -1,8 +1,16 @@ {% extends "crud/detail.html" %} {% load i18n %} {% load crispy_forms_tags common_tags%} +{% load waffle_tags %} {% block base_content %} +
+ {% with url_reverse='sapl.sessao:pauta_sessao_detail' sessao=object.pk %} + {% include "crud/format_options.html" %} + {% endwith %} +
+
+
Impressão PDF
Identificação Básica diff --git a/sapl/utils.py b/sapl/utils.py index 0733d8152..187289326 100644 --- a/sapl/utils.py +++ b/sapl/utils.py @@ -1311,9 +1311,10 @@ def render_to_response(self, context, **response_kwargs): raise ValidationError( 'Formato Inválido e/ou não implementado!') - object_list = context['object_list'] - object_list.query.low_mark = 0 - object_list.query.high_mark = 0 + if 'object_list' in context: + object_list = context['object_list'] + object_list.query.low_mark = 0 + object_list.query.high_mark = 0 return getattr(self, f'render_to_{format_result}')(context) @@ -1329,7 +1330,7 @@ def render_to_json(self, context): data = [] for obj in object_list: - wr = list(self._write_row(obj, 'json')) + wr = list(self._write_row(obj, self.fields_report['json'])) if not data: data.append([wr]) @@ -1355,7 +1356,7 @@ def render_to_json(self, context): json_metadata = { 'headers': dict( - map(lambda i, j: (i, j), self.fields_report['json'], self._headers('json'))), + map(lambda i, j: (i, j), self.fields_report['json'], self._headers(self.fields_report['json']))), 'results': data } response = JsonResponse(json_metadata) @@ -1381,9 +1382,9 @@ def render_to_csv(self, context): object_list = object_list.values( *self.fields_report['csv']) - data = [[list(self._headers('csv'))], ] + data = [[list(self._headers(self.fields_report['csv']))], ] for obj in object_list: - wr = list(self._write_row(obj, 'csv')) + wr = list(self._write_row(obj, self.fields_report['csv'])) if wr[0] != data[-1][0][0]: data.append([wr]) else: @@ -1411,9 +1412,9 @@ def render_to_xlsx(self, context): object_list = object_list.values( *self.fields_report['xlsx']) - data = [[list(self._headers('xlsx'))], ] + data = [[list(self._headers(self.fields_report['xlsx']))], ] for obj in object_list: - wr = list(self._write_row(obj, 'xlsx')) + wr = list(self._write_row(obj, self.fields_report['xlsx'])) if wr[0] != data[-1][0][0]: data.append([wr]) else: @@ -1453,9 +1454,12 @@ def render_to_xlsx(self, context): return response - def _write_row(self, obj, format_result): + def _write_row(self, obj, fields_report): - for fname in self.fields_report[format_result]: + for fname in fields_report: + + if type(fname) is tuple: + fname = fname[0] if hasattr(self, f'hook_{fname}'): v = getattr(self, f'hook_{fname}')(obj) @@ -1477,9 +1481,9 @@ def _write_row(self, obj, format_result): yield v - def _headers(self, format_result): + def _headers(self, fields_report): - for fname in self.fields_report[format_result]: + for fname in fields_report: verbose_name = [] @@ -1488,22 +1492,181 @@ def _headers(self, format_result): yield h continue - fname = fname.split('__') + if type(fname) is tuple: + verbose_name.append(fname[1]) + else: - m = self.model - for fp in fname: + fname = fname.split('__') - f = m._meta.get_field(fp) + m = self.model + for fp in fname: - vn = str(f.verbose_name) if hasattr(f, 'verbose_name') else fp - if f.is_relation: - m = f.related_model - if m == self.model: - m = f.field.model + f = m._meta.get_field(fp) - if vn == fp: - vn = str(m._meta.verbose_name_plural) - verbose_name.append(vn.strip()) + vn = str(f.verbose_name) if hasattr(f, 'verbose_name') else fp + if f.is_relation: + m = f.related_model + if m == self.model: + m = f.field.model + + if vn == fp: + vn = str(m._meta.verbose_name_plural) + verbose_name.append(vn.strip()) verbose_name = '/'.join(verbose_name).strip() yield f'{verbose_name}' + + +class PautaMultiFormatOutputMixin(MultiFormatOutputMixin): + + def render_to_csv(self, context): + response = HttpResponse(content_type='text/csv') + response['Content-Disposition'] = f'attachment; filename="sapl_{self.request.resolver_match.url_name}.csv"' + response['Cache-Control'] = 'no-cache' + response['Pragma'] = 'no-cache' + response['Expires'] = 0 + writer = csv.writer(response, delimiter=";", + quoting=csv.QUOTE_NONNUMERIC) + + writer.writerow(['Pauta da ' + str(context['sessaoplenaria'])]) + writer.writerow('') + + for item in self.item_context: + if item[0] in context: + + index = self.item_context.index(item) + writer.writerow([self.item_context[index][1]]) + + data = [[list(self._headers(self.fields_report['csv'][index]))], ] + for obj in context.get(item[0]): + wr = list(self._write_row(obj, self.fields_report['csv'][index])) + if wr[0] != data[-1][0][0]: + data.append([wr]) + else: + data[-1].append(wr) + + for mri, multirows in enumerate(data): + if len(multirows) == 1: + writer.writerow(multirows[0]) + else: + v = multirows[0] + for ri, cols in enumerate(multirows[1:]): + for rc, cell in enumerate(cols): + if v[rc] != cell: + v[rc] = f'{v[rc]}\r\n{cell}' + + writer.writerow(v) + writer.writerow('') + + return response + + def render_to_json(self, context): + + json_metadata = {'sessaoplenaria': str(context['sessaoplenaria'])} + for item in self.item_context: + if item[0] in context: + index = self.item_context.index(item) + json_metadata.update({item[0]: {}}) + data = [] + + for obj in context.get(item[0]): + wr = list(self._write_row(obj, self.fields_report['json'][index])) + + if not data: + data.append([wr]) + continue + + if wr[0] != data[-1][0][0]: + data.append([wr]) + else: + data[-1].append(wr) + + for mri, multirows in enumerate(data): + if len(multirows) == 1: + try: + v = multirows[0] + except TypeError: + v = str(multirows[0]) + else: + try: + v = str(multirows[0]) + except TypeError: + v = multirows[0] + for ri, cols in enumerate(multirows[1:]): + for rc, cell in enumerate(cols): + if v[rc] != cell: + v[rc] = f'{v[rc]}\r\n{cell}' + + data[mri] = dict( + map(lambda i, j: (i[0], j if type(j) in [str, int, list] else str(j)), self.fields_report['json'][index], v)) + + json_metadata.update({item[0]:{ + 'headers': dict( + map(lambda i, j: (i[0], j), self.fields_report['json'][index], self._headers(self.fields_report['json'][index]))), + 'results': data} + }) + response = JsonResponse(json_metadata) + response['Content-Disposition'] = f'attachment; filename="sapl_{self.request.resolver_match.url_name}.json"' + response['Cache-Control'] = 'no-cache' + response['Pragma'] = 'no-cache' + response['Expires'] = 0 + + return response + + def render_to_xlsx(self, context): + + output = io.BytesIO() + wb = Workbook(output, {'in_memory': True}) + + ws = wb.add_worksheet() + ws.write('A1', 'Pauta da ' + str(context['sessaoplenaria'])) + row = 2 + + for item in self.item_context: + if item[0] in context: + index = self.item_context.index(item) + ws.write(row, 0, self.item_context[index][1]) + row += 1 + data = [[list(self._headers(self.fields_report['xlsx'][index]))], ] + + for obj in context.get(item[0]): + wr = list(self._write_row(obj, self.fields_report['xlsx'][index])) + if wr[0] != data[-1][0][0]: + data.append([wr]) + else: + data[-1].append(wr) + + for mri, multirows in enumerate(data): + if len(multirows) == 1: + for rc, cell in enumerate(multirows[0]): + try: + ws.write(row, rc, cell) + except TypeError: + ws.write(row, rc, str(cell)) + row += 1 + else: + v = multirows[0] + for ri, cols in enumerate(multirows[1:]): + for rc, cell in enumerate(cols): + if v[rc] != cell: + v[rc] = f'{v[rc]}\r\n{cell}' + + for rc, cell in enumerate(v): + ws.write(row, rc, cell) + row += 1 + row += 1 + ws.autofit() + wb.close() + + output.seek(0) + + response = HttpResponse(output.read( + ), content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") + response['Content-Disposition'] = f'attachment; filename="sapl_{self.request.resolver_match.url_name}.xlsx"' + response['Cache-Control'] = 'no-cache' + response['Pragma'] = 'no-cache' + response['Expires'] = 0 + + output.close() + + return response