Browse Source

Dashboards convertidos para a app Django-dashboard

pull/185/head
Sesóstris Vieira 2 months ago
parent
commit
2f48c38b19
  1. 73
      sigi/apps/casas/dashboards.py
  2. 194
      sigi/apps/convenios/dashboards.py
  3. 30
      sigi/apps/convenios/templates/convenios/dashboard/resumo_convenios.html
  4. 153
      sigi/apps/eventos/dashboards.py
  5. 393
      sigi/apps/servicos/dashboards.py

73
sigi/apps/casas/dashboards.py

@ -0,0 +1,73 @@
import django_filters
from dashboard import Dashcard, getcolor
from django.db.models import Count
from django.utils.translation import gettext as _
from sigi.apps.casas.models import Orgao
from sigi.apps.servidores.models import Servidor
class GerenteFilter(django_filters.FilterSet):
servidor = django_filters.ModelChoiceFilter(
field_name="gerentes_interlegis",
label="Gerente",
queryset=Servidor.objects.exclude(casas_que_gerencia=None),
)
class Meta:
model = Orgao
fields = ["servidor"]
class CasasGerente(Dashcard):
chart_type = Dashcard.TYPE_DOUGHNUT
title = _("Distribuição de Casas por Gerente")
model = Servidor
label_field = "nome_completo"
datasets = [{"data_field": ("casas_que_gerencia", Count)}]
def apply_filters(self, request, queryset):
return (
super()
.apply_filters(request, queryset)
.exclude(casas_que_gerencia=None)
)
class PerformanceCarteira(Dashcard):
chart_type = Dashcard.TYPE_DOUGHNUT
title = _("Performance da gerência de carteiras")
model = Orgao
filterset = GerenteFilter
LABEL_USAM = _("Utilizam serviços")
LABEL_NAO_USAM = _("Não utilizam servços")
def apply_filters(self, request, queryset):
filter = self.filterset(request.GET, queryset=queryset)
valid = filter.is_valid()
if filter.form.cleaned_data["servidor"] is None:
if (
request.user.servidor
and request.user.servidor.casas_que_gerencia.exists()
):
return request.user.servidor.casas_que_gerencia.all()
return (
super()
.apply_filters(request, queryset)
.exclude(gerentes_interlegis=None)
)
def get_labels(self, request, queryset=None):
return [self.LABEL_USAM, self.LABEL_NAO_USAM]
def get_datasets(self, request, queryset=None):
if queryset is None:
queryset = self.get_queryset(request)
return [
{
"data": [
queryset.exclude(servico=None).count(),
queryset.filter(servico=None).count(),
]
}
]

194
sigi/apps/convenios/dashboards.py

@ -0,0 +1,194 @@
import numpy as np
import pandas as pd
import django_filters
from dashboard import Dashcard
from django.db.models import Q, Count
from django.utils import timezone
from django.utils.translation import gettext as _
from sigi.apps.casas.models import TipoOrgao, Orgao
from sigi.apps.convenios.models import Convenio, Projeto
def get_tipos():
tipos = list(
TipoOrgao.objects.filter(sigla__in=["CM", "AL"]).values_list(
"sigla", "nome"
)
)
tipos.extend(
[
("_legislativo", _("Todo o legislativo")),
("_outros", _("Demais órgãos")),
]
)
return tipos
class ResumoConveniosFilter(django_filters.FilterSet):
tipo = django_filters.ChoiceFilter(
field_name="casa_legislativa__tipo__sigla",
label=_("Tipo"),
choices=get_tipos,
method="filter_tipo",
empty_label=None,
initial="CM",
)
class Meta:
model = Convenio
fields = ["tipo"]
def filter_tipo(self, queryset, name, value):
if value == "_legislativo":
tipos = TipoOrgao.objects.filter(legislativo=True).values_list(
"sigla", flat=True
)
elif value == "_outros":
tipos = TipoOrgao.objects.filter(legislativo=False).values_list(
"sigla", flat=True
)
else:
tipos = [value]
return queryset.filter(**{f"{name}__in": tipos})
class ResumoConvenios(Dashcard):
chart_type = Dashcard.TYPE_TABLE
title = _("Resumo de informações")
model = Convenio
filterset = ResumoConveniosFilter
template_table = "convenios/dashboard/resumo_convenios.html"
def apply_filters(self, request, queryset):
if "tipo" not in request.GET:
request.GET = request.GET.copy()
request.GET["tipo"] = "CM"
return super().apply_filters(request, queryset)
def get_labels(self, request, queryset=None):
return []
def get_datasets(self, request, queryset=None):
if queryset is None:
queryset = self.get_queryset(request)
filter = self.get_filter(request.GET, queryset)
if filter and filter.is_valid():
label_tipo = dict(get_tipos())[filter.form.cleaned_data["tipo"]]
else:
label_tipo = dict(get_tipos())["CM"]
convenios = queryset
camaras = filter.filter_tipo(
Orgao.objects.all(),
"tipo__sigla",
filter.form.cleaned_data["tipo"],
)
convenios_vigentes = convenios.exclude(
data_retorno_assinatura=None
).filter(
Q(data_termino_vigencia__gte=timezone.localdate())
| Q(data_termino_vigencia=None)
)
convenios_andando = convenios.filter(data_retorno_assinatura=None)
convenios_vencidos = convenios.exclude(
Q(data_retorno_assinatura=None) | Q(data_termino_vigencia=None)
).filter(data_termino_vigencia__lt=timezone.localdate())
dataset = {
_(f"{label_tipo} com convênios vigentes"): {
k: v
for k, v in convenios_vigentes.values_list(
"projeto__sigla"
).annotate(Count("casa_legislativa_id", distinct=True))
},
_(f"{label_tipo} com convênios em andamento"): {
k: v
for k, v in convenios_andando.values_list(
"projeto__sigla"
).annotate(Count("casa_legislativa_id", distinct=True))
},
_(f"{label_tipo} com convênios vencidos"): {
k: v
for k, v in convenios_vencidos.values_list(
"projeto__sigla"
).annotate(Count("casa_legislativa_id", distinct=True))
},
}
ds_totais = (
(_(f"Total de {label_tipo} do país"), camaras.count()),
(
_(f"Total de {label_tipo} com convênio vigente"),
convenios_vigentes.order_by("casa_legislativa_id")
.distinct("casa_legislativa_id")
.count(),
),
(
_(f"Total de {label_tipo} com convênio em andamento"),
convenios_andando.order_by("casa_legislativa_id")
.distinct("casa_legislativa_id")
.count(),
),
(
_(f"Total de {label_tipo} com convênio vencido"),
convenios_vencidos.order_by("casa_legislativa_id")
.distinct("casa_legislativa_id")
.count(),
),
)
df = (
pd.DataFrame.from_dict(dataset, orient="index")
.replace(np.nan, 0)
.convert_dtypes()
)
return {"data_frame": df, "totais": ds_totais}
class ConvenioServico(Dashcard):
chart_type = Dashcard.TYPE_TABLE
title = _("Convenios e serviços")
model = Orgao
label_name = _("Situação")
def get_queryset(self, request):
return (
Orgao.objects.exclude(servico=None)
.filter(servico__data_desativacao=None, convenio=None)
.aggregate(
total=Count("id", distinct=True),
hospedagem=Count(
"id",
filter=Q(servico__tipo_servico__modo="H"),
distinct=True,
),
)
)
def get_labels(self, request, queryset=None):
return [_("Total")]
def get_datasets(self, request, queryset=None):
if queryset is None:
queryset = self.get_queryset(request)
values = dict(queryset)
datasets = [
{
"label": _(
"Casas sem convenio que utilizam algum serviço de hospedagem"
),
"data": [values["hospedagem"]],
},
{
"label": _(
"Casas sem convenio que utilizam somente serviço de registro"
),
"data": [values["total"] - values["hospedagem"]],
},
{
"label": _(
"Casas sem convenio que utilizam algum serviço de registro e/ou hospedagem"
),
"data": [values["total"]],
},
]
return datasets

30
sigi/apps/convenios/templates/convenios/dashboard/resumo_convenios.html

@ -0,0 +1,30 @@
{% load i18n %}
<table class="table table-hover table-sm">
<thead>
<tr>
<th>&nbsp;</th>
{% for item in datasets.data_frame.columns %}
<th>{{ item }}</th>
{% endfor %}
</tr>
</thead>
<tbody class="table-group-divider">
{% for label, values in datasets.data_frame.iterrows %}
<tr>
<th>{{ label }}</th>
{% for value in values %}
<td>{{ value }}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
<table class="table table-hover table-sm">
{% for label, value in datasets.totais %}
<tr>
<th>{{ label }}</th>
<td>{{ value }}</td>
</tr>
{% endfor %}
</table>

153
sigi/apps/eventos/dashboards.py

@ -0,0 +1,153 @@
import calendar
import datetime
import django_filters
from dashboard import Dashcard, getcolor
from django.db.models import F, Count, Q
from django.utils import timezone
from django.utils.translation import gettext as _
from sigi.apps.eventos.models import Evento, TipoEvento
def get_anos():
return [
(str(a), str(a))
for a in Evento.objects.filter(status=Evento.STATUS_REALIZADO)
.order_by("data_inicio__year")
.values_list("data_inicio__year", flat=True)
.distinct("data_inicio__year")
]
class AnoFilterset(django_filters.FilterSet):
ano = django_filters.ChoiceFilter(
field_name="data_inicio",
lookup_expr="year",
label=_("Ano"),
distinct=True,
choices=get_anos,
)
class Meta:
model = Evento
fields = ["ano"]
class EventosStatus(Dashcard):
chart_type = Dashcard.TYPE_DOUGHNUT
title = _("Eventos por status")
model = Evento
label_field = ("status", F, lambda s: dict(Evento.STATUS_CHOICES)[s])
datasets = [{"data_field": ("id", Count)}]
def get_dataset_color(self, dataset_label):
return getcolor(dataset_label)
class EventosAno(Dashcard):
chart_type = Dashcard.TYPE_LINE
title = _("Eventos nos últimos 12 meses")
model = TipoEvento
def get_dataset_color(self, dataset_label):
print(f"EventosAno: {dataset_label}, {getcolor(dataset_label)}")
return getcolor(dataset_label)
def get_meses(self, request=None):
if request is None:
mes = timezone.localdate().month
ano = timezone.localdate().year
else:
mes = int(request.GET.get("mes", timezone.localdate().month))
ano = int(request.GET.get("ano", timezone.localdate().year))
mes_ano = datetime.date(
year=ano, month=mes, day=1
) + datetime.timedelta(days=calendar.monthrange(ano, mes)[1])
meses = []
for i in range(13):
meses.append(mes_ano)
mes_ano = (mes_ano - datetime.timedelta(days=1)).replace(day=1)
meses.reverse()
return meses
def get_labels(self, request, queryset=None):
return [f"{m:%m/%Y}" for m in self.get_meses(request)[:-1]]
def get_counters(self, request):
counts = {}
for mes in self.get_meses(request)[:-1]:
counts[f"count_{mes:%Y_%m}"] = Count(
"evento",
Q(
evento__data_inicio__year=mes.year,
evento__data_inicio__month=mes.month,
),
)
return counts
def apply_filters(self, request, queryset):
return (
super()
.apply_filters(request, queryset)
.filter(evento__status=Evento.STATUS_REALIZADO)
)
def get_queryset(self, request):
counts = self.get_counters(request)
return (
super()
.get_queryset(request)
.values("categoria")
.annotate(**counts)
)
def get_dataset_color(self, dataset_label):
return getcolor(dataset_label)
def get_datasets(self, request, queryset=None):
if queryset is None:
queryset = self.get_queryset(request)
categorias = dict(TipoEvento.CATEGORIA_CHOICES)
counters = self.get_counters(request).keys()
return [
{
"label": categorias[r["categoria"]],
"data": [r[c] for c in counters],
"backgroundColor": self.get_dataset_color(r["categoria"]),
}
for r in queryset
]
def get_next_page(self, request=None, queryset=None):
mes = self.get_meses(request)[-1]
return f"ano={mes.year}&mes={mes.month}"
def get_prev_page(self, request=None, queryset=None):
mes = self.get_meses(request)[-3]
return f"ano={mes.year}&mes={mes.month}"
class EventosCategoria(Dashcard):
chart_type = Dashcard.TYPE_DOUGHNUT
title = _("Eventos por categoria")
filterset = AnoFilterset
model = Evento
label_field = (
"tipo_evento__categoria",
F,
lambda x: dict(TipoEvento.CATEGORIA_CHOICES)[x],
)
datasets = [{"data_field": ("id", Count)}]
def apply_filters(self, request, queryset):
return (
super()
.apply_filters(request, queryset)
.filter(status=Evento.STATUS_REALIZADO)
)
def get_dataset_color(self, dataset_label):
return getcolor(dataset_label)

393
sigi/apps/servicos/dashboards.py

@ -0,0 +1,393 @@
import calendar
import datetime
import locale
from dashboard import Dashcard, getcolor
from random import randint, seed
from django.db.models import Count, F, Q
from django.db.models.functions import TruncMonth
from django.http import QueryDict
from django.utils import timezone
from django.utils.text import slugify
from django.utils.translation import gettext as _, to_locale, get_language
import django_filters
from sigi.apps.servicos.models import Servico, TipoServico
from sigi.apps.contatos.models import UnidadeFederativa
class UsoServicosFilter(django_filters.FilterSet):
uf = django_filters.ModelChoiceFilter(
field_name="servico__casa_legislativa__municipio__uf",
label=_("UF"),
queryset=UnidadeFederativa.objects.all(),
)
class Meta:
model = TipoServico
fields = ["uf"]
class AnoServicoFilter(django_filters.FilterSet):
ano = django_filters.ModelChoiceFilter(
field_name="data_ativacao__year",
label=_("Ano"),
queryset=(
Servico.objects.filter(hospedagem_interlegis=True)
.order_by("data_ativacao__year")
.values_list("data_ativacao__year", flat=True)
.distinct("data_ativacao__year")
),
)
class Meta:
model = Servico
fields = ["ano"]
class Sazonalidade(Dashcard):
title = _("Sazonalidade da hospedagem de serviços")
chart_type = Dashcard.TYPE_LINE
model = Servico
label_field = ("data_ativacao", TruncMonth, lambda d: d.strftime("%m/%Y"))
datasets = [
{
"label_field": "tipo_servico__sigla",
"data_field": ("*", Count),
}
]
def get_dataset_color(self, dataset_label):
return getcolor(dataset_label)
def get_queryset(self, request):
qs = (
super()
.get_queryset(request)
.filter(data_desativacao=None)
.order_by("tipo_servico__sigla", "data_ativacao")
)
ano = request.GET.get("ano", None)
if ano is None:
ano = (
qs.dates("data_ativacao", "year")
.values_list("data_ativacao__year", flat=True)
.last()
)
return qs.filter(data_ativacao__year=ano)
def get_prev_page(self, request=None, queryset=None):
anos = Servico.objects.dates("data_ativacao", "year").values_list(
"data_ativacao__year", flat=True
)
if request is None:
params = QueryDict().copy()
params["ano"] = anos.last() - 1
else:
params = request.GET.copy()
params["ano"] = int(request.GET.get("ano", anos.last())) - 1
if params["ano"] not in anos:
return None
return params.urlencode()
def get_next_page(self, request=None, queryset=None):
if request is None:
return None
anos = Servico.objects.dates("data_ativacao", "year").values_list(
"data_ativacao__year", flat=True
)
params = request.GET.copy()
params["ano"] = int(request.GET.get("ano", anos.last())) + 1
if params["ano"] not in anos:
return None
return params.urlencode()
class ResumoSeit(Dashcard):
title = _("Serviços hospedados no Interlegis")
chart_type = Dashcard.TYPE_TABLE
label_name = _("Serviço")
model = Servico
def get_meses(self, request=None):
if request is None:
mes = datetime.date.today().month
ano = datetime.date.today().year
else:
mes = int(request.GET.get("mes", datetime.date.today().month))
ano = int(request.GET.get("ano", datetime.date.today().year))
mes_atual = datetime.date(year=ano, month=mes, day=1)
mes_anterior = (mes_atual - datetime.timedelta(days=1)).replace(day=1)
mes_proximo = mes_atual + datetime.timedelta(
days=calendar.monthrange(mes_atual.year, mes_atual.month)[1]
)
return mes_atual, mes_anterior, mes_proximo
def get_queryset(self, request):
mes_atual, mes_anterior, mes_proximo = self.get_meses(request)
return (
super()
.get_queryset(request)
.filter(
(
Q(data_ativacao__year=mes_atual.year)
& Q(Q(data_ativacao__month=mes_atual.month))
)
| (
Q(data_ativacao__year=mes_anterior.year)
& Q(Q(data_ativacao__month=mes_anterior.month))
)
)
.values(
servico=F("tipo_servico__nome"),
mes=TruncMonth("data_ativacao"),
)
.annotate(ativados=Count("casa_legislativa__id", distinct=True))
)
def get_labels(self, request, queryset=None):
mes_atual, mes_anterior, mes_proximo = self.get_meses(request)
return [
_("Total de casas atendidas"),
_(f"Novas casas em {mes_anterior:%m/%Y}"),
_(f"Novas casas em {mes_atual:%m/%Y}"),
]
def get_datasets(self, request, queryset=None):
mes_atual, mes_anterior, mes_proximo = self.get_meses(request)
if queryset is None:
queryset = self.get_queryset(request)
labels = self.get_labels(request, queryset)
datasets = {
s["servico"]: {
"total": s["total"],
mes_anterior: 0,
mes_atual: 0,
}
for s in Servico.objects.filter(data_desativacao=None)
.values(servico=F("tipo_servico__nome"))
.annotate(total=Count("casa_legislativa", distinct=True))
}
for data in queryset:
datasets[data["servico"]][data["mes"]] = data["ativados"]
return [
{
"label": label,
"data": {
labels[0]: data["total"],
labels[1]: data[mes_atual],
labels[2]: data[mes_anterior],
},
}
for label, data in datasets.items()
]
def get_prev_page(self, request=None, queryset=None):
mes_atual, mes_anterior, mes_proximo = self.get_meses(request)
params = QueryDict().copy()
params["ano"] = mes_anterior.year
params["mes"] = mes_anterior.month
return params.urlencode()
def get_next_page(self, request=None, queryset=None):
mes_atual, mes_anterior, mes_proximo = self.get_meses(request)
params = QueryDict().copy()
params["ano"] = mes_proximo.year
params["mes"] = mes_proximo.month
return params.urlencode()
class AtualizacaoServicos(Dashcard):
title = _("Frequência de atualização")
chart_type = Dashcard.TYPE_BAR
model = TipoServico
intervalos = [
("Na semana", 7),
("No mês", 30),
("No trimestre", 3 * 30),
("No semestre", 6 * 30),
("No ano", 365),
("Mais de ano", None),
]
def get_queryset(self, request):
counts = {}
hoje = timezone.localdate()
ate = hoje
for label, dias in self.intervalos:
if dias is not None:
de = hoje - datetime.timedelta(days=dias)
counts[slugify(label)] = Count(
"servico", Q(servico__data_ultimo_uso__range=(de, ate))
)
ate = de - datetime.timedelta(days=1)
else:
counts[slugify(label)] = Count(
"servico", Q(servico__data_ultimo_uso__lte=ate)
)
return (
super()
.get_queryset(request)
.exclude(string_pesquisa="")
.filter(servico__data_desativacao=None)
.annotate(**counts)
)
def get_labels(self, request, queryset=None):
return [label for label, *__ in self.intervalos]
def get_datasets(self, request, queryset=None):
if queryset is None:
queryset = self.get_queryset(request)
return [
{
"type": "bar",
"label": ts.sigla,
"data": {
label: getattr(ts, slugify(label))
for label, *__ in self.intervalos
},
"backgroundColor": getcolor(ts.sigla),
}
for ts in queryset
]
class UsoServicos(Dashcard):
title = _("Uso dos serviços")
chart_type = Dashcard.TYPE_BAR
model = TipoServico
filterset = UsoServicosFilter
label_field = "sigla"
def get_dataset_color(self, dataset_label):
return getcolor(dataset_label)
def apply_filters(self, request, queryset):
queryset = queryset.exclude(string_pesquisa="").filter(
servico__data_desativacao=None
)
return super().apply_filters(request, queryset)
def get_datasets(self, request, queryset=None):
if queryset is None:
queryset = self.get_queryset(request)
counts = {
f"{key}_count": Count(
"servico",
distinct=True,
filter=Q(servico__resultado_verificacao=key),
)
for key, *__ in Servico.RESULTADO_CHOICES
}
queryset = queryset.annotate(**counts)
return [
{
"label": label,
"data": {
r.sigla: getattr(r, f"{key}_count") for r in queryset
},
}
for key, label in Servico.RESULTADO_CHOICES
]
class ServicosAno(Dashcard):
title = _("Serviços hospedados por ano")
chart_type = Dashcard.TYPE_BAR
model = Servico
filterset = AnoServicoFilter
chart_options = {
"scales": {"x": {"stacked": True}, "y": {"stacked": True}},
"plugins": {"tooltip": {"mode": "index"}},
}
def get_dataset_color(self, dataset_label):
return getcolor(dataset_label)
def apply_filters(self, request, queryset):
return (
super()
.apply_filters(request, queryset)
.filter(hospedagem_interlegis=True)
)
def get_queryset(self, request):
qs = super().get_queryset(request)
if request.GET.get("ano", None):
# Usuário informou um ano, então vamos mostrar os meses daquele ano
qs = (
qs.order_by("data_ativacao__month", "tipo_servico__sigla")
.values(
label=F("data_ativacao__month"),
sigla=F("tipo_servico__sigla"),
)
.annotate(total=Count("id"))
)
else:
qs = (
qs.order_by("data_ativacao__year", "tipo_servico__sigla")
.values(
label=F("data_ativacao__year"),
sigla=F("tipo_servico__sigla"),
)
.annotate(total=Count("id"))
)
return qs
def get_labels(self, request, queryset=None):
if queryset is None:
return list(
Servico.objects.filter(hospedagem_interlegis=True)
.order_by("data_ativacao__year")
.values_list("data_ativacao__year")
.distinct("data_ativacao__year")
)
if request.GET.get("ano", None):
lang = to_locale(get_language()) + ".UTF-8"
locale.setlocale(locale.LC_ALL, lang)
map_function = lambda x: _(calendar.month_abbr[x])
else:
map_function = str
labels = list({r["label"] for r in queryset})
labels.sort()
labels = list(map(map_function, labels))
return labels
def get_datasets(self, request, queryset=None):
if queryset is None:
queryset = self.get_queryset(request)
if request.GET.get("ano", None):
lang = to_locale(get_language()) + ".UTF-8"
locale.setlocale(locale.LC_ALL, lang)
map_function = lambda x: _(calendar.month_abbr[x])
else:
map_function = str
labels = self.get_labels(request, queryset)
series = {}
for d in queryset:
sigla = d["sigla"]
label = map_function(d["label"])
if sigla not in series:
series[sigla] = dict(zip(labels, [0] * len(labels)))
series[sigla][label] = d["total"]
return [
{
"label": s,
"data": series[s],
"backgroundColor": getcolor(s),
}
for s in series
]
Loading…
Cancel
Save