Browse Source

Implementa relatórios CSV, XLSX e JSON para pauta de sessão

pull/3744/head
root 3 months ago
parent
commit
4c485a5bb7
  1. 21
      sapl/sessao/views.py
  2. 6
      sapl/templates/crud/format_options.html
  3. 8
      sapl/templates/sessao/pauta_sessao_detail.html
  4. 213
      sapl/utils.py

21
sapl/sessao/views.py

@ -46,7 +46,7 @@ from sapl.sessao.forms import ExpedienteMateriaForm, OrdemDiaForm, OrdemExpedien
from sapl.sessao.models import Correspondencia from sapl.sessao.models import Correspondencia
from sapl.settings import TIME_ZONE from sapl.settings import TIME_ZONE
from sapl.utils import show_results_filter_set, remover_acentos, get_client_ip,\ from sapl.utils import show_results_filter_set, remover_acentos, get_client_ip,\
MultiFormatOutputMixin MultiFormatOutputMixin, PautaMultiFormatOutputMixin
from .forms import (AdicionarVariasMateriasFilterSet, BancadaForm, from .forms import (AdicionarVariasMateriasFilterSet, BancadaForm,
ExpedienteForm, JustificativaAusenciaForm, OcorrenciaSessaoForm, ListMateriaForm, ExpedienteForm, JustificativaAusenciaForm, OcorrenciaSessaoForm, ListMateriaForm,
@ -3809,10 +3809,27 @@ class PautaSessaoView(TemplateView):
reverse('sapl.sessao:pauta_sessao_detail', kwargs={'pk': sessao.pk})) reverse('sapl.sessao:pauta_sessao_detail', kwargs={'pk': sessao.pk}))
class PautaSessaoDetailView(DetailView): class PautaSessaoDetailView(PautaMultiFormatOutputMixin, DetailView):
template_name = "sessao/pauta_sessao_detail.html" template_name = "sessao/pauta_sessao_detail.html"
model = SessaoPlenaria 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): def get(self, request, *args, **kwargs):
from sapl.relatorios.views import relatorio_pauta_sessao_weasy # Evitar import ciclico from sapl.relatorios.views import relatorio_pauta_sessao_weasy # Evitar import ciclico

6
sapl/templates/crud/format_options.html

@ -3,8 +3,14 @@
<div class=" d-flex justify-content-center"> <div class=" d-flex justify-content-center">
<div class="actions btn-group" role="group"> <div class="actions btn-group" role="group">
{% if sessao %}
<a class="btn btn-outline-secondary" href="{% url url_reverse sessao %}?format=csv{{ filter_url }}" title="Download do resultado da pesquisa em CSV."><i class="fas fa-file-csv"></i></a>
<a class="btn btn-outline-secondary" href="{% url url_reverse sessao %}?format=xlsx{{ filter_url }}" title="Download do resultado da pesquisa em XLSX."><i class="fas fa-file-excel"></i></a>
<a class="btn btn-outline-secondary" href="{% url url_reverse sessao %}?format=json{{ filter_url }}" title="Download do resultado da pesquisa em JSON."><i class="far fa-file-code"></i></a>
{% else %}
<a class="btn btn-outline-secondary" href="{% url url_reverse %}?format=csv{{ filter_url }}" title="Download do resultado da pesquisa em CSV."><i class="fas fa-file-csv"></i></a> <a class="btn btn-outline-secondary" href="{% url url_reverse %}?format=csv{{ filter_url }}" title="Download do resultado da pesquisa em CSV."><i class="fas fa-file-csv"></i></a>
<a class="btn btn-outline-secondary" href="{% url url_reverse %}?format=xlsx{{ filter_url }}" title="Download do resultado da pesquisa em XLSX."><i class="fas fa-file-excel"></i></a> <a class="btn btn-outline-secondary" href="{% url url_reverse %}?format=xlsx{{ filter_url }}" title="Download do resultado da pesquisa em XLSX."><i class="fas fa-file-excel"></i></a>
<a class="btn btn-outline-secondary" href="{% url url_reverse %}?format=json{{ filter_url }}" title="Download do resultado da pesquisa em JSON."><i class="far fa-file-code"></i></a> <a class="btn btn-outline-secondary" href="{% url url_reverse %}?format=json{{ filter_url }}" title="Download do resultado da pesquisa em JSON."><i class="far fa-file-code"></i></a>
{% endif %}
</div> </div>
</div> </div>

8
sapl/templates/sessao/pauta_sessao_detail.html

@ -1,8 +1,16 @@
{% extends "crud/detail.html" %} {% extends "crud/detail.html" %}
{% load i18n %} {% load i18n %}
{% load crispy_forms_tags common_tags%} {% load crispy_forms_tags common_tags%}
{% load waffle_tags %}
{% block base_content %} {% block base_content %}
<div class="float-left">
{% with url_reverse='sapl.sessao:pauta_sessao_detail' sessao=object.pk %}
{% include "crud/format_options.html" %}
{% endwith %}
</div>
<br />
<div align=right><a href="{% url 'sapl.sessao:pauta_sessao_detail' object.pk %}pdf"> Impressão PDF</a></li></div> <div align=right><a href="{% url 'sapl.sessao:pauta_sessao_detail' object.pk %}pdf"> Impressão PDF</a></li></div>
<fieldset> <fieldset>
<legend>Identificação Básica</legend> <legend>Identificação Básica</legend>

213
sapl/utils.py

@ -1311,9 +1311,10 @@ class MultiFormatOutputMixin:
raise ValidationError( raise ValidationError(
'Formato Inválido e/ou não implementado!') 'Formato Inválido e/ou não implementado!')
object_list = context['object_list'] if 'object_list' in context:
object_list.query.low_mark = 0 object_list = context['object_list']
object_list.query.high_mark = 0 object_list.query.low_mark = 0
object_list.query.high_mark = 0
return getattr(self, f'render_to_{format_result}')(context) return getattr(self, f'render_to_{format_result}')(context)
@ -1329,7 +1330,7 @@ class MultiFormatOutputMixin:
data = [] data = []
for obj in object_list: 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: if not data:
data.append([wr]) data.append([wr])
@ -1355,7 +1356,7 @@ class MultiFormatOutputMixin:
json_metadata = { json_metadata = {
'headers': dict( '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 'results': data
} }
response = JsonResponse(json_metadata) response = JsonResponse(json_metadata)
@ -1381,9 +1382,9 @@ class MultiFormatOutputMixin:
object_list = object_list.values( object_list = object_list.values(
*self.fields_report['csv']) *self.fields_report['csv'])
data = [[list(self._headers('csv'))], ] data = [[list(self._headers(self.fields_report['csv']))], ]
for obj in object_list: 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]: if wr[0] != data[-1][0][0]:
data.append([wr]) data.append([wr])
else: else:
@ -1411,9 +1412,9 @@ class MultiFormatOutputMixin:
object_list = object_list.values( object_list = object_list.values(
*self.fields_report['xlsx']) *self.fields_report['xlsx'])
data = [[list(self._headers('xlsx'))], ] data = [[list(self._headers(self.fields_report['xlsx']))], ]
for obj in object_list: 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]: if wr[0] != data[-1][0][0]:
data.append([wr]) data.append([wr])
else: else:
@ -1453,9 +1454,12 @@ class MultiFormatOutputMixin:
return response 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}'): if hasattr(self, f'hook_{fname}'):
v = getattr(self, f'hook_{fname}')(obj) v = getattr(self, f'hook_{fname}')(obj)
@ -1477,9 +1481,9 @@ class MultiFormatOutputMixin:
yield v 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 = [] verbose_name = []
@ -1488,22 +1492,181 @@ class MultiFormatOutputMixin:
yield h yield h
continue continue
fname = fname.split('__') if type(fname) is tuple:
verbose_name.append(fname[1])
else:
m = self.model fname = fname.split('__')
for fp in fname:
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 f = m._meta.get_field(fp)
if f.is_relation:
m = f.related_model
if m == self.model:
m = f.field.model
if vn == fp: vn = str(f.verbose_name) if hasattr(f, 'verbose_name') else fp
vn = str(m._meta.verbose_name_plural) if f.is_relation:
verbose_name.append(vn.strip()) 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() verbose_name = '/'.join(verbose_name).strip()
yield f'{verbose_name}' 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

Loading…
Cancel
Save