Browse Source

Adiciona relatório de solicitações de evento por período

pull/173/head
Sesóstris Vieira 9 months ago
parent
commit
7390716720
  1. 7
      sigi/apps/eventos/admin_urls.py
  2. 46
      sigi/apps/eventos/forms.py
  3. 38
      sigi/apps/eventos/templates/eventos/snippets/solicitacoes_por_periodo_legenda_snippet.html
  4. 200
      sigi/apps/eventos/templates/eventos/snippets/solicitacoes_por_periodo_snippet.html
  5. 112
      sigi/apps/eventos/templates/eventos/solicitacoes_por_periodo.html
  6. 54
      sigi/apps/eventos/templates/eventos/solicitacoes_por_periodo_pdf.html
  7. 258
      sigi/apps/eventos/views.py
  8. 2
      sigi/menu_conf.yaml

7
sigi/apps/eventos/admin_urls.py

@ -1,4 +1,4 @@
from django.urls import path, include
from django.urls import path
from sigi.apps.eventos import views
urlpatterns = [
@ -7,4 +7,9 @@ urlpatterns = [
"alocacaoequipe/", views.alocacao_equipe, name="eventos_alocacaoequipe"
),
path("eventosporuf/", views.eventos_por_uf, name="eventos_eventosporuf"),
path(
"solicitacoesporperiodo/",
views.solicitacoes_por_periodo,
name="eventos_solicitacoesporperiodo",
),
]

46
sigi/apps/eventos/forms.py

@ -16,6 +16,7 @@ from sigi.apps.eventos.models import (
ModeloDeclaracao,
Evento,
TipoEvento,
Solicitacao,
)
from sigi.apps.parlamentares.models import Parlamentar
@ -163,6 +164,51 @@ class EventosPorUfForm(forms.Form):
]
class SolicitacoesPorPeriodoForm(forms.Form):
MODO_CHOICES = (
(True, _("Virtual")),
(False, _("Presencial")),
)
data_inicio = forms.DateField(
required=True,
label=_("data de início"),
widget=MaterialAdminDateWidget,
)
data_fim = forms.DateField(
required=True,
label=_("data de término"),
widget=MaterialAdminDateWidget,
)
tipos_evento = forms.ModelMultipleChoiceField(
required=False,
label=_("Tipos de evento"),
queryset=TipoEvento.objects.all(),
)
virtual = forms.MultipleChoiceField(
required=False,
label=_("Modo"),
choices=MODO_CHOICES,
widget=forms.CheckboxSelectMultiple,
)
status = forms.MultipleChoiceField(
required=False,
label=_("Status"),
choices=Solicitacao.STATUS_CHOICES,
widget=forms.CheckboxSelectMultiple,
)
class Media:
css = {"all": ["css/change_form.css"]}
js = [
"admin/js/vendor/select2/select2.full.js",
"admin/js/change_form.js",
"admin/js/vendor/select2/i18n/pt-BR.js",
"material/admin/js/widgets/TimeInput.js",
"admin/js/core.js",
"/admin/jsi18n/",
]
class ConviteForm(forms.ModelForm):
class Meta:
model = Convite

38
sigi/apps/eventos/templates/eventos/snippets/solicitacoes_por_periodo_legenda_snippet.html

@ -0,0 +1,38 @@
{% load i18n %}
<div class="legenda">
<table>
<colgroup>
<col style="width: 20%;"/>
<col/>
</colgroup>
<tr>
<th>{% trans 'Período' %}</th>
<td>
{% blocktranslate with inicio=data_inicio|date:"SHORT_DATE_FORMAT" fim=data_fim|date:"SHORT_DATE_FORMAT" %}
{{ inicio }} a {{ fim }}
{% endblocktranslate %}
</td>
</tr>
<tr>
<th>{% trans 'Status' %}</th>
<td>
<ul>
{% for i, label in status_choices %}
<li><strong>{{ i }}</strong>: {{ label }}</li>
{% endfor %}
</ul>
</td>
</tr>
<tr>
<th>{% trans 'Oficinas' %}</th>
<td>
<ul>
{% for sigla, nome in legenda_oficinas %}
<li><strong>{{ sigla }}</strong>: {{ nome }}</li>
{% endfor %}
</ul>
</td>
</tr>
</table>
</div>

200
sigi/apps/eventos/templates/eventos/snippets/solicitacoes_por_periodo_snippet.html

@ -0,0 +1,200 @@
{% load i18n %}
<div class="row">
<div class="col s12">
<div class="card">
<div class="card-content">
<span class="card-title">{% trans 'Solicitações' %}</span>
<div class="table-responsive">
<table class="striped">
<thead>
<tr>
<th rowspan="2">{% trans 'UF' %}</th>
<th rowspan="2">{% trans 'Microrregião' %}</th>
<th rowspan="2">{% trans 'Casa solicitante' %}</th>
<th rowspan="2">{% trans 'Senador' %}</th>
<th rowspan="2">{% trans 'Data pedido' %}</th>
<th rowspan="2">{% trans 'Oficinas (status)' %}</th>
<th colspan="4" class="center">{% trans 'Quantidade' %}</th>
<th rowspan="2" class="numero">{% trans 'Custo total' %}</th>
</tr>
<tr>
<th class="numero">{% trans 'Solicitada' %}</th>
<th class="numero">{% trans 'Atendida' %}</th>
<th class="numero">{% trans 'Não atendida' %}</th>
<th class="numero">{% trans 'Participantes' %}</th>
</tr>
</thead>
<tbody>
{% for sol in solicitacoes.all %}
{% ifchanged sol.casa.municipio.uf.regiao %}
<tr>
<th colspan="11" class="center">
{{ sol.casa.municipio.uf.get_regiao_display }}
</th>
</tr>
{% endifchanged %}
<tr>
<td>{{ sol.casa.municipio.uf.sigla }}</td>
<td>{{ sol.casa.municipio.microrregiao.nome }}</td>
<td>{{ sol.casa.nome }}</td>
<td>{{ sol.senador }}</td>
<td>{{ sol.data_pedido|date:"SHORT_DATE_FORMAT" }}</td>
<td>
<ul class="report-list">
{% for item in sol.itemsolicitado_set.all %}
<li title="{{ item.tipo_evento.nome }} ({{ item.get_status_display }})">{{ item.tipo_evento.sigla }} ({{ item.status }})</li>
{% endfor %}
</ul>
</td>
<td class="numero">{{ sol.qtde_solicitadas }}</td>
<td class="numero">{{ sol.qtde_atendidas|default:"-" }}</td>
<td class="numero">{{ sol.qtde_rejeitadas|default:"-" }}</td>
<td class="numero">{{ sol.participantes|default:"-" }}</td>
<td class="numero">{{ sol.custo_total|floatformat:2|default:"-" }}</td>
</tr>
{% endfor %}
<tr>
<th colspan="6">{% trans 'Sumário' %}</th>
{% for valor in sumario %}
<th class="numero">
{% if forloop.last %}
{{ valor|floatformat:2|default:"-" }}
{% else %}
{{ valor|default:"-" }}
{% endif %}
</th>
{% endfor %}
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="row new-page">
<div class="col s12">
<div class="card">
<div class="card-content">
<span class="card-title">{% trans 'Resumo por Senador' %}</span>
<div class="table-responsive">
<table class="striped">
<thead>
<tr>
<th rowspan="2">{% trans 'UF' %}</th>
<th rowspan="2">{% trans 'Senador' %}</th>
<th colspan="4" class="center">{% trans 'Quantidade' %}</th>
<th rowspan="2" class="numero">{% trans 'Custo total' %}</th>
</tr>
<tr>
<th class="numero">{% trans 'Solicitada' %}</th>
<th class="numero">{% trans 'Atendida' %}</th>
<th class="numero">{% trans 'Não atendida' %}</th>
<th class="numero">{% trans 'Participantes' %}</th>
</tr>
</thead>
<tbody>
{% for uf in resumo_uf.itertuples %}
{% ifchanged uf.regiao %}
<tr>
<th class="sep_regiao" colspan="7">{{ uf.regiao }}</th>
</tr>
{% endifchanged %}
<tr>
<td>{{ uf.uf }}</td>
<td>{{ uf.senador }}</td>
<td class="numero">{{ uf.qtde_solicitadas }}</td>
<td class="numero">{{ uf.qtde_atendidas|default:"-" }}</td>
<td class="numero">{{ uf.qtde_rejeitadas|default:"-" }}</td>
<td class="numero">{{ uf.participantes|default:"-" }}</td>
<td class="numero">{{ uf.custo_total|floatformat:2|default:"-" }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="row new-page">
<div class="col s12">
<div class="card">
<div class="card-content">
<span class="card-title">{% trans 'Resumo por Região' %}</span>
<div class="table-responsive">
<table class="striped">
<thead>
<tr>
<th rowspan="2">{% trans 'Região' %}</th>
<th colspan="4" class="center">{% trans 'Quantidade' %}</th>
<th rowspan="2" class="numero">{% trans 'Custo total' %}</th>
</tr>
<tr>
<th class="numero">{% trans 'Solicitada' %}</th>
<th class="numero">{% trans 'Atendida' %}</th>
<th class="numero">{% trans 'Não atendida' %}</th>
<th class="numero">{% trans 'Participantes' %}</th>
</tr>
</thead>
<tbody>
{% for regiao in resumo_regiao.itertuples %}
<tr>
<td>{{ regiao.regiao }}</td>
<td class="numero">{{ regiao.qtde_solicitadas }}</td>
<td class="numero">{{ regiao.qtde_atendidas|default:"-" }}</td>
<td class="numero">{{ regiao.qtde_rejeitadas|default:"-" }}</td>
<td class="numero">{{ regiao.participantes|default:"-" }}</td>
<td class="numero">{{ regiao.custo_total|floatformat:2|default:"-" }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="row new-page">
<div class="col s12">
<div class="card">
<div class="card-content">
<span class="card-title">{% trans 'Resumo por tipo de evento' %}</span>
<div class="table-responsive">
<table class="striped">
<thead>
<tr>
<th rowspan="2">{% trans 'Sigla' %}</th>
<th rowspan="2">{% trans 'Nome' %}</th>
<th colspan="4" class="center">{% trans 'Quantidade' %}</th>
<th rowspan="2" class="numero">{% trans 'Custo total' %}</th>
</tr>
<tr>
<th class="numero">{% trans 'Solicitada' %}</th>
<th class="numero">{% trans 'Atendida' %}</th>
<th class="numero">{% trans 'Não atendida' %}</th>
<th class="numero">{% trans 'Participantes' %}</th>
</tr>
</thead>
<tbody>
{% for tipo in resumo_tipo_evento.itertuples %}
<tr>
<td>{{ tipo.sigla }}</td>
<td>{{ tipo.nome }}</td>
<td class="numero">{{ tipo.qtde_solicitadas }}</td>
<td class="numero">{{ tipo.qtde_atendidas|default:"-" }}</td>
<td class="numero">{{ tipo.qtde_rejeitadas|default:"-" }}</td>
<td class="numero">{{ tipo.participantes|default:"-" }}</td>
<td class="numero">{{ tipo.custo_total|floatformat:2|default:"-" }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>

112
sigi/apps/eventos/templates/eventos/solicitacoes_por_periodo.html

@ -0,0 +1,112 @@
{% extends "admin/base_site.html" %}
{% load static i18n %}
{% block extrastyle %}
{{ block.super }}
<style type="text/css">
#content {
display: block;
}
.table-responsive {
overflow: auto;
width: 100%;
}
table {
table-layout: auto !important;
width: 100%;
}
table.fixed {
table-layout: fixed;
}
th.sep_regiao {
text-align: center;
text-transform: uppercase;
}
tr:nth-child(even) {
background: var(--body-bg);
}
.numero {
text-align: right;
}
</style>
{% endblock %}
{% block extrahead %}
{{ block.super }}
{% endblock %}
{% block coltype %}colMS{% endblock %}
{% block content_title %}
<h5>{% trans 'Solicitações de evento por período' %}</h5>
{% endblock %}
{% block breadcrumbs %}{% endblock %}
{% block content %}
{% if solicitacoes is None %}
<div class="row">
<div class="col s12">
<div class="card">
<form>
<div class="card-content">
{{ form }}
</div>
<div class="card-action">
<button type="submit" class="waves-effect waves-light btn">{% trans 'Pesquisar' %}</button>
{% if not solicitacoes is None %}
<div class="fixed-action-btn">
<a class="btn-floating">
<i class="large material-icons">print</i>
</a>
<ul>
<li><button type="submit" name="fmt" value="pdf" class="btn-floating" title="{% trans 'Exportar para PDF' %}"><i class="material-icons">picture_as_pdf</i></button></li>
<li><button type="submit" name="fmt" value="csv" class="btn-floating" title="{% trans 'Exportar para CSV' %}"><i class="material-icons">file_download</i></button></li>
</ul>
</div>
{% endif %}
</div>
</form>
</div>
</div>
</div>
{% else %}
<div class="row">
<div class="col s12">
<div class="card">
<div class="card-content">
<span class="card-title">{% trans 'Legenda' %}</span>
{% include "eventos/snippets/solicitacoes_por_periodo_legenda_snippet.html" %}
</div>
<div class="card-action">
<a href="{% url 'eventos_solicitacoesporperiodo' %}">Nova pesquisa</a>
</div>
</div>
</div>
</div>
{% include "eventos/snippets/solicitacoes_por_periodo_snippet.html" with mode="html" %}
<form>
{% for field in form %}{{ field.as_hidden }}{% endfor %}
<div class="fixed-action-btn">
<a class="btn-floating">
<i class="large material-icons">print</i>
</a>
<ul>
<li><button type="submit" name="fmt" value="pdf" class="btn-floating" title="{% trans 'Exportar para PDF' %}"><i class="material-icons">picture_as_pdf</i></button></li>
<li><button type="submit" name="fmt" value="csv" class="btn-floating" title="{% trans 'Exportar para CSV' %}"><i class="material-icons">file_download</i></button></li>
</ul>
</div>
</form>
{% endif %}
{% endblock %}
{% block footer %}
{{ block.super }}
{{ form.media }}
<script>
$(document).ready(function(){
M.FloatingActionButton.init($('.fixed-action-btn'), {hoverEnabled: false});
M.Modal.init($(".modal"));
})
</script>
{% endblock %}

54
sigi/apps/eventos/templates/eventos/solicitacoes_por_periodo_pdf.html

@ -0,0 +1,54 @@
{% extends "pdf/base_report.html" %}
{% load static %}
{% load i18n %}
{% block page_size %}A4 landscape{% endblock page_size %}
{% block extra_style %}
{{ block.super }}
a {
color: black;
text-decoration: none;
}
h4 {
padding: 24px 0 24px 0;
line-height: 1.5em;
}
.row {
margin-bottom: 12px;
}
.card-title {
font-size: 1.2em;
margin: 20px 4px;
padding-left: 1.5rem;
border-left: 5px solid #ee6e73;
}
.sep_regiao {
text-align: center;
text-transform: uppercase;
}
.center {
text-align: center;
}
.numero {
text-align: right;
}
ul.report-list>li {
list-style: inside square;
white-space: nowrap;
}
div.legenda {
margin-bottom: 12px;
}
div.legenda th::after {
content: ":";
}
div.legenda tr:nth-child(even) {
background-color: white;
}
{% endblock %}
{% block main_content %}
{% include "eventos/snippets/solicitacoes_por_periodo_legenda_snippet.html" %}
{% include "eventos/snippets/solicitacoes_por_periodo_snippet.html" %}
{% endblock %}

258
sigi/apps/eventos/views.py

@ -5,49 +5,40 @@ import locale
import pandas as pd
from functools import reduce
from itertools import groupby
from rest_framework import mixins, generics
from rest_framework import generics
from typing import OrderedDict
from django import forms
from django.contrib import messages
from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.auth.decorators import login_required
from django.core.paginator import Paginator
from django.db.models import Count, Sum, Q
from django.db.models import Count, Sum, Q, F, OuterRef, Subquery
from django.http import HttpResponse
from django.shortcuts import redirect, render, get_object_or_404
from django.template import Template, Context
from django.shortcuts import render
from django.utils import timezone
from django.utils.text import slugify
from django.utils.translation import (
to_locale,
get_language,
ngettext,
gettext as _,
)
from django.urls import reverse
from django.views.decorators.clickjacking import xframe_options_exempt
from django.views.generic import ListView
from django_weasyprint.utils import django_url_fetcher
from django_weasyprint.views import WeasyTemplateResponse
from weasyprint import HTML
from sigi.apps.casas.models import Funcionario, Orgao
from sigi.apps.contatos.models import UnidadeFederativa
from sigi.apps.convenios.models import Projeto
from sigi.apps.eventos.models import TipoEvento, Evento
from sigi.apps.eventos.models import (
TipoEvento,
Evento,
Equipe,
Solicitacao,
ItemSolicitado,
)
from sigi.apps.eventos.forms import (
SelecionaModeloForm,
ConviteForm,
EventosPorUfForm,
CasaForm,
FuncionarioForm,
ParlamentarForm,
SolicitacoesPorPeriodoForm,
)
from sigi.apps.eventos.serializers import (
EventoSerializer,
EventoListSerializer,
)
from sigi.apps.parlamentares.models import Parlamentar
from sigi.apps.servidores.models import Servidor
@login_required
@ -154,7 +145,6 @@ def calendario(request):
if pdf:
context["title"] = _("Calendário de eventos")
context["pdf"] = True
# return render(request, "eventos/calendario_pdf.html", context)
return WeasyTemplateResponse(
filename=f"calendario_{ano_pesquisa:04}{mes_pesquisa:02}.pdf",
request=request,
@ -620,6 +610,232 @@ def eventos_por_uf(request):
return render(request, "eventos/eventos_por_uf.html", context=context)
@login_required
@staff_member_required
def solicitacoes_por_periodo(request):
formato = request.GET.get("fmt", "html")
initials = {
"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]
),
"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)
if not form.is_valid():
return render(
request,
"eventos/solicitacoes_por_periodo.html",
context={"form": form},
)
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"])
sq_equipe = (
Equipe.objects.order_by()
.annotate(
tot=Sum(
F("qtde_diarias") * F("valor_diaria") + F("total_passagens")
)
)
.values("tot")
)
sq_equipe.query.group_by = []
solicitacoes = Solicitacao.objects.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(
"itemsolicitado__tipo_evento__sigla",
"itemsolicitado__tipo_evento__nome",
)
.distinct()
)
solicitacoes = (
solicitacoes.order_by(
"casa__municipio__uf__regiao",
"casa__municipio__uf",
"casa__nome",
"data_pedido",
)
.annotate(
qtde_solicitadas=Count("itemsolicitado__id"),
qtde_atendidas=Count(
"itemsolicitado__id",
filter=Q(
itemsolicitado__status=ItemSolicitado.STATUS_AUTORIZADO
),
),
qtde_rejeitadas=Count(
"itemsolicitado__id",
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]
),
)
.select_related(
"casa",
"casa__municipio",
"casa__municipio__uf",
"casa__municipio__microrregiao",
)
.prefetch_related("itemsolicitado_set")
)
sumario = solicitacoes.aggregate(
Sum("qtde_solicitadas"),
Sum("qtde_atendidas"),
Sum("qtde_rejeitadas"),
Sum("participantes"),
Sum("custo_total"),
).values()
resumo_uf = (
pd.DataFrame(
solicitacoes.values(
"casa__municipio__uf__regiao",
"casa__municipio__uf__sigla",
"senador",
"qtde_solicitadas",
"qtde_atendidas",
"qtde_rejeitadas",
"participantes",
"custo_total",
)
)
.rename(
columns={
"casa__municipio__uf__regiao": "regiao",
"casa__municipio__uf__sigla": "uf",
}
)
.fillna(0)
.replace({"regiao": dict(UnidadeFederativa.REGIAO_CHOICES)})
.groupby(["regiao", "uf", "senador"], as_index=False)
.sum()
)
resumo_uf["participantes"] = resumo_uf["participantes"].astype("int")
resumo_regiao = resumo_uf.groupby(["regiao"], as_index=False)[
[
"qtde_solicitadas",
"qtde_atendidas",
"qtde_rejeitadas",
"participantes",
"custo_total",
]
].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)
),
participantes=Sum("evento__total_participantes"),
custo_total=Subquery(
sq_equipe.filter(evento__itemsolicitado=OuterRef("pk"))[:1]
),
)
)
.rename(
columns={
"tipo_evento__sigla": "sigla",
"tipo_evento__nome": "nome",
}
)
.groupby(["sigla", "nome"], as_index=False)
.sum()
.fillna(0)
)
resumo_tipo_evento["participantes"] = resumo_tipo_evento[
"participantes"
].astype("int")
resumo_tipo_evento.replace([0], [None], inplace=True)
# Imprimir
context = {
"form": form,
"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
],
"solicitacoes": solicitacoes,
"sumario": sumario,
"resumo_uf": resumo_uf,
"resumo_regiao": resumo_regiao,
"resumo_tipo_evento": resumo_tipo_evento,
}
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",
context=context,
content_type="application/pdf",
)
elif formato == "csv":
response = HttpResponse(content_type="text/csv")
response["Content-Disposition"] = (
f'attachment; filename="solicitacoes_por_periodo-{data_inicio}-{data_fim}.csv"'
)
fieldnames = [
"id",
"casa__nome",
"casa__municipio__microrregiao__nome",
"casa__municipio__uf__sigla",
"casa__municipio__uf__regiao",
"senador",
"data_pedido",
"qtde_solicitadas",
"qtde_atendidas",
"qtde_rejeitadas",
"participantes",
"custo_total",
]
writer = csv.DictWriter(response, fieldnames)
writer.writeheader()
writer.writerows(solicitacoes.values(*fieldnames))
return response
return render(
request, "eventos/solicitacoes_por_periodo.html", context=context
)
class ApiEventoAbstract:
queryset = (
Evento.objects.filter(publicar=True)

2
sigi/menu_conf.yaml

@ -31,6 +31,8 @@ main_menu:
children:
- title: Eventos por UF
view_name: eventos_eventosporuf
- title: Solicitações de eventos por período
view_name: eventos_solicitacoesporperiodo
- title: Calendário de eventos
view_name: eventos_calendario
- title: Alocação de equipe eventos

Loading…
Cancel
Save