@ -9,9 +9,11 @@ from django.contrib import messages
from django . contrib . auth . decorators import permission_required
from django . contrib . auth . mixins import PermissionRequiredMixin
from django . core . exceptions import ObjectDoesNotExist
from django . db import IntegrityError , transaction
from django . db . models import Max , Q
from django . http import JsonResponse
from django . http . response import Http404 , HttpResponseRedirect
from django . middleware . csrf import get_token
from django . urls import reverse
from django . urls . base import reverse_lazy
from django . utils import timezone
@ -52,7 +54,7 @@ from sapl.utils import show_results_filter_set, remover_acentos, get_client_ip,
MultiFormatOutputMixin , PautaMultiFormatOutputMixin , ratelimit_ip
from . forms import ( AdicionarVariasMateriasFilterSet , AdicionarVariasMateriasForm , BancadaForm ,
ExpedienteForm , JustificativaAusenciaForm , OcorrenciaSessaoForm , ListMateriaForm ,
ExpedienteForm , JustificativaAusenciaForm , OcorrenciaSessaoForm ,
MesaForm , OradorExpedienteForm , OradorForm , PautaSessaoFilterSet ,
PresencaForm , ResumoOrdenacaoForm , SessaoPlenariaFilterSet ,
SessaoPlenariaForm , VotacaoEditForm , VotacaoForm ,
@ -199,31 +201,75 @@ def abrir_votacao(request, pk, spk):
if not model :
raise Http404 ( )
query_params = " ? "
materia_votacao = model . objects . get ( id = pk )
is_leitura = materia_votacao . tipo_votacao == 4
is_expediente = model is ExpedienteMateria
opened = False
with transaction . atomic ( ) :
# select_for_update trava a linha da SessaoPlenaria durante toda a
# checagem+fechamento+abertura, para que dois "abrir votação"
# concorrentes (duplo clique, ou uma requisição lenta seguida de
# nova tentativa) não deixem duas matérias com votacao_aberta=True
# ao mesmo tempo — sapl/painel/views.py::votacao_aberta() trata
# esse caso redirecionando todos os tablets sem nenhuma mensagem
# clara de erro.
SessaoPlenaria . objects . select_for_update ( ) . get ( id = spk )
# Reflete o estado mais atual sob o lock: outra requisição
# concorrente pode ter mudado votacao_aberta entre o SELECT inicial
# (antes do lock) e aqui.
materia_votacao . refresh_from_db ( )
ja_aberta = materia_votacao . votacao_aberta
# Reabrir a própria matéria que já está aberta precisa ser
# idempotente: verifica_votacoes_abertas() existe para fechar
# OUTRAS matérias concorrentes, e sua mensagem ("já existem
# votações abertas... foram fechadas") não faz sentido quando a
# única "conflitante" é ela mesma.
if ( verifica_presenca ( request , presenca_model , spk , is_leitura ) and
verifica_votacoes_abertas ( request ) and
( ja_aberta or verifica_votacoes_abertas ( request ) ) and
verifica_sessao_iniciada ( request , spk , is_leitura ) ) :
materia_votacao . votacao_aberta = True
sessao = SessaoPlenaria . objects . get ( id = spk )
sessao . painel_aberto = True
sessao . save ( )
materia_votacao . save ( )
opened = True
if opened :
# Leva direto para a tela de registro do tipo de votação recém
# aberta, em vez de voltar para a lista — sem isso, o usuário
# precisa de um segundo clique em "Registrar Votação"/"Registrar
# Leitura" (que só aparece depois que a lista recarrega) para
# chegar aonde queria.
registro_view_names_ordem = {
SIMBOLICA : ' votacaosimbolica ' , NOMINAL : ' votacaonominal ' ,
SECRETA : ' votacaosecreta ' , LEITURA : ' leituraod ' ,
}
registro_view_names_expediente = {
SIMBOLICA : ' votacaosimbolicaexp ' , NOMINAL : ' votacaonominalexp ' ,
SECRETA : ' votacaosecretaexp ' , LEITURA : ' leituraexp ' ,
}
registro_view_names = ( registro_view_names_expediente if is_expediente
else registro_view_names_ordem )
registro_view_name = registro_view_names [ materia_votacao . tipo_votacao ]
success_url = reverse ( ' sapl.sessao: ' + registro_view_name , kwargs = {
' pk ' : spk , ' oid ' : materia_votacao . pk ,
' mid ' : materia_votacao . materia_id } )
if ' page ' in request . GET :
success_url + = ' ?page= {} ' . format ( request . GET [ ' page ' ] )
else :
query_params = " ? "
if ' page ' in request . GET :
query_params + = ' page= {} & ' . format ( request . GET [ ' page ' ] )
query_params + = " #id {} " . format ( materia_votacao . materia . id )
success_url = reverse ( ' sapl.sessao: ' + redirect_url , kwargs = { ' pk ' : spk } )
success_url + = query_params
success_url = reverse ( ' sapl.sessao: ' + redirect_url ,
kwargs = { ' pk ' : spk } ) + query_params
return HttpResponseRedirect ( success_url )
def customize_link_materia ( context , pk , has_permission , is_expediente ) :
def customize_link_materia ( context , pk , has_permission , is_expediente , request = None ) :
for i , row in enumerate ( context [ ' rows ' ] ) :
materia = context [ ' object_list ' ] [ i ] . materia
obj = context [ ' object_list ' ] [ i ]
@ -351,13 +397,26 @@ def customize_link_materia(context, pk, has_permission, is_expediente):
if has_permission :
if obj . tipo_votacao != LEITURA :
# Votação Nominal é a única que envolve votos individuais
# pelos tablets (VOTACAO_NOMINAL em sapl/painel/views.py), e
# é a única cuja tela de registro (VotacaoNominalAbstract)
# sabe tratar POST sem efeito colateral — por isso só ela é
# convertida para POST aqui; Simbólica/Secreta continuam GET.
metodo = ' '
csrf_input = ' '
if obj . tipo_votacao == NOMINAL :
metodo = ' method= " post " '
csrf_input = (
' <input type= " hidden " name= " csrfmiddlewaretoken " value= " %s " /> '
% get_token ( request ) )
btn_registrar = '''
< form action = " %s " >
< form action = " %s " % s >
% s
< input type = " submit " class = " btn btn-primary "
value = " Registrar Votação " / >
% s
< / form > ''' % (
url , page_number )
url , metodo , csrf_input , page_number )
else :
btn_registrar = '''
< form action = " %s " >
@ -845,7 +904,8 @@ class MateriaOrdemDiaCrud(MasterDetailCrud):
context = super ( ) . get_context_data ( * * kwargs )
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 ,
request = self . request )
def recuperar_materia ( request ) :
@ -921,7 +981,8 @@ class ExpedienteMateriaCrud(MasterDetailCrud):
context [ ' page ' ] = self . request . GET . get ( ' page ' )
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 ,
request = self . request )
class CreateView ( MasterDetailCrud . CreateView ) :
form_class = ExpedienteMateriaForm
@ -1570,112 +1631,6 @@ class PresencaOrdemDiaView(FormMixin, PresencaMixin, DetailView):
return reverse ( ' sapl.sessao:presencaordemdia ' , kwargs = { ' pk ' : pk } )
class ListMateriaOrdemDiaView ( FormMixin , DetailView ) :
template_name = ' sessao/materia_ordemdia_list.html '
form_class = ListMateriaForm
model = SessaoPlenaria
def get ( self , request , * args , * * kwargs ) :
self . object = self . get_object ( )
context = self . get_context_data ( object = self . object )
pk = self . kwargs [ ' pk ' ]
ordem = OrdemDia . objects . filter ( sessao_plenaria_id = pk )
materias_ordem = [ ]
for o in ordem :
ementa = o . materia . ementa
titulo = o . materia
numero = o . numero_ordem
autoria = Autoria . objects . filter ( materia_id = o . materia_id )
autor = [ str ( a . autor ) for a in autoria ]
mat = { ' pk ' : pk ,
' oid ' : o . id ,
' ordem_id ' : o . materia_id ,
' ementa ' : ementa ,
' titulo ' : titulo ,
' numero ' : numero ,
' resultado ' : o . resultado ,
' autor ' : autor ,
' votacao_aberta ' : o . votacao_aberta ,
' tipo_votacao ' : o . tipo_votacao
}
materias_ordem . append ( mat )
sorted ( materias_ordem , key = lambda x : x [ ' numero ' ] )
context . update ( { ' materias_ordem ' : materias_ordem } )
return self . render_to_response ( context )
@method_decorator ( permission_required ( ' sessao.change_ordemdia ' ) )
def post ( self , request , * args , * * kwargs ) :
self . object = self . get_object ( )
context = self . get_context_data ( object = self . object )
pk = self . kwargs [ ' pk ' ]
form = ListMateriaForm ( request . POST )
# TODO: Existe uma forma de atualizar em lote de acordo
# com a forma abaixo, mas como setar o primeiro para "1"?
# OrdemDia.objects.filter(sessao_plenaria_id=pk)
# .order_by('numero_ordem').update(numero_ordem=3)
if ' materia_reorder ' in request . POST :
ordens = OrdemDia . objects . filter ( sessao_plenaria_id = pk )
ordem_num = 1
for o in ordens :
o . numero_ordem = ordem_num
o . save ( )
ordem_num + = 1
elif ' abrir-votacao ' in request . POST :
existe_votacao_aberta = OrdemDia . objects . filter (
sessao_plenaria_id = pk , votacao_aberta = True ) . exists ( )
if existe_votacao_aberta :
context = self . get_context_data ( object = self . object )
form . _errors = { ' error_message ' : ' error_message ' }
context . update ( { ' form ' : form } )
pk = self . kwargs [ ' pk ' ]
ordem = OrdemDia . objects . filter ( sessao_plenaria_id = pk )
materias_ordem = [ ]
for o in ordem :
ementa = o . materia . ementa
titulo = o . materia
numero = o . numero_ordem
autoria = Autoria . objects . filter ( materia_id = o . materia_id )
autor = [ str ( a . autor ) for a in autoria ]
mat = { ' pk ' : pk ,
' oid ' : o . id ,
' ordem_id ' : o . materia_id ,
' ementa ' : ementa ,
' titulo ' : titulo ,
' numero ' : numero ,
' resultado ' : o . resultado ,
' autor ' : autor ,
' votacao_aberta ' : o . votacao_aberta ,
' tipo_votacao ' : o . tipo_votacao
}
materias_ordem . append ( mat )
sorted ( materias_ordem , key = lambda x : x [ ' numero ' ] )
context . update ( { ' materias_ordem ' : materias_ordem } )
return self . render_to_response ( context )
else :
ordem_id = request . POST [ ' ordem_id ' ]
ordem = OrdemDia . objects . get ( id = ordem_id )
ordem . votacao_aberta = True
ordem . registro_aberto = False
ordem . save ( )
return self . get ( self , request , args , kwargs )
class MesaView ( FormMixin , DetailView ) :
template_name = ' sessao/mesa.html '
form_class = MesaForm
@ -2977,7 +2932,15 @@ class VotacaoNominalAbstract(SessaoPermissionMixin):
logger = logging . getLogger ( __name__ )
def get ( self , request , * args , * * kwargs ) :
def _get_materia_votacao ( self , request , kwargs ) :
"""
Resolve a OrdemDia / ExpedienteMateria sendo registrada , sem nenhum
efeito colateral ( não altera registro_aberto nem qualquer outro
estado ) — apenas consulta . Retorna
( materia_votacao , presentes , total , redirect ) ; quando a matéria não
pode ser exibida ( já votada ou com a votação fechada ) , os três
primeiros valores são None e ` redirect ` é a resposta a devolver .
"""
username = request . user . username
if self . ordem :
ordem_id = kwargs [ ' oid ' ]
@ -2986,33 +2949,27 @@ class VotacaoNominalAbstract(SessaoPermissionMixin):
messages . add_message ( request , messages . ERROR , msg )
self . logger . info (
' user= ' + username + ' . Matéria (ordem_id= {} ) já votada! ' . format ( ordem_id ) )
return HttpResponseRedirect ( reverse (
return None , None , None , HttpResponseRedirect ( reverse (
' sapl.sessao:ordemdia_list ' , kwargs = { ' pk ' : kwargs [ ' pk ' ] } ) )
try :
ordem = OrdemDia . objects . get ( id = ordem_id )
materia_votacao = OrdemDia . objects . get ( id = ordem_id )
except ObjectDoesNotExist :
self . logger . error (
' user= ' + username + ' . Objeto OrdemDia (pk= {} ) não existe. ' . format ( ordem_id ) )
raise Http404 ( )
presentes = PresencaOrdemDia . objects . filter (
sessao_plenaria_id = ordem . sessao_plenaria_id )
total = presentes . count ( )
materia_votacao = ordem
sessao_plenaria_id = materia_votacao . sessao_plenaria_id )
if not ordem . votacao_aberta :
if not materia_votacao . votacao_aberta :
self . logger . error (
' user= ' + username + ' . A votação para esta OrdemDia (id= {} ) encontra-se fechada! ' . format ( ordem_id ) )
msg = _ ( ' A votação para esta matéria encontra-se fechada! ' )
messages . add_message ( request , messages . ERROR , msg )
return HttpResponseRedirect ( reverse (
return None , None , None , HttpResponseRedirect ( reverse (
' sapl.sessao:ordemdia_list ' , kwargs = { ' pk ' : kwargs [ ' pk ' ] } ) )
ordem . registro_aberto = True
ordem . save ( )
elif self . expediente :
expediente_id = kwargs [ ' oid ' ]
if ( RegistroVotacao . objects . filter (
@ -3021,194 +2978,270 @@ class VotacaoNominalAbstract(SessaoPermissionMixin):
" user= " + username + " . RegistroVotacao (expediente_id= {} ) já existe. " . format ( expediente_id ) )
msg = _ ( ' Esta matéria já foi votada! ' )
messages . add_message ( request , messages . ERROR , msg )
return HttpResponseRedirect ( reverse (
return None , None , None , HttpResponseRedirect ( reverse (
' sapl.sessao:expedientemateria_list ' ,
kwargs = { ' pk ' : kwargs [ ' pk ' ] } ) )
try :
self . logger . debug (
" user= " + username + " . Tentando obter Objeto ExpedienteMateria com id= {} . " . format ( expediente_id ) )
expediente = ExpedienteMateria . objects . get ( id = expediente_id )
materia_votacao = ExpedienteMateria . objects . get ( id = expediente_id )
except ObjectDoesNotExist :
self . logger . error (
' user= ' + username + ' . Objeto ExpedienteMateria com id= {} não existe. ' . format ( expediente_id ) )
raise Http404 ( )
presentes = SessaoPlenariaPresenca . objects . filter (
sessao_plenaria_id = expediente . sessao_plenaria_id )
total = presentes . count ( )
materia_votacao = expediente
sessao_plenaria_id = materia_votacao . sessao_plenaria_id )
if not expediente . votacao_aberta :
if not materia_votacao . votacao_aberta :
msg = _ (
' A votação para este ExpedienteMateria (id= {} ) encontra-se fechada! ' . format ( expediente_id ) )
messages . add_message ( request , messages . ERROR , msg )
return HttpResponseRedirect ( reverse (
return None , None , None , HttpResponseRedirect ( reverse (
' sapl.sessao:expedientemateria_list ' ,
kwargs = { ' pk ' : kwargs [ ' pk ' ] } ) )
expediente . registro_aberto = True
expediente . save ( )
total = presentes . count ( )
return materia_votacao , presentes , total , None
def _build_registro_context ( self , materia_votacao , presentes , total ) :
materia = { ' materia ' : materia_votacao . materia ,
' ementa ' : sub (
' ' , ' ' , strip_tags (
materia_votacao . materia . ementa ) ) }
context = { ' materia ' : materia , ' object ' : self . get_object ( ) ,
return { ' materia ' : materia , ' object ' : self . get_object ( ) ,
' parlamentares ' : self . get_parlamentares ( presentes ) ,
' form ' : self . get_form ( ) ,
' total ' : total }
' total ' : total ,
' registro_aberto ' : materia_votacao . registro_aberto }
def _redirect_same_registro ( self , kwargs , page ) :
view = ( ' sapl.sessao:votacaonominal ' if self . ordem
else ' sapl.sessao:votacaonominalexp ' )
return HttpResponseRedirect ( reverse ( view , kwargs = {
' pk ' : kwargs [ ' pk ' ] , ' oid ' : kwargs [ ' oid ' ] , ' mid ' : kwargs [ ' mid ' ] } ) + page )
def _redirect_lista ( self , kwargs , page ) :
view = ( ' sapl.sessao:ordemdia_list ' if self . ordem
else ' sapl.sessao:expedientemateria_list ' )
return HttpResponseRedirect (
reverse ( view , kwargs = { ' pk ' : kwargs [ ' pk ' ] } ) + page +
" #id {} " . format ( kwargs [ ' mid ' ] ) )
def get ( self , request , * args , * * kwargs ) :
if request . GET . get ( ' status ' ) == ' 1 ' :
return self . _status_json ( kwargs )
materia_votacao , presentes , total , redirect = self . _get_materia_votacao (
request , kwargs )
if redirect :
return redirect
context = self . _build_registro_context ( materia_votacao , presentes , total )
return self . render_to_response ( context )
def _status_json ( self , kwargs ) :
"""
Poll leve para a tela de registro ( nominal . html ) acompanhar , em tempo
real , os votos que chegam pelos tablets — sem os efeitos colaterais
de _get_materia_votacao ( mensagens , redirect quando já votada ) e sem
a máscara de mostrar_voto do sapl . painel : dados_painel ( que é para o
telão público ; aqui é a tela da própria Mesa , que precisa do valor
real para não sobrescrever por engano um voto que mudou ) .
"""
model = OrdemDia if self . ordem else ExpedienteMateria
lookup_field = ' ordem_id ' if self . ordem else ' expediente_id '
try :
materia_votacao = model . objects . get ( id = kwargs [ ' oid ' ] )
except ObjectDoesNotExist :
raise Http404 ( )
votos = dict ( VotoParlamentar . objects . filter (
* * { lookup_field : materia_votacao . id } ) . values_list (
' parlamentar_id ' , ' voto ' ) )
return JsonResponse ( {
' votacao_aberta ' : materia_votacao . votacao_aberta ,
' registro_aberto ' : materia_votacao . registro_aberto ,
' ja_registrada ' : RegistroVotacao . objects . filter (
* * { lookup_field : materia_votacao . id } ) . exists ( ) ,
' votos ' : votos ,
} )
def _get_or_create_voto_parlamentar ( self , lookup_field , lookup_value , parlamentar_id ) :
"""
get_or_create protegido contra a corrida de duas inserções
concorrentes para o mesmo ( parlamentar , matéria ) — ex . : o tablet do
parlamentar e o formulário em lote do operador chegando ao mesmo
tempo . Usa um savepoint próprio para que um IntegrityError aqui não
derrube a transação inteira do ' Encerrar Votação ' .
"""
try :
with transaction . atomic ( ) :
return VotoParlamentar . objects . select_for_update ( ) . get_or_create (
parlamentar_id = parlamentar_id , * * { lookup_field : lookup_value } )
except IntegrityError :
return VotoParlamentar . objects . select_for_update ( ) . get (
parlamentar_id = parlamentar_id , * * { lookup_field : lookup_value } ) , False
def _lock_materia_votacao ( self , materia_votacao ) :
model = OrdemDia if self . ordem else ExpedienteMateria
return model . objects . select_for_update ( ) . get ( pk = materia_votacao . pk )
def post ( self , request , * args , * * kwargs ) :
self . object = self . get_object ( )
form = self . get_form ( )
username = request . user . username
page = ' '
if ' page ' in self . request . GET :
page = ' ?page= {} ' . format ( self . request . GET [ ' page ' ] )
if self . ordem :
ordem_id = kwargs [ ' oid ' ]
try :
self . logger . debug (
" user= " + username + " . Tentando obter objeto OrdemDia com id= {} . " . format ( ordem_id ) )
materia_votacao = OrdemDia . objects . get ( id = ordem_id )
except ObjectDoesNotExist :
self . logger . error (
' user= ' + username + ' . Objeto OrdemDia com id= {} não existe. ' . format ( ordem_id ) )
raise Http404 ( )
elif self . expediente :
expediente_id = kwargs [ ' oid ' ]
try :
self . logger . debug (
" user= " + username + " . Tentando obter ExpedienteMateria com id= {} . " . format ( expediente_id ) )
materia_votacao = ExpedienteMateria . objects . get (
id = expediente_id )
except ObjectDoesNotExist :
self . logger . error (
' user= ' + username + ' . Objeto ExpedienteMateria com id= {} não existe. ' . format ( expediente_id ) )
raise Http404 ( )
materia_votacao , presentes , total , redirect = self . _get_materia_votacao (
request , kwargs )
if redirect :
return redirect
if form . is_valid ( ) :
votos_sim = 0
votos_nao = 0
abstencoes = 0
nao_votou = 0
fase_sessao_field = ' ordem_id ' if self . ordem else ' expediente_id '
fase_sessao_value = kwargs [ ' oid ' ]
if ' reabrir-votacao ' in request . POST :
with transaction . atomic ( ) :
materia_votacao = self . _lock_materia_votacao ( materia_votacao )
materia_votacao . registro_aberto = False
materia_votacao . save ( update_fields = [ ' registro_aberto ' ] )
self . logger . info (
' user= ' + username + ' . Reabriu a matéria (id= {} ) para novos votos. ' . format ( fase_sessao_value ) )
return self . _redirect_same_registro ( kwargs , page )
if ' bloquear-registro-votacao ' in request . POST :
with transaction . atomic ( ) :
materia_votacao = self . _lock_materia_votacao ( materia_votacao )
materia_votacao . registro_aberto = True
materia_votacao . save ( update_fields = [ ' registro_aberto ' ] )
self . logger . info (
' user= ' + username + ' . Bloqueou novos votos para a matéria (id= {} ). ' . format ( fase_sessao_value ) )
return self . _redirect_same_registro ( kwargs , page )
if ' cancelar-votacao ' in request . POST :
with transaction . atomic ( ) :
materia_votacao = self . _lock_materia_votacao ( materia_votacao )
fechar_votacao_materia ( materia_votacao )
if self . ordem :
return HttpResponseRedirect (
reverse (
' sapl.sessao:ordemdia_list ' ,
kwargs = { ' pk ' : kwargs [ ' pk ' ] }
) + page + " #id {} " . format ( self . kwargs [ ' mid ' ] )
)
else :
return HttpResponseRedirect (
reverse (
' sapl.sessao:expedientemateria_list ' ,
kwargs = { ' pk ' : kwargs [ ' pk ' ] }
) + page + " #id {} " . format ( self . kwargs [ ' mid ' ] )
)
else :
if form . cleaned_data [ ' resultado_votacao ' ] == None :
form . add_error ( None , ' Não é possível finalizar a votação sem '
' nenhum resultado da votação ' )
return self . _redirect_lista ( kwargs , page )
if ' salvar-votacao ' in request . POST :
form = self . get_form ( )
if not form . is_valid ( ) :
return self . form_invalid ( form )
for votos in request . POST . getlist ( ' voto_parlamentar ' ) :
v = votos . split ( ' : ' )
voto = v [ 0 ]
parlamentar_id = v [ 1 ]
if form . cleaned_data [ ' resultado_votacao ' ] is None :
form . add_error ( None , _ ( ' Não é possível finalizar a votação sem '
' nenhum resultado da votação ' ) )
return self . form_invalid ( form )
if voto == ' Sim ' :
votos_sim + = 1
elif voto == ' Não ' :
votos_nao + = 1
elif voto == ' Abstenção ' :
abstencoes + = 1
elif voto == ' Não Votou ' :
nao_votou + = 1
votos_submetidos = [ ]
for voto_codificado in request . POST . getlist ( ' voto_parlamentar ' ) :
try :
voto_submetido , parlamentar_id = voto_codificado . split ( ' : ' , 1 )
except ValueError :
form . add_error ( None , _ ( ' Formato de voto inválido. ' ) )
return self . form_invalid ( form )
if voto_submetido not in ( ' Sim ' , ' Não ' , ' Abstenção ' , ' Não Votou ' ) :
form . add_error ( None , _ ( ' Voto inválido. ' ) )
return self . form_invalid ( form )
votos_submetidos . append ( ( voto_submetido , parlamentar_id ) )
# Caso todas as opções sejam 'Não votou', fecha a votação
if nao_votou == len ( request . POST . getlist ( ' voto_parlamentar ' ) ) :
skipped_parlamentares = [ ]
with transaction . atomic ( ) :
materia_votacao = self . _lock_materia_votacao ( materia_votacao )
if ( not materia_votacao . votacao_aberta or
RegistroVotacao . objects . filter (
* * { fase_sessao_field : fase_sessao_value } ) . exists ( ) ) :
messages . error (
request , _ ( ' A votação já foi encerrada por outra operação. ' ) )
return self . _redirect_lista ( kwargs , page )
for voto_submetido , parlamentar_id in votos_submetidos :
voto_parlamentar , created = self . _get_or_create_voto_parlamentar (
fase_sessao_field , fase_sessao_value , parlamentar_id )
if created :
if voto_submetido == ' Não Votou ' :
# "Não Votou" é só o valor padrão do <select>
# para quem o operador não escolheu nada — não é
# um voto de fato. Persisti-lo aqui travaria a
# linha desse parlamentar (nominal.html desabilita
# o <select> sempre que existe um VotoParlamentar)
# mesmo quando o fechamento falha por falta de
# votos reais, impedindo o operador de corrigir e
# tentar de novo.
voto_parlamentar . delete ( )
continue
voto_parlamentar . voto = voto_submetido
voto_parlamentar . user = request . user
voto_parlamentar . ip = get_client_ip ( request )
voto_parlamentar . save ( )
elif voto_parlamentar . voto != voto_submetido :
# Alguém (tipicamente via tablet) já registrou um
# voto diferente do valor deste formulário desde que
# a tela de registro foi carregada. O voto já
# registrado prevalece — não sobrescrevemos com um
# valor obsoleto do formulário em lote.
skipped_parlamentares . append ( voto_parlamentar . parlamentar )
votos_atuais = VotoParlamentar . objects . filter (
* * { fase_sessao_field : fase_sessao_value } )
votos_sim = votos_atuais . filter ( voto = ' Sim ' ) . count ( )
votos_nao = votos_atuais . filter ( voto = ' Não ' ) . count ( )
abstencoes = votos_atuais . filter ( voto = ' Abstenção ' ) . count ( )
if votos_sim + votos_nao + abstencoes == 0 :
self . logger . error ( ' user= ' + username + ' . Não é possível finalizar a votação sem '
' nenhum voto ' )
form . add_error ( None , ' Não é possível finalizar a votação sem '
' nenhum voto ' )
form . add_error ( None , _ ( ' Não é possível finalizar a votação sem '
' nenhum voto ' ) )
return self . form_invalid ( form )
# Remove todas as votação desta matéria, caso existam
if self . ordem :
RegistroVotacao . objects . filter ( ordem_id = ordem_id ) . delete ( )
elif self . expediente :
RegistroVotacao . objects . filter (
expediente_id = expediente_id ) . delete ( )
votacao = RegistroVotacao ( )
votacao . numero_votos_sim = votos_sim
votacao . numero_votos_nao = votos_nao
votacao . numero_abstencoes = abstencoes
votacao . observacao = request . POST . get ( ' observacao ' , None )
votacao . user = request . user
votacao . ip = get_client_ip ( request )
votacao . materia_id = materia_votacao . materia . id
if self . ordem :
votacao . ordem_id = ordem_id
elif self . expediente :
votacao . expediente_id = expediente_id
votacao . tipo_resultado_votacao = form . cleaned_data [ ' resultado_votacao ' ]
* * { fase_sessao_field : fase_sessao_value } ) . delete ( )
votacao = RegistroVotacao (
numero_votos_sim = votos_sim ,
numero_votos_nao = votos_nao ,
numero_abstencoes = abstencoes ,
observacao = request . POST . get ( ' observacao ' , None ) ,
user = request . user ,
ip = get_client_ip ( request ) ,
materia_id = materia_votacao . materia . id ,
tipo_resultado_votacao = form . cleaned_data [ ' resultado_votacao ' ] )
setattr ( votacao , fase_sessao_field , fase_sessao_value )
votacao . save ( )
for votos in request . POST . getlist ( ' voto_parlamentar ' ) :
v = votos . split ( ' : ' )
voto = v [ 0 ]
parlamentar_id = v [ 1 ]
votos_atuais . update ( votacao_id = votacao . id )
if self . ordem :
voto_parlamentar = VotoParlamentar . objects . get_or_create (
parlamentar_id = parlamentar_id ,
ordem_id = ordem_id ) [ 0 ]
elif self . expediente :
voto_parlamentar = VotoParlamentar . objects . get_or_create (
parlamentar_id = parlamentar_id ,
expediente_id = expediente_id ) [ 0 ]
voto_parlamentar . voto = voto
voto_parlamentar . parlamentar_id = parlamentar_id
voto_parlamentar . votacao_id = votacao . id
voto_parlamentar . user = request . user
voto_parlamentar . ip = get_client_ip ( request )
voto_parlamentar . save ( )
resultado = form . cleaned_data [ ' resultado_votacao ' ]
materia_votacao . resultado = resultado . nome
materia_votacao . resultado = form . cleaned_data [ ' resultado_votacao ' ] . nome
materia_votacao . votacao_aberta = False
materia_votacao . registro_aberto = False
materia_votacao . save ( )
# Verifica se existe algum VotoParlamentar sem RegistroVotacao
# Por exemplo, se algum parlamentar votar e sua presença for
# removida da ordem do dia/expediente antes da conclusão da
# votação
if self . ordem :
VotoParlamentar . objects . filter (
ordem_id = ordem_id ,
votacao__isnull = True ) . delete ( )
elif self . expediente :
VotoParlamentar . objects . filter (
expediente_id = expediente_id ,
votacao__isnull = True ) . delete ( )
* * { fase_sessao_field : fase_sessao_value } , votacao__isnull = True ) . delete ( )
if skipped_parlamentares :
nomes = ' , ' . join ( p . nome_parlamentar for p in skipped_parlamentares )
messages . add_message (
request , messages . WARNING ,
_ ( ' O(s) voto(s) de %(nomes)s já haviam sido registrados '
' e não foram sobrescritos. ' ) % { ' nomes ' : nomes } )
return self . form_valid ( form )
else :
return self . form_invalid ( form )
# Nenhuma chave de ação reconhecida: navegação simples para a tela
# de registro (botão "Registrar Votação"), sem efeito colateral.
context = self . _build_registro_context ( materia_votacao , presentes , total )
return self . render_to_response ( context )
def form_invalid ( self , form ) :
errors_tuple = [ ( form [ e ] . label , form . errors [ e ] )