mirror of https://github.com/interlegis/sigi.git
Sesóstris Vieira
10 months ago
29 changed files with 1380 additions and 147 deletions
@ -0,0 +1,605 @@ |
|||
import requests |
|||
import datetime |
|||
from django.conf import settings |
|||
from django.db.models import Q |
|||
from django.utils import timezone |
|||
from django.utils.formats import localize |
|||
from django.utils.translation import gettext as _ |
|||
from django_extensions.management.jobs import HourlyJob |
|||
from sigi.apps.utils.management.jobs import JobReportMixin |
|||
from sigi.apps.espacos.models import ( |
|||
Espaco, |
|||
Recurso, |
|||
Reserva, |
|||
) |
|||
|
|||
DEPARA_SITUACAO = { |
|||
"Aguardando análise": Reserva.STATUS_ATIVO, |
|||
"Excluída": None, # Será tratado como caso especial |
|||
"Reserva aprovada": Reserva.STATUS_ATIVO, |
|||
"Reserva cancelada": Reserva.STATUS_CANCELADO, |
|||
"Reserva rejeitada": Reserva.STATUS_CANCELADO, |
|||
} |
|||
|
|||
|
|||
class Job(JobReportMixin, HourlyJob): |
|||
help = "Sincroniza dados do sistema de reserva de salas" |
|||
report_data = [] |
|||
resumo = [] |
|||
infos = [] |
|||
erros = [] |
|||
|
|||
@property |
|||
def auth_data(self): |
|||
return ( |
|||
settings.RESERVA_SALA_API_USER, |
|||
settings.RESERVA_SALA_API_PASSWORD, |
|||
) |
|||
|
|||
def do_job(self): |
|||
self.resumo = [] |
|||
self.infos = [] |
|||
self.erros = [] |
|||
|
|||
if ( |
|||
settings.RESERVA_SALA_BASE_URL is None |
|||
or settings.RESERVA_SALA_API_USER is None |
|||
or settings.RESERVA_SALA_API_PASSWORD is None |
|||
): |
|||
# Acesso ao sistema não configurado. Não fazer nada |
|||
return |
|||
|
|||
self.carrega_salas() |
|||
self.carrega_recursos() |
|||
self.carrega_reservas() |
|||
|
|||
def carrega_salas(self): |
|||
tit = ["", "\t*Carga de salas*", ""] |
|||
self.infos.extend(tit) |
|||
self.erros.extend(tit) |
|||
self.resumo.extend(tit) |
|||
|
|||
tot_novas = 0 |
|||
tot_erros = 0 |
|||
tot_atualizadas = 0 |
|||
req = requests.get( |
|||
settings.RESERVA_SALA_BASE_URL + "salas", auth=self.auth_data |
|||
) |
|||
if not req.ok: |
|||
self.erros.append( |
|||
_( |
|||
"\t* Erro de autenticação na API do sistema de reserva " |
|||
f"de salas, com a mensagem *{req.reason}*" |
|||
) |
|||
) |
|||
return |
|||
for sala in req.json(): |
|||
try: |
|||
espaco = Espaco.objects.get(id_sala=sala["id"]) |
|||
except Espaco.DoesNotExist: |
|||
# Criar espaço |
|||
espaco = Espaco( |
|||
nome=sala["nome"], |
|||
sigla=sala["nome"][:20], |
|||
descricao=sala["nome"], |
|||
local=sala["local"], |
|||
capacidade=sala["capacidade"], |
|||
id_sala=sala["id"], |
|||
) |
|||
espaco.save() |
|||
self.infos.append( |
|||
_( |
|||
f"\t* Criado espaço *{espaco.id}* para a " |
|||
f"sala *{sala['id']} - {sala['nome']}*" |
|||
) |
|||
) |
|||
tot_novas += 1 |
|||
continue |
|||
except Espaco.MultipleObjectsReturned: |
|||
self.erros.append( |
|||
_( |
|||
"\t* Existe mais de um espaço com o mesmo ID de sala. " |
|||
"Isso deve ser corrigido manualmente no SIGI. " |
|||
f"id_sala={sala['id']}" |
|||
) |
|||
) |
|||
tot_erros += 1 |
|||
continue |
|||
# verifica se precisa atualizar os dados do espaço |
|||
jespaco = { |
|||
"id": espaco.id_sala, |
|||
"nome": espaco.nome, |
|||
"local": espaco.local, |
|||
"capacidade": espaco.capacidade, |
|||
} |
|||
if sorted(sala) != sorted(jespaco): |
|||
# Atualizar o espaço |
|||
espaco.sala_id = sala["id"] |
|||
espaco.nome = sala["nome"] |
|||
espaco.local = sala["local"] |
|||
espaco.capacidade = sala["capacidade"] |
|||
espaco.save() |
|||
self.infos.append( |
|||
_( |
|||
f"\t* Espaço *{espaco.id}* atualizado com novos dados " |
|||
f"da sala *{sala['id']}*" |
|||
) |
|||
) |
|||
tot_atualizadas += 1 |
|||
self.resumo.append( |
|||
_(f"\t* Total de salas processadas: {len(req.json())}") |
|||
) |
|||
self.resumo.append(_(f"\t* Novos espaços criados: {tot_novas}")) |
|||
self.resumo.append(_(f"\t* Espaços atualizados: {tot_atualizadas}")) |
|||
self.resumo.append(_(f"\t* Erros encontrados nas salas: {tot_erros}")) |
|||
|
|||
def carrega_recursos(self): |
|||
tit = ["", "\t*Carga de recursos*", ""] |
|||
self.infos.extend(tit) |
|||
self.erros.extend(tit) |
|||
self.resumo.extend(tit) |
|||
|
|||
tot_novas = 0 |
|||
tot_erros = 0 |
|||
tot_atualizadas = 0 |
|||
|
|||
req = requests.get( |
|||
settings.RESERVA_SALA_BASE_URL + "equipamentos", |
|||
auth=self.auth_data, |
|||
) |
|||
if not req.ok: |
|||
self.erros.append( |
|||
_( |
|||
"\t* Erro na API do sistema de reserva ao ler " |
|||
f"equipamentos, com a mensagem *{req.reason}*" |
|||
) |
|||
) |
|||
return |
|||
|
|||
for equipamento in req.json(): |
|||
if equipamento["status"] == "Não": |
|||
# Não importar |
|||
continue |
|||
try: |
|||
recurso = Recurso.objects.get(id_equipamento=equipamento["id"]) |
|||
except Recurso.DoesNotExist: |
|||
recurso = Recurso( |
|||
nome=equipamento["nome"], |
|||
sigla=equipamento["nome"][:20], |
|||
descricao=equipamento["nome"], |
|||
id_equipamento=equipamento["id"], |
|||
) |
|||
recurso.save() |
|||
self.infos.append( |
|||
f"\t* Recurso *{recurso}* criado a partir do equipamento *{equipamento['id']} - {equipamento['nome']}*" |
|||
) |
|||
tot_novas += 1 |
|||
continue |
|||
except Recurso.MultipleObjectsReturned: |
|||
lista = ", ".join( |
|||
[ |
|||
str(r) |
|||
for r in Recurso.objects.filter( |
|||
id_equipamento=equipamento["id"] |
|||
) |
|||
] |
|||
) |
|||
self.erros.append( |
|||
f"\t* O equipamento *{equipamento['id']} - " |
|||
f"{equipamento['nome']}* possui os seguintes recursos " |
|||
f"com mesmo ID no SIGI: *{lista}*" |
|||
) |
|||
tot_erros += 1 |
|||
continue |
|||
if equipamento["nome"] != recurso.nome: |
|||
recurso.nome = equipamento["nome"] |
|||
recurso.save() |
|||
self.infos.append( |
|||
f"\t* Recurso *{str(recurso)}* atualizado com as alterações " |
|||
f"do equipamento *{equipamento['id']}*" |
|||
) |
|||
tot_atualizadas += 1 |
|||
|
|||
self.resumo.append( |
|||
_(f"\t* Total de equipamentos processados: {len(req.json())}") |
|||
) |
|||
self.resumo.append(_(f"\t* Novos recursos criados: {tot_novas}")) |
|||
self.resumo.append(_(f"\t* Recursos atualizados: {tot_atualizadas}")) |
|||
self.resumo.append( |
|||
_(f"\t* Erros encontrados nos equipamentos: {tot_erros}") |
|||
) |
|||
|
|||
def carrega_reservas( |
|||
self, |
|||
ontem=(timezone.localdate() - timezone.timedelta(days=1)).isoformat(), |
|||
): |
|||
tit = ["", "\t*Carga de reservas*", ""] |
|||
self.infos.extend(tit) |
|||
self.erros.extend(tit) |
|||
self.resumo.extend(tit) |
|||
|
|||
tot_processadas = 0 |
|||
tot_novas = 0 |
|||
tot_excluidas = 0 |
|||
tot_erros = 0 |
|||
tot_atualizadas = 0 |
|||
|
|||
for espaco in Espaco.objects.exclude(id_sala=None): |
|||
req = requests.get( |
|||
settings.RESERVA_SALA_BASE_URL |
|||
+ f"salas/{espaco.id_sala}/reservas/datas?dataInicio={ontem}", |
|||
auth=self.auth_data, |
|||
) |
|||
if not req.ok: |
|||
self.erros.append( |
|||
_( |
|||
"\t* Erro na API do sistema de reserva ao ler " |
|||
f"reservas da sala *{espaco.id_sala}*, com data de " |
|||
f"início maior que *{ontem}*, " |
|||
f"com a mensagem *{req.reason}*" |
|||
) |
|||
) |
|||
continue |
|||
tot_processadas += len(req.json()) |
|||
for reserva in req.json(): |
|||
# Hack sujo para campos igual a None |
|||
if reserva["horaInicio"] is None: |
|||
reserva["horaInicio"] = "00:00:00" |
|||
if reserva["horaFim"] is None: |
|||
reserva["horaFim"] = "23:59:59" |
|||
if reserva["descricao"] is None: |
|||
reserva["descricao"] = "" |
|||
if reserva["informacao"] is None: |
|||
reserva["informacao"] = "" |
|||
if reserva["ramal"] is None: |
|||
reserva["ramal"] = "" |
|||
# Hack sujo para strings muito grandes |
|||
reserva["evento"] = reserva["evento"][:100] |
|||
reserva["coordenador"] = reserva["coordenador"][:100] |
|||
reserva["ramal"] = reserva["ramal"][:100] |
|||
|
|||
data_inicio = datetime.date.fromisoformat( |
|||
reserva["dataInicio"] |
|||
) |
|||
hora_inicio = datetime.time.fromisoformat( |
|||
reserva["horaInicio"] |
|||
) |
|||
data_termino = datetime.date.fromisoformat(reserva["dataFim"]) |
|||
hora_termino = datetime.time.fromisoformat(reserva["horaFim"]) |
|||
status = DEPARA_SITUACAO[reserva["situacao"]] |
|||
# Tratar reservas excluídas no sistema de reservas |
|||
if reserva["situacao"] == "Excluída": |
|||
res = Reserva.objects.filter( |
|||
id_reserva=reserva["id"] |
|||
).delete()[1] |
|||
if "espacos.Reserva" in res: |
|||
tot_excluidas += res["espacos.Reserva"] |
|||
continue |
|||
# Tratar os demais casos |
|||
try: |
|||
reserva_sigi = Reserva.objects.get( |
|||
id_reserva=reserva["id"] |
|||
) |
|||
except Reserva.DoesNotExist: |
|||
conflitos = self.verifica_conflito( |
|||
espaco, |
|||
data_inicio, |
|||
data_termino, |
|||
hora_inicio, |
|||
hora_termino, |
|||
) |
|||
if conflitos: |
|||
# Verificar se existe um conflitante com as mesmas |
|||
# datas/horas e coordenador. |
|||
reserva_sigi = Reserva.objects.filter( |
|||
espaco=espaco, |
|||
id_reserva=None, |
|||
data_inicio=data_inicio, |
|||
data_termino=data_termino, |
|||
hora_inicio=hora_inicio, |
|||
hora_termino=hora_termino, |
|||
contato=reserva["coordenador"], |
|||
).first() |
|||
if reserva_sigi: |
|||
# Se existe, então é a mesma, bastando vincular |
|||
reserva_sigi.id_reserva = reserva["id"] |
|||
reserva_sigi.save() |
|||
# Deixa seguir para atualizar outros campos |
|||
else: |
|||
# Criar a reserva conflitante |
|||
if status != Reserva.STATUS_CANCELADO: |
|||
status = Reserva.STATUS_CONFLITO |
|||
reserva_sigi = self.cria_reserva( |
|||
reserva, |
|||
espaco, |
|||
status, |
|||
data_inicio, |
|||
data_termino, |
|||
hora_inicio, |
|||
hora_termino, |
|||
) |
|||
if reserva_sigi.status == Reserva.STATUS_CONFLITO: |
|||
# Reportar como erro se a reserva é conflitante |
|||
lista = ", ".join([str(c) for c in conflitos]) |
|||
self.erros.append( |
|||
f"\t* A reserva *{reserva['id']} - " |
|||
f"{reserva['evento']}" |
|||
"* do sistema de reservas conflita com " |
|||
"a(s) seguinte(s) reserva(s) do SIGI: " |
|||
f"*{lista}*. e foi copiada para o SIGI " |
|||
"como conflitante." |
|||
) |
|||
tot_erros += 1 |
|||
else: |
|||
# Reportar como nova se o status for cancelada |
|||
self.infos.append( |
|||
f"\t* Reserva *{str(reserva_sigi)}* " |
|||
"criada no SIGI a partir da reserva " |
|||
f"*{reserva['descricao']}* " |
|||
"do sistema de reservas" |
|||
) |
|||
tot_novas += 1 |
|||
continue |
|||
else: # Não há conflitos, basta criar a reserva |
|||
reserva_sigi = self.cria_reserva( |
|||
reserva, |
|||
espaco, |
|||
status, |
|||
data_inicio, |
|||
data_termino, |
|||
hora_inicio, |
|||
hora_termino, |
|||
) |
|||
self.infos.append( |
|||
f"\t* Reserva *{str(reserva_sigi)}* criada no SIGI" |
|||
f" a partir da reserva *{reserva['descricao']}* " |
|||
"do sistema de reservas" |
|||
) |
|||
tot_novas += 1 |
|||
continue |
|||
except Reserva.MultipleObjectsReturned: |
|||
# Esse erro nunca poderia acontecer, mas ... |
|||
self.erros.append( |
|||
_( |
|||
"\t* Existe mais de uma reserva no SIGI com o " |
|||
"mesmo ID de reserva do sistema de reservas. " |
|||
"Isso deve ser corrigido manualmente no SIGI. " |
|||
f"id_reserva=*{reserva['id']}*" |
|||
) |
|||
) |
|||
tot_erros += 1 |
|||
continue |
|||
# Reserva foi encontrada no SIGI. Podemos atualizar |
|||
atualizou = False |
|||
if ( |
|||
reserva_sigi.data_inicio != data_inicio |
|||
or reserva_sigi.data_termino != data_termino |
|||
or reserva_sigi.hora_inicio != hora_inicio |
|||
or reserva_sigi.hora_termino != hora_termino |
|||
): |
|||
# Se mudou de data/hora, pode ocorrer conflitos |
|||
conflitos = self.verifica_conflito( |
|||
espaco, |
|||
data_inicio, |
|||
data_termino, |
|||
hora_inicio, |
|||
hora_termino, |
|||
reserva_sigi, |
|||
) |
|||
if not conflitos: |
|||
# Nenhum conflito, podemos alterar as datas de boa |
|||
reserva_sigi.data_inicio = data_inicio |
|||
reserva_sigi.data_termino = data_termino |
|||
reserva_sigi.hora_inicio = hora_inicio |
|||
reserva_sigi.hora_termino = hora_termino |
|||
self.infos.append( |
|||
f"\t* *{str(reserva_sigi)}* mudou para o período " |
|||
f"de *{localize(data_inicio)} " |
|||
f"{localize(hora_inicio)}* a " |
|||
f"*{localize(data_termino)} " |
|||
f"{localize(hora_termino)}*" |
|||
) |
|||
atualizou = True |
|||
else: |
|||
lista = ", ".join([str(c) for c in conflitos]) |
|||
self.erros.append( |
|||
f"\t* A reserva *{reserva['evento']}* no sistema " |
|||
"de reservas mudou de data, mas esta mudança não " |
|||
"pode ser aplicada no SIGI pois gera conflito " |
|||
"com a(s) seguinte(s) outra(s) reserva(s): " |
|||
f"*{lista}*" |
|||
) |
|||
tot_erros += 1 |
|||
continue |
|||
# Verificar outras atualizações |
|||
if reserva_sigi.status != status: |
|||
reserva_sigi.status = status |
|||
self.infos.append( |
|||
f"\t* A reserva SIGI *{str(reserva_sigi)}* mudou de " |
|||
f"status para *{reserva_sigi.get_status_display()}*" |
|||
) |
|||
atualizou = True |
|||
rr = ( |
|||
reserva["evento"], |
|||
reserva["quantidadeAlunos"], |
|||
"\n".join( |
|||
[reserva["descricao"], str(reserva["informacao"])] |
|||
), |
|||
reserva["coordenador"], |
|||
reserva["coordenador"], |
|||
reserva["ramal"], |
|||
) |
|||
rs = ( |
|||
reserva_sigi.proposito, |
|||
reserva_sigi.total_participantes, |
|||
reserva_sigi.informacoes, |
|||
reserva_sigi.solicitante, |
|||
reserva_sigi.contato, |
|||
reserva_sigi.telefone_contato, |
|||
) |
|||
if rr != rs: |
|||
# Campos descritivos foram alterados |
|||
reserva_sigi.proposito = reserva["evento"] |
|||
reserva_sigi.total_participantes = reserva[ |
|||
"quantidadeAlunos" |
|||
] |
|||
reserva_sigi.informacoes = "\n".join( |
|||
[reserva["descricao"], str(reserva["informacao"])] |
|||
) |
|||
reserva_sigi.solicitante = reserva["coordenador"] |
|||
reserva_sigi.contato = reserva["coordenador"] |
|||
reserva_sigi.telefone_contato = reserva["ramal"] |
|||
reserva_sigi.save() |
|||
self.infos.append( |
|||
f"\t* A reserva SIGI *{str(reserva_sigi)}* foi " |
|||
"atualizada com as alterações da " |
|||
f"reserva *{reserva['id']}*" |
|||
) |
|||
atualizou = True |
|||
if self.recursos_solicitados( |
|||
reserva_sigi, reserva["equipamentos"] |
|||
): |
|||
self.infos.append( |
|||
"\t* Os recursos solicitados da reserva SIGI " |
|||
f"*{str(reserva_sigi)}* foram atualizados" |
|||
) |
|||
atualizou = True |
|||
if atualizou: |
|||
tot_atualizadas += 1 |
|||
self.resumo.append( |
|||
_(f"\t* Total de reservas processadas: {tot_processadas}") |
|||
) |
|||
self.resumo.append(_(f"\t* Novas reservas criadas: {tot_novas}")) |
|||
self.resumo.append(_(f"\t* Reservas atualizados: {tot_atualizadas}")) |
|||
self.resumo.append(_(f"\t* Reservas excluídas: {tot_excluidas}")) |
|||
self.resumo.append( |
|||
_(f"\t* Erros encontrados nas reservas: {tot_erros}") |
|||
) |
|||
|
|||
def verifica_conflito( |
|||
self, |
|||
espaco, |
|||
data_inicio, |
|||
data_termino, |
|||
hora_inicio, |
|||
hora_termino, |
|||
reserva_sigi=None, |
|||
): |
|||
# Verifica se existe alguma reserva do espaço que conflita com o |
|||
# período desejado |
|||
reservas_conflitantes = Reserva.objects.exclude( |
|||
status=Reserva.STATUS_CANCELADO |
|||
).filter( |
|||
espaco=espaco, |
|||
data_inicio__lte=data_termino, |
|||
data_termino__gte=data_inicio, |
|||
hora_inicio__lte=hora_termino, |
|||
hora_termino__gte=hora_inicio, |
|||
) |
|||
if reserva_sigi: |
|||
reservas_conflitantes = reservas_conflitantes.exclude( |
|||
id=reserva_sigi.id |
|||
) |
|||
|
|||
if not reservas_conflitantes.exists(): |
|||
return None |
|||
else: |
|||
return reservas_conflitantes.all() |
|||
|
|||
def cria_reserva( |
|||
self, |
|||
reserva, |
|||
espaco, |
|||
status, |
|||
data_inicio, |
|||
data_termino, |
|||
hora_inicio, |
|||
hora_termino, |
|||
): |
|||
reserva_sigi = Reserva( |
|||
status=status, |
|||
espaco=espaco, |
|||
proposito=reserva["evento"], |
|||
virtual=False, |
|||
total_participantes=reserva["quantidadeAlunos"], |
|||
data_pedido=timezone.localdate(), |
|||
data_inicio=data_inicio, |
|||
data_termino=data_termino, |
|||
hora_inicio=hora_inicio, |
|||
hora_termino=hora_termino, |
|||
informacoes="\n".join( |
|||
[ |
|||
reserva["descricao"], |
|||
str(reserva["informacao"]), |
|||
] |
|||
), |
|||
solicitante=reserva["coordenador"], |
|||
contato=reserva["coordenador"], |
|||
telefone_contato=reserva["ramal"], |
|||
id_reserva=reserva["id"], |
|||
data_ult_atualizacao=timezone.localtime(), |
|||
) |
|||
reserva_sigi.save() |
|||
self.recursos_solicitados(reserva_sigi, reserva["equipamentos"]) |
|||
return reserva_sigi |
|||
|
|||
def recursos_solicitados(self, reserva_sigi, equipamentos_solicitados): |
|||
atualizou = False |
|||
for equipamento in equipamentos_solicitados: |
|||
recurso = Recurso.objects.filter( |
|||
id_equipamento=equipamento["id"] |
|||
).first() |
|||
if not recurso: |
|||
# Cria um novo recurso na tabela de recursos |
|||
recurso = Recurso( |
|||
nome=equipamento["nome"], |
|||
sigla=equipamento["nome"][:20], |
|||
descricao=equipamento["nome"], |
|||
id_equipamento=equipamento["id"], |
|||
) |
|||
recurso.save() |
|||
atualizou = True |
|||
continue |
|||
|
|||
if not reserva_sigi.recursosolicitado_set.filter( |
|||
recurso=recurso |
|||
).exists(): |
|||
reserva_sigi.recursosolicitado_set.create( |
|||
recurso=recurso, quantidade=1 |
|||
) |
|||
atualizou = True |
|||
return atualizou |
|||
|
|||
def report(self, start_time, end_time): |
|||
self.report_data = [ |
|||
"", |
|||
"", |
|||
"RESUMO", |
|||
"------", |
|||
"", |
|||
"", |
|||
] |
|||
self.report_data.extend(self.resumo) |
|||
self.report_data.extend( |
|||
[ |
|||
"", |
|||
"", |
|||
"ERROS ENCONTRADOS", |
|||
"-----------------", |
|||
"", |
|||
"", |
|||
] |
|||
) |
|||
self.report_data.extend(self.erros) |
|||
self.report_data.extend( |
|||
[ |
|||
"", |
|||
"", |
|||
"MAIS INFORMAÇÕES", |
|||
"----------------", |
|||
"", |
|||
"", |
|||
] |
|||
) |
|||
self.report_data.extend(self.infos) |
|||
super().report(start_time, end_time) |
@ -0,0 +1,129 @@ |
|||
# Generated by Django 4.2.7 on 2024-03-12 12:36 |
|||
|
|||
from django.db import migrations, models |
|||
import django.utils.timezone |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
("espacos", "0004_alter_reserva_status"), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.AlterModelOptions( |
|||
name="reserva", |
|||
options={ |
|||
"ordering": ( |
|||
"data_inicio", |
|||
"hora_inicio", |
|||
"espaco", |
|||
"proposito", |
|||
), |
|||
"verbose_name": "reserva", |
|||
"verbose_name_plural": "reservas", |
|||
}, |
|||
), |
|||
migrations.RenameField( |
|||
model_name="reserva", |
|||
old_name="inicio", |
|||
new_name="data_inicio", |
|||
), |
|||
migrations.RenameField( |
|||
model_name="reserva", |
|||
old_name="termino", |
|||
new_name="data_termino", |
|||
), |
|||
migrations.AddField( |
|||
model_name="espaco", |
|||
name="capacidade", |
|||
field=models.PositiveBigIntegerField( |
|||
default=0, |
|||
help_text="Número de acentos ou lotação máxima do espaço", |
|||
verbose_name="capacidade", |
|||
), |
|||
), |
|||
migrations.AddField( |
|||
model_name="espaco", |
|||
name="id_sala", |
|||
field=models.PositiveIntegerField( |
|||
blank=True, |
|||
help_text="ID da sala no sistema de reserva de salas do ILB", |
|||
null=True, |
|||
verbose_name="ID da sala", |
|||
), |
|||
), |
|||
migrations.AddField( |
|||
model_name="espaco", |
|||
name="reserva_eventos", |
|||
field=models.BooleanField( |
|||
default=False, |
|||
help_text="Pode ser reservado para eventos cadastrados no SIGI", |
|||
verbose_name="reserva para eventos", |
|||
), |
|||
), |
|||
migrations.AddField( |
|||
model_name="recurso", |
|||
name="id_equipamento", |
|||
field=models.PositiveBigIntegerField( |
|||
blank=True, |
|||
help_text="ID do equipamento no sistema de reserva de salas do ILB", |
|||
null=True, |
|||
unique=True, |
|||
verbose_name="ID do equipamento", |
|||
), |
|||
), |
|||
migrations.AddField( |
|||
model_name="reserva", |
|||
name="data_ult_atualizacao", |
|||
field=models.DateTimeField( |
|||
blank=True, |
|||
editable=False, |
|||
null=True, |
|||
verbose_name="data da última atualização", |
|||
), |
|||
), |
|||
migrations.AddField( |
|||
model_name="reserva", |
|||
name="hora_inicio", |
|||
field=models.TimeField( |
|||
default=django.utils.timezone.now, |
|||
verbose_name="hora início", |
|||
), |
|||
preserve_default=False, |
|||
), |
|||
migrations.AddField( |
|||
model_name="reserva", |
|||
name="hora_termino", |
|||
field=models.TimeField( |
|||
default=django.utils.timezone.now, |
|||
verbose_name="hora término", |
|||
), |
|||
preserve_default=False, |
|||
), |
|||
migrations.AddField( |
|||
model_name="reserva", |
|||
name="id_reserva", |
|||
field=models.PositiveBigIntegerField( |
|||
blank=True, |
|||
editable=False, |
|||
null=True, |
|||
unique=True, |
|||
verbose_name="ID da reserva", |
|||
), |
|||
), |
|||
migrations.AlterField( |
|||
model_name="reserva", |
|||
name="status", |
|||
field=models.CharField( |
|||
choices=[ |
|||
("A", "Ativo"), |
|||
("C", "Cancelado"), |
|||
("O", "Conflito de datas"), |
|||
], |
|||
default="A", |
|||
max_length=1, |
|||
verbose_name="status", |
|||
), |
|||
), |
|||
] |
@ -0,0 +1,36 @@ |
|||
# Generated by Django 4.2.7 on 2024-03-12 12:39 |
|||
|
|||
from datetime import datetime |
|||
from django.db import migrations |
|||
from django.utils import timezone |
|||
|
|||
|
|||
def forward(apps, schema_editor): |
|||
Reserva = apps.get_model("espacos", "Reserva") |
|||
for reserva in Reserva.objects.all(): |
|||
reserva.hora_inicio = timezone.localtime(reserva.data_inicio).time() |
|||
reserva.hora_termino = timezone.localtime(reserva.data_termino).time() |
|||
reserva.save() |
|||
|
|||
|
|||
def backward(apps, schema_editor): |
|||
Reserva = apps.get_model("espacos", "Reserva") |
|||
for reserva in Reserva.objects.all(): |
|||
reserva.data_inicio = datetime.combine( |
|||
reserva.data_inicio, reserva.hora_inicio |
|||
).replace(tzinfo=timezone.get_current_timezone()) |
|||
reserva.data_termino = datetime.combine( |
|||
reserva.data_termino, reserva.hora_termino |
|||
).replace(tzinfo=timezone.get_current_timezone()) |
|||
reserva.save() |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
("espacos", "0005_alter_reserva_options_and_more"), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.RunPython(forward, backward), |
|||
] |
@ -0,0 +1,23 @@ |
|||
# Generated by Django 4.2.7 on 2024-03-12 12:46 |
|||
|
|||
from django.db import migrations, models |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
("espacos", "0006_separa_hora_da_data"), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.AlterField( |
|||
model_name="reserva", |
|||
name="data_inicio", |
|||
field=models.DateField(verbose_name="data início"), |
|||
), |
|||
migrations.AlterField( |
|||
model_name="reserva", |
|||
name="data_termino", |
|||
field=models.DateField(verbose_name="data término"), |
|||
), |
|||
] |
@ -0,0 +1,34 @@ |
|||
# Generated by Django 4.2.7 on 2024-03-12 19:05 |
|||
from datetime import datetime |
|||
from django.db import migrations |
|||
from django.db import migrations |
|||
from sigi.apps.espacos.jobs.hourly.sincroniza_reservas import Job |
|||
|
|||
|
|||
def forward(apps, schema_editor): |
|||
start = datetime.now() |
|||
Espaco = apps.get_model("espacos", "Espaco") |
|||
DEPARA_SALAS = [(5, 62), (4, 66), (3, 63)] |
|||
|
|||
for espaco_id, id_sala in DEPARA_SALAS: |
|||
espaco = Espaco.objects.get(id=espaco_id) |
|||
espaco.id_sala = id_sala |
|||
espaco.save() |
|||
|
|||
job = Job() |
|||
job.carrega_salas() |
|||
job.carrega_recursos() |
|||
job.carrega_reservas(ontem="2023-04-01") |
|||
job.report(start, datetime.now()) |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
( |
|||
"espacos", |
|||
"0007_alter_reserva_data_inicio_alter_reserva_data_termino", |
|||
), |
|||
] |
|||
|
|||
operations = [migrations.RunPython(forward, migrations.RunPython.noop)] |
@ -0,0 +1,52 @@ |
|||
{% extends 'admin/change_form.html' %} |
|||
{% load i18n admin_urls %} |
|||
|
|||
|
|||
{% block extrastyle %} |
|||
{{ block.super }} |
|||
<style> |
|||
.conflito { |
|||
color: var(--error-fg) !important; |
|||
border-color: var(--error-fg) !important; |
|||
} |
|||
</style> |
|||
{% endblock extrastyle %} |
|||
|
|||
{% block after_related_objects %} |
|||
{% if original.get_conflitantes.exists %} |
|||
<fieldset class="module"> |
|||
<h2 class="conflito">{% translate "Reservas conflitantes" %}</h2> |
|||
<table> |
|||
<thead> |
|||
<tr> |
|||
<th> </th> |
|||
<th scope="row" class="column-status">{% translate "Status" %}</th> |
|||
<th scope="row" class="column-proposito">{% translate "Propósito" %}</th> |
|||
<th scope="row" class="column-inicio">{% translate "Data/hora início" %}</th> |
|||
<th scope="row" class="column-termino">{% translate "Data/hora término" %}</th> |
|||
<th scope="row" class="column-solicitante">{% translate "Senador/autoridade solicitante" %}</th> |
|||
<th scope="row" class="column-contato">{% translate "Pessoa de contato" %}</th> |
|||
<th scope="row" class="column-telefone">{% translate "Telefone de contato" %}</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
{% for conf in original.get_conflitantes %} |
|||
<tr class="form-row {% cycle 'row1' 'row2' %}"> |
|||
<td> |
|||
<a href="{% url opts|admin_urlname:'change' conf.pk %}"> |
|||
<i class="material-icons small-icon" aria-hidden="true" title="Modificar">edit</i> |
|||
</a> |
|||
</td> |
|||
<td>{{ conf.get_status_display }}</td> |
|||
<td>{{ conf.proposito }}</td> |
|||
<td>{{ conf.data_inicio|date:"SHORT_DATE_FORMAT" }} {{ conf.hora_inicio|time }}</td> |
|||
<td>{{ conf.data_termino|date:"SHORT_DATE_FORMAT" }} {{ conf.hora_termino|time }}</td> |
|||
<td>{{ conf.solicitante }}</td> |
|||
<td>{{ conf.contato }}</td> |
|||
<td>{{ conf.telefone_contato }}</td> |
|||
</tr> |
|||
{% endfor %} |
|||
</table> |
|||
</fieldset> |
|||
{% endif %} |
|||
{% endblock %} |
@ -0,0 +1,30 @@ |
|||
# Generated by Django 4.2.7 on 2024-03-12 13:53 |
|||
|
|||
from django.db import migrations, models |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
( |
|||
"eventos", |
|||
"0057_alter_equipe_qtde_diarias_alter_equipe_valor_diaria", |
|||
), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.AddField( |
|||
model_name="evento", |
|||
name="hora_inicio", |
|||
field=models.TimeField( |
|||
blank=True, null=True, verbose_name="hora início" |
|||
), |
|||
), |
|||
migrations.AddField( |
|||
model_name="evento", |
|||
name="hora_termino", |
|||
field=models.TimeField( |
|||
blank=True, null=True, verbose_name="hora término" |
|||
), |
|||
), |
|||
] |
@ -0,0 +1,46 @@ |
|||
# Generated by Django 4.2.7 on 2024-03-12 14:15 |
|||
|
|||
from datetime import datetime |
|||
from django.db import migrations |
|||
from django.utils import timezone |
|||
|
|||
|
|||
def forward(apps, schema_editor): |
|||
Evento = apps.get_model("eventos", "Evento") |
|||
for evento in Evento.objects.all(): |
|||
if evento.data_inicio is not None: |
|||
evento.hora_inicio = timezone.localtime(evento.data_inicio).time() |
|||
if evento.data_termino is not None: |
|||
evento.hora_termino = timezone.localtime( |
|||
evento.data_termino |
|||
).time() |
|||
evento.save() |
|||
|
|||
|
|||
def backward(apps, schema_editor): |
|||
Evento = apps.get_model("eventos", "Evento") |
|||
for evento in Evento.objects.all(): |
|||
if evento.data_inicio is not None and evento.hora_inicio is not None: |
|||
evento.data_inicio = datetime.combine( |
|||
evento.data_inicio, evento.hora_inicio |
|||
).replace(tzinfo=timezone.get_current_timezone()) |
|||
elif evento.data_inicio is not None: |
|||
evento.data_inicio.replace(tzinfo=timezone.get_current_timezone()) |
|||
if evento.data_termino is not None and evento.hora_termino is not None: |
|||
evento.data_termino = datetime.combine( |
|||
evento.data_termino, evento.hora_termino |
|||
).replace(tzinfo=timezone.get_current_timezone()) |
|||
elif evento.data_termino is not None: |
|||
evento.data_termino.replace(tzinfo=timezone.get_current_timezone()) |
|||
evento.save() |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
("eventos", "0058_evento_hora_inicio_evento_hora_termino"), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.RunPython(forward, backward), |
|||
] |
@ -0,0 +1,48 @@ |
|||
# Generated by Django 4.2.7 on 2024-03-12 14:27 |
|||
|
|||
from django.db import migrations |
|||
|
|||
SQL_STMT = "DROP VIEW viw_eventos;" |
|||
SQL_REVERSE_STMT = """ |
|||
create view viw_eventos as |
|||
select e.id, e.nome, e.descricao, e.solicitante, e.data_inicio, e.data_termino, |
|||
e.local, e.publico_alvo, |
|||
(case |
|||
when e.status = 'P' then 'Previsto' |
|||
when e.status = 'O' then 'Autorizado' |
|||
when e.status = 'R' then 'Realizado' |
|||
when e.status = 'C' then 'Cancelado' |
|||
when e.status = 'Q' then 'Sobrestado' |
|||
else e.status -- Fallback retorna a sigla do status |
|||
end) as status, |
|||
e.data_cancelamento, e.motivo_cancelamento, |
|||
e.casa_anfitria_id, o.nome as casa_anfitria, |
|||
o.municipio_id, m.nome as municipio, uf.sigla as uf_sigla, |
|||
uf.nome as uf_nome, t.nome as tipo_evento, |
|||
(case |
|||
when t.categoria='C' then 'Curso' |
|||
when t.categoria='E' then 'Encontro' |
|||
when t.categoria='O' then 'Oficina' |
|||
when t.categoria='S' then 'Seminário' |
|||
when t.categoria='V' then 'Visita' |
|||
end) as categoria, |
|||
e.virtual, e.total_participantes, |
|||
e.data_pedido, e.num_processo, e.observacao, e.turma |
|||
from eventos_evento e |
|||
inner join casas_orgao o on o.id = e.casa_anfitria_id |
|||
inner join contatos_municipio m on m.codigo_ibge = o.municipio_id |
|||
inner join contatos_unidadefederativa uf on uf.codigo_ibge = m.uf_id |
|||
inner join eventos_tipoevento t on t.id = e.tipo_evento_id; |
|||
grant select on viw_eventos to sigi_qs; |
|||
""" |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
("eventos", "0059_separa_hora_da_data"), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.RunSQL(sql=SQL_STMT, reverse_sql=SQL_REVERSE_STMT) |
|||
] |
@ -0,0 +1,27 @@ |
|||
# Generated by Django 4.2.7 on 2024-03-12 14:49 |
|||
|
|||
from django.db import migrations, models |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
("eventos", "0060_drop_viw_eventos"), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.AlterField( |
|||
model_name="evento", |
|||
name="data_inicio", |
|||
field=models.DateField( |
|||
blank=True, null=True, verbose_name="data início" |
|||
), |
|||
), |
|||
migrations.AlterField( |
|||
model_name="evento", |
|||
name="data_termino", |
|||
field=models.DateField( |
|||
blank=True, null=True, verbose_name="data término" |
|||
), |
|||
), |
|||
] |
@ -0,0 +1,48 @@ |
|||
# Generated by Django 4.2.7 on 2024-03-12 14:50 |
|||
|
|||
from django.db import migrations |
|||
|
|||
SQL_STMT = """ |
|||
create view viw_eventos as |
|||
select e.id, e.nome, e.descricao, e.solicitante, (e.data_inicio + e.hora_inicio) as data_inicio, |
|||
(e.data_termino + e.hora_termino) as data_termino, e.local, e.publico_alvo, |
|||
(case |
|||
when e.status = 'P' then 'Previsto' |
|||
when e.status = 'O' then 'Autorizado' |
|||
when e.status = 'R' then 'Realizado' |
|||
when e.status = 'C' then 'Cancelado' |
|||
when e.status = 'Q' then 'Sobrestado' |
|||
else e.status -- Fallback retorna a sigla do status |
|||
end) as status, |
|||
e.data_cancelamento, e.motivo_cancelamento, |
|||
e.casa_anfitria_id, o.nome as casa_anfitria, |
|||
o.municipio_id, m.nome as municipio, uf.sigla as uf_sigla, |
|||
uf.nome as uf_nome, t.nome as tipo_evento, |
|||
(case |
|||
when t.categoria='C' then 'Curso' |
|||
when t.categoria='E' then 'Encontro' |
|||
when t.categoria='O' then 'Oficina' |
|||
when t.categoria='S' then 'Seminário' |
|||
when t.categoria='V' then 'Visita' |
|||
end) as categoria, |
|||
e.virtual, e.total_participantes, |
|||
e.data_pedido, e.num_processo, e.observacao, e.turma |
|||
from eventos_evento e |
|||
inner join casas_orgao o on o.id = e.casa_anfitria_id |
|||
inner join contatos_municipio m on m.codigo_ibge = o.municipio_id |
|||
inner join contatos_unidadefederativa uf on uf.codigo_ibge = m.uf_id |
|||
inner join eventos_tipoevento t on t.id = e.tipo_evento_id; |
|||
grant select on viw_eventos to sigi_qs; |
|||
""" |
|||
SQL_REVERSE_STMT = "DROP VIEW viw_eventos;" |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
("eventos", "0061_alter_evento_data_inicio_alter_evento_data_termino"), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.RunSQL(sql=SQL_STMT, reverse_sql=SQL_REVERSE_STMT) |
|||
] |
Loading…
Reference in new issue