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 = [
path("carteira/", views.painel_relacionamento, name="casas_carteira"),
path("gerentes/", views.GerentesListView.as_view(), name="casas_gerentes"),
path("cnpjduplicado/", views.cnpj_duplicado, name="casas_cnpj_duplicado"),
path("cnpjerrado/", views.cnpj_errado, name="casas_cnpj_errado"),
path(
"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 (
LoginRequiredMixin,
PermissionRequiredMixin,
UserPassesTestMixin,
)
from django.core.paginator import Paginator, InvalidPage, EmptyPage
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render, get_object_or_404
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 (
CreateView,
DeleteView,
@ -35,6 +36,7 @@ from sigi.apps.servicos.models import Servico, TipoServico
from sigi.apps.eventos.models import Evento, TipoEvento
from sigi.apps.convenios.models import Convenio
from sigi.apps.utils import valida_cnpj
from sigi.apps.utils.views import ReportListView
def resumo_carteira(casas):
@ -338,67 +340,61 @@ def painel_relacionamento(request):
return render(request, "casas/painel.html", context)
@login_required
@staff_member_required
def cnpj_duplicado(request):
formato = request.GET.get("fmt", "html")
dups = (
Orgao.objects.exclude(cnpj="")
.order_by("cnpj")
.values("cnpj")
.annotate(tot=Count("cnpj"))
.filter(tot__gt=1)
.values("cnpj")
)
orgaos = (
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"'
class CnpjDuplicadoReport(
LoginRequiredMixin, UserPassesTestMixin, ReportListView
):
title = _("Órgãos com CNPJ duplicado")
empty_message = _("Nenhum órgão com CNPJ duplicado!")
queryset = Orgao.objects.filter(
cnpj__in=(
Orgao.objects.exclude(cnpj="")
.order_by("cnpj")
.values("cnpj")
.annotate(tot=Count("cnpj"))
.filter(tot__gt=1)
.values("cnpj")
)
fieldnames = [
"id",
"cnpj",
"tipo__nome",
"sigla",
"nome",
"municipio__nome",
"municipio__uf__sigla",
]
writer = csv.DictWriter(response, fieldnames)
writer.writeheader()
writer.writerows(orgaos.values(*fieldnames))
return response
return render(request, "casas/cnpj_duplicado.html", context=context)
).prefetch_related("tipo", "municipio", "municipio__uf")
ordering = ["cnpj", "nome", "municipio__nome", "municipio__uf"]
list_fields = [
"id",
"cnpj",
"tipo__nome",
"sigla",
"nome",
"municipio__nome",
"municipio__uf__sigla",
]
list_labels = [
"ID",
"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="")
.order_by("tipo", "cnpj", "nome")
.annotate(
@ -407,46 +403,48 @@ def cnpj_errado(request):
uf_sigla=F("municipio__uf__sigla"),
)
)
if has_convenio:
todos_orgaos = todos_orgaos.exclude(convenio=None)
orgaos = []
for orgao in todos_orgaos:
if not valida_cnpj(orgao.cnpj):
orgaos.append(orgao)
context = {
"orgaos": orgaos,
"form": form,
"title": _("Órgãos com CNPJ digitado errado"),
}
if formato == "pdf":
return WeasyTemplateResponse(
filename="cnpj_errado.pdf",
request=request,
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"'
filter_form = CnpjErradoForm
list_fields = ["id", "cnpj", "sigla", "nome", "municipio_nome", "uf_sigla"]
list_labels = ["ID", "CNPJ", "Sigla", "Nome", "Cidade", "UF"]
link_fields = ["id"]
break_field = "tipo_nome"
def test_func(self):
return self.request.user.is_staff
def get_title(self):
count = len(self.get_queryset())
return ngettext(
"Um órgão com CNPJ digitado errado",
f"{count} órgãos com CNPJ digitado errado",
count,
)
fieldnames = [
"id",
"cnpj",
"tipo_nome",
"sigla",
"nome",
"municipio_nome",
"uf_sigla",
]
writer = csv.DictWriter(response, fieldnames)
writer.writeheader()
writer.writerows(
[{f: getattr(o, f) for f in fieldnames} for o in orgaos]
def filter_queryset(self, queryset):
form = self.get_filter_form_instance()
if form.is_valid():
has_convenio = form.cleaned_data["has_convenio"]
else:
has_convenio = False
if has_convenio:
queryset = queryset.exclude(convenio=None)
orgaos = []
for orgao in queryset:
if not valida_cnpj(orgao.cnpj):
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):

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 = [
path(
"errosgescon/",
views.report_erros_gescon,
views.ErrosGesconReportView.as_view(),
name="convenios-report_erros_gescon",
),
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.http import HttpResponse, HttpResponseForbidden
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.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django_weasyprint.views import WeasyTemplateResponse
from sigi.apps.casas.models import Orgao
from sigi.apps.contatos.models import UnidadeFederativa
from sigi.apps.convenios.models import Convenio, Gescon, Projeto
from sigi.apps.utils.views import ReportListView
@login_required
@staff_member_required
def report_erros_gescon(request):
formato = request.GET.get("fmt", "html")
convenios = (
Convenio.objects.filter(erro_gescon=True)
.order_by(
"num_processo_sf",
"projeto",
"num_convenio",
"casa_legislativa",
)
.prefetch_related(
"casa_legislativa", "casa_legislativa__municipio__uf"
)
class ErrosGesconReportView(
LoginRequiredMixin, UserPassesTestMixin, ReportListView
):
title = _("Convênios com erros no Gescon")
empty_message = _("Nenhum convênio com erro de importação do Gescon!")
queryset = Convenio.objects.filter(erro_gescon=True).prefetch_related(
"casa_legislativa", "casa_legislativa__municipio__uf"
)
rst = Gescon.load().ultima_importacao
parts = publish_parts(
rst,
writer_name="html5",
settings_overrides={
"input_encoding": "unicode",
"output_encoding": "unicode",
},
)
context = {
"convenios": convenios,
"ultima_importacao": mark_safe(parts["html_body"]),
"title": "Convênios com erros no Gescon",
}
if formato == "pdf":
return WeasyTemplateResponse(
filename="erros_gescon.pdf",
request=request,
template="convenios/erros_gescon_pdf.html",
context=context,
content_type="application/pdf",
ordering = [
"num_processo_sf",
"projeto",
"num_convenio",
"casa_legislativa__nome",
]
list_fields = [
"id",
"num_processo_sf",
"num_convenio",
"projeto__nome",
"casa_legislativa__nome",
"casa_legislativa__municipio__uf__sigla",
"observacao_gescon",
]
list_labels = [
_("id SIGI"),
_("NUP sigad"),
_("Número"),
_("Projeto"),
_("Ó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")
response["Content-Disposition"] = (
'attachment; filename="erros_gescon.csv"'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
rst = Gescon.load().ultima_importacao
parts = publish_parts(
rst,
writer_name="html5",
settings_overrides={
"input_encoding": "unicode",
"output_encoding": "unicode",
},
)
fieldnames = [
"id",
"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,
)
context["ultima_importacao"] = mark_safe(parts["html_body"])
return context
@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
from django import template
from django.conf import settings
from django.db import models
from django.utils import timezone
from django.utils.translation import gettext as _
@ -36,3 +36,13 @@ def sum(value, arg):
@register.simple_tag
def multiply(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 io
from contextlib import redirect_stdout, redirect_stderr
import re
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 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.utils import timezone
from django.views.generic import ListView
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

Loading…
Cancel
Save