From a66cc529b6aaeeb59973e1aa71d08a3c38c28130 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ses=C3=B3stris=20Vieira?= Date: Tue, 5 Sep 2023 08:26:45 -0300 Subject: [PATCH] =?UTF-8?q?Ajustes=20solicitados=20na=20homologa=C3=A7?= =?UTF-8?q?=C3=A3o=20do=20gertik=20#160523?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sigi/apps/eventos/admin.py | 114 +++++++++++++++++++++++--------- sigi/apps/eventos/models.py | 55 ++++++++++++++- sigi/static/css/change_form.css | 8 +++ 3 files changed, 145 insertions(+), 32 deletions(-) diff --git a/sigi/apps/eventos/admin.py b/sigi/apps/eventos/admin.py index ddcbcf2..fb65a57 100644 --- a/sigi/apps/eventos/admin.py +++ b/sigi/apps/eventos/admin.py @@ -35,7 +35,11 @@ from sigi.apps.eventos.models import ( from sigi.apps.eventos.forms import EventoAdminForm, SelecionaModeloForm from sigi.apps.utils import abreviatura from sigi.apps.utils.filters import EmptyFilter, DateRangeFilter -from sigi.apps.utils.mixins import CartExportMixin, ValueLabeledResource +from sigi.apps.utils.mixins import ( + CartExportMixin, + LabeledResourse, + ValueLabeledResource, +) class SolicitacaoStatusFilter(admin.SimpleListFilter): @@ -84,6 +88,50 @@ class SolicitacaoStatusFilter(admin.SimpleListFilter): return queryset +class SolicitacaoResource(LabeledResourse): + status = Field(column_name="status") + oficinas = Field(column_name="oficinas solicitadas") + oficinas_uf = Field(column_name="número de oficinas realizadas na UF") + + class Meta: + model = Solicitacao + fields = ( + "num_processo", + "status", + "senador", + "data_pedido", + "data_recebido_coperi", + "oficinas", + "casa__nome", + "casa__municipio__nome", + "casa__municipio__uf__nome", + "casa__municipio__uf__regiao", + "casa__municipio__populacao", + "oficinas_uf", + "estimativa_casas", + "estimativa_servidores", + ) + export_order = fields + + def dehydrate_status(self, obj): + return obj.get_status() + + def dehydrate_oficinas(self, obj): + return ", ".join( + [i.tipo_evento.sigla for i in obj.itemsolicitado_set.all()] + ) + + def dehydrate_oficinas_uf(sekf, obj): + return Evento.objects.filter( + status__in=[Evento.STATUS_CONFIRMADO, Evento.STATUS_REALIZADO], + casa_anfitria__municipio__uf=obj.casa.municipio.uf, + data_inicio__year__gte=timezone.localdate().year - 2, + ).count() + + def dehydrate_casa__municipio__uf__regiao(self, obj): + return obj.casa.municipio.uf.get_regiao_display() + + class EventoResource(ValueLabeledResource): # categoria_evento = Field(column_name="tipo_evento__categoria") # status = Field(column_name="status") @@ -178,9 +226,10 @@ class ItemSolicitadoInline(admin.StackedInline): "status", "justificativa", "servidor", + "data_analise", "evento", ) - readonly_fields = ("servidor", "evento") + readonly_fields = ("servidor", "data_analise", "evento") extra = 1 autocomplete_fields = ("tipo_evento",) @@ -194,10 +243,12 @@ class TipoEventoAdmin(admin.ModelAdmin): @admin.register(Solicitacao) -class SolicitacaoAdmin(admin.ModelAdmin): +class SolicitacaoAdmin(CartExportMixin, admin.ModelAdmin): + resource_class = SolicitacaoResource list_display = ( - "num_processo", "casa", + "get_sigad_url", + "get_status", "senador", "data_pedido", "data_recebido_coperi", @@ -209,7 +260,6 @@ class SolicitacaoAdmin(admin.ModelAdmin): "get_oficinas_uf", "estimativa_casas", "estimativa_servidores", - "get_status", ) list_filter = ( "casa__municipio__uf", @@ -219,6 +269,7 @@ class SolicitacaoAdmin(admin.ModelAdmin): SolicitacaoStatusFilter, ) list_select_related = ["casa", "casa__municipio", "casa__municipio__uf"] + list_display_links = ("casa",) search_fields = ( "casa__search_text", "casa__municipio__search_text", @@ -237,6 +288,8 @@ class SolicitacaoAdmin(admin.ModelAdmin): else: servidor = None + agora = timezone.localtime() + for item in instances: if ( item.status == ItemSolicitado.STATUS_SOLICITADO @@ -253,11 +306,14 @@ class SolicitacaoAdmin(admin.ModelAdmin): ) elif item.status == ItemSolicitado.STATUS_AUTORIZADO: item.servidor = servidor + item.data_analise = agora if item.evento is None: item.evento = Evento( tipo_evento=item.tipo_evento, nome=_( - f"{item.tipo_evento} em {item.solicitacao.casa}" + f"{item.tipo_evento} em {item.solicitacao.casa}"[ + :100 + ] ), descricao=_( f"{item.tipo_evento} em {item.solicitacao.casa}" @@ -298,27 +354,30 @@ class SolicitacaoAdmin(admin.ModelAdmin): ), messages.INFO, ) - elif ItemSolicitado.STATUS_REJEITADO and item.evento is not None: - item.evento.status = Evento.STATUS_CANCELADO - item.evento.observacao += _( - f"\nCancelado por {servidor} com a justificativa: {item.justificativa}" - ) - item.evento.data_cancelamento = timezone.localdate() - item.evento.motivo_cancelamento = _( - f"\nCancelado por {servidor} com a justificativa: {item.justificativa}" - ) - self.message_user( - request, - _( - f"Status do evento {item.evento} alterado para " - f"{item.evento.get_status_display()}" - ), - messages.INFO, - ) + elif item.status == ItemSolicitado.STATUS_REJEITADO: + item.servidor = servidor + item.data_analise = agora + if item.evento is not None: + item.evento.status = Evento.STATUS_CANCELADO + item.evento.observacao += _( + f"\nCancelado por {servidor} com a justificativa: {item.justificativa}" + ) + item.evento.data_cancelamento = timezone.localdate() + item.evento.motivo_cancelamento = _( + f"\nCancelado por {servidor} com a justificativa: {item.justificativa}" + ) + self.message_user( + request, + _( + f"Status do evento {item.evento} alterado para " + f"{item.evento.get_status_display()}" + ), + messages.INFO, + ) if item.evento: item.evento.tipo_evento = item.tipo_evento item.evento.nome = _( - f"{item.tipo_evento} em {item.solicitacao.casa}" + f"{item.tipo_evento} em {item.solicitacao.casa}"[:100] ) item.evento.descricao = _( f"{item.tipo_evento} em {item.solicitacao.casa}" @@ -374,13 +433,6 @@ class SolicitacaoAdmin(admin.ModelAdmin): def get_populacao(self, obj): return obj.casa.municipio.populacao - @admin.display(description=_("Oficinas atendidas/confirmadas na UF")) - def get_oficinas_uf(self, obj): - return Evento.objects.filter( - status__in=[Evento.STATUS_CONFIRMADO, Evento.STATUS_REALIZADO], - casa_anfitria__municipio__uf=obj.casa.municipio.uf, - ).count() - @admin.register(Funcao) class FuncaoAdmin(admin.ModelAdmin): diff --git a/sigi/apps/eventos/models.py b/sigi/apps/eventos/models.py index ec54762..b225d9a 100644 --- a/sigi/apps/eventos/models.py +++ b/sigi/apps/eventos/models.py @@ -5,9 +5,10 @@ from django.contrib import admin from django.core.validators import RegexValidator from django.core.exceptions import ValidationError from django.db import models -from django.db.models import Sum, Count +from django.db.models import Sum, Count, Q from django.urls import reverse from django.utils import timezone +from django.utils.safestring import mark_safe from django.utils.translation import gettext as _ from sigi.apps.casas.models import Orgao, Servidor from sigi.apps.contatos.models import Municipio @@ -142,6 +143,52 @@ class Solicitacao(models.Model): else: return _("Concluído") + @admin.display(description=_("SIGAD"), ordering="num_processo") + def get_sigad_url(self): + m = re.match( + "(?P00100|00200)\.(?P\d{6})/(?P" + "\d{4})-\d{2}", + self.num_processo, + ) + if m: + return mark_safe( + ( + '{processo}' + ).format(processo=self.num_processo, **m.groupdict()) + ) + return self.num_processo + + @admin.display(description=_("Oficinas atendidas/confirmadas na UF")) + def get_oficinas_uf(self): + ano_corrente = timezone.localdate().year + counters = Evento.objects.filter( + status__in=[Evento.STATUS_CONFIRMADO, Evento.STATUS_REALIZADO], + casa_anfitria__municipio__uf=self.casa.municipio.uf, + ).aggregate( + total=Count("id"), + no_ano=Count("id", filter=Q(data_inicio__year=ano_corrente)), + dois_anos=Count( + "id", + filter=Q(data_inicio__year__gte=ano_corrente - 1), + ), + tres_anos=Count( + "id", + filter=Q(data_inicio__year__gte=ano_corrente - 2), + ), + ) + return _( + ( + "Total: {total}, no ano corrente: {no_ano}, " + "nos dois últimos anos: {dois_anos}, " + "nos três últimos anos: {tres_anos}" + ).format(**counters) + ) + class ItemSolicitado(models.Model): STATUS_SOLICITADO = "S" @@ -187,6 +234,12 @@ class ItemSolicitado(models.Model): null=True, editable=False, ) + data_analise = models.DateTimeField( + _("data de autorização/rejeição"), + blank=True, + null=True, + editable=False, + ) justificativa = models.TextField( verbose_name=_("Justificativa"), blank=True ) diff --git a/sigi/static/css/change_form.css b/sigi/static/css/change_form.css index 1bb3777..5e3076d 100644 --- a/sigi/static/css/change_form.css +++ b/sigi/static/css/change_form.css @@ -14,4 +14,12 @@ .sigi-object-tools>ul>li>a { color: var(--object-tools-fg); border-color: var(--object-tools-fg); +} + +.input-field>label { + position: relative; +} + +.readonly-label { + height: unset; } \ No newline at end of file