Browse Source

WIP

websockets-2025
Edward Ribeiro 2 weeks ago
parent
commit
e3ac0769be
  1. 37
      frontend/src/__apps/painel/main.js
  2. 35
      frontend/src/components/PainelMateria.vue
  3. 31
      frontend/src/components/PainelOradores.vue
  4. 2
      frontend/src/components/PainelParlamentares.vue
  5. 47
      frontend/src/components/PainelResultado.vue
  6. 36
      sapl/painel/consumers.py
  7. 2
      sapl/painel/views.py
  8. 31
      sapl/sessao/migrations/0070_views_sessao_plenaria.py
  9. 2
      sapl/sessao/models.py
  10. 42
      sapl/templates/painel/painel_v2.html

37
frontend/src/__apps/painel/main.js

@ -5,11 +5,17 @@ import Vue from 'vue'
import StopWatch from '../../components/StopWatch.vue'
import PainelHeader from '../../components/PainelHeader.vue'
import PainelParlamentares from '../../components/PainelParlamentares.vue'
import PainelOradores from '../../components/PainelOradores.vue'
import PainelMateria from '../../components/PainelMateria.vue'
import PainelResultado from '../../components/PainelResultado.vue'
// register components
Vue.component('cronometro', StopWatch)
Vue.component('painel-header', PainelHeader)
Vue.component('painel-parlamentares', PainelParlamentares)
Vue.component('painel-oradores', PainelOradores)
Vue.component('painel-materia', PainelMateria)
Vue.component('painel-resultado', PainelResultado)
new Vue({
el: '#painel',
@ -17,12 +23,16 @@ new Vue({
data() {
return {
controllerId: null,
// TODO: state here is really needed?
state: {
sessao_plenaria: '',
sessao_plenaria_data: '',
sessao_plenaria_hora_inicio: '',
votacao: [],
parlamentares: []
parlamentares: [],
oradores: [],
materia: {},
resultado: {},
},
ws: null,
isOpen: false,
@ -54,21 +64,40 @@ new Vue({
// SETUP STATE
console.log(`FROM VUEJS: ${evt.data}`) // DEBUG
// PARLAMENTARES
const parlamentaresInstance = this.$refs.parlamentares
parlamentaresInstance.parlamentares = msg.parlamentares
// HEADER DO PAINEL
//TODO: group in a single SessaoPlenaria object
const headerInstance = this.$refs.painelHeader;
// SESSAO_PLENARIA
//TODO: setup as child's props?
headerInstance.sessao_plenaria = msg.sessao.sessao_plenaria
headerInstance.sessao_plenaria_data = msg.sessao.sessao_plenaria_data
headerInstance.sessao_plenaria_hora_inicio = msg.sessao.sessao_plenaria_hora_inicio
headerInstance.brasao = msg.sessao.brasao
this.state.oradores = msg.oradores
this.state.materia_legislativa_ementa = msg.mensagem_legislativa_ementa
this.state.resultado = msg.votacao
// ORADORES
const oradoresInstance = this.$refs.oradores
oradoresInstance.oradores = msg.oradores
// MATERIA
const materiaInstance = this.$refs.materia
materiaInstance.materia = msg.materia
// RESULTADO
const resultadoInstance = this.$refs.resultado
resultadoInstance.resultado = msg.resultado
if (msg.votos_parlamentares) { // && mostrar voto
parlamentaresInstance.parlamentares.forEach((p)=>{
if (p.parlamentar_id in msg.votos_parlamentares) {
p.voto = msg.votos_parlamentares[p.parlamentar_id].voto
}
});
}
if (msg.type === 'state') this.state = msg.payload || {}
} catch (e) { console.warn('WS parse error:', e) }

35
frontend/src/components/PainelMateria.vue

@ -0,0 +1,35 @@
<template>
<div class="col-md-6 text-center painel" id="obs_materia_div">
<h2 class="text-subtitle" id="mat_em_votacao">Matéria em Votação</h2>
<span id="materia_legislativa_texto" class="text-value">{{ materia.texto }}</span>
<br>
<span id="materia_legislativa_ementa" class="text-value">{{ materia.ementa }} </span>
<br>
<span id="observacao_materia" class="text-value">{{ materia.observacao }}</span>
</div>
</template>
<script>
export default {
name: 'PainelMateria',
data() {
return {
materia: {
texto: '',
ementa: '',
observacao: '',
}
};
},
mounted() {
console.log('PainelMateria mounted');
},
beforeDestroy() {
},
};
</script>
<style scoped>
/* Optional styling */
</style>

31
frontend/src/components/PainelOradores.vue

@ -0,0 +1,31 @@
<template>
<div class="col-md-6 text-center painel" id="aparecer_oradores">
<h2 class="text-subtitle">Oradores</h2>
<table id="oradores_list">
<tr v-for="o in oradores" :key="o.ordem_pronunciamento"><td style="padding-right:20px; color:white">
{{ o.ordem_pronunciamento }}º &nbsp {{ o.nome_parlamentar }}</td>
</tr>
</table>
</div>
</template>
<script>
export default {
name: 'PainelOradores',
data() {
return {
oradores: [],
};
},
mounted() {
console.log('PainelOradores mounted');
},
beforeDestroy() {
},
};
</script>
<style scoped>
/* Optional styling */
</style>

2
frontend/src/components/PainelParlamentares.vue

@ -7,7 +7,7 @@
<tr v-for="p in parlamentares" :key="p.parlamentar_id" class="text-value text-center">
<td style="padding-right:20px; color:yellow" > {{ p.nome_parlamentar }}</td>
<td style="padding-right:20px; color:yellow"> {{ p.filiacao }}</td>
<td style="padding-right:20px; color:yellow"></td>
<td style="padding-right:20px; color:yellow"> {{ p.voto }} </td>
</tr>
</table>
</div>

47
frontend/src/components/PainelResultado.vue

@ -0,0 +1,47 @@
<template>
<div class="col-md-6 text-left painel" id="resultado_votacao_div">
<div class="d-flex align-items-left justify-content-left mb-2">
<h2 class="text-subtitle mb-0">Resultado</h2>
</div>
<div id="box_votacao">
<div id="votacao" class="text-value">
<li>Sim: {{ resultado.numero_votos.votos_sim }}</li>
<li>Não: {{ resultado.numero_votos.votos_nao }}</li>
<li>Abstenções: {{ resultado.numero_votos.abstencoes }}</li>
<li>Presentes: {{ resultado.numero_votos.num_presentes }}</li>
<li>Total votos: {{ resultado.numero_votos.total_votos }}</li>
</div>
<div id="resultado_votacao" class="text-title">{{ resultado.resultado_votacao }}</div>
</div>
</div>
</template>
<script>
export default {
name: 'PainelResultado',
data() {
return {
resultado: {
numero_votos: {
votos_sim: 0,
votos_nao: 0,
abstencoes: 0,
total_votos: 0,
num_presentes: 0,
},
resultado_votacao: '',
}
};
},
mounted() {
console.log('PainelResultado mounted');
},
beforeDestroy() {
},
};
</script>
<style scoped>
/* Optional styling */
</style>

36
sapl/painel/consumers.py

@ -27,7 +27,7 @@ def get_dados_painel(pk: int) -> dict:
# { 1: { "id": 1, "nome": "fulano", "filiacao": "aquela"}, ...}
# [ { "id": 1, "nome": "fulano", "filiacao": "aquela"}, ... ]
# sessao_plenaria_id/pk = 2546
# Painel
presentes = SessaoPresencaView.objects.filter(sessao_plenaria_id=pk,
@ -42,22 +42,26 @@ def get_dados_painel(pk: int) -> dict:
)
oradores = [dict(zip(['ordem_pronunciamento', 'nome_parlamentar'], o)) for o in oradores]
votos = SessaoMateriaVotacaoView.objects.get(sessao_plenaria_id=pk,
etapa_sessao='expediente',
materia_id=31919)
votacao = SessaoMateriaVotacaoView.objects.get(sessao_plenaria_id=pk,
etapa_sessao='expediente',
materia_id=31919)
# TODO: check if votacao has numero_votos!
votacao.numero_votos.update({"num_presentes": len(parlamentares)})
# 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
"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",
"sessao": {
"sessao_plenaria_id": sessao.id,
"status_painel": sessao.painel_aberto,
"brasao": brasao,
"mostrar_voto": app_config.mostrar_voto,
@ -71,9 +75,17 @@ def get_dados_painel(pk: int) -> dict:
},
"parlamentares": parlamentares,
"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,
"resultado": {
"resultado_votacao": votacao.resultado_votacao,
"resultado": votacao.resultado,
"numero_votos": votacao.numero_votos,
},
"votos_parlamentares": {p["parlamentar_id"]: p for p in votacao.votos_parlamentares},
"materia": {
"materia_id": votacao.materia.id,
"texto": str(votacao.materia),
"ementa": votacao.materia.ementa,
},
"stopwatch": stopwatch, # TODO: array of stopwatches
}

2
sapl/painel/views.py

@ -658,7 +658,7 @@ def painel_controller_view(request):
if command:
layer = get_channel_layer()
controller_id = 2393 # TODO: recover from template call
controller_id = 2393 # TODO: sessaoplenaria_id recover from template call
group = f"controller_{controller_id}"
print(group)

31
sapl/sessao/migrations/0070_views_sessao_plenaria.py

@ -4,7 +4,6 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('sessao', '0069_auto_20220919_1705'),
]
@ -153,7 +152,8 @@ class Migration(migrations.Migration):
'expediente' etapa_sessao,
em.numero_ordem,
em.materia_id,
ml.ementa,
tm.descricao||''||ml.numero||' de '||ml.ano as materia_texto,
ml.ementa materia_ementa,
tipo_votacao,
CASE tipo_votacao
WHEN 1 THEN 'Simbólia'
@ -164,18 +164,20 @@ class Migration(migrations.Migration):
END as tipo_votacao_descricao,
resultado_votacao,
em.resultado,
total_votos,
numero_votos,
votos_parlamentares,
votacao_aberta
FROM sessao_expedientemateria em
JOIN materia_materialegislativa ml ON (em.materia_id = ml.id)
JOIN materia_tipomaterialegislativa tm ON (ml.tipo_id = tm.id)
LEFT JOIN sessao_registroleitura rl on (rl.expediente_id = em.id)
LEFT JOIN LATERAL (
SELECT jsonb_build_object(
'sim', coalesce(rv.numero_votos_sim, 0),
'não', coalesce(rv.numero_votos_nao, 0),
'abstencoes', coalesce(rv.numero_abstencoes, 0)
) as total_votos,
'votos_sim', coalesce(rv.numero_votos_sim, 0),
'votos_nao', coalesce(rv.numero_votos_nao, 0),
'abstencoes', coalesce(rv.numero_abstencoes, 0),
'total_votos', coalesce(rv.numero_votos_sim, 0) + coalesce(rv.numero_votos_nao, 0) + coalesce(rv.numero_abstencoes, 0)
) as numero_votos,
trv.nome resultado_votacao
FROM sessao_registrovotacao rv
JOIN sessao_tiporesultadovotacao trv on (rv.tipo_resultado_votacao_id = trv.id)
@ -204,6 +206,7 @@ class Migration(migrations.Migration):
'ordemdia' etapa_sessao,
od.numero_ordem,
od.materia_id,
tm.descricao||''||ml.numero||' de '||ml.ano as materia_texto,
ml.ementa,
tipo_votacao,
CASE tipo_votacao
@ -215,18 +218,20 @@ class Migration(migrations.Migration):
END as tipo_votacao_descricao,
resultado_votacao,
od.resultado,
total_votos,
numero_votos,
votos_parlamentares,
votacao_aberta
FROM sessao_ordemdia od
JOIN materia_materialegislativa ml ON (od.materia_id = ml.id)
JOIN materia_tipomaterialegislativa tm ON (ml.tipo_id = tm.id)
LEFT JOIN sessao_registroleitura rl on (od.id = rl.expediente_id)
LEFT JOIN LATERAL (
SELECT jsonb_build_object(
'sim', coalesce(rv.numero_votos_sim, 0),
'não', coalesce(rv.numero_votos_nao, 0),
'abstencoes', coalesce(rv.numero_abstencoes, 0)
) as total_votos,
'votos_sim', coalesce(rv.numero_votos_sim, 0),
'votos_nao', coalesce(rv.numero_votos_nao, 0),
'abstencoes', coalesce(rv.numero_abstencoes, 0),
'total_votos', coalesce(rv.numero_votos_sim, 0) + coalesce(rv.numero_votos_nao, 0) + coalesce(rv.numero_abstencoes, 0)
) as numero_votos,
trv.nome resultado_votacao
FROM sessao_registrovotacao rv
JOIN sessao_tiporesultadovotacao trv on (rv.tipo_resultado_votacao_id = trv.id)
@ -249,7 +254,7 @@ class Migration(migrations.Migration):
)
SELECT *
FROM votacao_materias
ORDER BY sessao_plenaria_id, etapa_sessao, numero_ordem
ORDER BY sessao_plenaria_id, etapa_sessao, numero_ordem
"""
)
]

2
sapl/sessao/models.py

@ -1117,7 +1117,7 @@ class SessaoMateriaVotacaoView(models.Model):
numero_ordem = models.PositiveIntegerField(verbose_name=_('Número de Ordem'))
total_votos = JSONField(null=True, verbose_name=_('Total Votos'))
numero_votos = JSONField(null=True, verbose_name=_('Total Votos'))
votos_parlamentares = JSONField(null=True, verbose_name=_('Votos Parlamentares'))

42
sapl/templates/painel/painel_v2.html

@ -53,27 +53,12 @@
<div class="d-flex justify-content-start">
<painel-parlamentares ref="parlamentares"></painel-parlamentares>
<div class="d-flex col-md-8 painels">
<div class="col-md-6 text-center painel" id="aparecer_oradores">
<h2 class="text-subtitle">Oradores</h2>
<table id="oradores_list">
<tr v-for="o in state.oradores" :key="o.ordem_pronunciamento"><td style="padding-right:20px; color:white">
[[ o.ordem_pronunciamento ]]º &nbsp [[ o.nome_parlamentar ]]</td>
</tr>
</table>
</div>
<painel-oradores ref="oradores"></painel-oradores>
<div class="col-md-6 text-left painel">
<div class="d-flex align-items-left justify-content-left mb-2">
<h2 class="text-subtitle mb-0">Cronômetros</h2>
<button class="btn btn-sm btn-secondary ms-2" onclick="changeFontSize('box_cronometros', -1)">
A-
</button>
<button class="btn btn-sm btn-secondary ms-2" onclick="changeFontSize('box_cronometros', 1)">
A+
</button>
</div>
<div class="text-value" id="box_cronometros">
<!-- <cronometro></cronometro>-->
Discurso: <span id="cronometro_discurso"></span><br>
Aparte: <span id="cronometro_aparte"></span><br>
Questão de Ordem: <span id="cronometro_ordem"></span><br>
@ -81,30 +66,9 @@
</div>
</div>
<div class="col-md-6 text-left painel" id="resultado_votacao_div">
<div class="d-flex align-items-left justify-content-left mb-2">
<h2 class="text-subtitle mb-0">Resultado</h2>
<button class="btn btn-sm btn-secondary ms-2" onclick="changeFontSize('box_votacao', -1)">
A-
</button>
<button class="btn btn-sm btn-secondary ms-2" onclick="changeFontSize('box_votacao', 1)">
A+
</button>
</div>
<div id="box_votacao">
<span id="votacao" class="text-value"></span>
<span id="resultado_votacao" lass="text-title"></span>
</div>
</div>
<painel-resultado ref="resultado"></painel-resultado>
<div class="col-md-6 text-center painel" id="obs_materia_div">
<h2 class="text-subtitle" id="mat_em_votacao">Matéria em Votação</h2>
<span id="materia_legislativa_texto" class="text-value"></span>
<br>
<span id="materia_legislativa_ementa" class="text-value">[[ materia_legislativa_ementa ]] </span>
<br>
<span id="observacao_materia" class="text-value"></span>
</div>
<painel-materia ref="materia"></painel-materia>
<div class="col-md-6 text-center painel" id="tema_solene_div" style="display: none">
<h2 class="text-subtitle">Tema da Sessão Solene</h2>

Loading…
Cancel
Save