Browse Source

Refatora relatórios de erros para class based views

dependabot/pip/requirements/djangorestframework-3.15.2 3.0.85
Sesóstris Vieira 7 months ago
parent
commit
865cad5a6a
  1. 12
      sigi/apps/casas/admin_urls.py
  2. 44
      sigi/apps/casas/templates/casas/cnpj_duplicado.html
  3. 19
      sigi/apps/casas/templates/casas/cnpj_duplicado_pdf.html
  4. 57
      sigi/apps/casas/templates/casas/cnpj_errado.html
  5. 19
      sigi/apps/casas/templates/casas/cnpj_errado_pdf.html
  6. 8
      sigi/apps/casas/templates/casas/report/cnpj_errado_report/report.html
  7. 37
      sigi/apps/casas/templates/casas/snippets/cnpj_duplicado_snippet.html
  8. 38
      sigi/apps/casas/templates/casas/snippets/cnpj_errado_snippet.html
  9. 190
      sigi/apps/casas/views.py
  10. 41
      sigi/apps/convenios/templates/convenios/erros_gescon.html
  11. 21
      sigi/apps/convenios/templates/convenios/erros_gescon_pdf.html
  12. 11
      sigi/apps/convenios/templates/convenios/report/erros_gescon_report_view/report_pdf.html
  13. 39
      sigi/apps/convenios/templates/convenios/snippets/erros_gescon_snippet.html
  14. 2
      sigi/apps/convenios/urls.py
  15. 118
      sigi/apps/convenios/views.py
  16. 73
      sigi/apps/utils/templates/utils/report/report.html
  17. 48
      sigi/apps/utils/templates/utils/report/report_items_snippet.html
  18. 20
      sigi/apps/utils/templates/utils/report/report_pdf.html
  19. 12
      sigi/apps/utils/templatetags/sigi_tags.py
  20. 151
      sigi/apps/utils/views.py

12
sigi/apps/casas/admin_urls.py

@ -4,6 +4,14 @@ from sigi.apps.casas import views
urlpatterns = [ urlpatterns = [
path("carteira/", views.painel_relacionamento, name="casas_carteira"), path("carteira/", views.painel_relacionamento, name="casas_carteira"),
path("gerentes/", views.GerentesListView.as_view(), name="casas_gerentes"), path("gerentes/", views.GerentesListView.as_view(), name="casas_gerentes"),
path("cnpjduplicado/", views.cnpj_duplicado, name="casas_cnpj_duplicado"), path(
path("cnpjerrado/", views.cnpj_errado, name="casas_cnpj_errado"), "cnpjduplicado/",
views.CnpjDuplicadoReport.as_view(),
name="casas_cnpj_duplicado",
),
path(
"cnpjerrado/",
views.CnpjErradoReport.as_view(),
name="casas_cnpj_errado",
),
] ]

44
sigi/apps/casas/templates/casas/cnpj_duplicado.html

@ -1,44 +0,0 @@
{% extends "admin/base_site.html" %}
{% load static i18n %}
{% block extrastyle %}
{{ block.super }}
<style type="text/css">
table {
width: auto;
}
.bordered {
border-top: 1px solid var(--body-fg);
}
</style>
<link rel="stylesheet" type="text/css" href="/static/css/calendario.css">
{% endblock %}
{% block coltype %}colMS{% endblock %}
{% block content_title %}
<h5>{% blocktranslate with count=orgaos.count %}{{ count }} órgãos com CNPJ duplicado{% endblocktranslate %}</h5>
{% endblock %}
{% block content %}
<div class="fixed-action-btn">
<a class="btn-floating">
<i class="large material-icons">print</i>
</a>
<ul>
<li><a class="btn-floating" href="?fmt=pdf" title="{% trans 'Exportar para PDF' %}" ><i class="material-icons">picture_as_pdf</i></a></li>
<li><a class="btn-floating" href="?fmt=csv" title="{% trans 'Exportar para CSV' %}" ><i class="material-icons">file_download</i></a></li>
</ul>
</div>
{% include "casas/snippets/cnpj_duplicado_snippet.html" with mode="html" %}
{% endblock %}
{% block footer %}
{{ block.super }}
<script>
$(document).ready(function(){
M.FloatingActionButton.init($('.fixed-action-btn'), {hoverEnabled: false});
M.Modal.init($(".modal"));
})
</script>
{% endblock %}

19
sigi/apps/casas/templates/casas/cnpj_duplicado_pdf.html

@ -1,19 +0,0 @@
{% extends "pdf/base_report.html" %}
{% load static i18n %}
{% block page_size %}A4 landscape{% endblock %}
{% block extra_style %}
{{ block.super }}
.bordered td {
border-top: 1px solid #000;
}
{% endblock %}
{% block report_name %}
{% blocktranslate with count=orgaos.count %}{{ count }} órgãos com CNPJ duplicado{% endblocktranslate %}
{% endblock report_name %}
{% block main_content %}
{% include "casas/snippets/cnpj_duplicado_snippet.html" %}
{% endblock %}

57
sigi/apps/casas/templates/casas/cnpj_errado.html

@ -1,57 +0,0 @@
{% extends "admin/base_site.html" %}
{% load static i18n %}
{% block extrastyle %}
{{ block.super }}
<style type="text/css">
table {
width: auto;
}
.bordered {
border-top: 1px solid var(--body-fg);
}
</style>
<link rel="stylesheet" type="text/css" href="/static/css/calendario.css">
{% endblock %}
{% block coltype %}colMS{% endblock %}
{% block content_title %}
<h5>{% blocktranslate with count=orgaos|length %}{{ count }} órgãos com CNPJ digitado errado{% endblocktranslate %}</h5>
{% endblock %}
{% block content %}
<form>
<div class="card">
<div class="card-content">
<label for="{{ form.has_convenio.id_for_label }}">
{{ form.has_convenio }}
<span>{{ form.has_convenio.label }}</span>
</label>
</div>
<div class="card-action">
<button class="waves-effect waves-light btn" type="submit">{% translate "Submit" %}</button>
</div>
</div>
<div class="fixed-action-btn">
<a class="btn-floating">
<i class="large material-icons">print</i>
</a>
<ul>
<li><button class="btn-floating" type="submit" name="fmt" value="pdf" title="{% trans 'Exportar para PDF' %}" ><i class="material-icons">picture_as_pdf</i></button></li>
<li><button class="btn-floating" type="submit" name="fmt" value="csv" title="{% trans 'Exportar para CSV' %}" ><i class="material-icons">file_download</i></button></li>
</ul>
</div>
</form>
{% include "casas/snippets/cnpj_errado_snippet.html" with mode="html" %}
{% endblock %}
{% block footer %}
{{ block.super }}
<script>
$(document).ready(function(){
M.FloatingActionButton.init($('.fixed-action-btn'), {hoverEnabled: false});
M.Modal.init($(".modal"));
})
</script>
{% endblock %}

19
sigi/apps/casas/templates/casas/cnpj_errado_pdf.html

@ -1,19 +0,0 @@
{% extends "pdf/base_report.html" %}
{% load static i18n %}
{% block page_size %}A4 landscape{% endblock %}
{% block extra_style %}
{{ block.super }}
.bordered td {
border-top: 1px solid #000;
}
{% endblock %}
{% block report_name %}
{% blocktranslate with count=orgaos|length %}{{ count }} órgãos com CNPJ digitado errado{% endblocktranslate %}
{% endblock report_name %}
{% block main_content %}
{% include "casas/snippets/cnpj_errado_snippet.html" %}
{% endblock %}

8
sigi/apps/casas/templates/casas/report/cnpj_errado_report/report.html

@ -0,0 +1,8 @@
{% extends 'utils/report/report.html' %}
{% block filterform %}
<label for="{{ form.has_convenio.id_for_label }}">
{{ form.has_convenio }}
<span>{{ form.has_convenio.label }}</span>
</label>
{% endblock filterform %}

37
sigi/apps/casas/templates/casas/snippets/cnpj_duplicado_snippet.html

@ -1,37 +0,0 @@
{% load i18n %}
<div class="card">
<div class="card-content">
<table class="striped">
<thead>
<tr>
<th>{% translate "ID" %}</th>
<th>{% translate "CNPJ" %}</th>
<th>{% translate "Tipo de órgão" %}</th>
<th>{% translate "Sigla" %}</th>
<th>{% translate "Nome" %}</th>
<th>{% translate "Cidade" %}</th>
<th>{% translate "UF" %}</th>
</tr>
</thead>
<tbody>
{% for orgao in orgaos %}
<tr{% ifchanged orgao.cnpj %} class="bordered"{% endifchanged %}>
<td><a href="{% url 'admin:casas_orgao_change' orgao.id %}">{{ orgao.id|stringformat:"s" }}</a></td>
<td>{{ orgao.cnpj }}</td>
<td>{{ orgao.tipo.nome }}</td>
<td>{{ orgao.sigla }}</td>
<td>{{ orgao.nome }}</td>
<td>{{ orgao.municipio.nome }}</td>
<td>{{ orgao.municipio.uf.sigla }}</td>
</tr>
{% empty %}
<tr>
<td colspam="7">
{% translate "Nenhum órgão com CNPJ duplicado" %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>

38
sigi/apps/casas/templates/casas/snippets/cnpj_errado_snippet.html

@ -1,38 +0,0 @@
{% load i18n %}
<div class="card">
<div class="card-content">
<table class="striped">
<thead>
<tr>
<th>{% translate "ID" %}</th>
<th>{% translate "CNPJ" %}</th>
<th>{% translate "Sigla" %}</th>
<th>{% translate "Nome" %}</th>
<th>{% translate "Cidade" %}</th>
<th>{% translate "UF" %}</th>
</tr>
</thead>
<tbody>
{% for orgao in orgaos %}
{% ifchanged orgao.tipo_nome %}
<tr><th colspan="6">{{ orgao.tipo_nome }}</th></tr>
{% endifchanged %}
<tr>
<td><a href="{% url 'admin:casas_orgao_change' orgao.id %}">{{ orgao.id|stringformat:"s" }}</a></td>
<td>{{ orgao.cnpj }}</td>
<td>{{ orgao.sigla }}</td>
<td>{{ orgao.nome }}</td>
<td>{{ orgao.municipio_nome }}</td>
<td>{{ orgao.uf_sigla }}</td>
</tr>
{% empty %}
<tr>
<td colspam="7">
{% translate "Nenhum órgão com CNPJ digitado errado" %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>

190
sigi/apps/casas/views.py

@ -6,12 +6,13 @@ from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import ( from django.contrib.auth.mixins import (
LoginRequiredMixin, LoginRequiredMixin,
PermissionRequiredMixin, PermissionRequiredMixin,
UserPassesTestMixin,
) )
from django.core.paginator import Paginator, InvalidPage, EmptyPage from django.core.paginator import Paginator, InvalidPage, EmptyPage
from django.http import HttpResponse, HttpResponseRedirect from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render, get_object_or_404 from django.shortcuts import render, get_object_or_404
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _, ngettext
from django.views.generic import ( from django.views.generic import (
CreateView, CreateView,
DeleteView, DeleteView,
@ -35,6 +36,7 @@ from sigi.apps.servicos.models import Servico, TipoServico
from sigi.apps.eventos.models import Evento, TipoEvento from sigi.apps.eventos.models import Evento, TipoEvento
from sigi.apps.convenios.models import Convenio from sigi.apps.convenios.models import Convenio
from sigi.apps.utils import valida_cnpj from sigi.apps.utils import valida_cnpj
from sigi.apps.utils.views import ReportListView
def resumo_carteira(casas): def resumo_carteira(casas):
@ -338,67 +340,61 @@ def painel_relacionamento(request):
return render(request, "casas/painel.html", context) return render(request, "casas/painel.html", context)
@login_required class CnpjDuplicadoReport(
@staff_member_required LoginRequiredMixin, UserPassesTestMixin, ReportListView
def cnpj_duplicado(request): ):
formato = request.GET.get("fmt", "html") title = _("Órgãos com CNPJ duplicado")
dups = ( empty_message = _("Nenhum órgão com CNPJ duplicado!")
Orgao.objects.exclude(cnpj="") queryset = Orgao.objects.filter(
.order_by("cnpj") cnpj__in=(
.values("cnpj") Orgao.objects.exclude(cnpj="")
.annotate(tot=Count("cnpj")) .order_by("cnpj")
.filter(tot__gt=1) .values("cnpj")
.values("cnpj") .annotate(tot=Count("cnpj"))
) .filter(tot__gt=1)
orgaos = ( .values("cnpj")
Orgao.objects.filter(cnpj__in=dups)
.order_by("cnpj", "nome", "municipio__nome", "municipio__uf")
.prefetch_related("tipo", "municipio", "municipio__uf")
)
context = {
"orgaos": orgaos,
"title": _("Órgãos com CNPJ duplicado"),
}
if formato == "pdf":
return WeasyTemplateResponse(
filename="cnpj_duplicado.pdf",
request=request,
template="casas/cnpj_duplicado_pdf.html",
context=context,
content_type="application/pdf",
)
elif formato == "csv":
response = HttpResponse(content_type="text/csv")
response["Content-Disposition"] = (
'attachment; filename="cnpj_duplicado.csv"'
) )
fieldnames = [ ).prefetch_related("tipo", "municipio", "municipio__uf")
"id", ordering = ["cnpj", "nome", "municipio__nome", "municipio__uf"]
"cnpj", list_fields = [
"tipo__nome", "id",
"sigla", "cnpj",
"nome", "tipo__nome",
"municipio__nome", "sigla",
"municipio__uf__sigla", "nome",
] "municipio__nome",
writer = csv.DictWriter(response, fieldnames) "municipio__uf__sigla",
writer.writeheader() ]
writer.writerows(orgaos.values(*fieldnames)) list_labels = [
return response "ID",
return render(request, "casas/cnpj_duplicado.html", context=context) "CNPJ",
"Tipo de órgão",
"Sigla",
"Nome",
"Cidade",
"UF",
]
link_fields = ["id"]
change_field = "cnpj"
def test_func(self):
return self.request.user.is_staff
def get_title(self):
count = self.get_queryset().count()
return ngettext(
"Um órgão com CNPJ duplicado",
f"{count} órgãos com CNPJ duplicado",
count,
)
@login_required
@staff_member_required
def cnpj_errado(request):
formato = request.GET.get("fmt", "html")
form = CnpjErradoForm(request.GET)
if form.is_valid():
has_convenio = form.cleaned_data.get("has_convenio", False)
else:
has_convenio = False
todos_orgaos = ( class CnpjErradoReport(
LoginRequiredMixin, UserPassesTestMixin, ReportListView
):
title = _("Órgãos com CNPJ digitado errado")
empty_message = _("Nenhum órgão com CNPJ digitado errado")
queryset = (
Orgao.objects.exclude(cnpj="") Orgao.objects.exclude(cnpj="")
.order_by("tipo", "cnpj", "nome") .order_by("tipo", "cnpj", "nome")
.annotate( .annotate(
@ -407,46 +403,48 @@ def cnpj_errado(request):
uf_sigla=F("municipio__uf__sigla"), uf_sigla=F("municipio__uf__sigla"),
) )
) )
if has_convenio: filter_form = CnpjErradoForm
todos_orgaos = todos_orgaos.exclude(convenio=None) list_fields = ["id", "cnpj", "sigla", "nome", "municipio_nome", "uf_sigla"]
orgaos = [] list_labels = ["ID", "CNPJ", "Sigla", "Nome", "Cidade", "UF"]
for orgao in todos_orgaos: link_fields = ["id"]
if not valida_cnpj(orgao.cnpj): break_field = "tipo_nome"
orgaos.append(orgao)
context = { def test_func(self):
"orgaos": orgaos, return self.request.user.is_staff
"form": form,
"title": _("Órgãos com CNPJ digitado errado"), def get_title(self):
} count = len(self.get_queryset())
if formato == "pdf": return ngettext(
return WeasyTemplateResponse( "Um órgão com CNPJ digitado errado",
filename="cnpj_errado.pdf", f"{count} órgãos com CNPJ digitado errado",
request=request, count,
template="casas/cnpj_errado_pdf.html",
context=context,
content_type="application/pdf",
)
elif formato == "csv":
response = HttpResponse(content_type="text/csv")
response["Content-Disposition"] = (
'attachment; filename="cnpj_errado.csv"'
) )
fieldnames = [
"id", def filter_queryset(self, queryset):
"cnpj", form = self.get_filter_form_instance()
"tipo_nome", if form.is_valid():
"sigla", has_convenio = form.cleaned_data["has_convenio"]
"nome", else:
"municipio_nome", has_convenio = False
"uf_sigla", if has_convenio:
] queryset = queryset.exclude(convenio=None)
writer = csv.DictWriter(response, fieldnames) orgaos = []
writer.writeheader() for orgao in queryset:
writer.writerows( if not valida_cnpj(orgao.cnpj):
[{f: getattr(o, f) for f in fieldnames} for o in orgaos] orgaos.append(orgao)
return orgaos
def get_dataset(self):
return (
[
{f: getattr(o, f) for f in self.list_fields}
for o in self.get_queryset()
],
self.list_fields,
) )
return response
return render(request, "casas/cnpj_errado.html", context=context) def _get_options(self):
return Orgao._meta
class GerentesListView(PermissionRequiredMixin, ListView): class GerentesListView(PermissionRequiredMixin, ListView):

41
sigi/apps/convenios/templates/convenios/erros_gescon.html

@ -1,41 +0,0 @@
{% extends "admin/base_site.html" %}
{% load static i18n %}
{% block extrastyle %}
{{ block.super }}
<style type="text/css">
table {
width: auto;
}
</style>
<link rel="stylesheet" type="text/css" href="/static/css/calendario.css">
{% endblock %}
{% block coltype %}colMS{% endblock %}
{% block content_title %}
<h5>
{% blocktranslate count counter=convenios.count %}
Um convênio com erro na importação do Gescon
{% plural %}
{{ counter }} convênios com erro na importação do Gescon
{% endblocktranslate %}
</h5>
{% endblock %}
{% block breadcrumbs %}
{% endblock %}
{% block content %}
{% include "convenios/snippets/erros_gescon_snippet.html" with mode="html" %}
{% endblock %}
{% block footer %}
{{ block.super }}
<script>
$(document).ready(function(){
M.FloatingActionButton.init($('.fixed-action-btn'), {hoverEnabled: false});
M.Modal.init($(".modal"));
})
</script>
{% endblock %}

21
sigi/apps/convenios/templates/convenios/erros_gescon_pdf.html

@ -1,21 +0,0 @@
{% extends "pdf/base_report.html" %}
{% load static i18n %}
{% block page_size %}A4 landscape{% endblock %}
{% block report_name %}
{% blocktranslate count counter=convenios.count %}
Um convênio com erro na importação do Gescon
{% plural %}
{{ counter }} convênios com erro na importação do Gescon
{% endblocktranslate %}
{% endblock report_name %}
{% block main_content %}
{% include "convenios/snippets/erros_gescon_snippet.html" %}
<div class="new-page"></div>
<div style="padding: 24px;">
<h1>{% translate "Resumo da última importação de dados do Gescon" %}</h1>
{{ ultima_importacao }}
</div>
{% endblock %}

11
sigi/apps/convenios/templates/convenios/report/erros_gescon_report_view/report_pdf.html

@ -0,0 +1,11 @@
{% extends "utils/report/report_pdf.html" %}
{% load i18n %}
{% block main_content %}
{{ block.super }}
<div class="new-page"></div>
<div style="padding: 24px;">
<h1>{% translate "Resumo da última importação de dados do Gescon" %}</h1>
{{ ultima_importacao }}
</div>
{% endblock %}

39
sigi/apps/convenios/templates/convenios/snippets/erros_gescon_snippet.html

@ -1,39 +0,0 @@
{% load i18n %}
<div class="card">
<div class="card-content">
<table class="striped">
<thead>
<tr>
<th>{% translate "id SIGI" %}</th>
<th>{% translate "NUP sigad" %}</th>
<th>{% translate "Número" %}</th>
<th>{% translate "Projeto" %}</th>
<th>{% translate "Órgão conveniado" %}</th>
<th>{% translate "UF" %}</th>
<th>{% translate "Erro encontrado" %}</th>
</tr>
</thead>
<tbody>
{% for convenio in convenios %}
<tr>
<td>
<a href="{% url 'admin:convenios_convenio_change' convenio.id %}">{{ convenio.id|stringformat:"s" }}</a>
</td>
<td>{{ convenio.num_processo_sf }}</td>
<td>{{ convenio.num_convenio }}</td>
<td>{{ convenio.projeto.nome }}</td>
<td>{{ convenio.casa_legislativa }}</td>
<td>{{ convenio.casa_legislativa.municipio.uf.sigla }}</td>
<td>{{ convenio.observacao_gescon|safe }}</td>
</tr>
{% empty %}
<tr>
<td colspam="7">
{% translate "Nenhum convênio com erro de importação do Gescon." %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>

2
sigi/apps/convenios/urls.py

@ -4,7 +4,7 @@ from sigi.apps.convenios import views
urlpatterns = [ urlpatterns = [
path( path(
"errosgescon/", "errosgescon/",
views.report_erros_gescon, views.ErrosGesconReportView.as_view(),
name="convenios-report_erros_gescon", name="convenios-report_erros_gescon",
), ),
path( path(

118
sigi/apps/convenios/views.py

@ -3,78 +3,74 @@ from docutils.core import publish_parts
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.http import HttpResponse, HttpResponseForbidden from django.http import HttpResponse, HttpResponseForbidden
from django.shortcuts import render, get_list_or_404 from django.shortcuts import render, get_list_or_404
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _, ngettext
from django.contrib.admin.views.decorators import staff_member_required from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django_weasyprint.views import WeasyTemplateResponse from django_weasyprint.views import WeasyTemplateResponse
from sigi.apps.casas.models import Orgao from sigi.apps.casas.models import Orgao
from sigi.apps.contatos.models import UnidadeFederativa from sigi.apps.contatos.models import UnidadeFederativa
from sigi.apps.convenios.models import Convenio, Gescon, Projeto from sigi.apps.convenios.models import Convenio, Gescon, Projeto
from sigi.apps.utils.views import ReportListView
@login_required class ErrosGesconReportView(
@staff_member_required LoginRequiredMixin, UserPassesTestMixin, ReportListView
def report_erros_gescon(request): ):
formato = request.GET.get("fmt", "html") title = _("Convênios com erros no Gescon")
convenios = ( empty_message = _("Nenhum convênio com erro de importação do Gescon!")
Convenio.objects.filter(erro_gescon=True) queryset = Convenio.objects.filter(erro_gescon=True).prefetch_related(
.order_by( "casa_legislativa", "casa_legislativa__municipio__uf"
"num_processo_sf",
"projeto",
"num_convenio",
"casa_legislativa",
)
.prefetch_related(
"casa_legislativa", "casa_legislativa__municipio__uf"
)
) )
rst = Gescon.load().ultima_importacao ordering = [
parts = publish_parts( "num_processo_sf",
rst, "projeto",
writer_name="html5", "num_convenio",
settings_overrides={ "casa_legislativa__nome",
"input_encoding": "unicode", ]
"output_encoding": "unicode", list_fields = [
}, "id",
) "num_processo_sf",
context = { "num_convenio",
"convenios": convenios, "projeto__nome",
"ultima_importacao": mark_safe(parts["html_body"]), "casa_legislativa__nome",
"title": "Convênios com erros no Gescon", "casa_legislativa__municipio__uf__sigla",
} "observacao_gescon",
if formato == "pdf": ]
return WeasyTemplateResponse( list_labels = [
filename="erros_gescon.pdf", _("id SIGI"),
request=request, _("NUP sigad"),
template="convenios/erros_gescon_pdf.html", _("Número"),
context=context, _("Projeto"),
content_type="application/pdf", _("Órgão conveniado"),
_("UF"),
_("Erro encontrado"),
]
def test_func(self):
return self.request.user.is_staff
def get_title(self):
count = self.get_queryset().count()
return ngettext(
"Um convênio com erro no Gescon",
f"{count} convênios com erros no Gescon",
count,
) )
elif formato == "csv":
response = HttpResponse(content_type="text/csv") def get_context_data(self, **kwargs):
response["Content-Disposition"] = ( context = super().get_context_data(**kwargs)
'attachment; filename="erros_gescon.csv"' rst = Gescon.load().ultima_importacao
parts = publish_parts(
rst,
writer_name="html5",
settings_overrides={
"input_encoding": "unicode",
"output_encoding": "unicode",
},
) )
fieldnames = [ context["ultima_importacao"] = mark_safe(parts["html_body"])
"id", return context
"num_processo_sf",
"num_convenio",
"projeto__nome",
"casa_legislativa__nome",
"casa_legislativa__municipio__uf__sigla",
"observacao_gescon",
]
writer = csv.DictWriter(response, fieldnames)
writer.writeheader()
writer.writerows(convenios.values(*fieldnames))
response.write("\n\nResumo da última importação do Gescon\n\n")
response.write('"' + rst.replace("\n", '"\n"') + '"')
return response
return render(
request,
"convenios/erros_gescon.html",
context=context,
)
@login_required @login_required

73
sigi/apps/utils/templates/utils/report/report.html

@ -0,0 +1,73 @@
{% extends "admin/base_site.html" %}
{% load static i18n %}
{% block breadcrumbs %}{% endblock %}
{% block extrastyle %}
{{ block.super }}
<style type="text/css">
table {
width: auto;
}
tr.changed {
border-top: 1px solid var(--body-fg);
}
tr.changed td {
border-top: 1px solid var(--body-fg);
}
</style>
<link rel="stylesheet" type="text/css" href="/static/css/calendario.css">
{% endblock %}
{% block coltype %}colMS{% endblock %}
{% block content_title %}
<h5>{{ report_title }}</h5>
{% endblock %}
{% block content %}
{% if form %}
<form class="row">
<div class="col s12">
<div class="card">
<div class="card-content">
{% block filterform %}{{ form }}{% endblock filterform %}
</div>
<div class="card-action">
<button class="waves-effect waves-light btn" type="submit">{% translate "Submit" %}</button>
<div class="fixed-action-btn">
<a class="btn-floating">
<i class="large material-icons">print</i>
</a>
<ul>
<li><button class="btn-floating" type="submit" name="fmt" value="pdf" title="{% trans 'Exportar para PDF' %}" ><i class="material-icons">picture_as_pdf</i></button></li>
<li><button class="btn-floating" type="submit" name="fmt" value="csv" title="{% trans 'Exportar para CSV' %}" ><i class="material-icons">file_download</i></button></li>
</ul>
</div>
</div>
</div>
</div>
</form>
{% else %}
<div class="fixed-action-btn">
<a class="btn-floating">
<i class="large material-icons">print</i>
</a>
<ul>
<li><a class="btn-floating" href="?fmt=pdf" title="{% trans 'Exportar para PDF' %}" ><i class="material-icons">picture_as_pdf</i></a></li>
<li><a class="btn-floating" href="?fmt=csv" title="{% trans 'Exportar para CSV' %}" ><i class="material-icons">file_download</i></a></li>
</ul>
</div>
{% endif %}
{% include "utils/report/report_items_snippet.html" with mode="html" %}
{% endblock %}
{% block footer %}
{{ block.super }}
<script>
$(document).ready(function(){
M.FloatingActionButton.init($('.fixed-action-btn'), {hoverEnabled: false});
M.Modal.init($(".modal"));
})
</script>
{% endblock %}

48
sigi/apps/utils/templates/utils/report/report_items_snippet.html

@ -0,0 +1,48 @@
{% load i18n admin_urls sigi_tags %}
<div class="row">
<div class="col s12">
<div class="card">
<div class="card-content">
<table class="striped">
<thead>
<tr>
{% for label in list_labels %}
<th>{{ label }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for obj in object_list %}
{% if break_field %}
{% with obj|valueof:break_field as monitor_break %}
{% ifchanged monitor_break %}
<tr><th colspan="{{ list_fields|length }}">{{ monitor_break }}</th></tr>
{% endifchanged %}
{% endwith %}
{% endif %}
<tr{% if change_field %}{% with obj|valueof:change_field as monitor_change %}{% ifchanged monitor_change %} class="changed {{ change_field }}-changed"{% endifchanged %}{% endwith %}{% endif %}>
{% for field_name in list_fields %}
<td>
{% if field_name in link_fields %}
<a href="{% url opts|admin_urlname:'change' obj.pk %}">
{{ obj|valueof:field_name|safe }}
</a>
{% else %}
{{ obj|valueof:field_name|safe }}
{% endif %}
</td>
{% endfor %}
</tr>
{% empty %}
<tr>
<td colspam="{{ list_labels|length }}">
{{ empty_message }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>

20
sigi/apps/utils/templates/utils/report/report_pdf.html

@ -0,0 +1,20 @@
{% extends "pdf/base_report.html" %}
{% load static i18n %}
{% block page_size %}A4 landscape{% endblock %}
{% block extra_style %}
{{ block.super }}
tr.changed {
border-top: 1px solid var(--body-fg);
}
tr.changed td {
border-top: 1px solid var(--body-fg);
}
{% endblock %}
{% block report_name %}{{ report_title }}{% endblock report_name %}
{% block main_content %}
{% include "utils/report/report_items_snippet.html" %}
{% endblock %}

12
sigi/apps/utils/templatetags/sigi_tags.py

@ -1,6 +1,6 @@
import datetime import datetime
from django import template from django import template
from django.conf import settings from django.db import models
from django.utils import timezone from django.utils import timezone
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
@ -36,3 +36,13 @@ def sum(value, arg):
@register.simple_tag @register.simple_tag
def multiply(value, arg): def multiply(value, arg):
return value * arg return value * arg
@register.filter
def valueof(obj, attr_name):
if hasattr(obj, attr_name):
return getattr(obj, attr_name)
if isinstance(obj, models.Model):
for part in attr_name.split("__"):
obj = getattr(obj, part)
return str(obj)

151
sigi/apps/utils/views.py

@ -1,14 +1,159 @@
import csv
import docutils.core import docutils.core
import io import io
from contextlib import redirect_stdout, redirect_stderr import re
from parsel import Selector from parsel import Selector
from contextlib import redirect_stdout, redirect_stderr
from django.apps import apps
from django.http.response import HttpResponse as HttpResponse
from django.contrib.auth.decorators import user_passes_test from django.contrib.auth.decorators import user_passes_test
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.shortcuts import render, get_object_or_404 from django.contrib.admin.utils import label_for_field, get_fields_from_path
from django.core.exceptions import ImproperlyConfigured
from django.shortcuts import render
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils import timezone from django.utils import timezone
from django.views.generic import ListView
from django_extensions.management.jobs import get_job from django_extensions.management.jobs import get_job
from sigi.apps.utils.models import JobSchedule from django_weasyprint import WeasyTemplateResponse
from django.utils.translation import gettext_lazy as _
class ReportListView(ListView):
filter_form = None
filter_form_initials = None
template_name = None
template_name_pdf = None
pdf_suffix = "_pdf"
format_param_name = "fmt"
filename = None
title = None
list_fields = None
list_labels = None
link_fields = None
change_field = None
break_field = None
empty_message = _("No data to display")
def get_queryset(self):
queryset = super().get_queryset()
return self.filter_queryset(queryset)
def get_template_names(self):
snake_name = re.sub(
r"(?<!^)(?=[A-Z])", "_", self.__class__.__name__
).lower()
app = apps.get_containing_app_config(self.__class__.__module__)
app_label = "." if app is None else app.label
if self._is_pdf():
if self.template_name_pdf:
return [self.template_name_pdf]
if self.template_name:
name = self.template_name[::-1].replace(
".", f"{self.pdf_suffix}."[::-1], 1
)[::-1]
if self.pdf_suffix not in name:
name += self.pdf_suffix
return [name]
return [
f"{app_label}/report/{snake_name}/report_pdf.html",
f"{app_label}/report/report_pdf.html",
"utils/report/report_pdf.html",
]
else:
if self.template_name is not None:
return [self.template_name]
else:
return [
f"{app_label}/report/{snake_name}/report.html",
f"{app_label}/report/report.html",
"utils/report/report.html",
]
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["report_title"] = self.get_title()
context["list_labels"] = self.get_list_labels()
context["list_fields"] = self.list_fields
context["link_fields"] = self.link_fields
context["change_field"] = self.change_field
context["break_field"] = self.break_field
context["empty_message"] = self.empty_message
context["opts"] = self._get_options()
context["form"] = self.get_filter_form_instance()
return context
def render_to_response(self, context, **response_kwargs):
if self._is_csv():
dataset, fieldnames = self.get_dataset()
response = HttpResponse(content_type="text/csv")
response["Content-Disposition"] = (
f'attachment; filename="{self.get_filename()}.csv"'
)
writer = csv.DictWriter(response, fieldnames)
writer.writeheader()
writer.writerows(dataset)
return response
if self._is_pdf():
self.response_class = WeasyTemplateResponse
self.content_type = "application/pdf"
response_kwargs["filename"] = f"{self.get_filename()}.pdf"
return super().render_to_response(context, **response_kwargs)
def filter_queryset(self, queryset):
form = self.get_filter_form_instance()
if form:
if form.is_valid():
filter = form.cleaned_data
else:
filter = self.filter_form_initials
if filter:
queryset = queryset.filter(**filter)
return queryset
def get_filter_form_instance(self):
form = None
if self.filter_form:
form = self.filter_form(initial=self.filter_form_initials)
if set(form.fields.keys()) & set(self.request.GET.keys()):
form = self.filter_form(self.request.GET)
return form
def get_dataset(self):
return (
self.get_queryset().values(*self.list_fields),
self.list_fields,
)
def get_filename(self):
return self.filename or self.title or self.__class__.__name__
def get_title(self):
return self.title or ""
def get_list_labels(self):
if self.list_labels:
return self.list_labels
if not self.list_fields:
raise ImproperlyConfigured(
"ReportListView requires a list of field names to be "
"displayed on report list data"
)
queryset = self.get_queryset()
fields = [
get_fields_from_path(queryset.model, path)[-1]
for path in self.list_fields
]
return [label_for_field(f.name, f.model) for f in fields]
def _get_options(self):
return self.get_queryset().model._meta
def _is_pdf(self):
return self.request.GET.get(self.format_param_name, "html") == "pdf"
def _is_csv(self):
return self.request.GET.get(self.format_param_name, "html") == "csv"
@login_required @login_required

Loading…
Cancel
Save