Browse Source

Replicando alterações emergenciais do SIGI legado

pull/159/head
Sesostris Vieira 3 years ago
parent
commit
88c3365f0e
  1. 18
      sigi/apps/convenios/migrations/0020_gescon_orgaos_gestores.py
  2. 193
      sigi/apps/convenios/models.py
  3. 63
      sigi/apps/eventos/admin.py
  4. 8
      sigi/apps/eventos/forms.py
  5. 33
      sigi/apps/eventos/migrations/0018_evento_data_pedido_evento_num_processo_and_more.py
  6. 18
      sigi/apps/eventos/migrations/0019_alter_evento_status.py
  7. 35
      sigi/apps/eventos/models.py
  8. 1
      sigi/apps/eventos/templates/admin/eventos/evento/change_list.html
  9. 68
      sigi/apps/utils/filters.py
  10. 14
      sigi/apps/utils/mixins.py

18
sigi/apps/convenios/migrations/0020_gescon_orgaos_gestores.py

@ -0,0 +1,18 @@
# Generated by Django 4.0.3 on 2022-04-20 13:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('convenios', '0019_alter_anexo_arquivo_alter_anexo_descricao_and_more'),
]
operations = [
migrations.AddField(
model_name='gescon',
name='orgaos_gestores',
field=models.TextField(default='SCCO', help_text='Siglas de órgãos gestores que devem aparecer no campoORGAOSGESTORESTITULARES<ul><li>Informe um sigla por linha.</li><li>Ocorrendo qualquer uma das siglas, o contrato será importado.</li></ul>', verbose_name='Órgãos gestores'),
),
]

193
sigi/apps/convenios/models.py

@ -381,6 +381,15 @@ class Gescon(models.Model):
"<li>Ocorrendo qualquer uma das palavras, o contrato será " "<li>Ocorrendo qualquer uma das palavras, o contrato será "
"importado.</li></ul>") "importado.</li></ul>")
) )
orgaos_gestores = models.TextField(
_("Órgãos gestores"),
default="SCCO",
help_text=_("Siglas de órgãos gestores que devem aparecer no campo"
"ORGAOSGESTORESTITULARES"
"<ul><li>Informe um sigla por linha.</li>"
"<li>Ocorrendo qualquer uma das siglas, o contrato será "
"importado.</li></ul>")
)
email = models.EmailField( email = models.EmailField(
_("E-mail"), _("E-mail"),
help_text=_("Caixa de e-mail para onde o relatório diário de " help_text=_("Caixa de e-mail para onde o relatório diário de "
@ -429,16 +438,13 @@ class Gescon(models.Model):
def importa_contratos(self): def importa_contratos(self):
self.ultima_importacao = "" self.ultima_importacao = ""
self.add_message( self.add_message(
_("Importação iniciada em {:%d/%m/%Y %H:%M:%S}\n" _(f"Importação iniciada em {datetime.now():%d/%m/%Y %H:%M:%S}\n"
"==========================================\n").format( "==========================================================\n")
datetime.now()
)
) )
if self.palavras == "": if self.palavras == "" or self.orgaos_gestores == "":
self.add_message(_("Nenhuma palavra de pesquisa definida - " self.add_message(_("Nenhuma palavra de pesquisa ou orgãos "
"processo abortado."), True) "gestores definidos - processo abortado."), True)
return
if self.subespecies == "": if self.subespecies == "":
self.add_message(_("Nenhuma subespécie definida - processo " self.add_message(_("Nenhuma subespécie definida - processo "
@ -457,11 +463,14 @@ class Gescon(models.Model):
return return
palavras = self.palavras.split() palavras = self.palavras.split()
orgaos = self.orgaos_gestores.split()
subespecies = {tuple(s.split("=")) for s in self.subespecies.split()} subespecies = {tuple(s.split("=")) for s in self.subespecies.split()}
lista_cnpj = {re.sub("[^\d]", "", o.cnpj).zfill(14): o
for o in Orgao.objects.exclude(cnpj="")
if re.sub("[^\d]", "", o.cnpj) != ''}
for sigla_gescon, sigla_sigi in subespecies: for sigla_gescon, sigla_sigi in subespecies:
self.add_message(_("\nImportando subespécie {s}".format( self.add_message(_(f"\nImportando subespécie {sigla_gescon}"))
s=sigla_gescon)))
url = self.url_gescon.format(s=sigla_gescon) url = self.url_gescon.format(s=sigla_gescon)
projeto = Projeto.objects.get(sigla=sigla_sigi) projeto = Projeto.objects.get(sigla=sigla_sigi)
@ -469,42 +478,34 @@ class Gescon(models.Model):
try: try:
response = requests.get(url, verify=False) response = requests.get(url, verify=False)
except Exception as e: except Exception as e:
self.add_message( self.add_message(_(f"\tErro ao acessar {url}: {e.message}"))
_("\tErro ao acessar {url}: {errmsg}").format(
url=url,
errmsg=e.message.decode("utf8")
)
)
continue continue
if not response.ok: if not response.ok:
self.add_message( self.add_message(
_("\tErro ao acessar {url}: {reason}").format( _(f"\tErro ao acessar {url}: {response.reason}")
url=url,
reason=response.reason
)
) )
continue continue
if not 'application/json' in response.headers.get('Content-Type'): if not 'application/json' in response.headers.get('Content-Type'):
self.add_message(_("\tResultado da consulta à {url} não " self.add_message(_(f"\tResultado da consulta à {url} não "
"retornou dados em formato json").format( "retornou dados em formato json"))
url=url
)
)
continue continue
contratos = response.json() contratos = response.json()
# Pegar só os contratos que possuem alguma das palavras-chave # Pegar só os contratos que possuem alguma das palavras-chave
nossos = [c for c in contratos nossos = [
if any(palavra in c['objeto'] for palavra in palavras)] c for c in contratos
if any(palavra in c['objeto'] for palavra in palavras) or
any(orgao in c['orgaosGestoresTitulares']
for orgao in orgaos
if c['orgaosGestoresTitulares'] is not None)
]
self.add_message( self.add_message(
_("\t{count} contratos encontrados no Gescon").format( _(f"\t{len(nossos)} contratos encontrados no Gescon")
count=len(nossos)
)
) )
novos = 0 novos = 0
@ -514,17 +515,14 @@ class Gescon(models.Model):
for contrato in nossos: for contrato in nossos:
numero = contrato['numero'].zfill(8) numero = contrato['numero'].zfill(8)
numero = "{}/{}".format(numero[:4], numero[4:]) numero = f"{numero[:4]}/{numero[4:]}"
sigad = contrato['processo'].zfill(17) sigad = contrato['processo'].zfill(17)
sigad = "{}.{}/{}-{}".format(sigad[:5], sigad[5:11], sigad = f"{sigad[:5]}.{sigad[5:11]}/{sigad[11:15]}-{sigad[15:]}"
sigad[11:15], sigad[15:])
if contrato['cnpjCpfFornecedor']: if contrato['cnpjCpfFornecedor']:
cnpj = contrato['cnpjCpfFornecedor'].zfill(14) cnpj = contrato['cnpjCpfFornecedor'].zfill(14)
cnpj = "{}.{}.{}/{}-{}".format(cnpj[:2], cnpj[2:5], cnpj_masked = (f"{cnpj[:2]}.{cnpj[2:5]}.{cnpj[5:8]}/"
cnpj[5:8], cnpj[8:12], f"{cnpj[8:12]}-{cnpj[12:]}")
cnpj[12:])
else: else:
cnpj = None cnpj = None
@ -541,8 +539,8 @@ class Gescon(models.Model):
if (cnpj is None) and (nome is None): if (cnpj is None) and (nome is None):
self.add_message( self.add_message(
_("\tO contrato {numero} no Gescon não informa o CNPJ " _(f"\tO contrato {numero} no Gescon não informa o CNPJ"
"nem o nome do órgão.").format(numero=numero) "nem o nome do órgão.")
) )
erros += 1 erros += 1
continue continue
@ -550,13 +548,16 @@ class Gescon(models.Model):
orgao = None orgao = None
if cnpj is not None: if cnpj is not None:
try: if cnpj in lista_cnpj:
orgao = Orgao.objects.get(cnpj=cnpj) orgao = lista_cnpj[cnpj]
except ( else:
Orgao.DoesNotExist, try:
Orgao.MultipleObjectsReturned) as e: orgao = Orgao.objects.get(cnpj=cnpj_masked)
orgao = None except (
pass Orgao.DoesNotExist,
Orgao.MultipleObjectsReturned) as e:
orgao = None
pass
if (orgao is None) and (nome is not None): if (orgao is None) and (nome is not None):
try: try:
@ -569,14 +570,11 @@ class Gescon(models.Model):
if orgao is None: if orgao is None:
self.add_message( self.add_message(
_("\tÓrgão não encontrado no SIGI ou mais de um órgão" _(f"\tÓrgão não encontrado no SIGI ou mais de um órgão"
"encontrado com o mesmo CNPJ ou nome. Favor " f"encontrado com o mesmo CNPJ ou nome. Favor "
"regularizar o cadastro: CNPJ: {cnpj}, " f"regularizar o cadastro: "
"Nome: {nome}".format( f"CNPJ: {contrato['cnpjCpfFornecedor']}, "
cnpj=contrato['cnpjCpfFornecedor'], f"Nome: {contrato['nomeFornecedor']}")
nome=contrato['nomeFornecedor']
)
)
) )
erros += 1 erros += 1
continue continue
@ -628,15 +626,9 @@ class Gescon(models.Model):
convenio.observacao_gescon = '' convenio.observacao_gescon = ''
if convenio.casa_legislativa != orgao: if convenio.casa_legislativa != orgao:
self.add_message( self.add_message(
_("\tO órgao no convênio {url} diverge do que " _(f"\tO órgao no convênio {convenio.id} diverge do "
"consta no Gescon ({cnpj}, {nome})").format( f"que consta no Gescon ({cnpj}, "
url=reverse('admin:%s_%s_change' % ( f"{contrato['nomeFornecedor']})")
convenio._meta.app_label,
convenio._meta.model_name),
args=[convenio.id]),
cnpj=cnpj,
nome=contrato['nomeFornecedor']
)
) )
convenio.observacao_gescon = _( convenio.observacao_gescon = _(
'ERRO: Órgão diverge do Gescon. Não atualizado!' 'ERRO: Órgão diverge do Gescon. Não atualizado!'
@ -647,19 +639,12 @@ class Gescon(models.Model):
if convenio.num_processo_sf != sigad: if convenio.num_processo_sf != sigad:
self.add_message( self.add_message(
_("\tO contrato Gescon nº {numero} corresponde" _(f"\tO contrato Gescon nº {numero} corresponde"
" ao convênio SIGI {url}, mas o NUP sigad " f" ao convênio SIGI {convenio.id}, mas o NUP "
"diverge (Gescon: {sigad_gescon}, " f"sigad diverge (Gescon: {sigad}, "
"SIGI: {sigad_sigi}). CORRIGIDO!").format( f"SIGI: {convenio.num_processo_sf}). "
numero=numero, "CORRIGIDO!")
url=reverse('admin:%s_%s_change' % ( )
convenio._meta.app_label,
convenio._meta.model_name),
args=[convenio.id]),
sigad_gescon=sigad,
sigad_sigi=convenio.num_processo_sf
)
)
convenio.num_processo_sf = sigad convenio.num_processo_sf = sigad
convenio.observacao_gescon += _( convenio.observacao_gescon += _(
"Número do SIGAD atualizado.\n" "Número do SIGAD atualizado.\n"
@ -668,19 +653,11 @@ class Gescon(models.Model):
if convenio.num_convenio != numero: if convenio.num_convenio != numero:
self.add_message( self.add_message(
_("\tO contrato Gescon ID {id} corresponde ao " _(f"\tO contrato Gescon ID {contrato['id']} "
"convênio SIGI {url}, mas o número do convênio" f"corresponde ao convênio SIGI {convenio.id}, "
" diverge (Gescon: {numero_gescon}, SIGI: " "mas o número do convênio diverge ("
"{numero_sigi}). CORRIGIDO!").format( f"Gescon: {numero}, SIGI: {convenio.num_convenio}"
id=contrato['id'], "). CORRIGIDO!")
url=reverse('admin:%s_%s_change' % (
convenio._meta.app_label,
convenio._meta.model_name),
args=[convenio.id]
),
numero_gescon=numero,
numero_sigi=convenio.num_convenio
)
) )
convenio.num_convenio = numero convenio.num_convenio = numero
convenio.observacao_gescon += _( convenio.observacao_gescon += _(
@ -713,44 +690,28 @@ class Gescon(models.Model):
convenio.save() convenio.save()
except Exception as e: except Exception as e:
self.add_message( self.add_message(
_("Ocorreu um erro ao salvar o convênio {url} no " _("Ocorreu um erro ao salvar o convênio "
"SIGI. Alguma informação do Gescon pode ter " f"{convenio.id} no SIGI. Alguma informação do "
"quebrado o sistema. Informe ao suporte. Erro:" "Gescon pode ter quebrado o sistema. Informe ao "
"{errmsg}").format( f"suporte. Erro: {e.message}")
url=reverse('admin:%s_%s_change' % (
convenio._meta.app_label,
convenio._meta.model_name),
args=[convenio.id]
),
errmsg=e.message.decode("utf8")
)
) )
erros += 1 erros += 1
continue continue
atualizados += 1 atualizados += 1
else: else:
self.add_message(_("\tExistem {count} convênios no SIGI " self.add_message(
"que correspondem ao mesmo contrato no " _(f"\tExistem {chk} convênios no SIGI que "
"Gescon (contrato {numero}, sigad " "correspondem ao mesmo contrato no Gescon (contrato "
"{sigad})").format( f"{numero}, sigad {sigad})")
count=chk,
numero=numero,
sigad=sigad
)
) )
erros += 1 erros += 1
continue continue
self.add_message( self.add_message(
_("\t{novos} novos convenios adicionados ao SIGI, " _(f"\t{novos} novos convenios adicionados ao SIGI, "
"{atualizados} atualizados, sendo {alertas} com alertas, e " f"{atualizados} atualizados, sendo {alertas} com alertas, e "
"{erros} reportados com erro.").format( f"{erros} reportados com erro.")
novos=novos,
atualizados=atualizados,
alertas=alertas,
erros=erros
)
) )
self.save() self.save()

63
sigi/apps/eventos/admin.py

@ -1,11 +1,54 @@
from django.contrib import admin from django.contrib import admin
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from import_export.fields import Field
from tinymce.models import HTMLField from tinymce.models import HTMLField
from tinymce.widgets import AdminTinyMCE from tinymce.widgets import AdminTinyMCE
from sigi.apps.eventos.models import (ModeloDeclaracao, Modulo, TipoEvento, from sigi.apps.eventos.models import (ModeloDeclaracao, Modulo, TipoEvento,
Funcao, Evento, Equipe, Convite, Anexo) Funcao, Evento, Equipe, Convite, Anexo)
from sigi.apps.eventos.forms import EventoAdminForm from sigi.apps.eventos.forms import EventoAdminForm
from sigi.apps.utils.filters import EmptyFilter
from sigi.apps.utils.mixins import CartExportMixin, ValueLabeledResource
class EventoResource(ValueLabeledResource):
# categoria_evento = Field(column_name="tipo_evento__categoria")
# status = Field(column_name="status")
class Meta:
model = Evento
fields = (
'id', 'tipo_evento__nome', 'tipo_evento__categoria', 'nome',
'descricao', 'virtual', 'solicitante', 'num_processo',
'data_pedido', 'data_inicio', 'data_termino', 'carga_horaria',
'casa_anfitria__nome', 'casa_anfitria__logradouro',
'casa_anfitria__bairro', 'casa_anfitria__municipio__nome',
'casa_anfitria__municipio__uf__sigla', 'casa_anfitria__cep',
'casa_anfitria__email', 'local', 'municipio__nome',
'municipio__uf__sigla', 'observacao', 'publico_alvo',
'total_participantes', 'status', 'data_cancelamento',
'motivo_cancelamento', 'equipe__membro__nome_completo',
'equipe__funcao__nome', 'convite__casa__nome',
'convite__casa__municipio__nome',
'convite__casa__municipio__uf__sigla', 'convite__casa__cep',
'convite__casa__email', 'convite__aceite', 'convite__data_convite',
'convite__participou', 'convite__qtde_participantes',
'convite__nomes_participantes',
)
export_order = fields
def dehydrate_tipo_evento__categoria(self, obj):
return dict(TipoEvento.CATEGORIA_CHOICES)[obj['tipo_evento__categoria']]
def dehydrate_virtual(self, obj):
return "Sim" if obj['virtual'] else "Não"
def dehydrate_status(self, obj):
return dict(Evento.STATUS_CHOICES)[obj['status']]
def dehydrate_convite__aceite(self, obj):
return "Sim" if obj['convite__aceite'] else "Não"
def dehydrate_convite__participou(self, obj):
return "Sim" if obj['convite__participou'] else "Não"
@admin.register(TipoEvento) @admin.register(TipoEvento)
class TipoEventAdmin(admin.ModelAdmin): class TipoEventAdmin(admin.ModelAdmin):
@ -36,18 +79,28 @@ class AnexoInline(admin.StackedInline):
exclude = ('data_pub',) exclude = ('data_pub',)
@admin.register(Evento) @admin.register(Evento)
class EventoAdmin(admin.ModelAdmin): class EventoAdmin(CartExportMixin, admin.ModelAdmin):
form = EventoAdminForm form = EventoAdminForm
resource_class = EventoResource
date_hierarchy = 'data_inicio' date_hierarchy = 'data_inicio'
list_display = ('nome', 'tipo_evento', 'status', 'data_inicio', list_display = ('nome', 'tipo_evento', 'status', 'link_sigad',
'data_termino', 'municipio', 'solicitante', 'data_inicio', 'data_termino', 'municipio', 'solicitante',
'total_participantes',) 'total_participantes',)
list_filter = ('status', 'tipo_evento', 'tipo_evento__categoria', 'virtual', list_filter = ('status', ('num_processo', EmptyFilter), 'tipo_evento',
'municipio__uf', 'solicitante') 'tipo_evento__categoria', 'virtual', 'municipio__uf',
'solicitante')
raw_id_fields = ('casa_anfitria', 'municipio',) raw_id_fields = ('casa_anfitria', 'municipio',)
search_fields = ('nome', 'tipo_evento__nome', 'casa_anfitria__search_text', search_fields = ('nome', 'tipo_evento__nome', 'casa_anfitria__search_text',
'municipio__search_text', 'solicitante') 'municipio__search_text', 'solicitante')
inlines = (EquipeInline, ConviteInline, ModuloInline, AnexoInline) inlines = (EquipeInline, ConviteInline, ModuloInline, AnexoInline)
save_as = True
def link_sigad(self, obj):
if obj.pk is None:
return ""
return obj.get_sigad_url()
link_sigad.short_description = _(u"número do processo SIGAD")
link_sigad.allow_tags = True
def lookup_allowed(self, lookup, value): def lookup_allowed(self, lookup, value):
return (super(EventoAdmin, self).lookup_allowed(lookup, value) or return (super(EventoAdmin, self).lookup_allowed(lookup, value) or

8
sigi/apps/eventos/forms.py

@ -6,10 +6,10 @@ class EventoAdminForm(forms.ModelForm):
class Meta: class Meta:
model = Evento model = Evento
fields = ('tipo_evento', 'nome', 'descricao', 'virtual', 'solicitante', fields = ('tipo_evento', 'nome', 'descricao', 'virtual', 'solicitante',
'data_inicio', 'data_termino', 'carga_horaria', 'num_processo', 'data_pedido', 'data_inicio', 'data_termino',
'casa_anfitria', 'municipio', 'local', 'publico_alvo', 'carga_horaria', 'casa_anfitria', 'municipio', 'observacao',
'total_participantes', 'status', 'data_cancelamento', 'local', 'publico_alvo', 'total_participantes', 'status',
'motivo_cancelamento', ) 'data_cancelamento', 'motivo_cancelamento', )
def clean(self): def clean(self):
cleaned_data = super(EventoAdminForm, self).clean() cleaned_data = super(EventoAdminForm, self).clean()

33
sigi/apps/eventos/migrations/0018_evento_data_pedido_evento_num_processo_and_more.py

@ -0,0 +1,33 @@
# Generated by Django 4.0.3 on 2022-04-19 12:39
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('eventos', '0017_alter_anexo_arquivo_alter_anexo_descricao_and_more'),
]
operations = [
migrations.AddField(
model_name='evento',
name='data_pedido',
field=models.DateField(blank=True, help_text='Data em que o pedido do Gabinete chegou à COPERI', null=True, verbose_name='Data do pedido'),
),
migrations.AddField(
model_name='evento',
name='num_processo',
field=models.CharField(blank=True, help_text='Formato:<em>XXXXX.XXXXXX/XXXX-XX</em>', max_length=20, verbose_name='número do processo SIGAD'),
),
migrations.AddField(
model_name='evento',
name='observacao',
field=models.TextField(blank=True, verbose_name='Observações e anotações'),
),
migrations.AlterField(
model_name='evento',
name='status',
field=models.CharField(choices=[('E', 'Em planejamento'), ('G', 'Aguardando abertura SIGAD'), ('P', 'Previsão'), ('A', 'A confirmar'), ('O', 'Confirmado'), ('R', 'Realizado'), ('C', 'Cancelado')], max_length=1, verbose_name='Status'),
),
]

18
sigi/apps/eventos/migrations/0019_alter_evento_status.py

@ -0,0 +1,18 @@
# Generated by Django 4.0.3 on 2022-04-20 13:03
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('eventos', '0018_evento_data_pedido_evento_num_processo_and_more'),
]
operations = [
migrations.AlterField(
model_name='evento',
name='status',
field=models.CharField(choices=[('E', 'Em planejamento'), ('G', 'Aguardando abertura SIGAD'), ('P', 'Previsão'), ('A', 'A confirmar'), ('O', 'Confirmado'), ('R', 'Realizado'), ('C', 'Cancelado'), ('Q', 'Arquivado')], max_length=1, verbose_name='Status'),
),
]

35
sigi/apps/eventos/models.py

@ -1,3 +1,4 @@
import re
from datetime import datetime from datetime import datetime
from django.db import models from django.db import models
from django.db.models import Sum from django.db.models import Sum
@ -33,11 +34,14 @@ class TipoEvento(models.Model):
class Evento(models.Model): class Evento(models.Model):
STATUS_CHOICES = ( STATUS_CHOICES = (
('E', _(u"Em planejamento")),
('G', _(u"Aguardando abertura SIGAD")),
('P', _("Previsão")), ('P', _("Previsão")),
('A', _("A confirmar")), ('A', _("A confirmar")),
('O', _("Confirmado")), ('O', _("Confirmado")),
('R', _("Realizado")), ('R', _("Realizado")),
('C', _("Cancelado")) ('C', _("Cancelado")),
('Q', _("Arquivado")),
) )
tipo_evento = models.ForeignKey( tipo_evento = models.ForeignKey(
@ -48,6 +52,18 @@ class Evento(models.Model):
descricao = models.TextField(_("Descrição do evento")) descricao = models.TextField(_("Descrição do evento"))
virtual = models.BooleanField(_("Virtual"), default=False) virtual = models.BooleanField(_("Virtual"), default=False)
solicitante = models.CharField(_("Solicitante"), max_length=100) solicitante = models.CharField(_("Solicitante"), max_length=100)
num_processo = models.CharField(
_(u'número do processo SIGAD'),
max_length=20,
blank=True,
help_text=_(u'Formato:<em>XXXXX.XXXXXX/XXXX-XX</em>')
)
data_pedido = models.DateField(
_(u"Data do pedido"),
null=True,
blank=True,
help_text=_(u"Data em que o pedido do Gabinete chegou à COPERI")
)
data_inicio = models.DateTimeField( data_inicio = models.DateTimeField(
_("Data/hora do Início"), _("Data/hora do Início"),
null=True, null=True,
@ -74,6 +90,7 @@ class Evento(models.Model):
on_delete=models.PROTECT on_delete=models.PROTECT
) )
local = models.TextField(_("Local do evento"), blank=True) local = models.TextField(_("Local do evento"), blank=True)
observacao = models.TextField(_(u"Observações e anotações"), blank=True)
publico_alvo = models.TextField(_("Público alvo"), blank=True) publico_alvo = models.TextField(_("Público alvo"), blank=True)
total_participantes = models.PositiveIntegerField( total_participantes = models.PositiveIntegerField(
_("Total de participantes"), _("Total de participantes"),
@ -96,6 +113,20 @@ class Evento(models.Model):
f"de {self.data_inicio} a {self.data_termino}" f"de {self.data_inicio} a {self.data_termino}"
) )
def get_sigad_url(self):
m = re.match('(?P<orgao>00100|00200)\.(?P<sequencial>\d{6})/(?P<ano>'
'\d{4})-\d{2}', self.num_processo)
if m:
return ('<a href="https://intra.senado.leg.br/'
'sigad/novo/protocolo/impressao.asp?area=processo'
'&txt_numero_orgao={orgao}'
'&txt_numero_sequencial={sequencial}'
'&txt_numero_ano={ano}"'
' target="_blank">{processo}</a>').format(
processo=self.num_processo,**m.groupdict()
)
return self.num_processo
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
if self.status != 'C': if self.status != 'C':
self.data_cancelamento = None self.data_cancelamento = None
@ -105,7 +136,7 @@ class Evento(models.Model):
"data de início")) "data de início"))
total = self.convite_set.aggregate(total=Sum('qtde_participantes')) total = self.convite_set.aggregate(total=Sum('qtde_participantes'))
total = total['total'] total = total['total']
if (total is not None) or (total > 0): if total and total > 0:
self.total_participantes = total self.total_participantes = total
return super(Evento, self).save(*args, **kwargs) return super(Evento, self).save(*args, **kwargs)

1
sigi/apps/eventos/templates/admin/eventos/evento/change_list.html

@ -1 +0,0 @@
{% extends "change_list_with_cart.html" %}

68
sigi/apps/utils/filters.py

@ -1,11 +1,15 @@
import string import string
from math import log10 from math import log10
from django import forms from django import forms
from django.db.models import Q
from django.contrib import admin from django.contrib import admin
from django.contrib.admin.options import IncorrectLookupParameters from django.contrib.admin.options import IncorrectLookupParameters
from django.utils.translation import ngettext, gettext as _ from django.utils.translation import ngettext, gettext as _
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
class NotEmptyableField(Exception):
pass
class AlphabeticFilter(admin.SimpleListFilter): class AlphabeticFilter(admin.SimpleListFilter):
title = '' title = ''
@ -20,6 +24,70 @@ class AlphabeticFilter(admin.SimpleListFilter):
(self.parameter_name + '__istartswith', self.value()) (self.parameter_name + '__istartswith', self.value())
) )
class EmptyFilter(admin.FieldListFilter):
EMPTY_STRING = _("Em branco")
NOT_EMPTY_STRING = _("Preenchido")
def __init__(self, field, request, params, model, model_admin, field_path):
self.model = model
self.model_admin = model_admin
self.parameter_name = f'{field_path}__empty'
if (not field.null) and (not field.blank):
raise NotEmptyableField(
f"Field {field.name} cannot be empty nor null"
)
super().__init__(field, request, params, model, model_admin, field_path)
def lookups(self):
return (
("1", self.EMPTY_STRING),
("0", self.NOT_EMPTY_STRING),
)
def value(self):
return self.used_parameters.get(self.parameter_name)
def choices(self, changelist):
yield {
'selected': self.value() is None,
'query_string': changelist.get_query_string(remove=[
self.parameter_name]),
'display': _('All'),
}
for value, display in self.lookups():
yield {
'selected': self.value() == value,
'query_string': changelist.get_query_string(
{self.parameter_name: value}),
'display': display,
}
def expected_parameters(self):
return [self.parameter_name,]
def queryset(self, request, queryset):
val = self.value()
if val is None:
return queryset
val = bool(int(val))
filter = Q()
if self.field.null:
filter = filter | Q(**{f"{self.field_path}__isnull": val})
if self.field.blank:
if val:
filter = filter | Q(**{f"{self.field_path}__exact": ""})
else:
filter = filter | ~Q(**{f"{self.field_path}__exact": ""})
return queryset.filter(filter)
class RangeFilter(admin.FieldListFilter): class RangeFilter(admin.FieldListFilter):
num_faixas = 4 num_faixas = 4
parameter_name = None parameter_name = None

14
sigi/apps/utils/mixins.py

@ -13,10 +13,17 @@ from django.urls import path
from django.utils.translation import gettext as _, ngettext from django.utils.translation import gettext as _, ngettext
from import_export import resources from import_export import resources
from import_export.admin import ExportMixin from import_export.admin import ExportMixin
from import_export.fields import Field
from import_export.forms import ExportForm from import_export.forms import ExportForm
from import_export.signals import post_export from import_export.signals import post_export
from sigi.apps.utils import field_label from sigi.apps.utils import field_label
class ValueField(Field):
def get_value(self, obj):
if self.attribute is None:
return None
return obj[self.attribute]
class ExportFormFields(ExportForm): class ExportFormFields(ExportForm):
def __init__(self, formats, field_list, *args, **kwargs): def __init__(self, formats, field_list, *args, **kwargs):
super().__init__(formats, *args, **kwargs) super().__init__(formats, *args, **kwargs)
@ -51,6 +58,13 @@ class LabeledResourse(resources.ModelResource):
self.selected_fields = selected_fields self.selected_fields = selected_fields
return super().export(queryset, *args, **kwargs) return super().export(queryset, *args, **kwargs)
class ValueLabeledResource(LabeledResourse):
DEFAULT_RESOURCE_FIELD = ValueField
def export(self, queryset=None, selected_fields=None, *args, **kwargs):
queryset = queryset.values(*selected_fields)
return super().export(queryset, selected_fields, *args, **kwargs)
class CartExportMixin(ExportMixin): class CartExportMixin(ExportMixin):
to_encoding = 'utf-8' to_encoding = 'utf-8'
change_list_template = 'admin/cart/change_list_cart_export.html' change_list_template = 'admin/cart/change_list_cart_export.html'

Loading…
Cancel
Save