Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementa relatórios CSV, XLSX e JSON para Pauta de Sessão #3744

Open
wants to merge 2 commits into
base: 3.1.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions sapl/sessao/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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

Expand Down
6 changes: 6 additions & 0 deletions sapl/templates/crud/format_options.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,14 @@

<div class=" d-flex justify-content-center">
<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=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>
{% endif %}
</div>
</div>
8 changes: 8 additions & 0 deletions sapl/templates/sessao/pauta_sessao_detail.html
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
{% extends "crud/detail.html" %}
{% load i18n %}
{% load crispy_forms_tags common_tags%}
{% load waffle_tags %}

{% 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>
<fieldset>
<legend>Identificação Básica</legend>
Expand Down
213 changes: 188 additions & 25 deletions sapl/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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])
Expand All @@ -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)
Expand All @@ -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:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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)
Expand All @@ -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 = []

Expand All @@ -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