|
|
|
@ -10,24 +10,53 @@ from django.db.models import Q |
|
|
|
|
|
|
|
from sapl.base.models import CasaLegislativa, AppConfig |
|
|
|
from sapl.sessao.models import SessaoPlenaria, OrdemDia, ExpedienteMateria, RegistroVotacao, RegistroLeitura, \ |
|
|
|
PresencaOrdemDia, SessaoPlenariaPresenca, OradorExpediente, VotoParlamentar, AbstractOrdemDia |
|
|
|
PresencaOrdemDia, SessaoPlenariaPresenca, OradorExpediente, VotoParlamentar, AbstractOrdemDia, SessaoPresencaView, \ |
|
|
|
SessaoOradorView, SessaoMateriaVotacaoView |
|
|
|
|
|
|
|
log = logging.getLogger(__name__) |
|
|
|
|
|
|
|
|
|
|
|
def get_dados_painel(pk: int) -> dict: |
|
|
|
app_config = AppConfig.objects.first() |
|
|
|
sessao = SessaoPlenaria.objects.get(id=pk) |
|
|
|
casa = CasaLegislativa.objects.first() |
|
|
|
app_config = AppConfig.objects.first() |
|
|
|
|
|
|
|
if casa and app_config and (bool(casa.logotipo)): |
|
|
|
brasao = casa.logotipo.url \ |
|
|
|
if app_config.mostrar_brasao_painel else None |
|
|
|
ordem_dia = OrdemDia.objects.filter(sessao_plenaria_id=pk, votacao_aberta=True).last() |
|
|
|
expediente = ExpedienteMateria.objects.filter(sessao_plenaria_id=pk, votacao_aberta=True).last() |
|
|
|
|
|
|
|
# FILIACAO |
|
|
|
# { 1: { "id": 1, "nome": "fulano", "filiacao": "aquela"}, ...} |
|
|
|
# [ { "id": 1, "nome": "fulano", "filiacao": "aquela"}, ... ] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Painel |
|
|
|
presentes = SessaoPresencaView.objects.filter(sessao_plenaria_id=4984, |
|
|
|
etapa_sessao='expediente').values_list('parlamentar_id', |
|
|
|
'nome_parlamentar', |
|
|
|
'filiacao', ) |
|
|
|
presentes = [dict(zip(['parlamentar_id', 'nome_parlamentar', 'filiacao'], p)) for p in presentes] |
|
|
|
|
|
|
|
oradores = SessaoOradorView.objects.filter(sessao_plenaria_id=4983, |
|
|
|
etapa_sessao='expediente').values_list('ordem_pronunciamento', |
|
|
|
'nome_parlamentar', |
|
|
|
) |
|
|
|
oradores = [dict(zip(['ordem_pronunciamento', 'nome_parlamentar'], o)) for o in oradores] |
|
|
|
|
|
|
|
votos = SessaoMateriaVotacaoView.objects.get(sessao_plenaria_id=4984, etapa_sessao='ordemdia', materia_id=4148) |
|
|
|
|
|
|
|
# TODO: recover stopwatch state from DB/Cache |
|
|
|
stopwatch = { |
|
|
|
"type": "stopwatch.state", |
|
|
|
"id": "sw:main", |
|
|
|
"status": "running", # "running" | "paused" | "stopped" |
|
|
|
"started_at_ms": 1699990000123, # epoch ms when (re)started |
|
|
|
"elapsed_ms": 5320 |
|
|
|
} |
|
|
|
|
|
|
|
dados_sessao = { |
|
|
|
"type": "data", |
|
|
|
"data": {}, # legacy |
|
|
|
"sessao": { |
|
|
|
"status_painel": sessao.painel_aberto, |
|
|
|
"brasao": brasao, |
|
|
|
"mostrar_voto": app_config.mostrar_voto, |
|
|
|
@ -37,260 +66,20 @@ def get_dados_painel(pk: int) -> dict: |
|
|
|
"sessao_solene": sessao.tipo.nome == "Solene", |
|
|
|
"sessao_finalizada": sessao.finalizada, |
|
|
|
"tema_solene": sessao.tema_solene, |
|
|
|
# "cronometro_aparte": get_cronometro_status(request, "aparte"), |
|
|
|
# "cronometro_discurso": get_cronometro_status(request, "discurso"), |
|
|
|
# "cronometro_ordem": get_cronometro_status(request, "ordem"), |
|
|
|
# "cronometro_consideracoes": get_cronometro_status(request, "consideracoes"), |
|
|
|
"status_painel": False, # TODO: recover from DB **and** move status to other place. |
|
|
|
}, |
|
|
|
"presentes": presentes, |
|
|
|
"oradores": oradores, |
|
|
|
"votacao": votos.total_votos, # TODO unify into single json |
|
|
|
"votos_parlamentar": votos.votos_parlamentares, # TODO: unify into single JSON |
|
|
|
"materia_legislativa_ementa": votos.materia.ementa, |
|
|
|
"stopwatch": stopwatch, # TODO: array of stopwatches |
|
|
|
} |
|
|
|
|
|
|
|
# Caso tenha alguma matéria com votação aberta, ela é mostrada no painel |
|
|
|
# com prioridade para Ordem Dia. |
|
|
|
if ordem_dia: |
|
|
|
dados_sessao.update(get_presentes(pk, ordem_dia)) |
|
|
|
dados_sessao.update(get_votos(ordem_dia, app_config.mostrar_voto)) |
|
|
|
elif expediente: |
|
|
|
dados_sessao.update(get_presentes(pk, expediente)) |
|
|
|
dados_sessao.update(get_votos(expediente, app_config.mostrar_voto)) |
|
|
|
|
|
|
|
# Caso não tenha nenhuma aberta, |
|
|
|
# a matéria a ser mostrada no Painel deve ser a última votada |
|
|
|
last_ordem_voto = RegistroVotacao.objects.filter( |
|
|
|
ordem__sessao_plenaria=sessao).order_by("data_hora").last() |
|
|
|
last_expediente_voto = RegistroVotacao.objects.filter( |
|
|
|
expediente__sessao_plenaria=sessao).order_by("data_hora").last() |
|
|
|
|
|
|
|
last_ordem_leitura = RegistroLeitura.objects.filter( |
|
|
|
ordem__sessao_plenaria=sessao).order_by("data_hora").last() |
|
|
|
last_expediente_leitura = RegistroLeitura.objects.filter( |
|
|
|
expediente__sessao_plenaria=sessao).order_by("data_hora").last() |
|
|
|
|
|
|
|
# Obtém última matéria votada, através do timestamp mais recente |
|
|
|
ordem_expediente = None |
|
|
|
ultimo_timestamp = None |
|
|
|
if last_ordem_voto: |
|
|
|
ordem_expediente = last_ordem_voto.ordem |
|
|
|
ultimo_timestamp = last_ordem_voto.data_hora |
|
|
|
if (last_expediente_voto and ultimo_timestamp and last_expediente_voto.data_hora > ultimo_timestamp) or \ |
|
|
|
(not ultimo_timestamp and last_expediente_voto): |
|
|
|
ordem_expediente = last_expediente_voto.expediente |
|
|
|
ultimo_timestamp = last_expediente_voto.data_hora |
|
|
|
if (last_ordem_leitura and ultimo_timestamp and last_ordem_leitura.data_hora > ultimo_timestamp) or \ |
|
|
|
(not ultimo_timestamp and last_ordem_leitura): |
|
|
|
ordem_expediente = last_ordem_leitura.ordem |
|
|
|
ultimo_timestamp = last_ordem_leitura.data_hora |
|
|
|
if (last_expediente_leitura and ultimo_timestamp and last_expediente_leitura.data_hora > ultimo_timestamp) or \ |
|
|
|
(not ultimo_timestamp and last_expediente_leitura): |
|
|
|
ordem_expediente = last_expediente_leitura.expediente |
|
|
|
ultimo_timestamp = last_expediente_leitura.data_hora |
|
|
|
|
|
|
|
# if ordem_expediente: |
|
|
|
# dados_sessao.update(get_presentes(pk, ordem_expediente)) |
|
|
|
# dados_sessao.update(get_votos(ordem_expediente, app_config.mostrar_voto)) |
|
|
|
|
|
|
|
# Retorna que não há nenhuma matéria já votada ou aberta |
|
|
|
dados_sessao.update({ |
|
|
|
'msg_painel': str('Nenhuma matéria disponivel para votação.')}) |
|
|
|
|
|
|
|
print(json.dumps(dados_sessao, indent=4)) |
|
|
|
return dados_sessao |
|
|
|
|
|
|
|
|
|
|
|
def get_votos(materia: AbstractOrdemDia, mostrar_voto: bool): |
|
|
|
logger = logging.getLogger(__name__) |
|
|
|
if type(materia) == OrdemDia: |
|
|
|
if materia.tipo_votacao != 4: |
|
|
|
registro = RegistroVotacao.objects.filter( |
|
|
|
ordem=materia, materia=materia.materia).order_by('data_hora').last() |
|
|
|
leitura = None |
|
|
|
else: |
|
|
|
leitura = RegistroLeitura.objects.filter( |
|
|
|
ordem=materia, materia=materia.materia).order_by('data_hora').last() |
|
|
|
registro = None |
|
|
|
tipo = 'ordem' |
|
|
|
elif type(materia) == ExpedienteMateria: |
|
|
|
if materia.tipo_votacao != 4: |
|
|
|
registro = RegistroVotacao.objects.filter( |
|
|
|
expediente=materia, materia=materia.materia).order_by('data_hora').last() |
|
|
|
leitura = None |
|
|
|
else: |
|
|
|
leitura = RegistroLeitura.objects.filter( |
|
|
|
expediente=materia, materia=materia.materia).order_by('data_hora').last() |
|
|
|
registro = None |
|
|
|
tipo = 'expediente' |
|
|
|
|
|
|
|
response = {} |
|
|
|
|
|
|
|
if not registro and not leitura: |
|
|
|
response.update({ |
|
|
|
'numero_votos_sim': 0, |
|
|
|
'numero_votos_nao': 0, |
|
|
|
'numero_abstencoes': 0, |
|
|
|
'registro': None, |
|
|
|
'total_votos': 0, |
|
|
|
'tipo_resultado': 'Ainda não foi votada.', |
|
|
|
}) |
|
|
|
|
|
|
|
if materia.tipo_votacao == 2: |
|
|
|
if tipo == 'ordem': |
|
|
|
votos_parlamentares = VotoParlamentar.objects.filter( |
|
|
|
ordem_id=materia.id).order_by( |
|
|
|
'parlamentar__nome_parlamentar') |
|
|
|
else: |
|
|
|
votos_parlamentares = VotoParlamentar.objects.filter( |
|
|
|
expediente_id=materia.id).order_by( |
|
|
|
'parlamentar__nome_parlamentar') |
|
|
|
|
|
|
|
for i, p in enumerate(response.get('presentes', [])): |
|
|
|
try: |
|
|
|
logger.info("Tentando obter votos do parlamentar (id={}).".format(p['parlamentar_id'])) |
|
|
|
voto = votos_parlamentares.get(parlamentar_id=p['parlamentar_id']).voto |
|
|
|
|
|
|
|
if voto: |
|
|
|
if mostrar_voto: |
|
|
|
response['presentes'][i]['voto'] = voto |
|
|
|
else: |
|
|
|
response['presentes'][i]['voto'] = 'Voto Informado' |
|
|
|
except ObjectDoesNotExist: |
|
|
|
# logger.error("Votos do parlamentar (id={}) não encontrados. Retornado vazio." |
|
|
|
# .format(p['parlamentar_id'])) |
|
|
|
response['presentes'][i]['voto'] = '' |
|
|
|
elif leitura: |
|
|
|
response.update({ |
|
|
|
'numero_votos_sim': 0, |
|
|
|
'numero_votos_nao': 0, |
|
|
|
'numero_abstencoes': 0, |
|
|
|
'registro': True, |
|
|
|
'total_votos': 0, |
|
|
|
'tipo_resultado': 'Matéria lida.', |
|
|
|
}) |
|
|
|
else: |
|
|
|
total = (registro.numero_votos_sim + |
|
|
|
registro.numero_votos_nao + |
|
|
|
registro.numero_abstencoes) |
|
|
|
|
|
|
|
if materia.tipo_votacao == 2: |
|
|
|
votos_parlamentares = VotoParlamentar.objects.filter( |
|
|
|
votacao_id=registro.id).order_by( |
|
|
|
'parlamentar__nome_parlamentar') |
|
|
|
|
|
|
|
for i, p in enumerate(response.get('presentes', [])): |
|
|
|
try: |
|
|
|
logger.debug("Tentando obter votos do parlamentar (id={}).".format(p['parlamentar_id'])) |
|
|
|
response['presentes'][i]['voto'] = votos_parlamentares.get( |
|
|
|
parlamentar_id=p['parlamentar_id']).voto |
|
|
|
except ObjectDoesNotExist: |
|
|
|
logger.error( |
|
|
|
"Votos do parlamentar (id={}) não encontrados. Retornado None.".format(p['parlamentar_id'])) |
|
|
|
response['presentes'][i]['voto'] = None |
|
|
|
|
|
|
|
response.update({ |
|
|
|
'numero_votos_sim': registro.numero_votos_sim, |
|
|
|
'numero_votos_nao': registro.numero_votos_nao, |
|
|
|
'numero_abstencoes': registro.numero_abstencoes, |
|
|
|
'registro': True, |
|
|
|
'total_votos': total, |
|
|
|
'tipo_resultado': registro.tipo_resultado_votacao.nome, |
|
|
|
}) |
|
|
|
|
|
|
|
return response |
|
|
|
|
|
|
|
|
|
|
|
def filiacao_data(parlamentar, data_inicio, data_fim=None): |
|
|
|
from sapl.parlamentares.models import Filiacao |
|
|
|
|
|
|
|
filiacoes_parlamentar = Filiacao.objects.filter( |
|
|
|
parlamentar=parlamentar) |
|
|
|
|
|
|
|
filiacoes = filiacoes_parlamentar.filter(Q( |
|
|
|
data__lte=data_inicio, |
|
|
|
data_desfiliacao__isnull=True) | Q( |
|
|
|
data__lte=data_inicio, |
|
|
|
data_desfiliacao__gte=data_inicio)) |
|
|
|
|
|
|
|
if data_fim: |
|
|
|
filiacoes = filiacoes | filiacoes_parlamentar.filter( |
|
|
|
data__gte=data_inicio, |
|
|
|
data__lte=data_fim) |
|
|
|
|
|
|
|
return ' | '.join([f.partido.sigla for f in filiacoes]) |
|
|
|
|
|
|
|
|
|
|
|
def get_presentes(pk: int, materia: AbstractOrdemDia): |
|
|
|
if type(materia) == OrdemDia: |
|
|
|
presentes = PresencaOrdemDia.objects.filter( |
|
|
|
sessao_plenaria_id=pk) |
|
|
|
else: |
|
|
|
presentes = SessaoPlenariaPresenca.objects.filter( |
|
|
|
sessao_plenaria_id=pk) |
|
|
|
|
|
|
|
sessao = SessaoPlenaria.objects.get(id=pk) |
|
|
|
num_presentes = len(presentes) |
|
|
|
data_sessao = sessao.data_inicio |
|
|
|
oradores = OradorExpediente.objects.filter( |
|
|
|
sessao_plenaria_id=pk).order_by('numero_ordem') |
|
|
|
|
|
|
|
oradores_list = [] |
|
|
|
for o in oradores: |
|
|
|
oradores_list.append( |
|
|
|
{ |
|
|
|
'nome': o.parlamentar.nome_parlamentar, |
|
|
|
'numero': o.numero_ordem |
|
|
|
}) |
|
|
|
|
|
|
|
presentes_list = [] |
|
|
|
for p in presentes: |
|
|
|
legislatura = sessao.legislatura |
|
|
|
# Recupera os mandatos daquele parlamentar |
|
|
|
mandatos = p.parlamentar.mandato_set.filter(legislatura=legislatura) |
|
|
|
|
|
|
|
if p.parlamentar.ativo and mandatos: |
|
|
|
filiacao = filiacao_data(p.parlamentar, data_sessao, data_sessao) |
|
|
|
if not filiacao: |
|
|
|
partido = 'Sem Registro' |
|
|
|
else: |
|
|
|
partido = filiacao |
|
|
|
|
|
|
|
presentes_list.append( |
|
|
|
{'id': p.id, |
|
|
|
'parlamentar_id': p.parlamentar.id, |
|
|
|
'nome': p.parlamentar.nome_parlamentar, |
|
|
|
'partido': partido, |
|
|
|
'voto': '' |
|
|
|
}) |
|
|
|
|
|
|
|
elif not p.parlamentar.ativo or not mandatos: |
|
|
|
num_presentes += -1 |
|
|
|
|
|
|
|
response = {} |
|
|
|
|
|
|
|
if materia: |
|
|
|
if materia.tipo_votacao == 1: |
|
|
|
tipo_votacao = 'Simbólica' |
|
|
|
elif materia.tipo_votacao == 2: |
|
|
|
tipo_votacao = 'Nominal' |
|
|
|
elif materia.tipo_votacao == 3: |
|
|
|
tipo_votacao = 'Secreta' |
|
|
|
elif materia.tipo_votacao == 4: |
|
|
|
tipo_votacao = 'Leitura' |
|
|
|
|
|
|
|
response.update({ |
|
|
|
'tipo_resultado': materia.resultado, |
|
|
|
'observacao_materia': html.unescape(materia.observacao), |
|
|
|
'tipo_votacao': tipo_votacao, |
|
|
|
'materia_legislativa_texto': str(materia.materia), |
|
|
|
'materia_legislativa_ementa': str(materia.materia.ementa) |
|
|
|
}) |
|
|
|
|
|
|
|
# presentes_list = sort_lista_chave(presentes_list, 'nome') |
|
|
|
|
|
|
|
response.update({ |
|
|
|
'presentes': presentes_list, |
|
|
|
'num_presentes': num_presentes, |
|
|
|
'oradores': oradores_list, |
|
|
|
'msg_painel': str('Votação aberta!'), |
|
|
|
}) |
|
|
|
return response |
|
|
|
|
|
|
|
|
|
|
|
class PainelConsumer(AsyncJsonWebsocketConsumer): |
|
|
|
|
|
|
|
# def __init__(self): |
|
|
|
@ -313,12 +102,15 @@ class PainelConsumer(AsyncJsonWebsocketConsumer): |
|
|
|
|
|
|
|
self.controller_id = controller_id |
|
|
|
self.group = f"controller_{controller_id}" |
|
|
|
print(self.group) |
|
|
|
await self.channel_layer.group_add(self.group, self.channel_name) |
|
|
|
|
|
|
|
await self.accept() |
|
|
|
# await self.send_json({"type": "data", |
|
|
|
# "text": "Connection established!"}) |
|
|
|
|
|
|
|
print("SENDING DATA DO CONSUMER ") |
|
|
|
print(get_dados_painel(controller_id)) |
|
|
|
await self.send_json(get_dados_painel(controller_id)) |
|
|
|
|
|
|
|
async def disconnect(self, code): |
|
|
|
@ -328,7 +120,7 @@ class PainelConsumer(AsyncJsonWebsocketConsumer): |
|
|
|
# Called by server via channel_layer.group_send |
|
|
|
async def notify(self, event): |
|
|
|
# event: {"type": "notify", "data": {...}} |
|
|
|
await self.send_json(event["data"]) |
|
|
|
await self.send_json(event) |
|
|
|
|
|
|
|
async def receive(self, text_data=None, bytes_data=None): |
|
|
|
data = json.loads(text_data or "{}") |
|
|
|
@ -337,7 +129,7 @@ class PainelConsumer(AsyncJsonWebsocketConsumer): |
|
|
|
print("PING") |
|
|
|
await self.send_json({"type": "pong", "ts": time.time()}) |
|
|
|
return |
|
|
|
await self.send_json({"type": "data", |
|
|
|
await self.send_json({"type": "echo", |
|
|
|
"text": f"Echo: {data}"}) |
|
|
|
|
|
|
|
@database_sync_to_async |
|
|
|
|