Browse Source

Merge e8fcccac99 into ecfd65a8b3

pull/3849/merge
Edward 1 week ago
committed by GitHub
parent
commit
e55f9be3d2
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 90
      sapl/materia/migrations/0088_fix_view_materiaemtramitacao.py
  2. 3
      sapl/materia/models.py
  3. 7
      sapl/painel/urls.py
  4. 15
      sapl/painel/views.py
  5. 4
      sapl/relatorios/forms.py
  6. 35
      sapl/relatorios/templates/pdf_sessao_plenaria_gerar.py
  7. 108
      sapl/relatorios/views.py
  8. 424
      sapl/sessao/views.py
  9. 120
      sapl/templates/painel/mensagem.html
  10. 128
      sapl/templates/painel/parlamentares.html
  11. 123
      sapl/templates/painel/votacao.html
  12. 6
      sapl/templates/relatorios/relatorio_materias_por_autor.html
  13. 58
      sapl/utils.py

90
sapl/materia/migrations/0088_fix_view_materiaemtramitacao.py

@ -0,0 +1,90 @@
from django.db import migrations, models
_OLD_VIEW = """
create or replace view materia_materiaemtramitacao as
select m.id as id,
m.id as materia_id,
t.id as tramitacao_id,
t.unidade_tramitacao_destino_id as unidade_tramitacao_atual_id
from materia_materialegislativa m
inner join materia_tramitacao t on (m.id = t.materia_id)
where t.id = (select max(id) from materia_tramitacao where materia_id = m.id)
order by m.id DESC
"""
_NEW_VIEW = """
CREATE OR REPLACE VIEW materia_materiaemtramitacao AS
SELECT
m.id,
m.id AS materia_id,
t.id AS tramitacao_id,
t.unidade_tramitacao_destino_id AS unidade_tramitacao_atual_id
FROM materia_materialegislativa m
JOIN LATERAL (
SELECT
t.id,
t.unidade_tramitacao_destino_id
FROM materia_tramitacao t
WHERE t.materia_id = m.id
ORDER BY t.id DESC
LIMIT 1
) t ON true;
"""
class Migration(migrations.Migration):
# CREATE INDEX CONCURRENTLY cannot run inside a transaction.
atomic = False
dependencies = [
('materia', '0087_update_viewdb_materiaemtramitacao'),
]
operations = [
migrations.RunSQL(sql=_NEW_VIEW, reverse_sql=_OLD_VIEW),
migrations.SeparateDatabaseAndState(
database_operations=[
# Um CREATE INDEX CONCURRENTLY interrompido deixa o índice
# criado porém inválido, e o IF NOT EXISTS abaixo o pularia
# na próxima execução. DROP INDEX sem CONCURRENTLY porque
# este não roda dentro de um bloco DO.
migrations.RunSQL(
sql="""
DO $$
BEGIN
IF EXISTS (
SELECT 1
FROM pg_index i
JOIN pg_class c ON c.oid = i.indexrelid
WHERE c.relname = 'tram_materia_id_desc'
AND NOT i.indisvalid
) THEN
DROP INDEX tram_materia_id_desc;
END IF;
END $$;
""",
reverse_sql=migrations.RunSQL.noop,
),
migrations.RunSQL(
sql="""
CREATE INDEX CONCURRENTLY IF NOT EXISTS
tram_materia_id_desc
ON materia_tramitacao (materia_id, id DESC)
""",
reverse_sql="""
DROP INDEX CONCURRENTLY IF EXISTS
tram_materia_id_desc
""",
),
],
state_operations=[
migrations.AddIndex(
model_name='tramitacao',
index=models.Index(
fields=['materia', '-id'],
name='tram_materia_id_desc',
),
),
],
),
]

3
sapl/materia/models.py

@ -1350,6 +1350,9 @@ class Tramitacao(models.Model):
verbose_name = _('Tramitação') verbose_name = _('Tramitação')
verbose_name_plural = _('Tramitações') verbose_name_plural = _('Tramitações')
ordering = ('-data_tramitacao', '-id') ordering = ('-data_tramitacao', '-id')
indexes = [
models.Index(fields=['materia', '-id'], name='tram_materia_id_desc'),
]
def __str__(self): def __str__(self):
return _('%(materia)s | %(status)s | %(data)s') % { return _('%(materia)s | %(status)s | %(data)s') % {

7
sapl/painel/urls.py

@ -1,8 +1,7 @@
from django.conf.urls import url from django.conf.urls import url
from .apps import AppConfig from .apps import AppConfig
from .views import (cronometro_painel, get_dados_painel, painel_mensagem_view, from .views import (cronometro_painel, get_dados_painel, painel_view,
painel_parlamentar_view, painel_view, painel_votacao_view,
switch_painel, verifica_painel, votante_view) switch_painel, verifica_painel, votante_view)
app_name = AppConfig.name app_name = AppConfig.name
@ -11,12 +10,8 @@ urlpatterns = [
url(r'^painel-principal/(?P<pk>\d+)$', painel_view, url(r'^painel-principal/(?P<pk>\d+)$', painel_view,
name="painel_principal"), name="painel_principal"),
url(r'^painel/(?P<pk>\d+)/dados$', get_dados_painel, name='dados_painel'), url(r'^painel/(?P<pk>\d+)/dados$', get_dados_painel, name='dados_painel'),
url(r'^painel/mensagem$', painel_mensagem_view, name="painel_mensagem"),
url(r'^painel/parlamentar$', painel_parlamentar_view,
name='painel_parlamentar'),
url(r'^painel/switch-painel$', switch_painel, url(r'^painel/switch-painel$', switch_painel,
name="switch_painel"), name="switch_painel"),
url(r'^painel/votacao$', painel_votacao_view, name='painel_votacao'),
url(r'^painel/verifica-painel$', verifica_painel, url(r'^painel/verifica-painel$', verifica_painel,
name="verifica_painel"), name="verifica_painel"),
url(r'^painel/cronometro$', cronometro_painel, name='cronometro_painel'), url(r'^painel/cronometro$', cronometro_painel, name='cronometro_painel'),

15
sapl/painel/views.py

@ -327,21 +327,6 @@ def verifica_painel(request):
return resposta return resposta
@user_passes_test(check_permission)
def painel_mensagem_view(request):
return render(request, 'painel/mensagem.html')
@user_passes_test(check_permission)
def painel_parlamentar_view(request):
return render(request, 'painel/parlamentares.html')
@user_passes_test(check_permission)
def painel_votacao_view(request):
return render(request, 'painel/votacao.html')
@user_passes_test(check_permission) @user_passes_test(check_permission)
def cronometro_painel(request): def cronometro_painel(request):
request.session[request.GET['tipo']] = request.GET['action'] request.session[request.GET['tipo']] = request.GET['action']

4
sapl/relatorios/forms.py

@ -543,9 +543,7 @@ class RelatorioMateriasTramitacaoFilterSet(django_filters.FilterSet):
@property @property
def qs(self): def qs(self):
parent = super(RelatorioMateriasTramitacaoFilterSet, self).qs parent = super(RelatorioMateriasTramitacaoFilterSet, self).qs
return parent.distinct().order_by( return parent.order_by('-materia__ano', 'materia__tipo', '-materia__numero')
'-materia__ano', 'materia__tipo', '-materia__numero'
)
class Meta: class Meta:
model = MateriaEmTramitacao model = MateriaEmTramitacao

35
sapl/relatorios/templates/pdf_sessao_plenaria_gerar.py

@ -7,6 +7,8 @@
import os import os
import time import time
import logging import logging
from xml.sax.saxutils import escape
from django.template.defaultfilters import safe from django.template.defaultfilters import safe
from django.utils.html import strip_tags from django.utils.html import strip_tags
from trml2pdf import parseString from trml2pdf import parseString
@ -198,6 +200,33 @@ def presenca(lst_presenca_sessao, lst_ausencia_sessao):
return tmp return tmp
def correspondencias(lst_correspondencias):
tmp = ''
if lst_correspondencias:
tmp += '\t\t<para style="P1">Correspondências</para>\n'
tmp += '\t\t<para style="P2">\n'
tmp += '\t\t\t<font color="white"> <br/></font>\n'
tmp += '\t\t</para>\n'
tmp += '<blockTable style="repeater" repeatRows="1" colWidths="3cm,4cm,3.5cm,6.5cm">\n'
tmp += '<tr><td>Tipo</td><td>Documento</td><td>Interessado</td><td>Assunto</td></tr>\n'
for c in lst_correspondencias:
tmp += '<tr>'
tmp += '<td><para style="P4">' + \
escape(str(c['tipo'])) + '</para></td>\n'
tmp += '<td><para style="P4">' + escape(str(c['epigrafe'])) + \
' - ' + escape(str(c['data'])) + '</para></td>\n'
tmp += '<td><para style="P4">' + \
escape(str(c['interessado'] or '')) + '</para></td>\n'
tmp += '<td><para style="P4">' + \
escape(str(c['assunto'] or '')) + '</para></td>\n'
tmp += '</tr>\n'
tmp += '</blockTable>\n'
tmp += '\t\t<para style="P2">\n'
tmp += '\t\t\t<font color="white"> <br/></font>\n'
tmp += '\t\t</para>\n'
return tmp
def expedientes(lst_expedientes): def expedientes(lst_expedientes):
tmp = '' tmp = ''
if lst_expedientes: if lst_expedientes:
@ -415,7 +444,7 @@ def consideracoes(lst_consideracoes):
return tmp return tmp
def principal(rodape_dic, imagem, inf_basicas_dic, cont_mult_dic, lst_mesa, lst_presenca_sessao, lst_ausencia_sessao, lst_expedientes, lst_expediente_materia, lst_expediente_materia_vot_nom, lst_oradores_expediente, lst_presenca_ordem_dia, lst_votacao, lst_votacao_vot_nom, lst_oradores_ordemdia, lst_oradores, lst_ocorrencias, lst_consideracoes): def principal(rodape_dic, imagem, inf_basicas_dic, cont_mult_dic, lst_mesa, lst_presenca_sessao, lst_ausencia_sessao, lst_correspondencias, lst_expedientes, lst_expediente_materia, lst_expediente_materia_vot_nom, lst_oradores_expediente, lst_presenca_ordem_dia, lst_votacao, lst_votacao_vot_nom, lst_oradores_ordemdia, lst_oradores, lst_ocorrencias, lst_consideracoes):
""" """
""" """
arquivoPdf = str(int(time.time() * 100)) + ".pdf" arquivoPdf = str(int(time.time() * 100)) + ".pdf"
@ -440,6 +469,7 @@ def principal(rodape_dic, imagem, inf_basicas_dic, cont_mult_dic, lst_mesa, lst_
ordenacao = ResumoOrdenacao.objects.first() ordenacao = ResumoOrdenacao.objects.first()
dict_ord_template = { dict_ord_template = {
'cont_mult': multimidia(cont_mult_dic), 'cont_mult': multimidia(cont_mult_dic),
'correspondencia': correspondencias(lst_correspondencias),
'exp': expedientes(lst_expedientes), 'exp': expedientes(lst_expedientes),
'id_basica': inf_basicas(inf_basicas_dic), 'id_basica': inf_basicas(inf_basicas_dic),
'lista_p': presenca(lst_presenca_sessao, lst_ausencia_sessao), 'lista_p': presenca(lst_presenca_sessao, lst_ausencia_sessao),
@ -473,6 +503,7 @@ def principal(rodape_dic, imagem, inf_basicas_dic, cont_mult_dic, lst_mesa, lst_
tmp += dict_ord_template[ordenacao.decimo_terceiro] tmp += dict_ord_template[ordenacao.decimo_terceiro]
tmp += dict_ord_template[ordenacao.decimo_quarto] tmp += dict_ord_template[ordenacao.decimo_quarto]
tmp += dict_ord_template[ordenacao.decimo_quinto] tmp += dict_ord_template[ordenacao.decimo_quinto]
tmp += dict_ord_template[ordenacao.decimo_sexto]
except KeyError as e: except KeyError as e:
logger.error("KeyError: " + str(e) + ". Erro ao tentar utilizar " logger.error("KeyError: " + str(e) + ". Erro ao tentar utilizar "
"configuração de ordenação. Utilizando ordenação padrão.") "configuração de ordenação. Utilizando ordenação padrão.")
@ -480,6 +511,7 @@ def principal(rodape_dic, imagem, inf_basicas_dic, cont_mult_dic, lst_mesa, lst_
tmp += multimidia(cont_mult_dic) tmp += multimidia(cont_mult_dic)
tmp += mesa(lst_mesa) tmp += mesa(lst_mesa)
tmp += presenca(lst_presenca_sessao, lst_ausencia_sessao) tmp += presenca(lst_presenca_sessao, lst_ausencia_sessao)
tmp += correspondencias(lst_correspondencias)
tmp += expedientes(lst_expedientes) tmp += expedientes(lst_expedientes)
tmp += expediente_materia(lst_expediente_materia) tmp += expediente_materia(lst_expediente_materia)
tmp += expediente_materia_vot_nom(lst_expediente_materia_vot_nom) tmp += expediente_materia_vot_nom(lst_expediente_materia_vot_nom)
@ -497,6 +529,7 @@ def principal(rodape_dic, imagem, inf_basicas_dic, cont_mult_dic, lst_mesa, lst_
tmp += multimidia(cont_mult_dic) tmp += multimidia(cont_mult_dic)
tmp += mesa(lst_mesa) tmp += mesa(lst_mesa)
tmp += presenca(lst_presenca_sessao, lst_ausencia_sessao) tmp += presenca(lst_presenca_sessao, lst_ausencia_sessao)
tmp += correspondencias(lst_correspondencias)
tmp += expedientes(lst_expedientes) tmp += expedientes(lst_expedientes)
tmp += expediente_materia(lst_expediente_materia) tmp += expediente_materia(lst_expediente_materia)
tmp += expediente_materia_vot_nom(lst_expediente_materia_vot_nom) tmp += expediente_materia_vot_nom(lst_expediente_materia_vot_nom)

108
sapl/relatorios/views.py

@ -8,6 +8,7 @@ import unidecode
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Count, Q, F from django.db.models import Count, Q, F
from django.http import Http404, HttpResponse from django.http import Http404, HttpResponse
from django.shortcuts import get_object_or_404
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils import timezone from django.utils import timezone
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
@ -23,7 +24,7 @@ from sapl.materia.models import (Autoria, MateriaLegislativa, Numeracao,
TipoMateriaLegislativa, MateriaEmTramitacao, DocumentoAcessorio, TipoDocumento, TipoMateriaLegislativa, MateriaEmTramitacao, DocumentoAcessorio, TipoDocumento,
StatusTramitacao) StatusTramitacao)
from sapl.norma.models import TipoNormaJuridica, NormaJuridica from sapl.norma.models import TipoNormaJuridica, NormaJuridica
from sapl.parlamentares.models import Filiacao, Parlamentar, SessaoLegislativa, Legislatura from sapl.parlamentares.models import Parlamentar, SessaoLegislativa, Legislatura
from sapl.protocoloadm.models import (DocumentoAdministrativo, Protocolo, from sapl.protocoloadm.models import (DocumentoAdministrativo, Protocolo,
TramitacaoAdministrativo, StatusTramitacaoAdministrativo, TramitacaoAdministrativo, StatusTramitacaoAdministrativo,
TipoDocumentoAdministrativo) TipoDocumentoAdministrativo)
@ -39,7 +40,8 @@ from sapl.sessao.models import (ExpedienteMateria, ExpedienteSessao,
OrdemDia, PresencaOrdemDia, SessaoPlenaria, OrdemDia, PresencaOrdemDia, SessaoPlenaria,
SessaoPlenariaPresenca, OcorrenciaSessao, SessaoPlenariaPresenca, OcorrenciaSessao,
RegistroVotacao, VotoParlamentar, OradorOrdemDia, RegistroVotacao, VotoParlamentar, OradorOrdemDia,
ConsideracoesFinais, ResumoOrdenacao, TipoSessaoPlenaria) ConsideracoesFinais, ResumoOrdenacao, TipoSessaoPlenaria,
restringe_sessoes_visiveis)
from sapl.sessao.views import (get_identificacao_basica, get_mesa_diretora, from sapl.sessao.views import (get_identificacao_basica, get_mesa_diretora,
get_presenca_sessao, get_expedientes, get_presenca_sessao, get_expedientes,
get_materias_expediente, get_oradores_expediente, get_materias_expediente, get_oradores_expediente,
@ -92,7 +94,8 @@ def get_imagem(casa):
def get_rodape(casa): def get_rodape(casa):
if len(casa.cep) == 8: if len(casa.cep) == 8:
cep = casa.cep[:4] + "-" + casa.cep[5:] # CEP tem 8 dígitos e se formata como XXXXX-XXX.
cep = casa.cep[:5] + "-" + casa.cep[5:]
else: else:
cep = "" cep = ""
@ -542,6 +545,8 @@ def get_sessao_plenaria(sessao, casa, user):
if sessao.tipo.nome == 'Solene': if sessao.tipo.nome == 'Solene':
inf_basicas_dic["tema_solene"] = sessao.tema_solene inf_basicas_dic["tema_solene"] = sessao.tema_solene
data_sessao = sessao.data_fim if sessao.data_fim else sessao.data_inicio
# Conteudo multimidia # Conteudo multimidia
cont_mult_dic = { cont_mult_dic = {
"multimidia_audio": str(sessao.url_audio) if sessao.url_audio else "Indisponível", "multimidia_audio": str(sessao.url_audio) if sessao.url_audio else "Indisponível",
@ -553,9 +558,11 @@ def get_sessao_plenaria(sessao, casa, user):
for composicao in IntegranteMesa.objects.select_related('parlamentar', 'cargo') \ for composicao in IntegranteMesa.objects.select_related('parlamentar', 'cargo') \
.filter(sessao_plenaria=sessao) \ .filter(sessao_plenaria=sessao) \
.order_by('cargo_id'): .order_by('cargo_id'):
partido_sigla = Filiacao.objects.filter( # Antes usava Filiacao...first() (filiação mais recente), enquanto a
parlamentar=composicao.parlamentar).first() # tela usava .last() (a mais antiga) — e as listas de presença deste
sigla = '' if not partido_sigla else partido_sigla.partido.sigla # mesmo PDF já usavam filiacao_data. Unifica no critério das listas
# de presença e do Resumo: o partido vigente em data_inicio.
sigla = filiacao_data(composicao.parlamentar, sessao.data_inicio)
lst_mesa.append({ lst_mesa.append({
'nom_parlamentar': composicao.parlamentar.nome_parlamentar, 'nom_parlamentar': composicao.parlamentar.nome_parlamentar,
'sgl_partido': sigla, 'sgl_partido': sigla,
@ -652,7 +659,7 @@ def get_sessao_plenaria(sessao, casa, user):
"num_ordem": expediente_materia.numero_ordem, "num_ordem": expediente_materia.numero_ordem,
"id_materia": f"{materia.tipo.sigla} {materia.tipo.descricao} {str(materia.numero)}/{str(materia.ano)}", "id_materia": f"{materia.tipo.sigla} {materia.tipo.descricao} {str(materia.numero)}/{str(materia.ano)}",
"des_numeracao": ' ', "des_numeracao": ' ',
"des_turno": get_turno(materia)[0], "des_turno": get_turno(materia, data_sessao)[0],
"situacao": materia_em_tramitacao.tramitacao.status if materia_em_tramitacao else _("Não informada"), "situacao": materia_em_tramitacao.tramitacao.status if materia_em_tramitacao else _("Não informada"),
"txt_ementa": str(materia.ementa), "txt_ementa": str(materia.ementa),
"materia_observacao": materia.observacao, "materia_observacao": materia.observacao,
@ -732,13 +739,11 @@ def get_sessao_plenaria(sessao, casa, user):
for orador_expediente in OradorExpediente.objects.filter(sessao_plenaria=sessao).order_by('numero_ordem'): for orador_expediente in OradorExpediente.objects.filter(sessao_plenaria=sessao).order_by('numero_ordem'):
parlamentar = Parlamentar.objects.get( parlamentar = Parlamentar.objects.get(
id=orador_expediente.parlamentar.id) id=orador_expediente.parlamentar.id)
partido_sigla = Filiacao.objects.filter(
parlamentar=parlamentar).first()
lst_oradores_expediente.append({ lst_oradores_expediente.append({
"num_ordem": orador_expediente.numero_ordem, "num_ordem": orador_expediente.numero_ordem,
"nom_parlamentar": parlamentar.nome_parlamentar, "nom_parlamentar": parlamentar.nome_parlamentar,
"observacao": orador_expediente.observacao, "observacao": orador_expediente.observacao,
"sgl_partido": "" if not partido_sigla else partido_sigla.partido.sigla "sgl_partido": filiacao_data(parlamentar, sessao.data_inicio)
}) })
# Lista presença na ordem do dia # Lista presença na ordem do dia
@ -774,7 +779,7 @@ def get_sessao_plenaria(sessao, casa, user):
materia_em_tramitacao = materia.materiaemtramitacao_set.first() materia_em_tramitacao = materia.materiaemtramitacao_set.first()
dic_votacao.update({ dic_votacao.update({
"des_turno": get_turno(materia)[0], "des_turno": get_turno(materia, data_sessao)[0],
# https://github.com/interlegis/sapl/issues/1009 # https://github.com/interlegis/sapl/issues/1009
"txt_ementa": html.unescape(materia.ementa), "txt_ementa": html.unescape(materia.ementa),
"materia_observacao": materia.observacao, "materia_observacao": materia.observacao,
@ -849,27 +854,23 @@ def get_sessao_plenaria(sessao, casa, user):
for orador_ordemdia in oradores_ordem_dia: for orador_ordemdia in oradores_ordem_dia:
parlamentar_orador = Parlamentar.objects.get( parlamentar_orador = Parlamentar.objects.get(
id=orador_ordemdia.parlamentar.id) id=orador_ordemdia.parlamentar.id)
sigla_partido = Filiacao.objects.filter(
parlamentar=parlamentar_orador).first()
lst_oradores_ordemdia.append({ lst_oradores_ordemdia.append({
"num_ordem": orador_ordemdia.numero_ordem, "num_ordem": orador_ordemdia.numero_ordem,
"nome_parlamentar": parlamentar_orador.nome_parlamentar, "nome_parlamentar": parlamentar_orador.nome_parlamentar,
"observacao": orador_ordemdia.observacao, "observacao": orador_ordemdia.observacao,
"sigla": "" if not sigla_partido else sigla_partido.partido.sigla "sigla": filiacao_data(parlamentar_orador, sessao.data_inicio)
}) })
# Lista dos oradores nas Explicações Pessoais # Lista dos oradores nas Explicações Pessoais
lst_oradores = [] lst_oradores = []
for orador in Orador.objects.select_related('parlamentar').filter(sessao_plenaria=sessao).order_by('numero_ordem'): for orador in Orador.objects.select_related('parlamentar').filter(sessao_plenaria=sessao).order_by('numero_ordem'):
parlamentar = orador.parlamentar parlamentar = orador.parlamentar
partido_sigla = orador.parlamentar.filiacao_set.select_related(
'partido', 'parlamentar').first()
lst_oradores.append({ lst_oradores.append({
"num_ordem": orador.numero_ordem, "num_ordem": orador.numero_ordem,
"nom_parlamentar": parlamentar.nome_parlamentar, "nom_parlamentar": parlamentar.nome_parlamentar,
"observacao": orador.observacao, "observacao": orador.observacao,
"sgl_partido": "" if not partido_sigla else partido_sigla.partido.sigla "sgl_partido": filiacao_data(parlamentar, sessao.data_inicio)
}) })
# Ocorrências da Sessão # Ocorrências da Sessão
@ -930,11 +931,20 @@ def get_sessao_plenaria(sessao, casa, user):
lst_consideracoes) lst_consideracoes)
def get_turno(materia): def get_turno(materia, data_sessao=None):
"""Turno e situação de uma matéria.
`data_sessao` corta as tramitações posteriores à sessão, como já fazem
`get_materias_expediente` / `get_materias_ordem_do_dia` em
sapl.sessao.views. Sem esse corte, o PDF do resumo exibia um turno
registrado depois da sessão que ele documenta.
"""
descricao_turno = '' descricao_turno = ''
descricao_tramitacao = '' descricao_tramitacao = ''
tramitacoes = materia.tramitacao_set.order_by( tramitacoes = materia.tramitacao_set.order_by(
'-data_tramitacao', '-id').all() '-data_tramitacao', '-id').all()
if data_sessao:
tramitacoes = tramitacoes.filter(data_tramitacao__lte=data_sessao)
tramitacoes_turno = tramitacoes.exclude(turno="") tramitacoes_turno = tramitacoes.exclude(turno="")
if tramitacoes: if tramitacoes:
@ -969,7 +979,8 @@ def relatorio_sessao_plenaria(request, pk):
try: try:
logger.debug("user=" + username + logger.debug("user=" + username +
". Tentando obter SessaoPlenaria com id={}.".format(pk)) ". Tentando obter SessaoPlenaria com id={}.".format(pk))
sessao = SessaoPlenaria.objects.get(id=pk) sessao = restringe_sessoes_visiveis(
SessaoPlenaria.objects.all(), request.user).get(id=pk)
except ObjectDoesNotExist as e: except ObjectDoesNotExist as e:
logger.error("user=" + username + logger.error("user=" + username +
". Essa SessaoPlenaria não existe (pk={}). ".format(pk) + str(e)) ". Essa SessaoPlenaria não existe (pk={}). ".format(pk) + str(e))
@ -1143,8 +1154,29 @@ def relatorio_etiqueta_protocolo(request, nro, ano):
def get_etiqueta_protocolos(prots): def get_etiqueta_protocolos(prots):
prot_list = list(prots)
if not prot_list:
return []
# Pre-fetch MateriaLegislativa for all protocols in one query.
materia_query = Q()
for p in prot_list:
materia_query |= Q(numero_protocolo=p.numero, ano=p.ano)
materias_map = {
(m.numero_protocolo, m.ano): m
for m in MateriaLegislativa.objects.filter(
materia_query).select_related('tipo')
}
# Pre-fetch DocumentoAdministrativo for all protocols in one query.
documentos_map = {
doc.protocolo_id: doc
for doc in DocumentoAdministrativo.objects.filter(
protocolo__in=prot_list).select_related('tipo')
}
protocolos = [] protocolos = []
for p in prots: for p in prot_list:
dic = {} dic = {}
dic['titulo'] = str(p.numero) + '/' + str(p.ano) dic['titulo'] = str(p.numero) + '/' + str(p.ano)
@ -1161,11 +1193,11 @@ def get_etiqueta_protocolos(prots):
dic['nom_autor'] = str(p.autor or ' ') dic['nom_autor'] = str(p.autor or ' ')
dic['num_materia'] = '' materia = materias_map.get((p.numero, p.ano))
for materia in MateriaLegislativa.objects.filter( dic['num_materia'] = (
numero_protocolo=p.numero, ano=p.ano): materia.tipo.sigla + ' ' + str(materia.numero) + '/' + str(materia.ano)
dic['num_materia'] = materia.tipo.sigla + ' ' + \ if materia else ''
str(materia.numero) + '/' + str(materia.ano) )
dic['natureza'] = '' dic['natureza'] = ''
if p.tipo_processo == 0: if p.tipo_processo == 0:
@ -1173,11 +1205,11 @@ def get_etiqueta_protocolos(prots):
if p.tipo_processo == 1: if p.tipo_processo == 1:
dic['natureza'] = 'Legislativo' dic['natureza'] = 'Legislativo'
dic['num_documento'] = '' documento = documentos_map.get(p.pk)
for documento in DocumentoAdministrativo.objects.filter( dic['num_documento'] = (
protocolo=p): documento.tipo.sigla + ' ' + str(documento.numero) + '/' + str(documento.ano)
dic['num_documento'] = documento.tipo.sigla + ' ' + \ if documento else ''
str(documento.numero) + '/' + str(documento.ano) )
dic['ident_processo'] = dic['num_materia'] or dic['num_documento'] dic['ident_processo'] = dic['num_materia'] or dic['num_documento']
@ -1382,9 +1414,15 @@ def make_pdf(base_url, main_template, header_template, main_css='', header_css='
def resumo_ata_pdf(request, pk): def resumo_ata_pdf(request, pk):
base_url = request.build_absolute_uri() base_url = request.build_absolute_uri()
casa = CasaLegislativa.objects.first() casa = CasaLegislativa.objects.first()
if not casa:
raise Http404
rodape = ' '.join(get_rodape(casa)) rodape = ' '.join(get_rodape(casa))
sessao_plenaria = SessaoPlenaria.objects.get(pk=pk) # A mesma restrição de visibilidade aplicada por ResumoAtaView: sem ela,
# uma sessão oculta na consulta pública era legível por aqui.
sessao_plenaria = get_object_or_404(
restringe_sessoes_visiveis(SessaoPlenaria.objects.all(), request.user),
pk=pk)
dict_ord_template = { dict_ord_template = {
'cont_mult': 'conteudo_multimidia.html', 'cont_mult': 'conteudo_multimidia.html',
@ -1420,7 +1458,10 @@ def resumo_ata_pdf(request, pk):
context.update(get_ocorrencias_da_sessao(sessao_plenaria)) context.update(get_ocorrencias_da_sessao(sessao_plenaria))
context.update(get_consideracoes_finais(sessao_plenaria)) context.update(get_consideracoes_finais(sessao_plenaria))
context.update(get_oradores_explicacoes_pessoais(sessao_plenaria)) context.update(get_oradores_explicacoes_pessoais(sessao_plenaria))
context.update(get_assinaturas(sessao_plenaria)) context.update(get_assinaturas(
sessao_plenaria,
mesa=context['mesa'],
presenca_ordem=context['presenca_ordem']))
context.update({'object': sessao_plenaria}) context.update({'object': sessao_plenaria})
context.update({'data': dt.today().strftime('%d/%m/%Y')}) context.update({'data': dt.today().strftime('%d/%m/%Y')})
context.update({'rodape': rodape}) context.update({'rodape': rodape})
@ -1594,7 +1635,8 @@ def relatorio_sessao_plenaria_pdf(request, pk):
try: try:
logger.debug("user=" + username + logger.debug("user=" + username +
". Tentando obter SessaoPlenaria com id={}.".format(pk)) ". Tentando obter SessaoPlenaria com id={}.".format(pk))
sessao = SessaoPlenaria.objects.get(id=pk) sessao = restringe_sessoes_visiveis(
SessaoPlenaria.objects.all(), request.user).get(id=pk)
except ObjectDoesNotExist as e: except ObjectDoesNotExist as e:
logger.error("user=" + username + logger.error("user=" + username +
". Essa SessaoPlenaria não existe (pk={}). ".format(pk) + str(e)) ". Essa SessaoPlenaria não existe (pk={}). ".format(pk) + str(e))

424
sapl/sessao/views.py

@ -1,4 +1,4 @@
from collections import OrderedDict from collections import OrderedDict, defaultdict
from datetime import datetime from datetime import datetime
import json import json
import logging import logging
@ -9,7 +9,7 @@ from django.contrib import messages
from django.contrib.auth.decorators import permission_required from django.contrib.auth.decorators import permission_required
from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.auth.mixins import PermissionRequiredMixin
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Max, Q from django.db.models import Max, Prefetch, Q
from django.http import JsonResponse from django.http import JsonResponse
from django.http.response import Http404, HttpResponseRedirect from django.http.response import Http404, HttpResponseRedirect
from django.urls import reverse from django.urls import reverse
@ -37,7 +37,7 @@ from sapl.crud.base import (RP_DETAIL, RP_LIST, Crud, CrudAux,
PermissionRequiredForAppCrudMixin, make_pagination) PermissionRequiredForAppCrudMixin, make_pagination)
from sapl.materia.forms import filtra_tramitacao_status from sapl.materia.forms import filtra_tramitacao_status
from sapl.materia.models import (Autoria, TipoMateriaLegislativa, from sapl.materia.models import (Autoria, TipoMateriaLegislativa,
Tramitacao, MateriaEmTramitacao, Numeracao) Tramitacao, MateriaEmTramitacao)
from sapl.materia.views import MateriaLegislativaPesquisaView from sapl.materia.views import MateriaLegislativaPesquisaView
from sapl.parlamentares.models import (Filiacao, Legislatura, Mandato, from sapl.parlamentares.models import (Filiacao, Legislatura, Mandato,
Parlamentar, SessaoLegislativa) Parlamentar, SessaoLegislativa)
@ -49,7 +49,8 @@ from sapl.sessao.forms import ExpedienteMateriaForm, OrdemDiaForm, OrdemExpedien
from sapl.sessao.models import Correspondencia from sapl.sessao.models import Correspondencia
from sapl.settings import TIME_ZONE, RATE_LIMITER_RATE from sapl.settings import TIME_ZONE, RATE_LIMITER_RATE
from sapl.utils import show_results_filter_set, remover_acentos, get_client_ip, \ from sapl.utils import show_results_filter_set, remover_acentos, get_client_ip, \
MultiFormatOutputMixin, PautaMultiFormatOutputMixin, ratelimit_ip MultiFormatOutputMixin, PautaMultiFormatOutputMixin, ratelimit_ip, \
filiacao_data
from .forms import (AdicionarVariasMateriasFilterSet, AdicionarVariasMateriasForm, BancadaForm, from .forms import (AdicionarVariasMateriasFilterSet, AdicionarVariasMateriasForm, BancadaForm,
ExpedienteForm, JustificativaAusenciaForm, OcorrenciaSessaoForm, ListMateriaForm, ExpedienteForm, JustificativaAusenciaForm, OcorrenciaSessaoForm, ListMateriaForm,
@ -224,38 +225,41 @@ def abrir_votacao(request, pk, spk):
def customize_link_materia(context, pk, has_permission, is_expediente): def customize_link_materia(context, pk, has_permission, is_expediente):
# sessao_plenaria is the same for every row — resolve once
object_list = context['object_list']
if object_list:
sessao_plenaria = object_list[0].sessao_plenaria
else:
sessao_plenaria = SessaoPlenaria.objects.get(id=pk)
data_sessao = sessao_plenaria.data_fim or sessao_plenaria.data_inicio
for i, row in enumerate(context['rows']): for i, row in enumerate(context['rows']):
materia = context['object_list'][i].materia obj = object_list[i]
obj = context['object_list'][i] materia = obj.materia # already select_related
url_materia = reverse( url_materia = reverse(
'sapl.materia:materialegislativa_detail', kwargs={'pk': materia.id}) 'sapl.materia:materialegislativa_detail', kwargs={'pk': materia.id})
numeracao = materia.numeracao_set.first() if materia.numeracao_set.first() else "-"
todos_autoria = materia.autoria_set.all() numeracao = materia._numeracao_prefetch[0] if materia._numeracao_prefetch else "-"
autoria = todos_autoria.filter(primeiro_autor=True)
todos_autoria = materia._autoria_prefetch
autoria = [a for a in todos_autoria if a.primeiro_autor]
autor = ', '.join([str(a.autor) for a in autoria]) if autoria else "-" autor = ', '.join([str(a.autor) for a in autoria]) if autoria else "-"
todos_autores = ', '.join([str(a.autor) for a in todos_autoria]) if autoria else "-"
todos_autores = ', '.join([str(a.autor) num_protocolo = materia.numero_protocolo or "-"
for a in todos_autoria]) if autoria else "-"
num_protocolo = materia.numero_protocolo if materia.numero_protocolo else "-" tramitacao = next(
sessao_plenaria = SessaoPlenaria.objects.get(id=pk) (t for t in materia._tramitacao_prefetch if t.data_tramitacao <= data_sessao),
data_sessao = sessao_plenaria.data_fim if sessao_plenaria.data_fim else sessao_plenaria.data_inicio None,
tramitacao = Tramitacao.objects \ )
.select_related('materia', 'status', 'materia__tipo') \
.filter(materia=materia, turno__isnull=False, data_tramitacao__lte=data_sessao) \
.exclude(turno__exact='') \
.order_by('-data_tramitacao', '-id') \
.first()
turno = '-' turno = '-'
if tramitacao: if tramitacao:
for t in Tramitacao.TURNO_CHOICES: for t in Tramitacao.TURNO_CHOICES:
if t[0] == tramitacao.turno: if t[0] == tramitacao.turno:
turno = t[1] turno = t[1]
break break
materia_em_tramitacao = MateriaEmTramitacao.objects \
.select_related("materia", "tramitacao") \
.filter(materia=materia) \
.first()
# idUnica para cada materia # idUnica para cada materia
idAutor = "autor" + str(i) idAutor = "autor" + str(i)
idAutores = "autores" + str(i) idAutores = "autores" + str(i)
@ -281,12 +285,17 @@ def customize_link_materia(context, pk, has_permission, is_expediente):
# url em toda a string de title_materia # url em toda a string de title_materia
context['rows'][i][1] = (title_materia, None) context['rows'][i][1] = (title_materia, None)
exist_resultado = obj.registrovotacao_set.filter( # A matéria do item pode ter sido trocada depois de votada: só contam
materia=obj.materia).exists() # os registros da matéria atual.
exist_retirada = obj.retiradapauta_set.filter( votacoes = [r for r in obj._votacao_prefetch
materia=obj.materia).exists() if r.materia_id == obj.materia_id]
exist_leitura = obj.registroleitura_set.filter( retiradas = [r for r in obj._retirada_prefetch
materia=obj.materia).exists() if r.materia_id == obj.materia_id]
leituras = [r for r in obj._leitura_prefetch
if r.materia_id == obj.materia_id]
exist_resultado = bool(votacoes)
exist_retirada = bool(retiradas)
exist_leitura = bool(leituras)
if (obj.tipo_votacao != LEITURA and not exist_resultado and not exist_retirada) or \ if (obj.tipo_votacao != LEITURA and not exist_resultado and not exist_retirada) or \
(obj.tipo_votacao == LEITURA and not exist_leitura): (obj.tipo_votacao == LEITURA and not exist_leitura):
@ -408,8 +417,7 @@ def customize_link_materia(context, pk, has_permission, is_expediente):
resultado = '''Não há resultado''' resultado = '''Não há resultado'''
elif exist_retirada: elif exist_retirada:
retirada = obj.retiradapauta_set.filter( retirada = retiradas[-1]
materia_id=obj.materia_id).last()
retirada_descricao = retirada.tipo_de_retirada.descricao retirada_descricao = retirada.tipo_de_retirada.descricao
retirada_observacao = retirada.observacao retirada_observacao = retirada.observacao
url = reverse('sapl.sessao:retiradapauta_detail', url = reverse('sapl.sessao:retiradapauta_detail',
@ -421,13 +429,11 @@ def customize_link_materia(context, pk, has_permission, is_expediente):
else: else:
if obj.tipo_votacao == LEITURA: if obj.tipo_votacao == LEITURA:
resultado = obj.registroleitura_set.filter( resultado = leituras[-1]
materia_id=obj.materia_id).last()
resultado_descricao = "Matéria lida" resultado_descricao = "Matéria lida"
resultado_observacao = resultado.observacao resultado_observacao = resultado.observacao
else: else:
resultado = obj.registrovotacao_set.filter( resultado = votacoes[-1]
materia_id=obj.materia_id).last()
resultado_descricao = resultado.tipo_resultado_votacao.nome resultado_descricao = resultado.tipo_resultado_votacao.nome
resultado_observacao = resultado.observacao resultado_observacao = resultado.observacao
@ -837,16 +843,16 @@ class MateriaOrdemDiaCrud(MasterDetailCrud):
ordering = ['numero_ordem', 'materia', 'resultado'] ordering = ['numero_ordem', 'materia', 'resultado']
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
if self.get_queryset().count() > 500: self.paginate_by = 50 if self.object_list.count() > 500 else None
self.paginate_by = 50
else:
self.paginate_by = None
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
has_permition = self.request.user.has_module_perms(AppConfig.label) has_permition = self.request.user.has_module_perms(AppConfig.label)
return customize_link_materia(context, self.kwargs['pk'], has_permition, False) return customize_link_materia(context, self.kwargs['pk'], has_permition, False)
def get_queryset(self):
return prefetch_materias_sessao(
super().get_queryset().select_related('sessao_plenaria'),
com_situacao=False)
def recuperar_materia(request): def recuperar_materia(request):
tipo = TipoMateriaLegislativa.objects.get(pk=request.GET['tipo_materia']) tipo = TipoMateriaLegislativa.objects.get(pk=request.GET['tipo_materia'])
@ -909,20 +915,18 @@ class ExpedienteMateriaCrud(MasterDetailCrud):
ordering = ['numero_ordem', 'materia', 'resultado'] ordering = ['numero_ordem', 'materia', 'resultado']
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
self.paginate_by = 50 if self.object_list.count() > 500 else None
if self.get_queryset().count() > 500:
self.paginate_by = 50
else:
self.paginate_by = None
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
if self.request.GET.get('page'): if self.request.GET.get('page'):
context['page'] = self.request.GET.get('page') context['page'] = self.request.GET.get('page')
has_permition = self.request.user.has_module_perms(AppConfig.label) has_permition = self.request.user.has_module_perms(AppConfig.label)
return customize_link_materia(context, self.kwargs['pk'], has_permition, True) return customize_link_materia(context, self.kwargs['pk'], has_permition, True)
def get_queryset(self):
return prefetch_materias_sessao(
super().get_queryset().select_related('sessao_plenaria'),
com_situacao=False)
class CreateView(MasterDetailCrud.CreateView): class CreateView(MasterDetailCrud.CreateView):
form_class = ExpedienteMateriaForm form_class = ExpedienteMateriaForm
@ -1990,9 +1994,24 @@ def get_conteudo_multimidia(sessao_plenaria):
return context return context
def prefetch_filiacao():
"""Prefetch de `filiacao_set` para os Parlamentares exibidos no resumo.
Permite que `filiacao_data` (usada pelo filtro `filiacao_data_filter` nos
blocos do resumo/ata) resolva a sigla do partido em memória, sem uma query
por parlamentar. A ordenação replica `Filiacao.Meta.ordering`.
"""
return Prefetch(
'parlamentar__filiacao_set',
queryset=Filiacao.objects.select_related('partido').order_by(
'-data', '-data_desfiliacao'))
def get_mesa_diretora(sessao_plenaria): def get_mesa_diretora(sessao_plenaria):
mesa = IntegranteMesa.objects.filter( mesa = IntegranteMesa.objects.filter(
sessao_plenaria=sessao_plenaria).order_by('cargo_id') sessao_plenaria=sessao_plenaria).select_related(
'parlamentar', 'cargo').prefetch_related(
prefetch_filiacao()).order_by('cargo_id')
integrantes = [{'parlamentar': m.parlamentar, integrantes = [{'parlamentar': m.parlamentar,
'cargo': m.cargo} for m in mesa] 'cargo': m.cargo} for m in mesa]
return {'mesa': integrantes} return {'mesa': integrantes}
@ -2001,18 +2020,21 @@ def get_mesa_diretora(sessao_plenaria):
def get_presenca_sessao(sessao_plenaria): def get_presenca_sessao(sessao_plenaria):
parlamentares_sessao = [p.parlamentar for p in SessaoPlenariaPresenca.objects.filter( parlamentares_sessao = [p.parlamentar for p in SessaoPlenariaPresenca.objects.filter(
sessao_plenaria_id=sessao_plenaria.id sessao_plenaria_id=sessao_plenaria.id
).order_by('parlamentar__nome_parlamentar').distinct()] ).select_related('parlamentar').prefetch_related(
prefetch_filiacao()).order_by('parlamentar__nome_parlamentar').distinct()]
ausentes_sessao = JustificativaAusencia.objects.filter( ausentes_sessao = JustificativaAusencia.objects.filter(
sessao_plenaria_id=sessao_plenaria.id sessao_plenaria_id=sessao_plenaria.id
).distinct().order_by('parlamentar__nome_parlamentar') ).select_related('parlamentar', 'tipo_ausencia').distinct().order_by(
'parlamentar__nome_parlamentar')
return ({'presenca_sessao': parlamentares_sessao, return ({'presenca_sessao': parlamentares_sessao,
'justificativa_ausencia': ausentes_sessao}) 'justificativa_ausencia': ausentes_sessao})
def get_correspondencias(sessao_plenaria, user): def get_correspondencias(sessao_plenaria, user):
qs = sessao_plenaria.correspondencia_set.all() qs = sessao_plenaria.correspondencia_set.select_related(
'documento', 'documento__tipo')
is_anon = user.is_anonymous is_anon = user.is_anonymous
is_ostensivo = AppsAppConfig.attr( is_ostensivo = AppsAppConfig.attr(
@ -2044,30 +2066,99 @@ def get_correspondencias(sessao_plenaria, user):
def get_expedientes(sessao_plenaria): def get_expedientes(sessao_plenaria):
expediente = ExpedienteSessao.objects.filter( expediente = ExpedienteSessao.objects.filter(
sessao_plenaria_id=sessao_plenaria.id).order_by('tipo__ordenacao', 'tipo__nome') sessao_plenaria_id=sessao_plenaria.id).select_related(
'tipo').order_by('tipo__ordenacao', 'tipo__nome')
expedientes = [] expedientes = []
for e in expediente: for e in expediente:
tipo = TipoExpediente.objects.get(id=e.tipo_id) tipo = e.tipo
conteudo = e.conteudo conteudo = e.conteudo
ex = {'tipo': tipo, 'conteudo': conteudo} ex = {'tipo': tipo, 'conteudo': conteudo}
expedientes.append(ex) expedientes.append(ex)
return ({'expedientes': expedientes}) return ({'expedientes': expedientes})
def prefetch_materias_sessao(qs, com_situacao=True):
"""Prefetches compartilhados por `get_materias_expediente`,
`get_materias_ordem_do_dia` e pelas ListViews de OrdemDia e
ExpedienteMateria.
Os querysets dos `Prefetch` não recebem `order_by` explícito justamente
para herdarem o `Meta.ordering` de cada model — é dele que dependem os
`.first()` / `.last()` que este código substitui por indexação de lista.
`com_situacao=False` dispensa o prefetch de `MateriaEmTramitacao`, que
as ListViews não usam.
"""
prefetches = [
Prefetch(
'materia__tramitacao_set',
queryset=Tramitacao.objects.exclude(turno='').order_by(
'-data_tramitacao', '-id'),
to_attr='_tramitacao_prefetch'),
Prefetch('materia__numeracao_set', to_attr='_numeracao_prefetch'),
Prefetch(
'materia__autoria_set',
queryset=Autoria.objects.select_related('autor'),
to_attr='_autoria_prefetch'),
Prefetch(
'registrovotacao_set',
queryset=RegistroVotacao.objects.select_related(
'tipo_resultado_votacao'),
to_attr='_votacao_prefetch'),
Prefetch(
'retiradapauta_set',
queryset=RetiradaPauta.objects.select_related('tipo_de_retirada'),
to_attr='_retirada_prefetch'),
Prefetch('registroleitura_set', to_attr='_leitura_prefetch'),
]
if com_situacao:
prefetches.append(Prefetch(
'materia__materiaemtramitacao_set',
queryset=MateriaEmTramitacao.objects.select_related(
'tramitacao', 'tramitacao__status'),
to_attr='_met_prefetch'))
return qs.select_related('materia', 'materia__tipo').prefetch_related(
*prefetches)
def agrupa_votos_por_materia(campo, ids):
"""Busca todos os VotoParlamentar de uma seção em uma única query.
`campo` é 'expediente' ou 'ordem' — as FKs desnormalizadas de
VotoParlamentar. O agrupamento preserva a ordem da query, que herda o
`Meta.ordering = ('id',)` do model.
"""
votos = defaultdict(list)
if not ids:
return votos
qs = VotoParlamentar.objects.filter(
**{'%s_id__in' % campo: ids}).select_related('parlamentar')
for voto in qs:
votos[getattr(voto, '%s_id' % campo)].append(voto)
return votos
def get_materias_expediente(sessao_plenaria): def get_materias_expediente(sessao_plenaria):
materias_expediente = [] materias_expediente = []
for m in ExpedienteMateria.objects.select_related("materia").filter(sessao_plenaria_id=sessao_plenaria.id): data_sessao = sessao_plenaria.data_fim if sessao_plenaria.data_fim else sessao_plenaria.data_inicio
tramitacao = ''
data_sessao = sessao_plenaria.data_fim if sessao_plenaria.data_fim else sessao_plenaria.data_inicio expedientes = list(prefetch_materias_sessao(
for aux_tramitacao in Tramitacao.objects.filter(materia=m.materia, data_tramitacao__lte=data_sessao).order_by( ExpedienteMateria.objects.filter(
'-data_tramitacao', '-id'): sessao_plenaria_id=sessao_plenaria.id)))
if aux_tramitacao.turno: votos_por_materia = agrupa_votos_por_materia(
tramitacao = aux_tramitacao 'expediente', [m.id for m in expedientes])
break
for m in expedientes:
rv = m.registrovotacao_set.filter(materia=m.materia).first() tramitacao = next(
rp = m.retiradapauta_set.filter(materia=m.materia).first() (t for t in m.materia._tramitacao_prefetch
rl = m.registroleitura_set.filter(materia=m.materia).first() if t.data_tramitacao <= data_sessao), '')
rv = next((r for r in m._votacao_prefetch
if r.materia_id == m.materia_id), None)
rp = next((r for r in m._retirada_prefetch
if r.materia_id == m.materia_id), None)
rl = next((r for r in m._leitura_prefetch
if r.materia_id == m.materia_id), None)
if rv: if rv:
resultado = rv.tipo_resultado_votacao.nome resultado = rv.tipo_resultado_votacao.nome
resultado_observacao = rv.observacao resultado_observacao = rv.observacao
@ -2084,11 +2175,11 @@ def get_materias_expediente(sessao_plenaria):
voto_nominal = [] voto_nominal = []
if m.tipo_votacao == 2: if m.tipo_votacao == 2:
for voto in VotoParlamentar.objects.filter(expediente=m.id): for voto in votos_por_materia[m.id]:
voto_nominal.append( voto_nominal.append(
(voto.parlamentar.nome_completo, voto.voto)) (voto.parlamentar.nome_completo, voto.voto))
voto = RegistroVotacao.objects.filter(expediente=m.id).last() voto = m._votacao_prefetch[-1] if m._votacao_prefetch else None
if voto: if voto:
voto_sim = voto.numero_votos_sim voto_sim = voto.numero_votos_sim
voto_nao = voto.numero_votos_nao voto_nao = voto.numero_votos_nao
@ -2098,7 +2189,9 @@ def get_materias_expediente(sessao_plenaria):
voto_nao = " Não Informado" voto_nao = " Não Informado"
voto_abstencoes = " Não Informado" voto_abstencoes = " Não Informado"
materia_em_tramitacao = m.materia.materiaemtramitacao_set.first() met = m.materia._met_prefetch
materia_em_tramitacao = met[0] if met else None
numeracao = m.materia._numeracao_prefetch
materias_expediente.append({ materias_expediente.append({
'ementa': m.materia.ementa, 'ementa': m.materia.ementa,
'titulo': m.materia, 'titulo': m.materia,
@ -2107,9 +2200,9 @@ def get_materias_expediente(sessao_plenaria):
'situacao': materia_em_tramitacao.tramitacao.status if materia_em_tramitacao else _("Não informada"), 'situacao': materia_em_tramitacao.tramitacao.status if materia_em_tramitacao else _("Não informada"),
'resultado': resultado, 'resultado': resultado,
'resultado_observacao': resultado_observacao, 'resultado_observacao': resultado_observacao,
'autor': [str(x.autor) for x in Autoria.objects.select_related("autor").filter(materia_id=m.materia_id)], 'autor': [str(x.autor) for x in m.materia._autoria_prefetch],
'numero_protocolo': m.materia.numero_protocolo, 'numero_protocolo': m.materia.numero_protocolo,
'numero_processo': m.materia.numeracao_set.last(), 'numero_processo': numeracao[-1] if numeracao else None,
'tipo_votacao': m.TIPO_VOTACAO_CHOICES[m.tipo_votacao], 'tipo_votacao': m.TIPO_VOTACAO_CHOICES[m.tipo_votacao],
'voto_sim': voto_sim, 'voto_sim': voto_sim,
'voto_nao': voto_nao, 'voto_nao': voto_nao,
@ -2125,12 +2218,13 @@ def get_materias_expediente(sessao_plenaria):
def get_oradores_expediente(sessao_plenaria): def get_oradores_expediente(sessao_plenaria):
oradores = [] oradores = []
for orador in OradorExpediente.objects.filter( for orador in OradorExpediente.objects.filter(
sessao_plenaria_id=sessao_plenaria.id).order_by('numero_ordem'): sessao_plenaria_id=sessao_plenaria.id).select_related(
'parlamentar').prefetch_related(
prefetch_filiacao()).order_by('numero_ordem'):
numero_ordem = orador.numero_ordem numero_ordem = orador.numero_ordem
url_discurso = orador.url_discurso url_discurso = orador.url_discurso
observacao = orador.observacao observacao = orador.observacao
parlamentar = Parlamentar.objects.get( parlamentar = orador.parlamentar
id=orador.parlamentar_id)
ora = {'numero_ordem': numero_ordem, ora = {'numero_ordem': numero_ordem,
'url_discurso': url_discurso, 'url_discurso': url_discurso,
'parlamentar': parlamentar, 'parlamentar': parlamentar,
@ -2143,21 +2237,32 @@ def get_oradores_expediente(sessao_plenaria):
def get_presenca_ordem_do_dia(sessao_plenaria): def get_presenca_ordem_do_dia(sessao_plenaria):
parlamentares_ordem = [p.parlamentar for p in PresencaOrdemDia.objects.filter( parlamentares_ordem = [p.parlamentar for p in PresencaOrdemDia.objects.filter(
sessao_plenaria_id=sessao_plenaria.id sessao_plenaria_id=sessao_plenaria.id
).distinct().order_by('parlamentar__nome_parlamentar')] ).select_related('parlamentar').prefetch_related(
prefetch_filiacao()).distinct().order_by('parlamentar__nome_parlamentar')]
return {'presenca_ordem': parlamentares_ordem} return {'presenca_ordem': parlamentares_ordem}
def get_assinaturas(sessao_plenaria): def get_assinaturas(sessao_plenaria, mesa=None, presenca_ordem=None):
mesa_dia = get_mesa_diretora(sessao_plenaria)['mesa'] """`mesa` e `presenca_ordem` podem ser reaproveitados de
`get_mesa_diretora` / `get_presenca_ordem_do_dia` quando o chamador já os
calculou, evitando repetir as duas queries. O `.distinct()` que
`get_presenca_ordem_do_dia` aplica é um no-op (a projeção inclui a PK),
então as duas listas são equivalentes.
"""
mesa_dia = get_mesa_diretora(
sessao_plenaria)['mesa'] if mesa is None else mesa
presidente_dia = [next(iter( presidente_dia = [next(iter(
[m['parlamentar'] for m in mesa_dia if m['cargo'].descricao == 'Presidente']), [m['parlamentar'] for m in mesa_dia if m['cargo'].descricao == 'Presidente']),
'')] '')]
parlamentares_ordem = [p.parlamentar for p in PresencaOrdemDia.objects.filter( if presenca_ordem is None:
sessao_plenaria_id=sessao_plenaria.id presenca_ordem = [p.parlamentar for p in PresencaOrdemDia.objects.filter(
).order_by('parlamentar__nome_parlamentar')] sessao_plenaria_id=sessao_plenaria.id
).select_related('parlamentar').prefetch_related(
prefetch_filiacao()).order_by('parlamentar__nome_parlamentar')]
parlamentares_ordem = presenca_ordem
parlamentares_mesa = [m['parlamentar'] for m in mesa_dia] parlamentares_mesa = [m['parlamentar'] for m in mesa_dia]
@ -2203,19 +2308,25 @@ def get_assinaturas_presidente(sessao_plenaria):
def get_materias_ordem_do_dia(sessao_plenaria): def get_materias_ordem_do_dia(sessao_plenaria):
materias_ordem = [] materias_ordem = []
for o in OrdemDia.objects.filter(sessao_plenaria_id=sessao_plenaria.id): data_sessao = sessao_plenaria.data_fim if sessao_plenaria.data_fim else sessao_plenaria.data_inicio
tramitacao = ''
data_sessao = sessao_plenaria.data_fim if sessao_plenaria.data_fim else sessao_plenaria.data_inicio ordens = list(prefetch_materias_sessao(
for aux_tramitacao in Tramitacao.objects.filter(materia=o.materia, data_tramitacao__lte=data_sessao).order_by( OrdemDia.objects.filter(sessao_plenaria_id=sessao_plenaria.id)))
'-data_tramitacao', '-id'): votos_por_materia = agrupa_votos_por_materia(
if aux_tramitacao.turno: 'ordem', [o.id for o in ordens])
tramitacao = aux_tramitacao
break for o in ordens:
tramitacao = next(
(t for t in o.materia._tramitacao_prefetch
if t.data_tramitacao <= data_sessao), '')
# Verificar resultado # Verificar resultado
rv = o.registrovotacao_set.filter(materia=o.materia).first() rv = next((r for r in o._votacao_prefetch
rp = o.retiradapauta_set.filter(materia=o.materia).first() if r.materia_id == o.materia_id), None)
rl = o.registroleitura_set.filter(materia=o.materia).first() rp = next((r for r in o._retirada_prefetch
if r.materia_id == o.materia_id), None)
rl = next((r for r in o._leitura_prefetch
if r.materia_id == o.materia_id), None)
if rv: if rv:
resultado = rv.tipo_resultado_votacao.nome resultado = rv.tipo_resultado_votacao.nome
resultado_observacao = rv.observacao resultado_observacao = rv.observacao
@ -2232,11 +2343,11 @@ def get_materias_ordem_do_dia(sessao_plenaria):
voto_nominal = [] voto_nominal = []
if o.tipo_votacao == 2: if o.tipo_votacao == 2:
for voto in VotoParlamentar.objects.filter(ordem=o.id): for voto in votos_por_materia[o.id]:
voto_nominal.append( voto_nominal.append(
(voto.parlamentar.nome_parlamentar, voto.voto)) (voto.parlamentar.nome_parlamentar, voto.voto))
voto = RegistroVotacao.objects.filter(ordem=o.id).last() voto = o._votacao_prefetch[-1] if o._votacao_prefetch else None
if voto: if voto:
voto_sim = voto.numero_votos_sim voto_sim = voto.numero_votos_sim
voto_nao = voto.numero_votos_nao voto_nao = voto.numero_votos_nao
@ -2246,7 +2357,9 @@ def get_materias_ordem_do_dia(sessao_plenaria):
voto_nao = " Não Informado" voto_nao = " Não Informado"
voto_abstencoes = " Não Informado" voto_abstencoes = " Não Informado"
materia_em_tramitacao = o.materia.materiaemtramitacao_set.first() met = o.materia._met_prefetch
materia_em_tramitacao = met[0] if met else None
numeracao = o.materia._numeracao_prefetch
materias_ordem.append({ materias_ordem.append({
'ementa': o.materia.ementa, 'ementa': o.materia.ementa,
'ementa_observacao': o.observacao, 'ementa_observacao': o.observacao,
@ -2256,9 +2369,9 @@ def get_materias_ordem_do_dia(sessao_plenaria):
'situacao': materia_em_tramitacao.tramitacao.status if materia_em_tramitacao else _("Não informada"), 'situacao': materia_em_tramitacao.tramitacao.status if materia_em_tramitacao else _("Não informada"),
'resultado': resultado, 'resultado': resultado,
'resultado_observacao': resultado_observacao, 'resultado_observacao': resultado_observacao,
'autor': [str(x.autor) for x in Autoria.objects.select_related("autor").filter(materia_id=o.materia_id)], 'autor': [str(x.autor) for x in o.materia._autoria_prefetch],
'numero_protocolo': o.materia.numero_protocolo, 'numero_protocolo': o.materia.numero_protocolo,
'numero_processo': o.materia.numeracao_set.last(), 'numero_processo': numeracao[-1] if numeracao else None,
'tipo_votacao': o.TIPO_VOTACAO_CHOICES[o.tipo_votacao], 'tipo_votacao': o.TIPO_VOTACAO_CHOICES[o.tipo_votacao],
'voto_sim': voto_sim, 'voto_sim': voto_sim,
'voto_nao': voto_nao, 'voto_nao': voto_nao,
@ -2275,15 +2388,14 @@ def get_oradores_ordemdia(sessao_plenaria):
oradores_ordem_dia = OradorOrdemDia.objects.filter( oradores_ordem_dia = OradorOrdemDia.objects.filter(
sessao_plenaria_id=sessao_plenaria.id sessao_plenaria_id=sessao_plenaria.id
).order_by('numero_ordem') ).select_related('parlamentar').prefetch_related(
prefetch_filiacao()).order_by('numero_ordem')
for orador in oradores_ordem_dia: for orador in oradores_ordem_dia:
numero_ordem = orador.numero_ordem numero_ordem = orador.numero_ordem
url_discurso = orador.url_discurso url_discurso = orador.url_discurso
observacao = orador.observacao observacao = orador.observacao
parlamentar = Parlamentar.objects.get( parlamentar = orador.parlamentar
id=orador.parlamentar_id
)
o = { o = {
'numero_ordem': numero_ordem, 'numero_ordem': numero_ordem,
'url_discurso': url_discurso, 'url_discurso': url_discurso,
@ -2299,26 +2411,26 @@ def get_oradores_ordemdia(sessao_plenaria):
def get_oradores_explicacoes_pessoais(sessao_plenaria): def get_oradores_explicacoes_pessoais(sessao_plenaria):
oradores_explicacoes = [] oradores_explicacoes = []
for orador in Orador.objects.filter( for orador in Orador.objects.filter(
sessao_plenaria_id=sessao_plenaria.id).order_by('numero_ordem'): sessao_plenaria_id=sessao_plenaria.id).select_related(
for parlamentar in Parlamentar.objects.filter( 'parlamentar').prefetch_related(
id=orador.parlamentar.id): prefetch_filiacao()).order_by('numero_ordem'):
partido_sigla = Filiacao.objects.filter( parlamentar = orador.parlamentar
parlamentar=parlamentar).last() # Antes era Filiacao...last() (a filiação mais antiga, dado o
if not partido_sigla: # ordering decrescente por data), enquanto o PDF usava .first().
sigla = '' # Unifica no critério já usado pelas listas de presença: o partido
else: # vigente na data da sessão.
sigla = partido_sigla.partido.sigla sigla = filiacao_data(parlamentar, sessao_plenaria.data_inicio)
observacao = orador.observacao observacao = orador.observacao
url_discurso = orador.url_discurso url_discurso = orador.url_discurso
oradores = { oradores = {
'numero_ordem': orador.numero_ordem, 'numero_ordem': orador.numero_ordem,
'parlamentar': parlamentar, 'parlamentar': parlamentar,
'sgl_partido': sigla, 'sgl_partido': sigla,
'observacao': observacao, 'observacao': observacao,
'url_discurso': url_discurso 'url_discurso': url_discurso
} }
oradores_explicacoes.append(oradores) oradores_explicacoes.append(oradores)
context = {'oradores_explicacoes': oradores_explicacoes} context = {'oradores_explicacoes': oradores_explicacoes}
return context return context
@ -2337,6 +2449,34 @@ def get_consideracoes_finais(sessao_plenaria):
return context return context
def get_votos_nominais(sessao_plenaria_id, model, campo):
"""Blocos de votação nominal de uma seção, em 3 queries em vez de 2 por matéria.
Replica `VotoParlamentar.objects.filter(votacao__in=registro).order_by('parlamentar')`:
a query dos votos é ordenada globalmente por parlamentar e o agrupamento
preserva essa ordem dentro de cada matéria.
"""
materias = list(model.objects.filter(
sessao_plenaria_id=sessao_plenaria_id, tipo_votacao=2
).select_related('materia').order_by('numero_ordem'))
votos_por_materia = defaultdict(list)
if materias:
registro_para_materia = {
r.id: getattr(r, '%s_id' % campo)
for r in RegistroVotacao.objects.filter(
**{'%s_id__in' % campo: [m.id for m in materias]})
}
if registro_para_materia:
for vp in VotoParlamentar.objects.filter(
votacao_id__in=registro_para_materia).select_related(
'parlamentar').order_by('parlamentar'):
votos_por_materia[registro_para_materia[vp.votacao_id]].append(vp)
return [{'titulo': m.materia, 'votos': votos_por_materia[m.id]}
for m in materias]
class ResumoView(DetailView): class ResumoView(DetailView):
template_name = 'sessao/resumo.html' template_name = 'sessao/resumo.html'
model = SessaoPlenaria model = SessaoPlenaria
@ -2351,22 +2491,8 @@ class ResumoView(DetailView):
context = self.get_context_data(object=self.object) context = self.get_context_data(object=self.object)
# Votos de Votação Nominal de Matérias Expediente # Votos de Votação Nominal de Matérias Expediente
votacoes = [] context.update({'votos_nominais_materia_expediente': get_votos_nominais(
for mevn in ExpedienteMateria.objects.filter(sessao_plenaria_id=self.object.id, tipo_votacao=2) \ self.object.id, ExpedienteMateria, 'expediente')})
.order_by('numero_ordem'):
votos_materia = []
titulo_materia = mevn.materia
registro = RegistroVotacao.objects.filter(expediente=mevn)
if registro:
for vp in VotoParlamentar.objects.filter(votacao__in=registro).order_by('parlamentar'):
votos_materia.append(vp)
votacoes.append({
'titulo': titulo_materia,
'votos': votos_materia
})
context.update({'votos_nominais_materia_expediente': votacoes})
# ===================================================================== # =====================================================================
# Identificação Básica # Identificação Básica
@ -2397,25 +2523,15 @@ class ResumoView(DetailView):
context.update(get_presenca_ordem_do_dia(self.object)) context.update(get_presenca_ordem_do_dia(self.object))
# ===================================================================== # =====================================================================
# Assinaturas # Assinaturas
context.update(get_assinaturas(self.object)) context.update(get_assinaturas(
self.object,
mesa=context['mesa'],
presenca_ordem=context['presenca_ordem']))
# ===================================================================== # =====================================================================
# Matérias Ordem do Dia # Matérias Ordem do Dia
# Votos de Votação Nominal de Matérias Ordem do Dia # Votos de Votação Nominal de Matérias Ordem do Dia
votacoes_od = [] context.update({'votos_nominais_materia_ordem_dia': get_votos_nominais(
for modvn in OrdemDia.objects.filter(sessao_plenaria_id=self.object.id, tipo_votacao=2).order_by('numero_ordem'): self.object.id, OrdemDia, 'ordem')})
votos_materia_od = []
t_materia = modvn.materia
registro_od = RegistroVotacao.objects.filter(ordem=modvn)
if registro_od:
for vp_od in VotoParlamentar.objects.filter(votacao__in=registro_od).order_by('parlamentar'):
votos_materia_od.append(vp_od)
votacoes_od.append({
'titulo': t_materia,
'votos': votos_materia_od
})
context.update({'votos_nominais_materia_ordem_dia': votacoes_od})
context.update(get_materias_ordem_do_dia(self.object)) context.update(get_materias_ordem_do_dia(self.object))
# ===================================================================== # =====================================================================

120
sapl/templates/painel/mensagem.html

@ -1,120 +0,0 @@
{% load i18n %}
{% load common_tags %}
{% load render_bundle from webpack_loader %}
{% load webpack_static from webpack_loader %}
<!DOCTYPE HTML>
<!--[if IE 8]> <html class="no-js lt-ie9" lang="pt-br"> <![endif]-->
<!--[if gt IE 8]><!-->
<html lang="pt-br">
<!--<![endif]-->
<head>
<meta charset="UTF-8">
<!-- TODO: does it need this head_title here? -->
<title>{% block head_title %}{% trans 'SAPL - Sistema de Apoio ao Processo Legislativo' %}{% endblock %}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{% render_chunk_vendors 'css' %}
{% render_bundle 'global' 'css' %}
{% render_bundle 'painel' 'css' %}
<STYLE type="text/css">
@media screen {
body {font-size: medium; color: white; line-height: 1em; background: black;}
}
</STYLE>
</head>
<body>
<h1>{{ context.title }}</h1>
<input id="json_url" type="hidden" value="{% url 'sapl.painel:dados_painel' %}">
<h2>Ajax refresh counter: <span id="counter"></span></h2>
<h3>
<span id="sessao_plenaria"></span><br/><br/>
<span id="sessao_plenaria_data"></span><br/><br/>
<span id="sessao_plenaria_hora_inicio"></span></br><br/>
<h2><span id="relogio"></span></h2></br><br/><br/>
<span id="materia_legislativa_texto"></span><br/>
<span id="observacao_materia"></span>
</h3>
</body>
{% render_chunk_vendors 'js' %}
{% render_bundle 'global' 'js' %}
{% render_bundle 'painel' 'js' %}
<script type="text/javascript">
$(document).ready(function() {
//TODO: replace by a fancy jQuery clock
function checkTime(i) {
if (i<10) {i = "0" + i}; // add zero in front of numbers < 10
return i;
}
function startTime() {
var today=new Date();
var h=today.getHours();
var m=today.getMinutes();
var s=today.getSeconds();
m = checkTime(m);
s = checkTime(s);
$("#relogio").text(h+":"+m+":"+s)
var t = setTimeout(function(){
startTime()
},500);
}
startTime();
var counter = 1;
(function poll() {
$.ajax({
url: $("#json_url").val(),
type: "GET",
success: function(data) {
//TODO: json spitted out is very complex, have to simplify/flat it
//TODO: probably building it by hand on REST side
console.debug(data)
var presentes = $("#parlamentares");
presentes.children().remove();
presentes_ordem_dia = data.presentes_ordem_dia
$.each(presentes_ordem_dia, function(index, parlamentar) {
$('<li />', {text: parlamentar.nome + '/' + parlamentar.partido + ' ' + parlamentar.voto }).appendTo(presentes);
});
var votacao = $("#votacao")
votacao.children().remove()
votacao.append("<li>Sim: " + data["numero_votos_sim"] + "</li>")
votacao.append("<li>Não: " + data["numero_votos_nao"] + "</li>")
votacao.append("<li>Abstenções: " + data["numero_abstencoes"] + "</li>")
votacao.append("<li>Presentes: " + data["presentes"] + "</li>")
votacao.append("<li>Total votos: " + data["total_votos"] + "</li>")
$("#sessao_plenaria").text(data["sessao_plenaria"])
$("#sessao_plenaria_data").text("Data Início: " + data["sessao_plenaria_data"])
$("#sessao_plenaria_hora_inicio").text("Hora Início: " + data["sessao_plenaria_hora_inicio"])
$("#materia_legislativa_texto").text(data["materia_legislativa_texto"])
$("#observacao_materia").text(data["observacao_materia"])
$("#resultado_votacao").text(data["tipo_resultado"])
$("#counter").text(counter);
counter++;
},
error: function(err) {
console.error(err);
},
dataType: "json",
//complete: setTimeout(function() {poll()}, 5000),
timeout: 20000 // TODO: decrease
})
})();
});
</script>
</html>

128
sapl/templates/painel/parlamentares.html

@ -1,128 +0,0 @@
{% load i18n %}
{% load common_tags %}
{% load render_bundle from webpack_loader %}
{% load webpack_static from webpack_loader %}
<!DOCTYPE HTML>
<!--[if IE 8]> <html class="no-js lt-ie9" lang="pt-br"> <![endif]-->
<!--[if gt IE 8]><!-->
<html lang="pt-br">
<!--<![endif]-->
<head>
<meta charset="UTF-8">
<!-- TODO: does it need this head_title here? -->
<title>{% block head_title %}{% trans 'SAPL - Sistema de Apoio ao Processo Legislativo' %}{% endblock %}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{% render_chunk_vendors 'css' %}
{% render_bundle 'global' 'css' %}
{% render_bundle 'painel' 'css' %}
<STYLE type="text/css">
@media screen {
body {font-size: medium; color: white; line-height: 1em; background: black;}
}
</STYLE>
</head>
<body>
<h1>{{ context.title }}</h1>
<input id="json_url" type="hidden" value="{% url 'sapl.painel:dados_painel' %}">
<h3>
<span id="sessao_plenaria"></span><br/><br/>
<span id="sessao_plenaria_data"></span><br/><br/>
<span id="sessao_plenaria_hora_inicio"></span></br><br/>
<h2><span id="relogio"></span></h2>
<table>
<tr>
<td>
<ul id="parlamentares">
</ul>
</td>
</tr>
</table>
</h3>
</body>
{% render_chunk_vendors 'js' %}
{% render_bundle 'global' 'js' %}
{% render_bundle 'painel' 'js' %}
<script type="text/javascript">
$(document).ready(function() {
//TODO: replace by a fancy jQuery clock
function checkTime(i) {
if (i<10) {i = "0" + i}; // add zero in front of numbers < 10
return i;
}
function startTime() {
var today=new Date();
var h=today.getHours();
var m=today.getMinutes();
var s=today.getSeconds();
m = checkTime(m);
s = checkTime(s);
$("#relogio").text(h+":"+m+":"+s)
var t = setTimeout(function(){
startTime()
},500);
}
startTime();
var counter = 1;
(function poll() {
$.ajax({
url: $("#json_url").val(),
type: "GET",
success: function(data) {
//TODO: json spitted out is very complex, have to simplify/flat it
//TODO: probably building it by hand on REST side
console.debug(data)
var presentes = $("#parlamentares");
presentes.children().remove();
presentes_ordem_dia = data.presentes_ordem_dia
$.each(presentes_ordem_dia, function(index, parlamentar) {
$('<li />', {text: parlamentar.nome + '/' + parlamentar.partido }).appendTo(presentes);
/*$('<li />', {text: parlamentar.nome + '/' + parlamentar.partido + ' ' + parlamentar.voto }).appendTo(presentes);*/
});
var votacao = $("#votacao")
votacao.children().remove()
votacao.append("<li>Sim: " + data["numero_votos_sim"] + "</li>")
votacao.append("<li>Não: " + data["numero_votos_nao"] + "</li>")
votacao.append("<li>Abstenções: " + data["numero_abstencoes"] + "</li>")
votacao.append("<li>Presentes: " + data["presentes"] + "</li>")
votacao.append("<li>Total votos: " + data["total_votos"] + "</li>")
$("#sessao_plenaria").text(data["sessao_plenaria"])
$("#sessao_plenaria_data").text("Data Início: " + data["sessao_plenaria_data"])
$("#sessao_plenaria_hora_inicio").text("Hora Início: " + data["sessao_plenaria_hora_inicio"])
$("#materia_legislativa_texto").text(data["materia_legislativa_texto"])
$("#observacao_materia").text(data["observacao_materia"])
$("#resultado_votacao").text(data["tipo_resultado"])
$("#counter").text(counter);
counter++;
},
error: function(err) {
console.error(err);
},
dataType: "json",
//complete: setTimeout(function() {poll()}, 5000),
timeout: 20000 // TODO: decrease
})
})();
});
</script>
</html>

123
sapl/templates/painel/votacao.html

@ -1,123 +0,0 @@
{% load i18n %}
{% load render_bundle from webpack_loader %}
{% load webpack_static from webpack_loader %}
<!DOCTYPE HTML>
<!--[if IE 8]> <html class="no-js lt-ie9" lang="pt-br"> <![endif]-->
<!--[if gt IE 8]><!-->
<html lang="pt-br">
<!--<![endif]-->
<head>
<meta charset="UTF-8">
<!-- TODO: does it need this head_title here? -->
<title>{% block head_title %}{% trans 'SAPL - Sistema de Apoio ao Processo Legislativo' %}{% endblock %}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{% render_chunk_vendors 'css' %}
{% render_bundle 'global' 'css' %}
{% render_bundle 'painel' 'css' %}
<STYLE type="text/css">
@media screen {
body {font-size: medium; color: white; line-height: 1em; background: black;}
}
</STYLE>
</head>
<body>
<h1>{{ context.title }}</h1>
<input id="json_url" type="hidden" value="{% url 'sapl.painel:dados_painel' %}">
<h3>
<span id="sessao_plenaria"></span><br/><br/>
<span id="sessao_plenaria_data"></span><br/><br/>
<span id="sessao_plenaria_hora_inicio"></span></br><br/>
<h2><span id="relogio"></span></h2>
<table>
<tr>
<td>
<ul id="votacao">
</ul>
</td>
</tr>
</table>
<span id="resultado_votacao"></span><br/>
</h3>
</body>
{% render_chunk_vendors 'js' %}
{% render_bundle 'global' 'js' %}
{% render_bundle 'painel' 'js' %}
<script type="text/javascript">
$(document).ready(function() {
//TODO: replace by a fancy jQuery clock
function checkTime(i) {
if (i<10) {i = "0" + i}; // add zero in front of numbers < 10
return i;
}
function startTime() {
var today=new Date();
var h=today.getHours();
var m=today.getMinutes();
var s=today.getSeconds();
m = checkTime(m);
s = checkTime(s);
$("#relogio").text(h+":"+m+":"+s)
var t = setTimeout(function(){
startTime()
},500);
}
startTime();
var counter = 1;
(function poll() {
$.ajax({
url: $("#json_url").val(),
type: "GET",
success: function(data) {
//TODO: json spitted out is very complex, have to simplify/flat it
//TODO: probably building it by hand on REST side
console.debug(data)
var presentes = $("#parlamentares");
presentes.children().remove();
presentes_ordem_dia = data.presentes_ordem_dia
$.each(presentes_ordem_dia, function(index, parlamentar) {
$('<li />', {text: parlamentar.nome + '/' + parlamentar.partido + ' ' + parlamentar.voto }).appendTo(presentes);
});
var votacao = $("#votacao")
votacao.children().remove()
votacao.append("<li>Sim: " + data["numero_votos_sim"] + "</li>")
votacao.append("<li>Não: " + data["numero_votos_nao"] + "</li>")
votacao.append("<li>Abstenções: " + data["numero_abstencoes"] + "</li>")
votacao.append("<li>Presentes: " + data["presentes"] + "</li>")
votacao.append("<li>Total votos: " + data["total_votos"] + "</li>")
$("#sessao_plenaria").text(data["sessao_plenaria"])
$("#sessao_plenaria_data").text("Data Início: " + data["sessao_plenaria_data"])
$("#sessao_plenaria_hora_inicio").text("Hora Início: " + data["sessao_plenaria_hora_inicio"])
$("#materia_legislativa_texto").text(data["materia_legislativa_texto"])
$("#observacao_materia").text(data["observacao_materia"])
$("#resultado_votacao").text(data["tipo_resultado"])
$("#counter").text(counter);
counter++;
},
error: function(err) {
console.error(err);
},
dataType: "json",
//complete: setTimeout(function() {poll()}, 5000),
timeout: 20000 // TODO: decrease
})
})();
});
</script>
</html>

6
sapl/templates/relatorios/relatorio_materias_por_autor.html

@ -10,7 +10,7 @@
&emsp;Tipo de matéria: {{ tipo }}<br /> &emsp;Tipo de matéria: {{ tipo }}<br />
&emsp;Data de apresentação: {{periodo}}<br /><br /><br/> &emsp;Data de apresentação: {{periodo}}<br /><br /><br/>
{% if materias_resultado|length %} {% if object_list|length %}
<table class="table table-bordered table-hover"> <table class="table table-bordered table-hover">
<thead class="thead-default"> <thead class="thead-default">
<tr class="active"> <tr class="active">
@ -31,8 +31,8 @@
</tbody> </tbody>
</table> </table>
</br></br> </br></br>
{% if materias_resultado|length %} {% if object_list|length %}
{% for materia in materias_resultado %} {% for materia in object_list %}
<strong>Matéria:</strong> {{materia.tipo.descricao}} {{materia.numero}}/{{materia.ano}} <strong>Matéria:</strong> {{materia.tipo.descricao}} {{materia.numero}}/{{materia.ano}}
</br> </br>
<strong>Ementa:</strong> <strong>Ementa:</strong>

58
sapl/utils.py

@ -944,23 +944,59 @@ def qs_override_django_filter(self):
def filiacao_data(parlamentar, data_inicio, data_fim=None): def filiacao_data(parlamentar, data_inicio, data_fim=None):
from sapl.parlamentares.models import Filiacao from sapl.parlamentares.models import Filiacao
filiacoes_parlamentar = Filiacao.objects.filter( filiacoes = _filiacoes_prefetchadas(parlamentar, data_inicio, data_fim)
parlamentar=parlamentar)
filiacoes = filiacoes_parlamentar.filter(Q( if filiacoes is None:
data__lte=data_inicio, filiacoes_parlamentar = Filiacao.objects.filter(
data_desfiliacao__isnull=True) | Q( parlamentar=parlamentar)
data__lte=data_inicio,
data_desfiliacao__gte=data_inicio))
if data_fim: filiacoes = filiacoes_parlamentar.filter(Q(
filiacoes = filiacoes | filiacoes_parlamentar.filter( data__lte=data_inicio,
data__gte=data_inicio, data_desfiliacao__isnull=True) | Q(
data__lte=data_fim) 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]) return ' | '.join([f.partido.sigla for f in filiacoes])
def _filiacoes_prefetchadas(parlamentar, data_inicio, data_fim=None):
"""Aplica em memória o mesmo predicado de `filiacao_data`, quando
`filiacao_set` já veio prefetchado.
O resumo/ata da sessão chama `filiacao_data_filter` duas vezes por
parlamentar exibido; sem isso cada chamada custa 1+N queries. Retorna
`None` quando não há prefetch, para que o chamador use o caminho SQL.
O queryset do prefetch deve replicar `Filiacao.Meta.ordering` para que a
sequência de siglas saia idêntica à da consulta ao banco.
"""
if not data_inicio:
return None
cache = getattr(parlamentar, '_prefetched_objects_cache', None)
if not cache:
return None
cache_name = parlamentar.filiacao_set.field.remote_field.get_cache_name()
if cache_name not in cache:
return None
def selecionada(f):
if f.data <= data_inicio and (f.data_desfiliacao is None
or f.data_desfiliacao >= data_inicio):
return True
if data_fim:
return data_inicio <= f.data <= data_fim
return False
return [f for f in cache[cache_name] if selecionada(f)]
def parlamentares_ativos(data_inicio, data_fim=None): def parlamentares_ativos(data_inicio, data_fim=None):
from sapl.parlamentares.models import Mandato, Parlamentar from sapl.parlamentares.models import Mandato, Parlamentar
''' '''

Loading…
Cancel
Save