Browse Source

Aprimora log dos JOBS

pull/160/head
Sesostris Vieira 2 years ago
parent
commit
ae2f2a84b6
  1. 52
      sigi/apps/casas/jobs/daily/usuario_contato.py
  2. 92
      sigi/apps/contatos/jobs/monthly/atualiza_ibge.py
  3. 28
      sigi/apps/convenios/jobs/daily/importa_gescon.py
  4. 112
      sigi/apps/servicos/jobs/daily/sincroniza_dns.py
  5. 185
      sigi/apps/servicos/jobs/daily/sincroniza_rancher.py
  6. 30
      sigi/apps/servicos/templates/servicos/emails/report_sincroniza_dns.rst
  7. 11
      sigi/apps/servicos/templates/servicos/emails/report_sincroniza_rancher.rst
  8. 34
      sigi/apps/servidores/migrations/0012_add_user_interlegis.py
  9. 112
      sigi/apps/utils/mixins.py
  10. 1
      sigi/templates/emails/base_email.rst
  11. 17
      sigi/templates/emails/base_report.rst
  12. 3
      sigi/templates/emails/report_error.rst

52
sigi/apps/casas/jobs/daily/usuario_contato.py

@ -1,13 +1,21 @@
from django.db.models import Q from django.db.models import Q
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.utils.translation import gettext as _
from django_extensions.management.jobs import DailyJob from django_extensions.management.jobs import DailyJob
from sigi.apps.casas.models import Orgao, Funcionario from sigi.apps.casas.models import Funcionario
from sigi.apps.servidores.models import Servidor
from sigi.apps.utils.mixins import JobReportMixin
class Job(DailyJob): class Job(JobReportMixin, DailyJob):
help = "Ativa / desativa usuários para os Contatos Interlegis" help = "Ativa / desativa usuários para os Contatos Interlegis"
def execute(self): def do_job(self):
self.report_data = []
tot_news = 0
tot_updates = 0
tot_deactivated = 0
# Seleciona contatos interlegis com mínimo de informações # Seleciona contatos interlegis com mínimo de informações
# (nome, cpf, email). Elegíveis para fazer login no sistema # (nome, cpf, email). Elegíveis para fazer login no sistema
contatos = Funcionario.objects.filter( contatos = Funcionario.objects.filter(
@ -29,13 +37,37 @@ class Job(DailyJob):
}, },
username=email, username=email,
) )
print( if created:
f"{['Updated', 'Created'][created]} user {user.username} for contato {contato.id}" tot_news += 1
self.admin_log_addition(user, "Novo contato técnico habilitado")
else:
tot_updates += 1
self.admin_log_change(user, "Contato técnico atualizado")
self.report_data.append(
_(
f"Usuário '{user.username}' "
f"{['atualizado', 'criado'][created]} "
f"para o contato {contato.id}"
)
) )
# Desativa usuários de contatos que não estão na lista de elegíveis # Desativa usuários de contatos que não estão na lista de elegíveis
qtd_desativados = ( for user in User.objects.filter(
User.objects.filter(username__contains="@", is_active=True) username__contains="@", is_active=True
.exclude(username__in=contatos.values_list("email", flat=True)) ).exclude(username__in=contatos.values_list("email", flat=True)):
.update(is_active=False) user.is_active = False
user.save()
self.admin_log_change(
user,
_("Desativado pelo sistema - Não é mais contato técnico"),
) )
print(f"{qtd_desativados} usuários desativados") tot_deactivated += 1
self.report_data.append("")
self.report_data.append(_("RESUMO"))
self.report_data.append("------")
self.report_data.append("")
self.report_data.append(_(f"{tot_news} novos usuários"))
self.report_data.append(_(f"{tot_updates} usuários atualizados"))
self.report_data.append(_(f"{tot_deactivated} usuários desativados"))

92
sigi/apps/contatos/jobs/monthly/atualiza_ibge.py

@ -1,7 +1,6 @@
import docutils.core
from datetime import datetime
from django_extensions.management.jobs import MonthlyJob from django_extensions.management.jobs import MonthlyJob
from django.conf import settings from django.conf import settings
from django.contrib.admin.models import ADDITION, CHANGE
from django.core.mail import mail_admins from django.core.mail import mail_admins
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
@ -12,13 +11,16 @@ from sigi.apps.contatos.models import (
Microrregiao, Microrregiao,
Municipio, Municipio,
) )
from sigi.apps.servidores.models import Servidor
from sigi.apps.utils.mixins import JobReportMixin
class Job(MonthlyJob): class Job(JobReportMixin, MonthlyJob):
help = _( help = _(
"Atualiza Unidades Federativas, mesorregiões, microrregiões e " "Atualiza Unidades Federativas, mesorregiões, microrregiões e "
"municípios com dados do IBGE" "municípios com dados do IBGE"
) )
report_template = "contatos/emails/report_atualiza_ibge.rst"
uf_novas = [] uf_novas = []
uf_atualizadas = [] uf_atualizadas = []
@ -28,25 +30,27 @@ class Job(MonthlyJob):
meso_atualizadas = [] meso_atualizadas = []
micro_novas = [] micro_novas = []
micro_atualizadas = [] micro_atualizadas = []
sigi_user = None
def execute(self): def do_job(self):
print(
"Atualizando dados do IBGE. "
f"Início: {datetime.now(): %d/%m/%Y %H:%M:%S}"
)
self.atualiza_ufs() self.atualiza_ufs()
self.atualiza_municipios() self.atualiza_municipios()
self.report()
print(f"Término: {datetime.now(): %d/%m/%Y %H:%M:%S}") self.report_data = {
"uf_novas": self.uf_novas,
"uf_atualizadas": self.uf_atualizadas,
"municipios_novos": self.municipios_novos,
"municipios_atualizados": self.municipios_atualizados,
"meso_novas": self.meso_novas,
"meso_atualizadas": self.meso_atualizadas,
"micro_novas": self.micro_novas,
"micro_atualizadas": self.micro_atualizadas,
}
def atualiza_ufs(self): def atualiza_ufs(self):
ibge_ufs = Estados().json()
regioes_map = {"N": "NO", "NE": "NE", "SE": "SE", "S": "SL", "CO": "CO"} regioes_map = {"N": "NO", "NE": "NE", "SE": "SE", "S": "SL", "CO": "CO"}
self.uf_novas = [] for ibge_uf in Estados().json():
self.uf_atualizadas = []
for ibge_uf in ibge_ufs:
regiao = regioes_map[ibge_uf["regiao"]["sigla"]] regiao = regioes_map[ibge_uf["regiao"]["sigla"]]
try: try:
sigi_uf = UnidadeFederativa.objects.get( sigi_uf = UnidadeFederativa.objects.get(
@ -62,6 +66,7 @@ class Job(MonthlyJob):
) )
sigi_uf.save() sigi_uf.save()
self.uf_novas.append(sigi_uf) self.uf_novas.append(sigi_uf)
self.admin_log_addition(sigi_uf, "Nova UF encontrada no IBGE")
if ( if (
sigi_uf.nome != ibge_uf["nome"] sigi_uf.nome != ibge_uf["nome"]
or sigi_uf.sigla != ibge_uf["sigla"] or sigi_uf.sigla != ibge_uf["sigla"]
@ -72,19 +77,10 @@ class Job(MonthlyJob):
sigi_uf.regiao = regiao sigi_uf.regiao = regiao
sigi_uf.save() sigi_uf.save()
self.uf_atualizadas.append(sigi_uf) self.uf_atualizadas.append(sigi_uf)
self.admin_log_change(sigi_uf, "Atualizada pelo IBGE")
def atualiza_municipios(self): def atualiza_municipios(self):
for ibge_mun in Municipios().json():
ibge_municipios = Municipios().json()
self.municipios_novos = []
self.municipios_atualizados = []
self.meso_novas = []
self.meso_atualizadas = []
self.micro_novas = []
self.micro_atualizadas = []
for ibge_mun in ibge_municipios:
uf_id = ibge_mun["microrregiao"]["mesorregiao"]["UF"]["id"] uf_id = ibge_mun["microrregiao"]["mesorregiao"]["UF"]["id"]
cod_meso = ibge_mun["microrregiao"]["mesorregiao"]["id"] cod_meso = ibge_mun["microrregiao"]["mesorregiao"]["id"]
cod_micro = int( cod_micro = int(
@ -101,10 +97,14 @@ class Job(MonthlyJob):
) )
meso.save() meso.save()
self.meso_novas.append(meso) self.meso_novas.append(meso)
self.admin_log_addition(
meso, "Nova mesorregião encontrada no IBGE"
)
if meso.nome != ibge_mun["microrregiao"]["mesorregiao"]["nome"]: if meso.nome != ibge_mun["microrregiao"]["mesorregiao"]["nome"]:
meso.nome = ibge_mun["microrregiao"]["mesorregiao"]["nome"] meso.nome = ibge_mun["microrregiao"]["mesorregiao"]["nome"]
meso.save() meso.save()
self.meso_atualizadas.append(meso) self.meso_atualizadas.append(meso)
self.admin_log_change(meso, "Atualizada pelo IBGE")
# Atualiza ou cria a microrregião # # Atualiza ou cria a microrregião #
try: try:
micro = Microrregiao.objects.get(codigo_ibge=cod_micro) micro = Microrregiao.objects.get(codigo_ibge=cod_micro)
@ -115,6 +115,10 @@ class Job(MonthlyJob):
nome=ibge_mun["microrregiao"]["nome"], nome=ibge_mun["microrregiao"]["nome"],
) )
micro.save() micro.save()
self.micro_novas.append(micro)
self.admin_log_addition(
micro, "Nova microrregião encontrada no IBGE"
)
if ( if (
micro.nome != ibge_mun["microrregiao"]["nome"] micro.nome != ibge_mun["microrregiao"]["nome"]
or micro.mesorregiao != meso or micro.mesorregiao != meso
@ -123,6 +127,7 @@ class Job(MonthlyJob):
micro.mesorregiao = meso micro.mesorregiao = meso
micro.save() micro.save()
self.micro_atualizadas.append(micro) self.micro_atualizadas.append(micro)
self.admin_log_change(micro, "Atualizada pelo IBGE")
# Atualiza ou cria o município # # Atualiza ou cria o município #
try: try:
sigi_mun = Municipio.objects.get(codigo_ibge=ibge_mun["id"]) sigi_mun = Municipio.objects.get(codigo_ibge=ibge_mun["id"])
@ -137,6 +142,9 @@ class Job(MonthlyJob):
) )
sigi_mun.save() sigi_mun.save()
self.municipios_novos.append(sigi_mun) self.municipios_novos.append(sigi_mun)
self.admin_log_addition(
sigi_mun, "Novo município encontrado no IBGE"
)
if ( if (
sigi_mun.nome != ibge_mun["nome"] sigi_mun.nome != ibge_mun["nome"]
or sigi_mun.uf_id != uf_id or sigi_mun.uf_id != uf_id
@ -147,34 +155,4 @@ class Job(MonthlyJob):
sigi_mun.microrregiao = micro sigi_mun.microrregiao = micro
sigi_mun.save() sigi_mun.save()
self.municipios_atualizados.append(sigi_mun) self.municipios_atualizados.append(sigi_mun)
self.admin_log_change(sigi_mun, "Atualizada pelo IBGE")
def report(self):
rst = render_to_string(
"contatos/emails/report_atualiza_ibge.rst",
{
"title": self.help,
"uf_novas": self.uf_novas,
"uf_atualizadas": self.uf_atualizadas,
"municipios_novos": self.municipios_novos,
"municipios_atualizados": self.municipios_atualizados,
"meso_novas": self.meso_novas,
"meso_atualizadas": self.meso_atualizadas,
"micro_novas": self.micro_novas,
"micro_atualizadas": self.micro_atualizadas,
},
)
html = docutils.core.publish_string(
rst,
writer_name="html5",
settings_overrides={
"input_encoding": "unicode",
"output_encoding": "unicode",
},
)
mail_admins(
subject=self.help,
message=rst,
html_message=html,
fail_silently=True,
)
print(rst)

28
sigi/apps/convenios/jobs/daily/importa_gescon.py

@ -4,33 +4,13 @@ from django.core.mail import mail_admins
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from django_extensions.management.jobs import DailyJob from django_extensions.management.jobs import DailyJob
from sigi.apps.convenios.models import Gescon from sigi.apps.convenios.models import Gescon
from sigi.apps.utils.mixins import JobReportMixin
class Job(DailyJob): class Job(JobReportMixin, DailyJob):
help = "Carga de dados do Gescon." help = "Carga de dados do Gescon."
def execute(self): def do_job(self):
now = datetime.datetime.now()
print(f"Import gescon data started at {now:%Y-%m-%d %H:%M:%S}")
gescon = Gescon.load() gescon = Gescon.load()
gescon.importa_contratos() gescon.importa_contratos()
self.report(gescon, now) self.report_data = gescon.ultima_importacao.splitlines()
now = datetime.datetime.now()
print(f"Import gescon data finished at {now:%Y-%m-%d %H:%M:%S}")
def report(self, gescon, now):
rst = gescon.ultima_importacao
html = docutils.core.publish_string(
rst,
writer_name="html5",
settings_overrides={
"input_encoding": "unicode",
"output_encoding": "unicode",
},
)
mail_admins(
subject=_(f"Importação do GESCON em {now:%d/%m/%Y às %Hh%M}"),
message=rst,
html_message=html,
fail_silently=True,
)

112
sigi/apps/servicos/jobs/daily/sincroniza_dns.py

@ -12,6 +12,7 @@ from sigi.apps.servicos import generate_instance_name
from sigi.apps.servicos.models import Servico, TipoServico from sigi.apps.servicos.models import Servico, TipoServico
from sigi.apps.casas.models import Orgao from sigi.apps.casas.models import Orgao
from sigi.apps.contatos.models import UnidadeFederativa from sigi.apps.contatos.models import UnidadeFederativa
from sigi.apps.utils.mixins import JobReportMixin
LOG_GERAL = _("Mensagens gerais") LOG_GERAL = _("Mensagens gerais")
IGNORES = ["_psl", "k8s", "www.", "sapl."] IGNORES = ["_psl", "k8s", "www.", "sapl."]
@ -35,33 +36,30 @@ def get_log_entry():
} }
class Job(DailyJob): class Job(JobReportMixin, DailyJob):
help = _("Sincronização dos registros de DNS da infraestrutura") help = _("Sincronização dos registros de DNS da infraestrutura")
_nomes_gerados = None report_template = "servicos/emails/report_sincroniza_dns.rst"
_log = {} nomes_gerados = None
report_data = {}
def execute(self): def do_job(self):
print(
_(
"Sincroniza os registros de domínio a partir do DNS."
f" Início: {datetime.datetime.now(): %d/%m/%Y %H:%M:%S}"
)
)
# TODO: Resolver # TODO: Resolver
print("Este CRON está desativado até resolvermos questões internas") raise Exception(
return "Este CRON está desativado até resolvermos questões internas"
)
self._log[LOG_GERAL] = get_log_entry() self.report_data[LOG_GERAL] = get_log_entry()
self.report_data["reativados"] = get_log_entry() # TODO: Remover
self.info("id,nome do orgao,instancia,tipo,url", "reativados")
if ( if (
not settings.REGISTRO_PATH.exists() not settings.REGISTRO_PATH.exists()
or not settings.REGISTRO_PATH.is_dir() or not settings.REGISTRO_PATH.is_dir()
): ):
self.error(_(f"Arquivos de DNS não encontrados.")) self.error(_(f"Arquivos de DNS não encontrados."))
self.report()
return return
self._nomes_gerados = { self.nomes_gerados = {
generate_instance_name(o): o generate_instance_name(o): o
for o in Orgao.objects.filter(tipo__legislativo=True) for o in Orgao.objects.filter(tipo__legislativo=True)
} }
@ -71,7 +69,7 @@ class Job(DailyJob):
).update(flag_confirmado=False) ).update(flag_confirmado=False)
for uf in UnidadeFederativa.objects.all(): for uf in UnidadeFederativa.objects.all():
self._log[uf] = get_log_entry() self.report_data[uf] = get_log_entry()
self.processa_uf(uf) self.processa_uf(uf)
self.processa_zones() self.processa_zones()
@ -80,15 +78,11 @@ class Job(DailyJob):
try: try:
shutil.rmtree(settings.REGISTRO_PATH) shutil.rmtree(settings.REGISTRO_PATH)
except Exception as e: except Exception as e:
print(_(f"Erro ao excluir diretório {settings.REGISTRO_PATH}")) self.info(_(f"Erro ao excluir diretório {settings.REGISTRO_PATH}"))
print("Relatório final:\n================")
self.report()
print(_(f" Término: {datetime.datetime.now():%H:%M:%S}."))
def processa_rec(self, dns_rec, log_entry=LOG_GERAL): def processa_rec(self, dns_rec, log_entry=LOG_GERAL):
dominio = dns_rec["name"][:-1] dominio = dns_rec["name"][:-1]
nivel = len(dominio.split(".")) nivel = dominio.count(".") + 1
iname = get_iname(dominio) iname = get_iname(dominio)
sigla_srv = get_sigla_serv(dominio) sigla_srv = get_sigla_serv(dominio)
@ -104,6 +98,25 @@ class Job(DailyJob):
# Ignorar esses registros sem fazer log # # Ignorar esses registros sem fazer log #
return return
apps = []
if "rrsets" in dns_rec:
apps = [
r["name"].split(".")[0]
for r in dns_rec["rrsets"]
if r["type"] != "TXT" and r["name"][:-1].count(".") + 1 > nivel
]
else:
detail_file = settings.REGISTRO_PATH / f"{dominio}."
if detail_file.exists() and detail_file.is_file():
detail_data = json.loads(detail_file.read_text())
if "rrsets" in detail_data:
apps = [
r["name"].split(".")[0]
for r in detail_data["rrsets"]
if r["type"] != "TXT"
and r["name"][:-1].count(".") + 1 > nivel
]
try: try:
tipo = TipoServico.objects.get(sigla=sigla_srv, modo="R") tipo = TipoServico.objects.get(sigla=sigla_srv, modo="R")
except TipoServico.DoesNotExist: except TipoServico.DoesNotExist:
@ -146,6 +159,7 @@ class Job(DailyJob):
tipo_servico=tipo, url=dominio, data_desativacao=None tipo_servico=tipo, url=dominio, data_desativacao=None
).first() ).first()
if servico is not None: if servico is not None:
servico.instancia = iname
self.log_update(servico) self.log_update(servico)
else: else:
# Tenta encontrar um registro desativado com mesmo domínio # # Tenta encontrar um registro desativado com mesmo domínio #
@ -153,13 +167,14 @@ class Job(DailyJob):
tipo_servico=tipo, url=dominio tipo_servico=tipo, url=dominio
).first() ).first()
if servico is not None: if servico is not None:
servico.data_desativacao = None
servico.instancia = iname servico.instancia = iname
self.log_reativa(servico) self.log_reativa(servico)
if servico is None: if servico is None:
# Tenta criar o registro # # Tenta criar o registro #
if iname in self._nomes_gerados: if iname in self.nomes_gerados:
orgao = self._nomes_gerados[iname] orgao = self.nomes_gerados[iname]
log_entry = orgao.municipio.uf log_entry = orgao.municipio.uf
servico = Servico( servico = Servico(
casa_legislativa=orgao, casa_legislativa=orgao,
@ -182,6 +197,7 @@ class Job(DailyJob):
# atualiza o serviço no SIGI # atualiza o serviço no SIGI
servico.url = dominio servico.url = dominio
servico.instancia = iname servico.instancia = iname
servico.apps = "\n".join(apps)
servico.hospedagem_interlegis = True servico.hospedagem_interlegis = True
servico.data_verificacao = timezone.localtime() servico.data_verificacao = timezone.localtime()
servico.resultado_verificacao = "F" # Funcionando servico.resultado_verificacao = "F" # Funcionando
@ -195,7 +211,7 @@ class Job(DailyJob):
return return
registros = json.loads(file_path.read_text())["rrsets"] registros = json.loads(file_path.read_text())["rrsets"]
self._log[uf]["sumario"]["total"] = len(registros) self.report_data[uf]["sumario"]["total"] = len(registros)
# Atualiza registros existentes e cria novos # # Atualiza registros existentes e cria novos #
for rec in registros: for rec in registros:
@ -239,12 +255,11 @@ class Job(DailyJob):
def processa_files(self): def processa_files(self):
file_list = list(settings.REGISTRO_PATH.iterdir()) file_list = list(settings.REGISTRO_PATH.iterdir())
self._log[LOG_GERAL]["sumario"]["total"] = len(file_list) self.report_data[LOG_GERAL]["sumario"]["total"] = len(file_list)
for file_path in file_list: for file_path in file_list:
if not file_path.is_file(): if not file_path.is_file():
self._log[LOG_GERAL]["sumario"]["total"] -= 1 self.report_data[LOG_GERAL]["sumario"]["total"] -= 1
continue continue
print(file_path)
data = json.loads(file_path.read_text()) data = json.loads(file_path.read_text())
self.processa_rec(data) self.processa_rec(data)
file_path.unlink() file_path.unlink()
@ -262,36 +277,11 @@ class Job(DailyJob):
servico.save() servico.save()
self.log_remove(servico) self.log_remove(servico)
def report(self):
rst = render_to_string(
"servicos/emails/report_sincroniza_dns.rst",
{
"log": self._log,
"title": _("Resultado da sincronização do SIGI com o DNS"),
},
)
html = docutils.core.publish_string(
rst,
writer_name="html5",
settings_overrides={
"input_encoding": "unicode",
"output_encoding": "unicode",
},
)
mail_admins(
subject=self.help,
message=rst,
html_message=html,
fail_silently=True,
)
print(rst)
def error(self, message, log_entry=LOG_GERAL): def error(self, message, log_entry=LOG_GERAL):
self._log[log_entry]["erros"].append(message) self.report_data[log_entry]["erros"].append(message)
def info(self, message, log_entry=LOG_GERAL): def info(self, message, log_entry=LOG_GERAL):
self._log[log_entry]["infos"].append(message) self.report_data[log_entry]["infos"].append(message)
def log_novo(self, srv): def log_novo(self, srv):
orgao = srv.casa_legislativa orgao = srv.casa_legislativa
@ -301,15 +291,15 @@ class Job(DailyJob):
f"para {orgao.nome} ({uf.sigla})" f"para {orgao.nome} ({uf.sigla})"
) )
self.info(msg, uf) self.info(msg, uf)
self._log[uf]["sumario"]["novos"] += 1 self.report_data[uf]["sumario"]["novos"] += 1
def log_ignore(self, dominio, motivo, log_entry=LOG_GERAL): def log_ignore(self, dominio, motivo, log_entry=LOG_GERAL):
self.error(_(f"Registro {dominio} ignorado pois {motivo}"), log_entry) self.error(_(f"Registro {dominio} ignorado pois {motivo}"), log_entry)
self._log[log_entry]["sumario"]["ignorados"] += 1 self.report_data[log_entry]["sumario"]["ignorados"] += 1
def log_update(self, srv): def log_update(self, srv):
uf = srv.casa_legislativa.municipio.uf uf = srv.casa_legislativa.municipio.uf
self._log[uf]["sumario"]["atualizados"] += 1 self.report_data[uf]["sumario"]["atualizados"] += 1
def log_reativa(self, srv): def log_reativa(self, srv):
orgao = srv.casa_legislativa orgao = srv.casa_legislativa
@ -318,13 +308,15 @@ class Job(DailyJob):
f"Instância {srv.instancia} de {srv.tipo_servico.nome} " f"Instância {srv.instancia} de {srv.tipo_servico.nome} "
f"para {orgao.nome} ({uf.sigla}) reativada no SIGI" f"para {orgao.nome} ({uf.sigla}) reativada no SIGI"
) )
self._log[uf]["sumario"]["atualizados"] += 1 self.report_data[uf]["sumario"]["atualizados"] += 1
self.info(msg, uf) self.info(msg, uf)
msg = f"{srv.id},{orgao.nome},{srv.instancia},{srv.tipo_servico.nome},{srv.url}"
self.info(msg, "reativados")
def log_remove(self, srv): def log_remove(self, srv):
orgao = srv.casa_legislativa orgao = srv.casa_legislativa
uf = orgao.municipio.uf uf = orgao.municipio.uf
self._log[uf]["sumario"]["desativados"] += 1 self.report_data[uf]["sumario"]["desativados"] += 1
self.info( self.info(
_( _(
f"Registro {srv.tipo_servico.sigla} {srv.instancia} ({srv.url})" f"Registro {srv.tipo_servico.sigla} {srv.instancia} ({srv.url})"

185
sigi/apps/servicos/jobs/daily/sincroniza_rancher.py

@ -11,72 +11,49 @@ from django_extensions.management.jobs import DailyJob
from sigi.apps.servicos import generate_instance_name from sigi.apps.servicos import generate_instance_name
from sigi.apps.servicos.models import Servico, TipoServico from sigi.apps.servicos.models import Servico, TipoServico
from sigi.apps.casas.models import Orgao from sigi.apps.casas.models import Orgao
from sigi.apps.utils.mixins import JobReportMixin
class Job(DailyJob): class Job(JobReportMixin, DailyJob):
help = _("Sincronização dos Serviços SEIT na infraestrutura") help = _("Sincronização dos Serviços SEIT na infraestrutura")
_nomes_gerados = None report_template = "servicos/emails/report_sincroniza_rancher.rst"
_errors = {} nomes_gerados = None
_infos = {} errors = {}
infos = {}
def execute(self): def do_job(self):
try: self.nomes_gerados = {
print(
_(
"Sincroniza os serviços SEIT a partir da infraestrutura."
f" Início: {datetime.datetime.now(): %d/%m/%Y %H:%M:%S}"
)
)
self._nomes_gerados = {
generate_instance_name(o): o generate_instance_name(o): o
for o in Orgao.objects.filter(tipo__legislativo=True) for o in Orgao.objects.filter(tipo__legislativo=True)
} }
print(
_(
f"\t{len(self._nomes_gerados)} órgãos que podem ter instâncias."
)
)
for tipo in TipoServico.objects.filter(modo="H").exclude( for tipo in TipoServico.objects.filter(modo="H").exclude(
tipo_rancher="" tipo_rancher=""
): ):
print(
_(
f"\tProcessando {tipo.nome}."
f" Início: {datetime.datetime.now():%H:%M:%S}."
),
end="",
)
self.process(tipo) self.process(tipo)
print(_(f" Término: {datetime.datetime.now():%H:%M:%S}."))
try: try:
shutil.rmtree(settings.HOSPEDAGEM_PATH) shutil.rmtree(settings.HOSPEDAGEM_PATH)
except Exception as e: except Exception as e:
print( pass
_(f"Erro ao excluir diretório {settings.HOSPEDAGEM_PATH}")
)
print("Relatório final:\n================")
self.report()
print(_(f"Término: {datetime.datetime.now(): %d/%m/%Y %H:%M:%S}")) self.report_data = {
except Exception as e: "erros": self.errors,
self.report_error(e) "infos": self.infos,
}
def process(self, tipo): def process(self, tipo):
self.nomeia_instancias(tipo) self.nomeia_instancias(tipo)
NAO_CONSTA = "*não-consta-no-rancher*" NAO_CONSTA = "*não-consta-no-rancher*"
self._errors[tipo] = [] self.errors[tipo] = []
self._infos[tipo] = [] self.infos[tipo] = []
file_path = settings.HOSPEDAGEM_PATH / tipo.arquivo_rancher file_path = settings.HOSPEDAGEM_PATH / tipo.arquivo_rancher
if not file_path.exists() or not file_path.is_file(): if not file_path.exists() or not file_path.is_file():
self._errors[tipo].append(_(f"Arquivo {file_path} não encontado.")) self.errors[tipo].append(_(f"Arquivo {file_path} não encontado."))
return return
with open(file_path, "r") as f: json_data = json.loads(file_path.read_text())
json_data = json.load(f)
portais = [ portais = [
item item
@ -88,7 +65,7 @@ class Job(DailyJob):
novos = 0 novos = 0
desativados = 0 desativados = 0
self._infos[tipo].append( self.infos[tipo].append(
_(f"{len(portais)} {tipo.nome} encontrados no Rancher") _(f"{len(portais)} {tipo.nome} encontrados no Rancher")
) )
@ -104,7 +81,7 @@ class Job(DailyJob):
hostname = p["spec"]["values"][tipo.spec_rancher]["domain"] hostname = p["spec"]["values"][tipo.spec_rancher]["domain"]
else: else:
hostname = NAO_CONSTA hostname = NAO_CONSTA
self._errors[tipo].append( self.errors[tipo].append(
_( _(
f"Instância {iname} de {tipo.nome} sem URL no " f"Instância {iname} de {tipo.nome} sem URL no "
"rancher" "rancher"
@ -120,17 +97,27 @@ class Job(DailyJob):
hostname = f"{tipo.prefixo_padrao}.{hostname}" hostname = f"{tipo.prefixo_padrao}.{hostname}"
else: else:
hostname = NAO_CONSTA hostname = NAO_CONSTA
self._errors[tipo].append( self.errors[tipo].append(
_(f"Instância {iname} de {tipo.nome} sem URL no rancher") _(f"Instância {iname} de {tipo.nome} sem URL no rancher")
) )
nova_versao = (
p["spec"]["values"]["image"]["tag"]
if "image" in p["spec"]["values"]
else ""
)
if NAO_CONSTA in hostname:
nova_url = ""
else:
nova_url = f"https://{hostname}/"
try: try:
portal = Servico.objects.get( portal = Servico.objects.get(
instancia=iname, tipo_servico=tipo, data_desativacao=None instancia=iname, tipo_servico=tipo, data_desativacao=None
) )
encontrados += 1 encontrados += 1
except Servico.MultipleObjectsReturned: except Servico.MultipleObjectsReturned:
self._errors[tipo].append( self.errors[tipo].append(
_( _(
f"Existe mais de um registro ativo da instância {iname}" f"Existe mais de um registro ativo da instância {iname}"
f" de {tipo}." f" de {tipo}."
@ -138,23 +125,28 @@ class Job(DailyJob):
) )
continue continue
except Servico.DoesNotExist: except Servico.DoesNotExist:
if iname in self._nomes_gerados: if iname in self.nomes_gerados:
orgao = self._nomes_gerados[iname] orgao = self.nomes_gerados[iname]
portal = Servico( portal = Servico(
casa_legislativa=orgao, casa_legislativa=orgao,
tipo_servico=tipo, tipo_servico=tipo,
instancia=iname, instancia=iname,
url=nova_url,
versao=nova_versao,
data_ativacao=p["spec"]["info"]["firstDeployed"][:10], data_ativacao=p["spec"]["info"]["firstDeployed"][:10],
hospedagem_interlegis=True,
) )
self._infos[tipo].append( portal.save()
self.admin_log_addition(portal, "Criado no Rancher")
novos += 1
self.infos[tipo].append(
_( _(
f"Criada instância {iname} de {tipo.nome} para " f"Criada instância {iname} de {tipo.nome} para "
f"{orgao.nome} ({orgao.municipio.uf.sigla})" f"{orgao.nome} ({orgao.municipio.uf.sigla})"
) )
) )
novos += 1
else: else:
self._errors[tipo].append( self.errors[tipo].append(
_( _(
f"{iname} ({hostname}) não parece pertencer a " f"{iname} ({hostname}) não parece pertencer a "
"nenhum órgão." "nenhum órgão."
@ -162,17 +154,34 @@ class Job(DailyJob):
) )
continue continue
# atualiza o serviço no SIGI # atualiza o serviço no SIGI
portal.versao = ( if (
p["spec"]["values"]["image"]["tag"] nova_versao != portal.versao
if "image" in p["spec"]["values"] or nova_url != portal.url
or not portal.hospedagem_interlegis
):
message = (
"Atualizado no Rancher: "
+ (
f"Versão: de '{portal.versao}' para '{nova_versao}' "
if portal.versao != nova_versao
else "" else ""
) )
if NAO_CONSTA in hostname: + (
portal.url = "" f"Url: de '{portal.url}' para '{nova_url}' "
else: if portal.url != nova_url
portal.url = f"https://{hostname}/" else ""
)
+ (
f"hospedagem interlegis"
if not portal.hospedagem_interlegis
else ""
)
)
portal.versao = nova_versao
portal.url = nova_url
portal.hospedagem_interlegis = True portal.hospedagem_interlegis = True
portal.save() portal.save()
self.admin_log_change(portal, message)
# Desativa portais registrados no SIGI que não estão no Rancher # # Desativa portais registrados no SIGI que não estão no Rancher #
nomes_instancias = [p["metadata"]["name"] for p in portais] nomes_instancias = [p["metadata"]["name"] for p in portais]
@ -186,20 +195,19 @@ class Job(DailyJob):
portal.data_desativacao = timezone.localdate() portal.data_desativacao = timezone.localdate()
portal.motivo_desativacao = _("Não encontrado no Rancher") portal.motivo_desativacao = _("Não encontrado no Rancher")
portal.save() portal.save()
self._infos[tipo].append( self.admin_log_change(portal, "Desativado no Rancher")
self.infos[tipo].append(
f"{portal.instancia} ({portal.url}) de " f"{portal.instancia} ({portal.url}) de "
f"{portal.casa_legislativa.nome} desativado pois não " f"{portal.casa_legislativa.nome} desativado pois não "
"foi encontrado no Rancher." "foi encontrado no Rancher."
) )
desativados += 1 desativados += 1
self._infos[tipo].append( self.infos[tipo].append(
_(f"{encontrados} {tipo.nome} do Rancher encontrados no SIGI") _(f"{encontrados} {tipo.nome} do Rancher encontrados no SIGI")
) )
self._infos[tipo].append( self.infos[tipo].append(_(f"{novos} novos {tipo.nome} criados no SIGI"))
_(f"{novos} novos {tipo.nome} criados no SIGI") self.infos[tipo].append(
)
self._infos[tipo].append(
_(f"{desativados} {tipo.nome} desativados no SIGI") _(f"{desativados} {tipo.nome} desativados no SIGI")
) )
@ -211,55 +219,4 @@ class Job(DailyJob):
): ):
s.instancia = generate_instance_name(s.casa_legislativa) s.instancia = generate_instance_name(s.casa_legislativa)
s.save() s.save()
self.admin_log_change(s, "Adicionado nome automático da instância")
def report(self):
rst = render_to_string(
"servicos/emails/report_sincroniza_rancher.rst",
{
"erros": self._errors,
"infos": self._infos,
"title": _("Resultado da sincronização do SIGI com o Rancher"),
},
)
html = docutils.core.publish_string(
rst,
writer_name="html5",
settings_overrides={
"input_encoding": "unicode",
"output_encoding": "unicode",
},
)
mail_admins(
subject=self.help,
message=rst,
html_message=html,
fail_silently=True,
)
print(rst)
def report_error(self, error):
import traceback
rst = render_to_string(
"emails/report_error.rst",
{
"title": _("Resultado da sincronização do SIGI com o Rancher"),
"process_name": self.help,
"traceback": traceback.format_exception(error),
},
)
html = docutils.core.publish_string(
rst,
writer_name="html5",
settings_overrides={
"input_encoding": "unicode",
"output_encoding": "unicode",
},
)
mail_admins(
subject=self.help,
message=rst,
html_message=html,
fail_silently=True,
)
print(rst)

30
sigi/apps/servicos/templates/servicos/emails/report_sincroniza_dns.rst

@ -1,43 +1,39 @@
{% extends 'emails/base_email.rst' %} {% extends 'emails/base_report.rst' %}
{% load i18n %} {% load i18n %}
{% block content %} {% block content %}
{% for uf, dados in report_data.items %}
{% trans "Resultado da sincronização dos dados de serviços de registro do SIGI com os registros encontrados no DNS." %} {% if dados.erros or dados.infos or dados.sumario.total > 0 or dados.sumario.novos > 0 or dados.sumario.atualizados > 0 or dados.sumario.desativados > 0 or dados.sumario.ignorados > 0 %}
* {% trans "Data/hora de execução" %}: {% now 'SHORT_DATETIME_FORMAT' %}
{% for uf, dados in log.items %}
**{{ uf|upper }}** **{{ uf|upper }}**
========================================= =========================================
{% if dados.sumario.total > 0 or dados.sumario.novos > 0 or dados.sumario.atualizados > 0 or dados.sumario.desativados > 0 or dados.sumario.ignorados > 0%}
**SUMÁRIO:** **{% trans "SUMÁRIO" %}:**
- Total de registros no DNS: {{ dados.sumario.total }} - Total de registros no DNS: {{ dados.sumario.total }}
- Registros criados no SIGI: {{ dados.sumario.novos }} - Registros criados no SIGI: {{ dados.sumario.novos }}
- Registros atualizados no SIGI: {{ dados.sumario.atualizados }} - Registros atualizados no SIGI: {{ dados.sumario.atualizados }}
- Registros desativados no SIGI: {{ dados.sumario.desativados }} - Registros desativados no SIGI: {{ dados.sumario.desativados }}
- Registros do DNS ignorados: {{ dados.sumario.ignorados }} - Registros do DNS ignorados: {{ dados.sumario.ignorados }}
{% endif %}
{% if dados.erros %}
**{% trans "ERROS" %}:**
**ERROS:**
{% for m in dados.erros %} {% for m in dados.erros %}
* {{ m }} * {{ m }}
{% empty %}
*{% trans "Nenhum erro encontrado" %}*
{% endfor %} {% endfor %}
{% endif %}
{% if dados.infos %}
**{% trans "INFORMAÇÕES ADICIONAIS" %}** **{% trans "INFORMAÇÕES ADICIONAIS" %}:**
{% for m in dados.infos %} {% for m in dados.infos %}
* {{ m }} * {{ m }}
{% empty %}
*{% trans "Nenhuma informação adicional gerada" %}*
{% endfor %} {% endfor %}
{% endif %}
{% endif %}
{% endfor %} {% endfor %}
{% endblock content %} {% endblock content %}

11
sigi/apps/servicos/templates/servicos/emails/report_sincroniza_rancher.rst

@ -1,16 +1,11 @@
{% extends 'emails/base_email.rst' %} {% extends 'emails/base_report.rst' %}
{% load i18n %} {% load i18n %}
{% block content %} {% block content %}
{% trans "Resultado da sincronização dos dados de serviços do SIGI com as instâncias instaladas no Rancher." %}
* {% trans "Data/hora de execução" %}: {% now 'SHORT_DATETIME_FORMAT' %}
**{% trans "ERROS ENCONTRADOS" %}** **{% trans "ERROS ENCONTRADOS" %}**
===================== =====================
{% for tipo, mensagens in erros.items %} {% for tipo, mensagens in report_data.erros.items %}
**{{ tipo.nome|upper }} - {{ tipo.sigla|upper }}** **{{ tipo.nome|upper }} - {{ tipo.sigla|upper }}**
{% for m in mensagens %} {% for m in mensagens %}
* {{ m }} * {{ m }}
@ -22,7 +17,7 @@
**{% trans "INFORMAÇÕES ADICIONAIS" %}** **{% trans "INFORMAÇÕES ADICIONAIS" %}**
========================== ==========================
{% for tipo, mensagens in infos.items %} {% for tipo, mensagens in report_data.infos.items %}
{{ tipo.nome|upper }} - {{ tipo.sigla|upper }} {{ tipo.nome|upper }} - {{ tipo.sigla|upper }}
{% for m in mensagens %} {% for m in mensagens %}
* {{ m }} * {{ m }}

34
sigi/apps/servidores/migrations/0012_add_user_interlegis.py

@ -0,0 +1,34 @@
# Generated by Django 4.1.5 on 2023-01-26 14:35
from django.db import migrations
from django.contrib.auth import get_user_model
def forwards_migration(apps, schema_editor):
Servidor = apps.get_model("servidores", "Servidor")
User = get_user_model()
sigi = Servidor.objects.get(sigi=True)
if sigi.user is not None:
# everything is already fine
return
try:
usuario = User.objects.get_by_natural_key("interlegis")
except User.DoesNotExist:
usuario = User.objects.create_superuser("interlegis")
sigi.user_id = usuario.id
sigi.save()
class Migration(migrations.Migration):
dependencies = [
("servidores", "0011_add_servidor_sigi"),
]
operations = [
migrations.RunPython(forwards_migration, migrations.RunPython.noop),
]

112
sigi/apps/utils/mixins.py

@ -1,13 +1,20 @@
import datetime
import docutils.core
import traceback
from collections import OrderedDict from collections import OrderedDict
from functools import update_wrapper from functools import update_wrapper
from django import forms from django import forms
from django.contrib import admin from django.contrib import admin
from django.contrib.admin import helpers from django.contrib.admin import helpers
from django.contrib.admin.models import LogEntry, ADDITION, CHANGE
from django.contrib.admin.options import csrf_protect_m from django.contrib.admin.options import csrf_protect_m
from django.contrib.admin.utils import pretty_name from django.contrib.admin.utils import pretty_name
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import PermissionDenied, ImproperlyConfigured from django.core.exceptions import PermissionDenied, ImproperlyConfigured
from django.core.mail import mail_admins
from django.http import Http404 from django.http import Http404
from django.http.response import HttpResponse, HttpResponseRedirect from django.http.response import HttpResponse, HttpResponseRedirect
from django.template.loader import render_to_string
from django.template.response import TemplateResponse from django.template.response import TemplateResponse
from django.urls import path from django.urls import path
from django.utils.translation import gettext as _, ngettext from django.utils.translation import gettext as _, ngettext
@ -19,6 +26,10 @@ from import_export.signals import post_export
from sigi.apps.utils import field_label from sigi.apps.utils import field_label
class MisconfiguredError(Exception):
pass
class ValueField(Field): class ValueField(Field):
def get_value(self, obj): def get_value(self, obj):
if self.attribute is None: if self.attribute is None:
@ -323,3 +334,104 @@ class ReturnMixin:
if self._return_path: if self._return_path:
return HttpResponseRedirect(self._return_path) return HttpResponseRedirect(self._return_path)
return response return response
class JobReportMixin:
error_report_template = "emails/report_error.rst"
report_template = "emails/base_report.rst"
report_data = None
sys_user = None
def execute(self):
start_time = datetime.datetime.now()
try:
from sigi.apps.servidores.models import Servidor
self.sys_user = Servidor.objects.get(sigi=True).user
except Exception:
pass
try:
self.do_job()
except Exception as e:
self.report_error(e)
return
end_time = datetime.datetime.now()
self.report(start_time, end_time)
def do_job(self):
raise NotImplementedError("Job needs to implement the 'do_job' method")
def _admin_log(self, object, action_flag, message=""):
if self.sys_user is None:
return # No admin log
LogEntry.objects.log_action(
user_id=self.sys_user.id,
content_type_id=ContentType.objects.get_for_model(type(object)).pk,
object_id=object.id,
object_repr=str(object),
action_flag=action_flag,
change_message=message,
)
def admin_log_addition(self, object, message=""):
self._admin_log(object, ADDITION, message)
def admin_log_change(self, object, message=""):
self._admin_log(object, CHANGE, message)
def report_error(self, error):
rst = render_to_string(
self.error_report_template,
{
"title": self.help,
"traceback": traceback.format_exception(error),
},
)
html = docutils.core.publish_string(
rst,
writer_name="html5",
settings_overrides={
"input_encoding": "unicode",
"output_encoding": "unicode",
},
)
mail_admins(
subject=self.help,
message=rst,
html_message=html,
fail_silently=True,
)
print(rst)
def report(self, start_time, end_time):
if self.report_data is None:
raise MisconfiguredError(
"Job needs to define 'report_data' property"
)
rst = render_to_string(
self.report_template,
{
"title": self.help,
"start_time": start_time,
"end_time": end_time,
"report_data": self.report_data,
},
)
html = docutils.core.publish_string(
rst,
writer_name="html5",
settings_overrides={
"input_encoding": "unicode",
"output_encoding": "unicode",
},
)
mail_admins(
subject=f"JOB: {self.help}",
message=rst,
html_message=html,
fail_silently=True,
)
print(rst)

1
sigi/templates/emails/base_email.rst

@ -2,6 +2,7 @@
{% for s in title %}={% endfor %} {% for s in title %}={% endfor %}
{{ title }} {{ title }}
{% for s in title %}={% endfor %} {% for s in title %}={% endfor %}
{% endblock title %} {% endblock title %}

17
sigi/templates/emails/base_report.rst

@ -0,0 +1,17 @@
{% extends 'emails/base_email.rst' %}
{% load i18n %}
{% block title %}
{{ block.super }}
**{% trans "Início:" %} {{ start_time|date:"SHORT_DATETIME_FORMAT" }}**
**{% trans "Término:" %} {{ end_time|date:"SHORT_DATETIME_FORMAT" }}**
{% endblock %}
{% block content %}
{% trans "RESULTADO" %}
=========
{% for row in report_data %}{{ row }}
{% endfor %}
{% endblock content %}

3
sigi/templates/emails/report_error.rst

@ -2,7 +2,7 @@
{% load i18n %} {% load i18n %}
{% block content %} {% block content %}
{% blocktrans %}UM ERRO OCORREU NO PROCESSO DE IMPORTAÇÃO DE {{ process_name }}{% endblocktrans %} {% blocktrans %}UM ERRO OCORREU NA EXECUÇÃO DA TAREFA{% endblocktrans %}
{% trans "VERIFIQUE O LOG DO SIGI PARA MAIORES DETALHES" %} {% trans "VERIFIQUE O LOG DO SIGI PARA MAIORES DETALHES" %}
@ -11,6 +11,7 @@
::{% autoescape off %}{% for error_row in traceback %} ::{% autoescape off %}{% for error_row in traceback %}
{{ error_row }} {{ error_row }}
{% endfor %} {% endfor %}
{% endautoescape %} {% endautoescape %}
{% endblock content %} {% endblock content %}
Loading…
Cancel
Save