diff --git a/sigi/apps/casas/dashboards.py b/sigi/apps/casas/dashboards.py index b0027fd..8dfb1b0 100644 --- a/sigi/apps/casas/dashboards.py +++ b/sigi/apps/casas/dashboards.py @@ -1,3 +1,4 @@ +from django.urls import reverse import django_filters from dashboard import Dashcard, getcolor from django.db.models import Count @@ -32,6 +33,16 @@ class CasasGerente(Dashcard): .exclude(casas_que_gerencia=None) ) + def get_view_link(self, x_axis, y_axis, value): + try: + s = Servidor.objects.exclude(casas_que_gerencia=None).get( + nome_completo=x_axis + ) + except: + return None + base_url = reverse("admin:casas_orgao_changelist") + return f"{base_url}?gerentes_interlegis__id__exact={s.id}" + class PerformanceCarteira(Dashcard): chart_type = Dashcard.TYPE_DOUGHNUT @@ -42,20 +53,9 @@ class PerformanceCarteira(Dashcard): 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_queryset(self, request): + queryset = super().get_queryset(request) + return queryset.exclude(gerentes_interlegis=None) def get_labels(self, request, queryset=None): return [self.LABEL_USAM, self.LABEL_NAO_USAM] @@ -63,11 +63,23 @@ class PerformanceCarteira(Dashcard): def get_datasets(self, request, queryset=None): if queryset is None: queryset = self.get_queryset(request) + base_url = reverse("admin:casas_orgao_changelist") + filter = self.get_filter(request.GET, queryset) + filter.is_valid() + servidor_id = filter.data.get("servidor") + if servidor_id: + servidor_qs = f"&gerentes_interlegis__id__exact={servidor_id}" + else: + servidor_qs = "" return [ { "data": [ queryset.exclude(servico=None).count(), queryset.filter(servico=None).count(), - ] + ], + "links": [ + f"{base_url}?tipo__legislativo__exact=1&servico=CS{servidor_qs}", + f"{base_url}?tipo__legislativo__exact=1&servico=SS{servidor_qs}", + ], } ] diff --git a/sigi/apps/casas/filters.py b/sigi/apps/casas/filters.py index d92c24e..cbf05ca 100644 --- a/sigi/apps/casas/filters.py +++ b/sigi/apps/casas/filters.py @@ -2,7 +2,7 @@ from django.contrib import admin from django.utils.translation import gettext as _ from sigi.apps.servidores.models import Servidor from sigi.apps.convenios.models import Projeto -from sigi.apps.servicos.models import TipoServico +from sigi.apps.servicos.models import TipoServico, Servico class GerentesInterlegisFilter(admin.filters.RelatedFieldListFilter): @@ -36,7 +36,14 @@ class ServicoFilter(admin.SimpleListFilter): ) elif self.value() == "CR": queryset = queryset.exclude( - servico__tipo_servico__modo="H" + id__in=( + Servico.objects.filter( + data_desativacao__isnull=True, + tipo_servico__modo="H", + ) + .distinct("casa_legislativa_id") + .values_list("casa_legislativa_id", flat=True) + ) ).exclude(servico=None) elif self.value() == "CH": queryset = queryset.filter( diff --git a/sigi/apps/convenios/dashboards.py b/sigi/apps/convenios/dashboards.py index 7a9a484..51c0dd5 100644 --- a/sigi/apps/convenios/dashboards.py +++ b/sigi/apps/convenios/dashboards.py @@ -1,3 +1,4 @@ +from django.urls import reverse import numpy as np import pandas as pd import django_filters @@ -171,24 +172,28 @@ class ConvenioServico(Dashcard): if queryset is None: queryset = self.get_queryset(request) values = dict(queryset) + base_url = reverse("admin:casas_orgao_changelist") datasets = [ { "label": _( "Casas sem convenio que utilizam algum serviço de hospedagem" ), "data": [values["hospedagem"]], + "links": [f"{base_url}?convenio=SC&servico=CH"], }, { "label": _( "Casas sem convenio que utilizam somente serviço de registro" ), "data": [values["total"] - values["hospedagem"]], + "links": [f"{base_url}?convenio=SC&servico=CR"], }, { "label": _( "Casas sem convenio que utilizam algum serviço de registro e/ou hospedagem" ), "data": [values["total"]], + "links": [f"{base_url}?convenio=SC&servico=CS"], }, ] return datasets diff --git a/sigi/apps/convenios/templates/convenios/dashboard/resumo_convenios.html b/sigi/apps/convenios/templates/convenios/dashboard/resumo_convenios.html index 27c977e..fae4524 100644 --- a/sigi/apps/convenios/templates/convenios/dashboard/resumo_convenios.html +++ b/sigi/apps/convenios/templates/convenios/dashboard/resumo_convenios.html @@ -13,7 +13,7 @@ {{ label }} {% for value in values %} - {{ value }} + {{ value }} {% endfor %} {% endfor %} @@ -24,7 +24,7 @@ {% for label, value in datasets.totais %} {{ label }} - {{ value }} + {{ value }} {% endfor %} \ No newline at end of file diff --git a/sigi/apps/eventos/dashboards.py b/sigi/apps/eventos/dashboards.py index a600108..a25452e 100644 --- a/sigi/apps/eventos/dashboards.py +++ b/sigi/apps/eventos/dashboards.py @@ -1,5 +1,6 @@ import calendar import datetime +from django.urls import reverse import django_filters from dashboard import Dashcard, getcolor from django.db.models import F, Count, Q @@ -32,9 +33,31 @@ class AnoFilterset(django_filters.FilterSet): fields = ["ano"] +class AnoCategoriaFilterset(django_filters.FilterSet): + ano = django_filters.ChoiceFilter( + field_name="data_inicio", + lookup_expr="year", + label=_("Ano"), + distinct=True, + choices=get_anos, + ) + categoria = django_filters.ChoiceFilter( + field_name="tipo_evento__categoria", + lookup_expr="exact", + label=_("Categoria"), + distinct=True, + choices=TipoEvento.CATEGORIA_CHOICES, + ) + + class Meta: + model = Evento + fields = ["ano", "categoria"] + + class EventosStatus(Dashcard): chart_type = Dashcard.TYPE_DOUGHNUT title = _("Eventos por status") + filterset = AnoCategoriaFilterset model = Evento label_field = ("status", F, lambda s: dict(Evento.STATUS_CHOICES)[s]) datasets = [{"data_field": ("id", Count)}] @@ -42,6 +65,19 @@ class EventosStatus(Dashcard): def get_dataset_color(self, dataset_label): return getcolor(dataset_label) + def get_view_link(self, x_axis, y_axis, value): + base_url = reverse("admin:eventos_evento_changelist") + querystrs = [f"status__exact={x_axis}"] + filter = self.get_filter(self.request.GET) + if filter.is_valid(): + ano = filter.data.get("ano") + categoria = filter.data.get("categoria") + if ano: + querystrs.append(f"data_inicio__year={ano}") + if categoria: + querystrs.append(f"tipo_evento__categoria__exact={categoria}") + return base_url + "?" + "&".join(querystrs) + class EventosAno(Dashcard): chart_type = Dashcard.TYPE_LINE @@ -98,10 +134,7 @@ class EventosAno(Dashcard): def get_queryset(self, request): counts = self.get_counters(request) return ( - super() - .get_queryset(request) - .values("categoria") - .annotate(**counts) + super().get_queryset(request).values("categoria").annotate(**counts) ) def get_dataset_color(self, dataset_label): @@ -116,6 +149,10 @@ class EventosAno(Dashcard): { "label": categorias[r["categoria"]], "data": [r[c] for c in counters], + "links": [ + self.get_view_link(r["categoria"], c, r[c]) + for c in counters + ], "backgroundColor": self.get_dataset_color(r["categoria"]), } for r in queryset @@ -129,6 +166,15 @@ class EventosAno(Dashcard): mes = self.get_meses(request)[-3] return f"ano={mes.year}&mes={mes.month}" + def get_view_link(self, x_axis, y_axis, value): + base_link = reverse("admin:eventos_evento_changelist") + return ( + f"{base_link}?status__exact={Evento.STATUS_REALIZADO}" + f"&tipo_evento__categoria__exact={x_axis}" + f'&data_inicio__year={y_axis.split("_")[1]}' + f'&data_inicio__month={y_axis.split("_")[2]}' + ) + class EventosCategoria(Dashcard): chart_type = Dashcard.TYPE_DOUGHNUT @@ -151,3 +197,16 @@ class EventosCategoria(Dashcard): def get_dataset_color(self, dataset_label): return getcolor(dataset_label) + + def get_view_link(self, x_axis, y_axis, value): + base_url = reverse("admin:eventos_evento_changelist") + q_ano = "" + filter = self.get_filter(self.request.GET) + if filter.is_valid(): + ano = filter.data.get("ano") + if ano: + q_ano = f"data_inicio__year={ano}&" + return ( + f"{base_url}?{q_ano}status__exact={Evento.STATUS_REALIZADO}" + f"&tipo_evento__categoria__exact={x_axis}" + ) diff --git a/sigi/apps/servicos/dashboards.py b/sigi/apps/servicos/dashboards.py index 8cb8bea..47bf19d 100644 --- a/sigi/apps/servicos/dashboards.py +++ b/sigi/apps/servicos/dashboards.py @@ -6,6 +6,7 @@ from random import choice, randint, seed from django.db.models import Count, F, Q from django.db.models.functions import TruncMonth from django.http import QueryDict +from django.urls import reverse from django.utils import timezone from django.utils.text import slugify from django.utils.translation import gettext as _, to_locale, get_language @@ -82,6 +83,18 @@ class Sazonalidade(Dashcard): ) return qs.filter(data_ativacao__year=ano) + def get_view_link(self, x_axis, y_axis, value): + try: + tipo = TipoServico.objects.get(sigla=x_axis) + except TipoServico.DoesNotExist: + return None + mes, ano = y_axis.split("/") + base_url = reverse("admin:servicos_servico_changelist") + return ( + f"{base_url}?tipo_servico__id__exact={tipo.pk}" + f"&data_ativacao__year={ano}&data_ativacao__month={mes}" + ) + def get_prev_page(self, request=None, queryset=None): anos = Servico.objects.dates("data_ativacao", "year").values_list( "data_ativacao__year", flat=True @@ -170,9 +183,13 @@ class ResumoSeit(Dashcard): "total": s["total"], mes_anterior: 0, mes_atual: 0, + "servico_id": s["servico_id"], } for s in Servico.objects.filter(data_desativacao=None) - .values(servico=F("tipo_servico__nome")) + .values( + servico=F("tipo_servico__nome"), + servico_id=F("tipo_servico__id"), + ) .annotate(total=Count("casa_legislativa", distinct=True)) } for data in queryset: @@ -186,10 +203,39 @@ class ResumoSeit(Dashcard): labels[1]: data[mes_atual], labels[2]: data[mes_anterior], }, + "links": [ + self.get_view_link( + "total", data["servico_id"], data["total"] + ), + self.get_view_link( + mes_atual, data["servico_id"], data[mes_atual] + ), + self.get_view_link( + mes_anterior, data["servico_id"], data[mes_anterior] + ), + ], } for label, data in datasets.items() ] + def get_view_link(self, x_axis, y_axis, value): + if value == 0: + return None + base_url = reverse("admin:servicos_servico_changelist") + qstrs = [ + "data_desativacao__isnull=True", + f"tipo_servico__id__exact={y_axis}", + ] + if not isinstance(x_axis, str): + qstrs.extend( + [ + f"data_ativacao__year={x_axis.year}", + f"data_ativacao__month={x_axis.month}", + ] + ) + qstrs = "&".join(qstrs) + return f"{base_url}?{qstrs}" + def get_prev_page(self, request=None, queryset=None): mes_atual, mes_anterior, mes_proximo = self.get_meses(request) params = QueryDict().copy() @@ -211,12 +257,13 @@ class AtualizacaoServicos(Dashcard): 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), + ("Na semana", 7, "updated"), + ("No mês", 30, "week"), + ("No trimestre", 90, "month"), + ("No semestre", 182, "quarter"), + ("No ano", 365, "semester"), + ("Mais de ano", 0, "year"), + ("Erro na verificação", None, "err"), ] def get_queryset(self, request): @@ -224,17 +271,21 @@ class AtualizacaoServicos(Dashcard): hoje = timezone.localdate() ate = hoje - for label, dias in self.intervalos: - if dias is not None: + for label, dias, *__ in self.intervalos: + if dias is None: + counts[slugify(label)] = Count( + "servico", ~Q(servico__erro_atualizacao="") + ) + elif dias > 0: 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) - ) + ate = de - datetime.timedelta(days=1) + else: + counts[slugify(label)] = Count( + "servico", Q(servico__data_ultimo_uso__lte=ate) + ) return ( super() @@ -259,10 +310,25 @@ class AtualizacaoServicos(Dashcard): for label, *__ in self.intervalos }, "backgroundColor": getcolor(ts.sigla), + "links": [ + self.get_view_link( + ts.id, atualizacao, getattr(ts, slugify(label)) + ) + for label, dias, atualizacao in self.intervalos + ], } for ts in queryset ] + def get_view_link(self, x_axis, y_axis, value): + print(x_axis, y_axis, value) + base_url = reverse("admin:servicos_servico_changelist") + return ( + f"{base_url}?data_desativacao__isnull=True" + f"&atualizacao={y_axis}" + f"&tipo_servico__id__exact={x_axis}" + ) + class UsoServicos(Dashcard): title = _("Uso dos serviços") @@ -299,10 +365,22 @@ class UsoServicos(Dashcard): { "label": label, "data": {r.sigla: getattr(r, f"{key}_count") for r in queryset}, + "links": [ + self.get_view_link(r.id, key, getattr(r, f"{key}_count")) + for r in queryset + ], } for key, label in Servico.RESULTADO_CHOICES ] + def get_view_link(self, x_axis, y_axis, value): + base_url = reverse("admin:servicos_servico_changelist") + return ( + f"{base_url}?data_desativacao__isnull=True" + f"&resultado_verificacao__exact={y_axis}" + f"&tipo_servico__id__exact={x_axis}" + ) + class ServicosAno(Dashcard): title = _("Serviços hospedados por ano")