Browse Source

Fix #2927 #2507 - Matérias apenas lidas e não votadas (#2953)

* Fix #2927 - Matérias apenas lidas, mas não votadas

* Adiciona Leitura para OrdemDia e cria model RegistroLeitura

* Adiciona cancelamento de leitura

* Adiciona matérias apenas lidas no painel

* Adiciona url de cancelar leitura

* Fix permissões

* Adiciona registro de usuário e ip no RegistroLeitura
pull/2940/head
Cesar Augusto de Carvalho 5 years ago
committed by Edward
parent
commit
5799669cb8
  1. 84
      sapl/painel/views.py
  2. 1
      sapl/rules/map_rules.py
  3. 56
      sapl/sessao/forms.py
  4. 62
      sapl/sessao/migrations/0046_auto_20190827_1228.py
  5. 30
      sapl/sessao/migrations/0047_auto_20190829_1253.py
  6. 53
      sapl/sessao/models.py
  7. 23
      sapl/sessao/urls.py
  8. 196
      sapl/sessao/views.py
  9. 26
      sapl/templates/painel/index.html
  10. 19
      sapl/templates/sessao/expedientemateria_form.html
  11. 2
      sapl/templates/sessao/layouts.yaml
  12. 6
      sapl/templates/sessao/votacao/leitura_form.html

84
sapl/painel/views.py

@ -21,7 +21,7 @@ from sapl.parlamentares.models import Legislatura, Parlamentar, Votante
from sapl.sessao.models import (ExpedienteMateria, OradorExpediente, OrdemDia, from sapl.sessao.models import (ExpedienteMateria, OradorExpediente, OrdemDia,
PresencaOrdemDia, RegistroVotacao, PresencaOrdemDia, RegistroVotacao,
SessaoPlenaria, SessaoPlenariaPresenca, SessaoPlenaria, SessaoPlenariaPresenca,
VotoParlamentar) VotoParlamentar, RegistroLeitura)
from sapl.utils import filiacao_data, get_client_ip, sort_lista_chave from sapl.utils import filiacao_data, get_client_ip, sort_lista_chave
from .models import Cronometro from .models import Cronometro
@ -361,7 +361,7 @@ def get_presentes(pk, response, materia):
else: else:
presentes = SessaoPlenariaPresenca.objects.filter( presentes = SessaoPlenariaPresenca.objects.filter(
sessao_plenaria_id=pk) sessao_plenaria_id=pk)
sessao = SessaoPlenaria.objects.get(id=pk) sessao = SessaoPlenaria.objects.get(id=pk)
num_presentes = len(presentes) num_presentes = len(presentes)
data_sessao = sessao.data_inicio data_sessao = sessao.data_inicio
@ -408,6 +408,8 @@ def get_presentes(pk, response, materia):
tipo_votacao = 'Nominal' tipo_votacao = 'Nominal'
elif materia.tipo_votacao == 3: elif materia.tipo_votacao == 3:
tipo_votacao = 'Secreta' tipo_votacao = 'Secreta'
elif materia.tipo_votacao == 4:
tipo_votacao = 'Leitura'
response.update({ response.update({
'tipo_resultado': materia.resultado, 'tipo_resultado': materia.resultado,
@ -442,15 +444,27 @@ def response_nenhuma_materia(response):
def get_votos(response, materia): def get_votos(response, materia):
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
if type(materia) == OrdemDia: if type(materia) == OrdemDia:
registro = RegistroVotacao.objects.filter( if materia.tipo_votacao != 4:
ordem=materia, materia=materia.materia).last() registro = RegistroVotacao.objects.filter(
ordem=materia, materia=materia.materia).order_by('data_hora').last()
leitura = None
else:
leitura = RegistroLeitura.objects.filter(
ordem=materia, materia=materia.materia).order_by('data_hora').last()
registro = None
tipo = 'ordem' tipo = 'ordem'
elif type(materia) == ExpedienteMateria: elif type(materia) == ExpedienteMateria:
registro = RegistroVotacao.objects.filter( if materia.tipo_votacao != 4:
expediente=materia, materia=materia.materia).last() registro = RegistroVotacao.objects.filter(
expediente=materia, materia=materia.materia).order_by('data_hora').last()
leitura = None
else:
leitura = RegistroLeitura.objects.filter(
expediente=materia, materia=materia.materia).order_by('data_hora').last()
registro = None
tipo = 'expediente' tipo = 'expediente'
if not registro: if not registro and not leitura:
response.update({ response.update({
'numero_votos_sim': 0, 'numero_votos_sim': 0,
'numero_votos_nao': 0, 'numero_votos_nao': 0,
@ -479,7 +493,15 @@ def get_votos(response, materia):
logger.error("Votos do parlamentar (id={}) não encontrados. Retornado vazio." logger.error("Votos do parlamentar (id={}) não encontrados. Retornado vazio."
.format(p['parlamentar_id'])) .format(p['parlamentar_id']))
response['presentes'][i]['voto'] = '' response['presentes'][i]['voto'] = ''
elif leitura:
response.update({
'numero_votos_sim': 0,
'numero_votos_nao': 0,
'numero_abstencoes': 0,
'registro': True,
'total_votos': 0,
'tipo_resultado': 'Matéria lida.',
})
else: else:
total = (registro.numero_votos_sim + total = (registro.numero_votos_sim +
registro.numero_votos_nao + registro.numero_votos_nao +
@ -556,33 +578,33 @@ def get_dados_painel(request, pk):
# Caso não tenha nenhuma aberta, # Caso não tenha nenhuma aberta,
# a matéria a ser mostrada no Painel deve ser a última votada # a matéria a ser mostrada no Painel deve ser a última votada
last_ordem_voto = RegistroVotacao.objects.filter( last_ordem_voto = RegistroVotacao.objects.filter(
ordem__sessao_plenaria=sessao).last() ordem__sessao_plenaria=sessao).order_by('data_hora').last()
last_expediente_voto = RegistroVotacao.objects.filter( last_expediente_voto = RegistroVotacao.objects.filter(
expediente__sessao_plenaria=sessao).last() expediente__sessao_plenaria=sessao).order_by('data_hora').last()
if last_ordem_voto:
ultima_ordem_votada = last_ordem_voto.ordem
if last_expediente_voto:
ultimo_expediente_votado = last_expediente_voto.expediente
if last_ordem_voto or last_expediente_voto: last_ordem_leitura = RegistroLeitura.objects.filter(
# Se alguma ordem E algum expediente já tiver sido votado... ordem__sessao_plenaria=sessao).order_by('data_hora').last()
if last_ordem_voto and last_expediente_voto: last_expediente_leitura = RegistroLeitura.objects.filter(
materia = ultima_ordem_votada\ expediente__sessao_plenaria=sessao).order_by('data_hora').last()
if last_ordem_voto.pk >= last_expediente_voto.pk\
else ultimo_expediente_votado
# Caso somente um deles tenha resultado, prioriza a Ordem do Dia
elif last_ordem_voto:
materia = ultima_ordem_votada
# Caso a Ordem do dia não tenha resultado, mostra o último expediente
elif last_expediente_voto:
materia = ultimo_expediente_votado
# Obtém última matéria que foi votada, através do timestamp mais recente
if last_ordem_voto:
ordem_expediente = last_ordem_voto.ordem
ultimo_timestamp = last_ordem_voto.data_hora
if last_expediente_voto and last_expediente_voto.data_hora > ultimo_timestamp:
ordem_expediente = last_expediente_voto.expediente
ultimo_timestamp = last_expediente_voto.data_hora
if last_ordem_leitura and last_ordem_leitura.data_hora > ultimo_timestamp:
ordem_expediente = last_ordem_leitura.ordem
ultimo_timestamp = last_ordem_leitura.data_hora
if last_expediente_leitura and last_expediente_leitura.data_hora > ultimo_timestamp:
ordem_expediente = last_expediente_leitura.expediente
ultimo_timestamp = last_expediente_leitura.data_hora
if ordem_expediente:
return JsonResponse(get_votos( return JsonResponse(get_votos(
get_presentes(pk, response, materia), get_presentes(pk, response, ordem_expediente),
materia)) ordem_expediente))
# Retorna que não há nenhuma matéria já votada ou aberta # Retorna que não há nenhuma matéria já votada ou aberta
return response_nenhuma_materia(get_presentes(pk, response, None)) return response_nenhuma_materia(get_presentes(pk, response, None))

1
sapl/rules/map_rules.py

@ -187,6 +187,7 @@ rules_group_sessao = {
(sessao.VotoParlamentar, __base__, __perms_publicas__), (sessao.VotoParlamentar, __base__, __perms_publicas__),
(sessao.JustificativaAusencia, __base__, __perms_publicas__), (sessao.JustificativaAusencia, __base__, __perms_publicas__),
(sessao.RetiradaPauta, __base__, __perms_publicas__), (sessao.RetiradaPauta, __base__, __perms_publicas__),
(sessao.RegistroLeitura, __base__, __perms_publicas__),
] ]
} }

56
sapl/sessao/forms.py

@ -28,7 +28,7 @@ from .models import (Bancada, ExpedienteMateria, JustificativaAusencia,
Orador, OradorExpediente, OrdemDia, PresencaOrdemDia, SessaoPlenaria, Orador, OradorExpediente, OrdemDia, PresencaOrdemDia, SessaoPlenaria,
SessaoPlenariaPresenca, TipoResultadoVotacao, SessaoPlenariaPresenca, TipoResultadoVotacao,
OcorrenciaSessao, RetiradaPauta, TipoRetiradaPauta, OradorOrdemDia, ORDENACAO_RESUMO, OcorrenciaSessao, RetiradaPauta, TipoRetiradaPauta, OradorOrdemDia, ORDENACAO_RESUMO,
ResumoOrdenacao) ResumoOrdenacao, RegistroLeitura)
MES_CHOICES = RANGE_MESES MES_CHOICES = RANGE_MESES
@ -358,6 +358,8 @@ class ExpedienteMateriaForm(ModelForm):
initial=datetime.strftime(timezone.now(), '%d/%m/%Y'), initial=datetime.strftime(timezone.now(), '%d/%m/%Y'),
widget=forms.TextInput(attrs={'readonly': 'readonly'})) widget=forms.TextInput(attrs={'readonly': 'readonly'}))
apenas_leitura = forms.BooleanField(label='Apenas Leitura', required=False)
class Meta: class Meta:
model = ExpedienteMateria model = ExpedienteMateria
fields = ['data_ordem', 'numero_ordem', 'tipo_materia', 'observacao', fields = ['data_ordem', 'numero_ordem', 'tipo_materia', 'observacao',
@ -493,7 +495,7 @@ class VotacaoForm(forms.Form):
resultado_votacao = forms.CharField(label='Resultado da Votação') resultado_votacao = forms.CharField(label='Resultado da Votação')
def clean(self): def clean(self):
cleaned_data = super(VotacaoForm, self).clean() cleaned_data = super().clean()
if not self.is_valid(): if not self.is_valid():
return cleaned_data return cleaned_data
@ -598,7 +600,7 @@ class AdicionarVariasMateriasFilterSet(MateriaLegislativaFilterSet):
] ]
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(MateriaLegislativaFilterSet, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.filters['tipo'].label = 'Tipo de Matéria' self.filters['tipo'].label = 'Tipo de Matéria'
self.filters['autoria__autor__tipo'].label = 'Tipo de Autor' self.filters['autoria__autor__tipo'].label = 'Tipo de Autor'
@ -1023,3 +1025,51 @@ class JustificativaAusenciaForm(ModelForm):
justificativa.materias_do_expediente.clear() justificativa.materias_do_expediente.clear()
justificativa.materias_da_ordem_do_dia.clear() justificativa.materias_da_ordem_do_dia.clear()
return justificativa return justificativa
class OrdemExpedienteLeituraForm(forms.ModelForm):
observacao = forms.CharField(required=False, label='Observação', widget=forms.Textarea,)
class Meta:
model = RegistroLeitura
fields = ['materia',
'ordem',
'expediente',
'observacao',
'user',
'ip']
widgets = {'materia': forms.HiddenInput(),
'ordem': forms.HiddenInput(),
'expediente': forms.HiddenInput(),
'user': forms.HiddenInput(),
'ip': forms.HiddenInput()
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
instance = self.initial['instance']
if instance:
self.instance = instance.first()
self.fields['observacao'].initial = self.instance.observacao
row1 = to_row(
[('observacao', 12)])
actions = [HTML('<a href="{{ view.cancel_url }}"'
' class="btn btn-warning">Cancelar Leitura</a>')]
self.helper = SaplFormHelper()
self.helper.form_method = 'POST'
self.helper.layout = Layout(
Fieldset(_('Leitura de Matéria'),
HTML('''
<b>Matéria:</b> {{materia}}<br>
<b>Ementa:</b> {{materia.ementa}} <br>
'''),
row1,
form_actions(more=actions),
)
)

62
sapl/sessao/migrations/0046_auto_20190827_1228.py

@ -0,0 +1,62 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.20 on 2019-08-27 15:28
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('materia', '0055_auto_20190816_0943'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('sessao', '0045_auto_20190816_1337'),
]
operations = [
migrations.CreateModel(
name='RegistroLeitura',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('observacao', models.TextField(blank=True, verbose_name='Observações')),
('ip', models.CharField(blank=True, default='', max_length=30, verbose_name='IP')),
('data_hora', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Data/Hora')),
],
options={
'verbose_name': 'Leitura',
'verbose_name_plural': 'Leituras',
},
),
migrations.AlterField(
model_name='expedientemateria',
name='tipo_votacao',
field=models.PositiveIntegerField(choices=[(1, 'Simbólica'), (2, 'Nominal'), (3, 'Secreta'), (4, 'Leitura')], default=1, verbose_name='Tipo de votação'),
),
migrations.AlterField(
model_name='ordemdia',
name='tipo_votacao',
field=models.PositiveIntegerField(choices=[(1, 'Simbólica'), (2, 'Nominal'), (3, 'Secreta'), (4, 'Leitura')], default=1, verbose_name='Tipo de votação'),
),
migrations.AddField(
model_name='registroleitura',
name='expediente',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='sessao.ExpedienteMateria'),
),
migrations.AddField(
model_name='registroleitura',
name='materia',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='materia.MateriaLegislativa'),
),
migrations.AddField(
model_name='registroleitura',
name='ordem',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='sessao.OrdemDia'),
),
migrations.AddField(
model_name='registroleitura',
name='user',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL),
),
]

30
sapl/sessao/migrations/0047_auto_20190829_1253.py

@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.20 on 2019-08-29 15:53
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('sessao', '0046_auto_20190827_1228'),
]
operations = [
migrations.AlterField(
model_name='registroleitura',
name='data_hora',
field=models.DateTimeField(auto_now=True, null=True, verbose_name='Data/Hora'),
),
migrations.AlterField(
model_name='registrovotacao',
name='data_hora',
field=models.DateTimeField(auto_now=True, null=True, verbose_name='Data/Hora'),
),
migrations.AlterField(
model_name='votoparlamentar',
name='data_hora',
field=models.DateTimeField(auto_now=True, null=True, verbose_name='Data/Hora'),
),
]

53
sapl/sessao/models.py

@ -311,6 +311,7 @@ class AbstractOrdemDia(models.Model):
(1, 'simbolica', 'Simbólica'), (1, 'simbolica', 'Simbólica'),
(2, 'nominal', 'Nominal'), (2, 'nominal', 'Nominal'),
(3, 'secreta', 'Secreta'), (3, 'secreta', 'Secreta'),
(4, 'leitura', 'Leitura')
) )
sessao_plenaria = models.ForeignKey(SessaoPlenaria, sessao_plenaria = models.ForeignKey(SessaoPlenaria,
@ -560,7 +561,7 @@ class RegistroVotacao(models.Model):
default='') default='')
data_hora = models.DateTimeField( data_hora = models.DateTimeField(
verbose_name=_('Data/Hora'), verbose_name=_('Data/Hora'),
auto_now_add=True, auto_now=True,
blank=True, blank=True,
null=True) null=True)
@ -611,7 +612,7 @@ class VotoParlamentar(models.Model): # RegistroVotacaoParlamentar
default='') default='')
data_hora = models.DateTimeField( data_hora = models.DateTimeField(
verbose_name=_('Data/Hora'), verbose_name=_('Data/Hora'),
auto_now_add=True, auto_now=True,
blank=True, blank=True,
null=True) null=True)
@ -880,3 +881,51 @@ class RetiradaPauta(models.Model):
'ReritadaPauta deve ter exatamente um dos campos ' 'ReritadaPauta deve ter exatamente um dos campos '
'ordem ou expediente preenchido. Ambos estão preenchidos: ' 'ordem ou expediente preenchido. Ambos estão preenchidos: '
'{}, {}'. format(self.ordem, self.expediente)) '{}, {}'. format(self.ordem, self.expediente))
@reversion.register()
class RegistroLeitura(models.Model):
materia = models.ForeignKey(MateriaLegislativa, on_delete=models.CASCADE)
ordem = models.ForeignKey(OrdemDia,
blank=True,
null=True,
on_delete=models.CASCADE)
expediente = models.ForeignKey(ExpedienteMateria,
blank=True,
null=True,
on_delete=models.CASCADE)
observacao = models.TextField(
blank=True, verbose_name=_('Observações'))
user = models.ForeignKey(get_settings_auth_user_model(),
on_delete=models.PROTECT,
null=True,
blank=True)
ip = models.CharField(verbose_name=_('IP'),
max_length=30,
blank=True,
default='')
data_hora = models.DateTimeField(
verbose_name=_('Data/Hora'),
auto_now=True,
blank=True,
null=True)
class Meta:
verbose_name = _('Leitura')
verbose_name_plural = _('Leituras')
def __str__(self):
return _('Leitura - '
'Matéria: %(materia)s') % {
'materia': self.materia}
def clean(self):
"""Exatamente um dos campos ordem ou expediente deve estar preenchido.
"""
# TODO remover esse método quando OrdemDia e ExpedienteMateria
# forem reestruturados e os campos ordem e expediente forem unificados
if not xor(bool(self.ordem), bool(self.expediente)):
raise ValidationError(
'RegistroLeitura deve ter exatamente um dos campos '
'ordem ou expediente preenchido. Ambos estão preenchidos: '
'{}, {}'. format(self.ordem, self.expediente))

23
sapl/sessao/urls.py

@ -34,7 +34,10 @@ from sapl.sessao.views import (AdicionarVariasMateriasExpediente,
sessao_legislativa_legislatura_ajax, sessao_legislativa_legislatura_ajax,
VotacaoEmBlocoOrdemDia, VotacaoEmBlocoExpediente, VotacaoEmBlocoOrdemDia, VotacaoEmBlocoExpediente,
VotacaoEmBlocoSimbolicaView, VotacaoEmBlocoNominalView, VotacaoEmBlocoSimbolicaView, VotacaoEmBlocoNominalView,
recuperar_nome_tipo_sessao) recuperar_nome_tipo_sessao,
ExpedienteLeituraView,
OrdemDiaLeituraView,
retirar_leitura)
from .apps import AppConfig from .apps import AppConfig
@ -150,13 +153,11 @@ urlpatterns = [
PesquisarSessaoPlenariaView.as_view(), name='pesquisar_sessao'), PesquisarSessaoPlenariaView.as_view(), name='pesquisar_sessao'),
url(r'^sessao/(?P<pk>\d+)/matordemdia/votnom/(?P<oid>\d+)/(?P<mid>\d+)$', url(r'^sessao/(?P<pk>\d+)/matordemdia/votnom/(?P<oid>\d+)/(?P<mid>\d+)$',
VotacaoNominalView.as_view(), name='votacaonominal'), VotacaoNominalView.as_view(), name='votacaonominal'),
url(r'^sessao/(?P<pk>\d+)/matordemdia/votnom' url(r'^sessao/(?P<pk>\d+)/matordemdia/votnom/edit/(?P<oid>\d+)/(?P<mid>\d+)$',
'/edit/(?P<oid>\d+)/(?P<mid>\d+)$',
VotacaoNominalEditView.as_view(), name='votacaonominaledit'), VotacaoNominalEditView.as_view(), name='votacaonominaledit'),
url(r'^sessao/(?P<pk>\d+)/matordemdia/votsec/(?P<oid>\d+)/(?P<mid>\d+)$', url(r'^sessao/(?P<pk>\d+)/matordemdia/votsec/(?P<oid>\d+)/(?P<mid>\d+)$',
VotacaoView.as_view(), name='votacaosecreta'), VotacaoView.as_view(), name='votacaosecreta'),
url(r'^sessao/(?P<pk>\d+)/matordemdia/votsec' url(r'^sessao/(?P<pk>\d+)/matordemdia/votsec/view/(?P<oid>\d+)/(?P<mid>\d+)$',
'/view/(?P<oid>\d+)/(?P<mid>\d+)$',
VotacaoEditView.as_view(), name='votacaosecretaedit'), VotacaoEditView.as_view(), name='votacaosecretaedit'),
url(r'^sessao/(?P<pk>\d+)/matordemdia/votsimb/(?P<oid>\d+)/(?P<mid>\d+)$', url(r'^sessao/(?P<pk>\d+)/matordemdia/votsimb/(?P<oid>\d+)/(?P<mid>\d+)$',
VotacaoView.as_view(), name='votacaosimbolica'), VotacaoView.as_view(), name='votacaosimbolica'),
@ -164,8 +165,7 @@ urlpatterns = [
url(r'^sessao/(?P<pk>\d+)/matordemdia/votsimbbloco/$', url(r'^sessao/(?P<pk>\d+)/matordemdia/votsimbbloco/$',
VotacaoView.as_view(), name='votacaosimbolicabloco'), VotacaoView.as_view(), name='votacaosimbolicabloco'),
url(r'^sessao/(?P<pk>\d+)/matordemdia/votsimb' url(r'^sessao/(?P<pk>\d+)/matordemdia/votsimb/view/(?P<oid>\d+)/(?P<mid>\d+)$',
'/view/(?P<oid>\d+)/(?P<mid>\d+)$',
VotacaoEditView.as_view(), name='votacaosimbolicaedit'), VotacaoEditView.as_view(), name='votacaosimbolicaedit'),
url(r'^sessao/(?P<pk>\d+)/matexp/votnom/(?P<oid>\d+)/(?P<mid>\d+)$', url(r'^sessao/(?P<pk>\d+)/matexp/votnom/(?P<oid>\d+)/(?P<mid>\d+)$',
VotacaoNominalExpedienteView.as_view(), name='votacaonominalexp'), VotacaoNominalExpedienteView.as_view(), name='votacaonominalexp'),
@ -192,4 +192,13 @@ urlpatterns = [
url(r'^sessao/mudar-ordem-materia-sessao/', url(r'^sessao/mudar-ordem-materia-sessao/',
mudar_ordem_materia_sessao, mudar_ordem_materia_sessao,
name='mudar_ordem_materia_sessao'), name='mudar_ordem_materia_sessao'),
url(r'^sessao/(?P<pk>\d+)/matexp/leitura/(?P<oid>\d+)/(?P<mid>\d+)$',
ExpedienteLeituraView.as_view(), name='leituraexp'),
url(r'^sessao/(?P<pk>\d+)/matordemdia/leitura/(?P<oid>\d+)/(?P<mid>\d+)$',
OrdemDiaLeituraView.as_view(), name='leituraod'),
url(r'^sessao/(?P<pk>\d+)/(?P<iso>\d+)/(?P<oid>\d+)/retirar-leitura$',
retirar_leitura, name='retirar_leitura'),
] ]

196
sapl/sessao/views.py

@ -34,7 +34,7 @@ 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)
from sapl.sessao.apps import AppConfig from sapl.sessao.apps import AppConfig
from sapl.sessao.forms import ExpedienteMateriaForm, OrdemDiaForm from sapl.sessao.forms import ExpedienteMateriaForm, OrdemDiaForm, OrdemExpedienteLeituraForm
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
from .forms import (AdicionarVariasMateriasFilterSet, BancadaForm, from .forms import (AdicionarVariasMateriasFilterSet, BancadaForm,
@ -49,7 +49,8 @@ from .models import (Bancada, CargoBancada, CargoMesa,
PresencaOrdemDia, RegistroVotacao, ResumoOrdenacao, PresencaOrdemDia, RegistroVotacao, ResumoOrdenacao,
SessaoPlenaria, SessaoPlenariaPresenca, TipoExpediente, SessaoPlenaria, SessaoPlenariaPresenca, TipoExpediente,
TipoResultadoVotacao, TipoSessaoPlenaria, VotoParlamentar, TipoRetiradaPauta, TipoResultadoVotacao, TipoSessaoPlenaria, VotoParlamentar, TipoRetiradaPauta,
RetiradaPauta, TipoJustificativa, JustificativaAusencia, OradorOrdemDia, ORDENACAO_RESUMO) RetiradaPauta, TipoJustificativa, JustificativaAusencia, OradorOrdemDia,
ORDENACAO_RESUMO, RegistroLeitura)
TipoSessaoCrud = CrudAux.build(TipoSessaoPlenaria, 'tipo_sessao_plenaria') TipoSessaoCrud = CrudAux.build(TipoSessaoPlenaria, 'tipo_sessao_plenaria')
@ -142,12 +143,12 @@ def verifica_votacoes_abertas(request):
kwargs={'pk': v.id}), kwargs={'pk': v.id}),
v.__str__())) v.__str__()))
username = request.user.username username = request.user.username
logger.info('user=' + username + '. Já existem votações abertas nas seguintes Sessões: ' + logger.info('user=' + username + '. Já existem votações ou leituras abertas nas seguintes Sessões: ' +
', '.join(msg_abertas) + '. Para abrir ' ', '.join(msg_abertas) + '. Para abrir '
'outra, termine ou feche as votações abertas.') 'outra, termine ou feche as votações ou leituras abertas.')
msg = _('Já existem votações abertas nas seguintes Sessões: ' + msg = _('Já existem votações ou leituras abertas nas seguintes Sessões: ' +
', '.join(msg_abertas) + '. Para abrir ' ', '.join(msg_abertas) + '. Para abrir '
'outra, termine ou feche as votações abertas.') 'outra, termine ou feche as votações ou leituras abertas.')
messages.add_message(request, messages.INFO, msg) messages.add_message(request, messages.INFO, msg)
return False return False
@ -254,7 +255,11 @@ def customize_link_materia(context, pk, has_permission, is_expediente):
materia=obj.materia).exists() materia=obj.materia).exists()
exist_retirada = obj.retiradapauta_set.filter( exist_retirada = obj.retiradapauta_set.filter(
materia=obj.materia).exists() materia=obj.materia).exists()
if not exist_resultado and not exist_retirada: exist_leitura = obj.registroleitura_set.filter(
materia=obj.materia).exists()
if (obj.tipo_votacao != 4 and not exist_resultado and not exist_retirada) or\
(obj.tipo_votacao == 4 and not exist_leitura):
if obj.votacao_aberta: if obj.votacao_aberta:
url = '' url = ''
if is_expediente: if is_expediente:
@ -276,6 +281,13 @@ def customize_link_materia(context, pk, has_permission, is_expediente):
'pk': obj.sessao_plenaria_id, 'pk': obj.sessao_plenaria_id,
'oid': obj.pk, 'oid': obj.pk,
'mid': obj.materia_id}) 'mid': obj.materia_id})
elif obj.tipo_votacao == 4:
url = reverse('sapl.sessao:leituraexp',
kwargs={
'pk': obj.sessao_plenaria_id,
'oid': obj.pk,
'mid': obj.materia_id})
else: else:
if obj.tipo_votacao == 1: if obj.tipo_votacao == 1:
url = reverse('sapl.sessao:votacaosimbolica', url = reverse('sapl.sessao:votacaosimbolica',
@ -295,13 +307,28 @@ def customize_link_materia(context, pk, has_permission, is_expediente):
'pk': obj.sessao_plenaria_id, 'pk': obj.sessao_plenaria_id,
'oid': obj.pk, 'oid': obj.pk,
'mid': obj.materia_id}) 'mid': obj.materia_id})
elif obj.tipo_votacao == 4:
url = reverse('sapl.sessao:leituraod',
kwargs={
'pk': obj.sessao_plenaria_id,
'oid': obj.pk,
'mid': obj.materia_id})
if has_permission: if has_permission:
btn_registrar = ''' if obj.tipo_votacao != 4:
<form action="%s"> btn_registrar = '''
<input type="submit" class="btn btn-primary" <form action="%s">
value="Registrar Votação" /> <input type="submit" class="btn btn-primary"
</form>''' % ( value="Registrar Votação" />
url) </form>''' % (
url)
else:
btn_registrar = '''
<form action="%s">
<input type="submit" class="btn btn-primary"
value="Registrar Leitura" />
</form>''' % (
url)
resultado = btn_registrar resultado = btn_registrar
else: else:
@ -319,12 +346,20 @@ def customize_link_materia(context, pk, has_permission, is_expediente):
}) + '?tipo_materia=ordem' }) + '?tipo_materia=ordem'
if has_permission: if has_permission:
btn_abrir = ''' if not obj.tipo_votacao == 4:
Matéria não votada<br /> btn_abrir = '''
<a href="%s" Matéria não votada<br />
class="btn btn-primary" <a href="%s"
role="button">Abrir Votação</a>''' % (url) class="btn btn-primary"
resultado = btn_abrir role="button">Abrir Votação</a>''' % (url)
resultado = btn_abrir
else:
btn_abrir = '''
Matéria não lida<br />
<a href="%s"
class="btn btn-primary"
role="button">Abrir para Leitura</a>''' % (url)
resultado = btn_abrir
else: else:
resultado = '''Não há resultado''' resultado = '''Não há resultado'''
@ -341,10 +376,16 @@ def customize_link_materia(context, pk, has_permission, is_expediente):
retirada_observacao)) retirada_observacao))
else: else:
resultado = obj.registrovotacao_set.filter( if obj.tipo_votacao == 4:
materia_id=obj.materia_id).last() resultado = obj.registroleitura_set.filter(
resultado_descricao = resultado.tipo_resultado_votacao.nome materia_id=obj.materia_id).last()
resultado_observacao = resultado.observacao resultado_descricao = "Matéria lida"
resultado_observacao = resultado.observacao
else:
resultado = obj.registrovotacao_set.filter(
materia_id=obj.materia_id).last()
resultado_descricao = obj.resultado
resultado_observacao = obj.observacao
if has_permission: if has_permission:
url = '' url = ''
@ -368,6 +409,12 @@ def customize_link_materia(context, pk, has_permission, is_expediente):
'pk': obj.sessao_plenaria_id, 'pk': obj.sessao_plenaria_id,
'oid': obj.pk, 'oid': obj.pk,
'mid': obj.materia_id}) 'mid': obj.materia_id})
elif obj.tipo_votacao == 4:
url = reverse('sapl.sessao:leituraexp',
kwargs={
'pk': obj.sessao_plenaria_id,
'oid': obj.pk,
'mid': obj.materia_id})
else: else:
if obj.tipo_votacao == 1: if obj.tipo_votacao == 1:
url = reverse('sapl.sessao:votacaosimbolicaedit', url = reverse('sapl.sessao:votacaosimbolicaedit',
@ -387,11 +434,17 @@ def customize_link_materia(context, pk, has_permission, is_expediente):
'pk': obj.sessao_plenaria_id, 'pk': obj.sessao_plenaria_id,
'oid': obj.pk, 'oid': obj.pk,
'mid': obj.materia_id}) 'mid': obj.materia_id})
elif obj.tipo_votacao == 4:
url = reverse('sapl.sessao:leituraod',
kwargs={
'pk': obj.sessao_plenaria_id,
'oid': obj.pk,
'mid': obj.materia_id})
resultado = ('<a href="%s">%s<br/>%s</a>' % resultado = ('<a href="%s">%s<br/><br/>%s</a>' %
(url, (url,
resultado_descricao, resultado_descricao,
resultado_observacao)) resultado_observacao))
else: else:
if obj.tipo_votacao == 2: if obj.tipo_votacao == 2:
@ -4325,3 +4378,92 @@ class RetiradaPautaCrud(MasterDetailCrud):
class DeleteView(MasterDetailCrud.DeleteView): class DeleteView(MasterDetailCrud.DeleteView):
pass pass
class AbstractLeituraView(FormView):
template_name = 'sessao/votacao/leitura_form.html'
success_url = '/'
form_class = OrdemExpedienteLeituraForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['materia'] = MateriaLegislativa.objects.get(id=self.kwargs['mid'])
return context
def get_initial(self):
initial = super().get_initial()
materia = MateriaLegislativa.objects.get(id=self.kwargs['mid'])
initial['materia'] = materia
initial['materia__ementa'] = materia.ementa
if self.expediente:
expediente = ExpedienteMateria.objects.get(id=self.kwargs['oid'])
instance = RegistroLeitura.objects.filter(materia=materia, expediente=expediente)
initial['expediente'] = expediente
else:
ordem = OrdemDia.objects.get(id=self.kwargs['oid'])
instance = RegistroLeitura.objects.filter(materia=materia, ordem=ordem)
initial['ordem'] = ordem
initial['instance'] = instance
initial['user'] = self.request.user
initial['ip'] = get_client_ip(self.request)
return initial
def form_valid(self, form):
if self.expediente:
model = ExpedienteMateria
else:
model = OrdemDia
ordem_expediente = model.objects.get(id=self.kwargs['oid'])
ordem_expediente.resultado = "Matéria lida"
ordem_expediente.votacao_aberta = False
ordem_expediente.save()
form.save()
return super().form_valid(form)
def get_success_url(self):
pk = self.kwargs['pk']
if self.expediente:
url = reverse('sapl.sessao:expedientemateria_list',
kwargs={'pk': pk})
else:
url = reverse('sapl.sessao:ordemdia_list',
kwargs={'pk': pk})
return url
def cancel_url(self):
url = reverse('sapl.sessao:retirar_leitura',
kwargs={
'pk': self.kwargs['pk'],
'iso': 1 if not self.expediente else 0,
'oid': self.kwargs['oid']})
return url
class ExpedienteLeituraView(AbstractLeituraView):
expediente = True
class OrdemDiaLeituraView(AbstractLeituraView):
expediente = False
@permission_required('sessao.change_expedientemateria',
'sessao.change_ordemdia')
def retirar_leitura(request, pk, iso, oid):
is_ordem = bool(int(iso))
if not is_ordem:
ordem_expediente = ExpedienteMateria.objects.get(id=oid)
RegistroLeitura.objects.filter(materia=ordem_expediente.materia, expediente=ordem_expediente).delete()
succ_url = reverse('sapl.sessao:expedientemateria_list',
kwargs={'pk': pk})
else:
ordem_expediente = OrdemDia.objects.get(id=oid)
RegistroLeitura.objects.filter(materia=ordem_expediente.materia, ordem=ordem_expediente).delete()
succ_url = reverse('sapl.sessao:ordemdia_list',
kwargs={'pk': pk})
ordem_expediente.resultado = ""
ordem_expediente.votacao_aberta = False
ordem_expediente.save()
return HttpResponseRedirect(succ_url)

26
sapl/templates/painel/index.html

@ -99,6 +99,7 @@
<div class="col-md-6 text-center painel" id="obs_materia_div"> <div class="col-md-6 text-center painel" id="obs_materia_div">
<h2 class="text-subtitle" id="mat_em_votacao">Matéria em Votação</h2> <h2 class="text-subtitle" id="mat_em_votacao">Matéria em Votação</h2>
<span id="materia_legislativa_texto" class="text-value"></span> <span id="materia_legislativa_texto" class="text-value"></span>
<br>
<span id="observacao_materia" class="text-value"></span> <span id="observacao_materia" class="text-value"></span>
</div> </div>
@ -421,23 +422,40 @@
else{ else{
$("#observacao_materia").text(''); $("#observacao_materia").text('');
} }
if (data['tipo_resultado'] && data['status_painel'] == true){ if (data['tipo_resultado'] && data['status_painel'] == true){
if(data['tipo_votacao'] != 'Leitura'){
$("#resultado_votacao").css("color", "#45919D");
$("#mat_em_votacao").text("Matéria em Votação");
$("#resultado_votacao_div").show();
}
else{
$("#resultado_votacao_div").hide();
$("#mat_em_votacao").text("Matéria em Leitura");
}
console.log(data["tipo_resultado"], data['tipo_votacao']);
$("#resultado_votacao").text(data["tipo_resultado"]); $("#resultado_votacao").text(data["tipo_resultado"]);
$("#resultado_votacao").css("color", "#45919D");
var resultado_votacao_upper = $("#resultado_votacao").text().toUpperCase(); var resultado_votacao_upper = $("#resultado_votacao").text().toUpperCase();
console.log(resultado_votacao_upper, data['tipo_resultado']);
if (resultado_votacao_upper.search("APROV") != -1){ if (resultado_votacao_upper.search("APROV") != -1){
$("#resultado_votacao").css("color", "green"); $("#resultado_votacao").css("color", "green");
$("#mat_em_votacao").text("Matéria Votada"); $("#mat_em_votacao").text("Matéria Votada");
} }
if (resultado_votacao_upper.search("REJEIT") != -1){ else if (resultado_votacao_upper.search("REJEIT") != -1){
$("#resultado_votacao").css("color", "red"); $("#resultado_votacao").css("color", "red");
$("#mat_em_votacao").text("Matéria Votada"); $("#mat_em_votacao").text("Matéria Votada");
} }
else if (resultado_votacao_upper.search("LIDA") != -1){
$("#mat_em_votacao").text("Matéria Lida");
}
} }
else{ else{
$("#resultado_votacao").text(''); $("#resultado_votacao").text('');
$("#mat_em_votacao").text("Matéria em Votação"); if(data['tipo_votacao'] != 'Leitura')
$("#mat_em_votacao").text("Matéria em Votação");
else{
$("#mat_em_votacao").text("Matéria em Leitura");
}
} }
}, },
error: function(err) { error: function(err) {

19
sapl/templates/sessao/expedientemateria_form.html

@ -32,11 +32,26 @@
}); });
} }
} }
var fields = ["#id_tipo_materia", "#id_numero_materia", "#id_ano_materia"] var fields = ["#id_tipo_materia", "#id_numero_materia", "#id_ano_materia"];
for (i = 0; i < fields.length; i++) { for (i = 0; i < fields.length; i++) {
$(fields[i]).change(recuperar_materia); $(fields[i]).change(recuperar_materia);
} }
recuperar_materia() recuperar_materia();
$(document).ready(function(){
$("select[name='tipo_votacao']").children("option[value='4']").remove();
$('#id_apenas_leitura').change(function(event){
$('#div_id_tipo_votacao').toggle();
if($('#id_apenas_leitura').prop('checked')){
$("select[name='tipo_votacao']").append(new Option('Leitura', '4'));
$("select[name='tipo_votacao']").val('4');
}
else{
$("select[name='tipo_votacao']").children("option[value='4']").remove();
$("select[name='tipo_votacao']").val('1');
}
})
});
</script> </script>
{% endblock %} {% endblock %}

2
sapl/templates/sessao/layouts.yaml

@ -59,6 +59,7 @@ ExpedienteMateria:
- data_ordem numero_ordem - data_ordem numero_ordem
- tipo_materia numero_materia ano_materia - tipo_materia numero_materia ano_materia
- tipo_votacao - tipo_votacao
- apenas_leitura
- observacao - observacao
OrdemDia: OrdemDia:
@ -66,6 +67,7 @@ OrdemDia:
- data_ordem numero_ordem - data_ordem numero_ordem
- tipo_materia numero_materia ano_materia - tipo_materia numero_materia ano_materia
- tipo_votacao - tipo_votacao
- apenas_leitura
- observacao - observacao
ExpedienteMateriaDetail: ExpedienteMateriaDetail:

6
sapl/templates/sessao/votacao/leitura_form.html

@ -0,0 +1,6 @@
{% extends "base.html" %}
{% load i18n crispy_forms_tags %}
{% block base_content %}
{% crispy form %}
{% endblock base_content %}
Loading…
Cancel
Save