Browse Source

Merge pull request #186 from moonshinerd/sigi-4.0

Refatoração dos Relatórios de Eventos para ReportListView
sigi-4.0
Sesostris Vieira 4 weeks ago
committed by GitHub
parent
commit
886c96ecf6
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 9
      sigi/apps/eventos/admin_urls.py
  2. 94
      sigi/apps/eventos/templates/eventos/calendario.html
  3. 585
      sigi/apps/eventos/views.py
  4. 0
      sigi/templates/admin/date_range_filter.html

9
sigi/apps/eventos/admin_urls.py

@ -1,12 +1,13 @@
from django.urls import path
from sigi.apps.eventos import views
from sigi.apps.eventos.views import EventosPorUfReportView, SolicitacoesPorPeriodoReportView, CalendarioReportView, AlocacaoEquipeReportView
urlpatterns = [
path("calendario/", views.calendario, name="eventos_calendario"),
path("calendario/", CalendarioReportView.as_view(), name="eventos_calendario"),
path(
"alocacaoequipe/", views.alocacao_equipe, name="eventos_alocacaoequipe"
"alocacaoequipe/", AlocacaoEquipeReportView.as_view(), name="eventos_alocacaoequipe"
),
path("eventosporuf/", views.eventos_por_uf, name="eventos_eventosporuf"),
path("eventosporuf/", EventosPorUfReportView.as_view(), name="eventos_eventosporuf"),
path(
"alunosporuf/",
views.AlunosPorUfReportView.as_view(),
@ -14,7 +15,7 @@ urlpatterns = [
),
path(
"solicitacoesporperiodo/",
views.solicitacoes_por_periodo,
SolicitacoesPorPeriodoReportView.as_view(),
name="eventos_solicitacoesporperiodo",
),
path(

94
sigi/apps/eventos/templates/eventos/calendario.html

@ -22,17 +22,76 @@
<form>
<div class="card mb-3">
<div class="card-header">
<a class="icon-link" data-bs-toggle="collapse" href="#filterFormCollapse" aria-expanded="{% if eventos is None %}true{% else %}false{% endif %}" aria-controls="filterFormCollapse">
<a class="icon-link" data-bs-toggle="collapse" href="#filterFormCollapse"
aria-expanded="{% if eventos is None %}true{% else %}false{% endif %}"
aria-controls="filterFormCollapse">
{% icon "filter" %} {% translate "Filtros" %}
</a>
</div>
<div class="card-body collapse{% if eventos is None %} show{% endif %}" id="filterFormCollapse">
{{ form }}
{% for field in form %}
{% if field.name != "mes_ano" and field.name != "categorias" and field.name != "status" %}
{{ field }}
{% endif %}
{% endfor %}
<div class="mb-3">
<fieldset>
<legend>Categorias:</legend>
{{ form.categorias }}
</fieldset>
</div>
<div class="mb-3">
<fieldset>
<legend>Status:</legend>
{{ form.status }}
</fieldset>
</div>
<div class="mb-3">
<fieldset>
<legend>Período:</legend>
<div class="mb-3">
<label for="id_year" class="form-label">Ano:</label>
<input type="number" id="id_year" class="form-control" placeholder="YYYY"
style="width: max-content;" required min="1000" max="9999">
</div>
<div class="mb-3">
<label for="id_month" class="form-label">Mês:</label>
<select id="id_month" class="form-select" style="width: max-content;" required>
<option value="">Selecione</option>
<option value="01">Janeiro</option>
<option value="02">Fevereiro</option>
<option value="03">Março</option>
<option value="04">Abril</option>
<option value="05">Maio</option>
<option value="06">Junho</option>
<option value="07">Julho</option>
<option value="08">Agosto</option>
<option value="09">Setembro</option>
<option value="10">Outubro</option>
<option value="11">Novembro</option>
<option value="12">Dezembro</option>
</select>
</div>
<input type="hidden" name="mes_ano" id="id_mes_ano" value="">
</fieldset>
</div>
</div>
<div class="card-footer d-flex justify-content-end">
<div>
<button type="submit" class="btn btn-primary">{% trans 'Ver calendário' %}</button>
<button type="submit" name="fmt" value="pdf" class="btn btn-primary" title="{% trans 'Exportar para PDF' %}">
<button type="submit" name="fmt" value="pdf" class="btn btn-primary"
title="{% trans 'Exportar para PDF' %}">
{% icon "pdf" %}
{% trans 'Exportar para PDF' %}
</button>
@ -45,7 +104,8 @@
<div class="accordion" id="accordionReport">
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseLegenda" aria-expanded="true" aria-controls="collapseLegenda">
<button class="accordion-button" type="button" data-bs-toggle="collapse"
data-bs-target="#collapseLegenda" aria-expanded="true" aria-controls="collapseLegenda">
{% translate "Legenda" %}
</button>
</h2>
@ -57,7 +117,8 @@
</div>
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseCalendario" aria-expanded="true" aria-controls="collapseCalendario">
<button class="accordion-button" type="button" data-bs-toggle="collapse"
data-bs-target="#collapseCalendario" aria-expanded="true" aria-controls="collapseCalendario">
{% trans 'Calendário' %}
</button>
</h2>
@ -69,7 +130,8 @@
</div>
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseLista" aria-expanded="false" aria-controls="collapseLista">
<button class="accordion-button" type="button" data-bs-toggle="collapse"
data-bs-target="#collapseLista" aria-expanded="false" aria-controls="collapseLista">
{% trans 'Lista' %}
</button>
</h2>
@ -83,4 +145,24 @@
{% include "eventos/snippets/calendario_modals.html" %}
{% endif %}
<script>
document.addEventListener('DOMContentLoaded', function() {
const yearInput = document.getElementById('id_year');
const monthSelect = document.getElementById('id_month');
const hiddenMesAno = document.getElementById('id_mes_ano');
function updateHiddenField() {
const year = yearInput.value.trim();
const month = monthSelect.value;
if (year && month) {
hiddenMesAno.value = year + '-' + month;
} else {
hiddenMesAno.value = '';
}
}
yearInput.addEventListener('input', updateHiddenField);
monthSelect.addEventListener('change', updateHiddenField);
});
</script>
{% endblock %}

585
sigi/apps/eventos/views.py

@ -190,31 +190,53 @@ class AlunosPorUfReportView(
return context
@login_required
@staff_member_required
def calendario(request):
fmt = request.GET.get("fmt", "html")
if "mes_ano" in request.GET:
form = CalendarioForm(request.GET)
else:
form = CalendarioForm(
initial={
class CalendarioReportView(LoginRequiredMixin, UserPassesTestMixin, ReportListView):
title = _("Calendário de eventos")
filter_form = CalendarioForm
template_name = "eventos/calendario.html"
template_name_pdf = "eventos/calendario_pdf.html"
list_fields = []
list_labels = []
def get_list_labels(self):
return []
def test_func(self):
return self.request.user.is_staff
def get_initial(self):
return {
"mes_ano": timezone.localdate().replace(day=1),
"categorias": [c[0] for c in TipoEvento.CATEGORIA_CHOICES],
"status": [s[0] for s in Evento.STATUS_CHOICES],
}
)
context = {"form": form}
def get_queryset(self):
return Evento.objects.none()
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
if "mes_ano" in self.request.GET:
form = CalendarioForm(self.request.GET)
else:
form = CalendarioForm(initial=self.get_initial())
context["form"] = form
if not form.is_valid():
return render(request, "eventos/calendario.html", context)
return context
mes_pesquisa = form.cleaned_data["mes_ano"].month
ano_pesquisa = form.cleaned_data["mes_ano"].year
sel_categorias = form.cleaned_data["categorias"]
sel_status = form.cleaned_data["status"]
lang = to_locale(get_language()) + ".UTF-8"
locale.setlocale(locale.LC_ALL, lang)
@ -232,32 +254,25 @@ def calendario(request):
semanas = [
{"datas": s, "eventos": []}
for s in calendar.Calendar().monthdatescalendar(
ano_pesquisa, mes_pesquisa
)
for s in calendar.Calendar().monthdatescalendar(ano_pesquisa, mes_pesquisa)
]
for e in eventos:
for s in semanas:
if not (
(e.data_termino < s["datas"][0])
or (e.data_inicio > s["datas"][-1])
):
if not (e.data_termino < s["datas"][0] or e.data_inicio > s["datas"][-1]):
start = max(s["datas"][0], e.data_inicio)
end = min(s["datas"][-1], e.data_termino)
s["eventos"].append(
(
s["eventos"].append((
e,
(
start.weekday(),
end.weekday() - start.weekday() + 1,
6 - end.weekday(),
),
)
)
))
context.update(
{
context.update({
"ano_pesquisa": ano_pesquisa,
"mes_pesquisa": mes_pesquisa,
"sel_categorias": sel_categorias,
@ -267,21 +282,22 @@ def calendario(request):
"status": Evento.STATUS_CHOICES,
"eventos": eventos,
"semanas": semanas,
}
)
})
return context
def render_to_response(self, context, **response_kwargs):
fmt = self.request.GET.get("fmt", "html")
if fmt == "pdf":
context["title"] = _("Calendário de eventos")
context["pdf"] = True
return WeasyTemplateResponse(
filename=f"calendario_{ano_pesquisa:04}{mes_pesquisa:02}.pdf",
request=request,
template="eventos/calendario_pdf.html",
filename=f"calendario_{context.get('ano_pesquisa'):04}{context.get('mes_pesquisa'):02}.pdf",
request=self.request,
template=self.template_name_pdf,
context=context,
content_type="application/pdf",
)
return render(request, "eventos/calendario.html", context)
return super().render_to_response(context, **response_kwargs)
class EventoListView(ListView):
model = Evento
@ -303,107 +319,145 @@ class EventoListView(ListView):
def get(self, request, *args, **kwargs):
return super().get(request, *args, **kwargs)
class AlocacaoEquipeReportView(LoginRequiredMixin, UserPassesTestMixin, ReportListView):
title = _("Alocação de equipe")
template_name = "eventos/alocacao_equipe.html"
template_name_pdf = "eventos/alocacao_equipe_pdf.html"
list_fields = []
list_labels = []
def test_func(self):
"""Restringe o acesso a usuários staff."""
return self.request.user.is_staff
def get_list_labels(self):
"""Sobrescreve para não exigir listagem de campos."""
return []
def get_queryset(self):
"""Retorna um queryset vazio, pois toda a lógica está em get_context_data."""
return Evento.objects.none()
def get_context_data(self, **kwargs):
"""Reproduz a lógica original da FBV, populando o contexto com dados."""
context = super().get_context_data(**kwargs)
ano_pesquisa = int(self.request.GET.get("ano", timezone.localdate().year))
mes_pesquisa = int(self.request.GET.get("mes", 0))
semana_pesquisa = int(self.request.GET.get("semana", 0))
formato = self.request.GET.get("fmt", "html")
@login_required
@staff_member_required
def alocacao_equipe(request):
ano_pesquisa = int(request.GET.get("ano", timezone.localdate().year))
mes_pesquisa = int(request.GET.get("mes", 0))
semana_pesquisa = int(request.GET.get("semana", 0))
formato = request.GET.get("fmt", "html")
lang = to_locale(get_language()) + ".UTF-8"
locale.setlocale(locale.LC_ALL, lang)
eventos = Evento.objects.exclude(
eventos = (
Evento.objects.exclude(
status__in=(Evento.STATUS_CANCELADO, Evento.STATUS_SOBRESTADO)
).prefetch_related("equipe_set")
)
.prefetch_related("equipe_set")
)
num_cols = 12
if mes_pesquisa > 0:
semanas = [
[s[0], s[-1]]
for s in calendar.Calendar().monthdatescalendar(
ano_pesquisa, mes_pesquisa
)
for s in calendar.Calendar().monthdatescalendar(ano_pesquisa, mes_pesquisa)
]
num_cols = len(semanas)
if semana_pesquisa > 0:
dias = calendar.Calendar().monthdatescalendar(
ano_pesquisa, mes_pesquisa
)[semana_pesquisa - 1]
dias = calendar.Calendar().monthdatescalendar(ano_pesquisa, mes_pesquisa)[semana_pesquisa - 1]
num_cols = len(dias)
eventos = eventos.filter(
data_inicio__gte=dias[0], data_inicio__lte=dias[-1]
)
else:
eventos = eventos.filter(
data_inicio__gte=semanas[0][0],
data_inicio__lte=semanas[-1][-1],
)
else:
eventos = eventos.filter(data_inicio__year=ano_pesquisa)
dados = []
for evento in eventos:
for p in evento.equipe_set.all():
for equipe in evento.equipe_set.all():
registro = None
for r in dados:
if r[0] == p.membro.pk:
if r[0] == equipe.membro.pk:
registro = r
break
if not registro:
if semana_pesquisa > 0:
registro = [
p.membro.pk,
p.membro.get_apelido(),
equipe.membro.pk,
equipe.membro.get_apelido(),
OrderedDict([(dia, []) for dia in dias]),
]
else:
registro = [
p.membro.pk,
p.membro.get_apelido(),
equipe.membro.pk,
equipe.membro.get_apelido(),
[{"dias": 0, "eventos": 0} for __ in range(num_cols)],
]
dados.append(registro)
if mes_pesquisa > 0:
if semana_pesquisa > 0:
for dia in dias:
if evento.data_inicio <= dia <= evento.data_termino:
registro[2][dia].append(evento)
else:
for idx, [inicio, fim] in enumerate(semanas):
for idx, (inicio, fim) in enumerate(semanas):
if inicio <= evento.data_inicio <= fim:
registro[2][idx]["dias"] += (
min(fim, evento.data_termino)
- evento.data_inicio
min(fim, evento.data_termino) - evento.data_inicio
).days + 1
registro[2][idx]["eventos"] += 1
elif inicio <= evento.data_termino <= fim:
registro[2][idx]["dias"] += (
min(fim, evento.data_termino)
- evento.data_inicio
min(fim, evento.data_termino) - evento.data_inicio
).days + 1
registro[2][idx]["eventos"] += 1
else:
registro[2][evento.data_inicio.month - 1]["dias"] += (
evento.data_termino - evento.data_inicio
).days + 1
registro[2][evento.data_inicio.month - 1]["eventos"] += 1
dados.sort(key=lambda x: x[1])
meses = list(calendar.month_abbr)[1:]
linhas = []
if semana_pesquisa:
linhas = [
[registro[1]] + list(registro[2].values()) for registro in dados
[registro[1]] + list(registro[2].values())
for registro in dados
]
else:
for r in dados:
r[2].append(
reduce(
lambda x, y: {
@ -413,14 +467,13 @@ def alocacao_equipe(request):
r[2],
)
)
linhas.append(
[r[1]]
+ [
(
row = [r[1]]
for d in r[2]:
if d["dias"] > 0 or d["eventos"] > 0:
texto = (
_(
ngettext(
"%(dias)s dia", "%(dias)s dias", d["dias"]
)
ngettext("%(dias)s dia", "%(dias)s dias", d["dias"])
+ " em "
+ ngettext(
"%(eventos)s evento",
@ -429,34 +482,44 @@ def alocacao_equipe(request):
)
)
% d
if d["dias"] > 0 or d["eventos"] > 0
else ""
)
for d in r[2]
]
)
row.append(texto)
else:
row.append("")
linhas.append(row)
context = {
"anos": Evento.objects.exclude(data_inicio=None)
context.update({
"anos": (
Evento.objects.exclude(data_inicio=None)
.order_by("data_inicio__year")
.distinct("data_inicio__year")
.values_list("data_inicio__year", flat=True),
.values_list("data_inicio__year", flat=True)
),
"ano_pesquisa": ano_pesquisa,
"linhas": linhas,
"meses": meses,
}
})
if mes_pesquisa > 0:
context["mes_pesquisa"] = mes_pesquisa
semanas = [
[s[0], s[-1]]
for s in calendar.Calendar().monthdatescalendar(ano_pesquisa, mes_pesquisa)
]
context["semanas"] = [
_(f"de {inicio:%d/%m} a {fim:%d/%m}") for inicio, fim in semanas
_(f"de {inicio:%d/%m} a {fim:%d/%m}")
for inicio, fim in semanas
]
if semana_pesquisa > 0:
cabecalho = [_("Servidor")] + dias
context["semana_pesquisa"] = semana_pesquisa
context["eventos"] = eventos
cabecalho = [_("Servidor")] + list(dias)
else:
cabecalho = (
[_("Servidor")]
+ [
@ -466,38 +529,59 @@ def alocacao_equipe(request):
+ ["total"]
)
else:
cabecalho = [_("Servidor")] + meses + ["total"]
context["cabecalho"] = cabecalho
context["formato"] = formato
if formato == "pdf":
context["title"] = _("Alocação de equipe")
return context
def render_to_response(self, context, **response_kwargs):
"""Decide se retorna HTML, PDF ou CSV."""
fmt = context.get("formato", "html")
if fmt == "pdf":
context["pdf"] = True
context["title"] = self.title
ano_pesquisa = context.get("ano_pesquisa", timezone.localdate().year)
return WeasyTemplateResponse(
filename=f"alocacao_equipe_{ano_pesquisa}.pdf",
request=request,
template="eventos/alocacao_equipe_pdf.html",
request=self.request,
template=self.template_name_pdf,
context=context,
content_type="application/pdf",
)
elif formato == "csv":
elif fmt == "csv":
ano_pesquisa = context.get("ano_pesquisa", timezone.localdate().year)
response = HttpResponse(content_type="text/csv")
response["Content-Disposition"] = (
f'attachment; filename="alocacao_equipe_{ano_pesquisa}.csv"'
)
response["Content-Disposition"] = f'attachment; filename="alocacao_equipe_{ano_pesquisa}.csv"'
writer = csv.writer(response)
writer.writerow(cabecalho)
writer.writerows(linhas)
writer.writerow(context["cabecalho"])
writer.writerows(context["linhas"])
return response
return render(request, "eventos/alocacao_equipe.html", context)
return super().render_to_response(context, **response_kwargs)
class EventosPorUfReportView(LoginRequiredMixin, UserPassesTestMixin, ReportListView):
title = _("Eventos por UF")
filter_form = EventosPorUfForm
template_name = "eventos/eventos_por_uf.html"
template_name_pdf = "eventos/eventos_por_uf_pdf.html"
@login_required
@staff_member_required
def eventos_por_uf(request):
formato = request.GET.get("fmt", "html")
initials = {
list_fields = []
list_labels = []
def get_list_labels(self):
return []
def test_func(self):
return self.request.user.is_staff
def get_initial(self):
return {
"data_inicio": datetime.date.today().replace(day=1),
"data_fim": datetime.date.today().replace(
day=calendar.monthrange(
@ -507,20 +591,27 @@ def eventos_por_uf(request):
"categoria": [c[0] for c in TipoEvento.CATEGORIA_CHOICES],
"virtual": [m[0] for m in EventosPorUfForm.MODO_CHOICES],
}
if "data_inicio" in request.GET or "data_fim" in request.GET:
form = EventosPorUfForm(request.GET)
else:
form = EventosPorUfForm(initial=initials)
def get_queryset(self):
return UnidadeFederativa.objects.none()
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
form = self.get_filter_form_instance()
context["form"] = form
if not form.is_valid():
return render(
request, "eventos/eventos_por_uf.html", context={"form": form}
)
return context
data_inicio = form.cleaned_data.get("data_inicio")
data_fim = form.cleaned_data.get("data_fim")
categorias = form.cleaned_data.get("categoria", initials["categoria"])
virtual = form.cleaned_data.get("virtual", initials["virtual"])
annotates = dict()
aggfuncs = dict()
initial = self.get_initial()
categorias = form.cleaned_data.get("categoria", initial["categoria"])
virtual = form.cleaned_data.get("virtual", initial["virtual"])
annotates = {}
aggfuncs = {}
if "P" in virtual:
annotates["eventos_presenciais"] = Count(
"municipio__orgao__evento__id",
@ -545,13 +636,11 @@ def eventos_por_uf(request):
)
aggfuncs["nº eventos virtuais"] = sum
aggfuncs["participantes virtuais"] = sum
eventos = (
UnidadeFederativa.objects.filter(
municipio__orgao__evento__status=Evento.STATUS_REALIZADO,
municipio__orgao__evento__data_inicio__range=(
data_inicio,
data_fim,
),
municipio__orgao__evento__data_inicio__range=(data_inicio, data_fim),
municipio__orgao__evento__tipo_evento__categoria__in=categorias,
)
.order_by("regiao", "nome")
@ -564,15 +653,13 @@ def eventos_por_uf(request):
)
df = pd.DataFrame(eventos)
if df.empty:
messages.add_message(
request,
messages.ERROR,
_("Nenhum evento foi realizado no período solicitado"),
messages.error(
self.request,
_("Nenhum evento foi realizado no período solicitado")
)
return render(
request, "eventos/eventos_por_uf.html", context={"form": form}
)
# Renomeia colunas
return context
df.rename(
columns={
"municipio__orgao__evento__tipo_evento__categoria": "categoria",
@ -583,13 +670,11 @@ def eventos_por_uf(request):
},
inplace=True,
)
# Troca a sigla pelo nome da região
for sigla, nome in UnidadeFederativa.REGIAO_CHOICES:
df["regiao"].replace(sigla, nome, inplace=True)
# Troca o código pelo nome da categoria de eventos
for cod, nome in TipoEvento.CATEGORIA_CHOICES:
df["categoria"].replace(cod, nome, inplace=True)
# Cria tabela pivot das UFs
pivo_uf = df.pivot_table(
index=["regiao", "nome"],
columns="categoria",
@ -597,103 +682,55 @@ def eventos_por_uf(request):
fill_value=0,
)
if len(categorias) > 1:
# calcula os totais de eventos e de participantes para as UFs
ix_eventos_presenciais = [
i for i in pivo_uf.columns if i[0] == "nº eventos presenciais"
]
ix_eventos_virtuais = [
i for i in pivo_uf.columns if i[0] == "nº eventos virtuais"
]
ix_participantes_presenciais = [
i for i in pivo_uf.columns if i[0] == "participantes presenciais"
]
ix_participantes_virtuais = [
i for i in pivo_uf.columns if i[0] == "participantes virtuais"
]
ix_eventos_presenciais = [i for i in pivo_uf.columns if i[0] == "nº eventos presenciais"]
ix_eventos_virtuais = [i for i in pivo_uf.columns if i[0] == "nº eventos virtuais"]
ix_participantes_presenciais = [i for i in pivo_uf.columns if i[0] == "participantes presenciais"]
ix_participantes_virtuais = [i for i in pivo_uf.columns if i[0] == "participantes virtuais"]
if ix_eventos_presenciais:
pivo_uf[("nº eventos presenciais", "total")] = pivo_uf[
ix_eventos_presenciais
].sum(axis=1)
pivo_uf[("nº eventos presenciais", "total")] = pivo_uf[ix_eventos_presenciais].sum(axis=1)
ix_eventos_presenciais.append(("nº eventos presenciais", "total"))
if ix_eventos_virtuais:
pivo_uf[("nº eventos virtuais", "total")] = pivo_uf[
ix_eventos_virtuais
].sum(axis=1)
pivo_uf[("nº eventos virtuais", "total")] = pivo_uf[ix_eventos_virtuais].sum(axis=1)
ix_eventos_virtuais.append(("nº eventos virtuais", "total"))
if ix_participantes_presenciais:
pivo_uf[("participantes presenciais", "total")] = pivo_uf[
ix_participantes_presenciais
].sum(axis=1)
ix_participantes_presenciais.append(
("participantes presenciais", "total")
)
pivo_uf[("participantes presenciais", "total")] = pivo_uf[ix_participantes_presenciais].sum(axis=1)
ix_participantes_presenciais.append(("participantes presenciais", "total"))
if ix_participantes_virtuais:
pivo_uf[("participantes virtuais", "total")] = pivo_uf[
ix_participantes_virtuais
].sum(axis=1)
ix_participantes_virtuais.append(
("participantes virtuais", "total")
)
pivo_uf[("participantes virtuais", "total")] = pivo_uf[ix_participantes_virtuais].sum(axis=1)
ix_participantes_virtuais.append(("participantes virtuais", "total"))
pivo_uf = pivo_uf[
ix_eventos_presenciais
+ ix_eventos_virtuais
+ ix_participantes_presenciais
+ ix_participantes_virtuais
ix_eventos_presenciais + ix_eventos_virtuais +
ix_participantes_presenciais + ix_participantes_virtuais
]
# Cria tabela pivot das regiões
pivo_regiao = df.pivot_table(
index="regiao",
columns="categoria",
aggfunc=aggfuncs,
fill_value=0,
)
# Calcula os totais de eventos e participantes para as regiões
if len(categorias) > 1:
ix_eventos_presenciais = [
i for i in pivo_regiao.columns if i[0] == "nº eventos presenciais"
]
ix_eventos_virtuais = [
i for i in pivo_regiao.columns if i[0] == "nº eventos virtuais"
]
ix_participantes_presenciais = [
i
for i in pivo_regiao.columns
if i[0] == "participantes presenciais"
]
ix_participantes_virtuais = [
i for i in pivo_regiao.columns if i[0] == "participantes virtuais"
]
ix_eventos_presenciais = [i for i in pivo_regiao.columns if i[0] == "nº eventos presenciais"]
ix_eventos_virtuais = [i for i in pivo_regiao.columns if i[0] == "nº eventos virtuais"]
ix_participantes_presenciais = [i for i in pivo_regiao.columns if i[0] == "participantes presenciais"]
ix_participantes_virtuais = [i for i in pivo_regiao.columns if i[0] == "participantes virtuais"]
if ix_eventos_presenciais:
pivo_regiao[("nº eventos presenciais", "total")] = pivo_regiao[
ix_eventos_presenciais
].sum(axis=1)
pivo_regiao[("nº eventos presenciais", "total")] = pivo_regiao[ix_eventos_presenciais].sum(axis=1)
ix_eventos_presenciais.append(("nº eventos presenciais", "total"))
if ix_eventos_virtuais:
pivo_regiao[("nº eventos virtuais", "total")] = pivo_regiao[
ix_eventos_virtuais
].sum(axis=1)
pivo_regiao[("nº eventos virtuais", "total")] = pivo_regiao[ix_eventos_virtuais].sum(axis=1)
ix_eventos_virtuais.append(("nº eventos virtuais", "total"))
if ix_participantes_presenciais:
pivo_regiao[("participantes presenciais", "total")] = pivo_regiao[
ix_participantes_presenciais
].sum(axis=1)
ix_participantes_presenciais.append(
("participantes presenciais", "total")
)
pivo_regiao[("participantes presenciais", "total")] = pivo_regiao[ix_participantes_presenciais].sum(axis=1)
ix_participantes_presenciais.append(("participantes presenciais", "total"))
if ix_participantes_virtuais:
pivo_regiao[("participantes virtuais", "total")] = pivo_regiao[
ix_participantes_virtuais
].sum(axis=1)
ix_participantes_virtuais.append(
("participantes virtuais", "total")
)
pivo_regiao[("participantes virtuais", "total")] = pivo_regiao[ix_participantes_virtuais].sum(axis=1)
ix_participantes_virtuais.append(("participantes virtuais", "total"))
pivo_regiao = pivo_regiao[
ix_eventos_presenciais
+ ix_eventos_virtuais
+ ix_participantes_presenciais
+ ix_participantes_virtuais
ix_eventos_presenciais + ix_eventos_virtuais +
ix_participantes_presenciais + ix_participantes_virtuais
]
# Cabeçalhos para impressão
cabecalho_uf = [
(k, [i[1] for i in v])
for k, v in groupby(pivo_uf.columns, lambda x: x[0])
@ -702,96 +739,111 @@ def eventos_por_uf(request):
(k, [i[1] for i in v])
for k, v in groupby(pivo_regiao.columns, lambda x: x[0])
]
# Fixar tudo em int
pivo_uf = pivo_uf.astype(int)
pivo_regiao = pivo_regiao.astype(int)
# Imprimir
context = {
"form": form,
context.update({
"data_inicio": data_inicio,
"data_fim": data_fim,
"categorias": [
c[1] for c in TipoEvento.CATEGORIA_CHOICES if c[0] in categorias
],
"virtual": [
m[1] for m in EventosPorUfForm.MODO_CHOICES if m[0] in virtual
],
"categorias": [c[1] for c in TipoEvento.CATEGORIA_CHOICES if c[0] in categorias],
"virtual": [m[1] for m in EventosPorUfForm.MODO_CHOICES if m[0] in virtual],
"pivo_uf": pivo_uf,
"pivo_regiao": pivo_regiao,
"cabecalho_uf": cabecalho_uf,
"cabecalho_regiao": cabecalho_regiao,
"total_uf": pivo_uf.sum(),
"total_regiao": pivo_regiao.sum(),
}
if formato == "pdf":
})
return context
def render_to_response(self, context, **response_kwargs):
fmt = self.request.GET.get("fmt", "html")
if fmt == "pdf":
context["title"] = _("Eventos por Unidade da Federação")
context["pdf"] = True
return WeasyTemplateResponse(
# filename=f"eventos_por_uf-{data_inicio}-{data_fim}.pdf",
request=request,
template="eventos/eventos_por_uf_pdf.html",
request=self.request,
template=self.template_name_pdf,
context=context,
content_type="application/pdf",
)
elif formato == "csv":
elif fmt == "csv":
response = HttpResponse(content_type="text/csv")
data_inicio = context.get("data_inicio")
data_fim = context.get("data_fim")
response["Content-Disposition"] = (
f'attachment; filename="eventos_por_uf-{data_inicio}-{data_fim}.csv"'
)
pivo_uf = context.get("pivo_uf")
pivo_uf.to_csv(response)
return response
return render(request, "eventos/eventos_por_uf.html", context=context)
return super().render_to_response(context, **response_kwargs)
class SolicitacoesPorPeriodoReportView(LoginRequiredMixin, UserPassesTestMixin, ReportListView):
title = _("Solicitações por período")
filter_form = SolicitacoesPorPeriodoForm
template_name = "eventos/solicitacoes_por_periodo.html"
template_name_pdf = "eventos/solicitacoes_por_periodo_pdf.html"
list_fields = []
list_labels = []
def get_list_labels(self):
return []
@login_required
@staff_member_required
def solicitacoes_por_periodo(request):
formato = request.GET.get("fmt", "html")
initials = {
def test_func(self):
return self.request.user.is_staff
def get_initial(self):
return {
"data_inicio": datetime.date.today().replace(day=1),
"data_fim": datetime.date.today().replace(
day=calendar.monthrange(
datetime.date.today().year, datetime.date.today().month
)[1]
day=calendar.monthrange(datetime.date.today().year, datetime.date.today().month)[1]
),
"tipos_evento": TipoEvento.objects.all(),
"virtual": [m[0] for m in SolicitacoesPorPeriodoForm.MODO_CHOICES],
"status": [s[0] for s in Solicitacao.STATUS_CHOICES],
}
if "data_inicio" in request.GET or "data_fim" in request.GET:
form = SolicitacoesPorPeriodoForm(request.GET)
else:
form = SolicitacoesPorPeriodoForm(initial=initials)
def get_queryset(self):
return Solicitacao.objects.none()
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
form = self.get_filter_form_instance()
context["form"] = form
if not form.is_valid():
return render(
request,
"eventos/solicitacoes_por_periodo.html",
context={"form": form},
)
return context
data_inicio = form.cleaned_data.get("data_inicio")
data_fim = form.cleaned_data.get("data_fim")
tipos_evento = form.cleaned_data.get(
"tipos_evento", initials["tipos_evento"]
)
virtual = form.cleaned_data.get("virtual", initials["virtual"])
status = form.cleaned_data.get("status", initials["status"])
initial = self.get_initial()
tipos_evento = form.cleaned_data.get("tipos_evento", initial["tipos_evento"])
virtual = form.cleaned_data.get("virtual", initial["virtual"])
status = form.cleaned_data.get("status", initial["status"])
sq_equipe = (
Equipe.objects.order_by()
.annotate(
tot=Sum(
F("qtde_diarias") * F("valor_diaria") + F("total_passagens")
)
tot=Sum(F("qtde_diarias") * F("valor_diaria") + F("total_passagens"))
)
.values("tot")
)
sq_equipe.query.group_by = []
solicitacoes = Solicitacao.objects.order_by().filter(
data_pedido__range=(data_inicio, data_fim),
itemsolicitado__tipo_evento__in=tipos_evento,
itemsolicitado__virtual__in=virtual,
status__in=status,
)
legenda_oficinas = (
solicitacoes.order_by("itemsolicitado__tipo_evento__sigla")
.values_list(
@ -800,6 +852,7 @@ def solicitacoes_por_periodo(request):
)
.distinct()
)
solicitacoes = (
solicitacoes.order_by(
"casa__municipio__uf__regiao",
@ -811,21 +864,15 @@ def solicitacoes_por_periodo(request):
qtde_solicitadas=Count("itemsolicitado__id"),
qtde_atendidas=Count(
"itemsolicitado__id",
filter=Q(
itemsolicitado__status=ItemSolicitado.STATUS_AUTORIZADO
),
filter=Q(itemsolicitado__status=ItemSolicitado.STATUS_AUTORIZADO)
),
qtde_rejeitadas=Count(
"itemsolicitado__id",
filter=Q(
itemsolicitado__status=ItemSolicitado.STATUS_REJEITADO
),
filter=Q(itemsolicitado__status=ItemSolicitado.STATUS_REJEITADO)
),
participantes=Sum("itemsolicitado__evento__total_participantes"),
custo_total=Subquery(
sq_equipe.filter(
evento__itemsolicitado__solicitacao=OuterRef("pk")
)[:1]
sq_equipe.filter(evento__itemsolicitado__solicitacao=OuterRef("pk"))[:1]
),
)
.select_related(
@ -836,6 +883,7 @@ def solicitacoes_por_periodo(request):
)
.prefetch_related("itemsolicitado_set")
)
sumario = solicitacoes.aggregate(
Sum("qtde_solicitadas"),
Sum("qtde_atendidas"),
@ -843,6 +891,7 @@ def solicitacoes_por_periodo(request):
Sum("participantes"),
Sum("custo_total"),
).values()
resumo_uf = pd.DataFrame(
solicitacoes.order_by(
"casa__municipio__uf__regiao",
@ -886,18 +935,15 @@ def solicitacoes_por_periodo(request):
].sum()
resumo_uf.replace([0], [None], inplace=True)
resumo_regiao.replace([0], [None], inplace=True)
resumo_tipo_evento = pd.DataFrame(
ItemSolicitado.objects.filter(solicitacao__in=solicitacoes)
.order_by("tipo_evento__sigla", "tipo_evento__nome")
.values("tipo_evento__sigla", "tipo_evento__nome")
.annotate(
qtde_solicitadas=Count("id"),
qtde_atendidas=Count(
"id", filter=Q(status=ItemSolicitado.STATUS_AUTORIZADO)
),
qtde_rejeitadas=Count(
"id", filter=Q(status=ItemSolicitado.STATUS_REJEITADO)
),
qtde_atendidas=Count("id", filter=Q(status=ItemSolicitado.STATUS_AUTORIZADO)),
qtde_rejeitadas=Count("id", filter=Q(status=ItemSolicitado.STATUS_REJEITADO)),
participantes=Sum("evento__total_participantes"),
custo_total=Subquery(
sq_equipe.filter(evento__itemsolicitado=OuterRef("pk"))[:1]
@ -916,41 +962,39 @@ def solicitacoes_por_periodo(request):
.sum()
.fillna(0)
)
resumo_tipo_evento["participantes"] = resumo_tipo_evento[
"participantes"
].astype("int")
resumo_tipo_evento["participantes"] = resumo_tipo_evento["participantes"].astype("int")
resumo_tipo_evento.replace([0], [None], inplace=True)
# Imprimir
context = {
"form": form,
context.update({
"data_inicio": data_inicio,
"data_fim": data_fim,
"status_choices": ItemSolicitado.STATUS_CHOICES,
"legenda_oficinas": legenda_oficinas,
"tipos_evento": tipos_evento,
"virtual": [
m[1]
for m in SolicitacoesPorPeriodoForm.MODO_CHOICES
if m[0] in virtual
],
"virtual": [m[1] for m in SolicitacoesPorPeriodoForm.MODO_CHOICES if m[0] in virtual],
"solicitacoes": solicitacoes,
"sumario": sumario,
"resumo_uf": resumo_uf,
"resumo_regiao": resumo_regiao,
"resumo_tipo_evento": resumo_tipo_evento,
}
})
return context
def render_to_response(self, context, **response_kwargs):
formato = self.request.GET.get("fmt", "html")
if formato == "pdf":
context["title"] = _("Solicitações por período")
context["pdf"] = True
return WeasyTemplateResponse(
filename=f"solicitacoes_por_periodo-{data_inicio}-{data_fim}.pdf",
request=request,
template="eventos/solicitacoes_por_periodo_pdf.html",
filename=f"solicitacoes_por_periodo-{context.get('data_inicio')}-{context.get('data_fim')}.pdf",
request=self.request,
template=self.template_name_pdf,
context=context,
content_type="application/pdf",
)
elif formato == "csv":
response = HttpResponse(content_type="text/csv")
data_inicio = context.get("data_inicio")
data_fim = context.get("data_fim")
response["Content-Disposition"] = (
f'attachment; filename="solicitacoes_por_periodo-{data_inicio}-{data_fim}.csv"'
)
@ -970,12 +1014,9 @@ def solicitacoes_por_periodo(request):
]
writer = csv.DictWriter(response, fieldnames)
writer.writeheader()
writer.writerows(solicitacoes.values(*fieldnames))
writer.writerows(context["solicitacoes"].values(*fieldnames))
return response
return render(
request, "eventos/solicitacoes_por_periodo.html", context=context
)
return super().render_to_response(context, **response_kwargs)
class ApiEventoAbstract:
queryset = (

0
sigi/templates/admin/date_range_filter.html

Loading…
Cancel
Save