Browse Source

Fix #2903 #1373 - Afastamento de Parlamentar (#2904)

* Fix #2903

* Adiciona verbose_name em tipo de afastamento de Mandato

* Merge migrations

* Mensagem de explicação do funcionamento do indicador do tipo de afastamento

* Adiciona testes para as alterações

* Adiciona verificação de ausência de parlamentar na Sessão Plenaria

* Adiciona informação de afastamento no relatório de presenças
pull/2995/head
Cesar Augusto de Carvalho 5 years ago
committed by Edward
parent
commit
138a78b8cc
  1. 40
      sapl/base/views.py
  2. 87
      sapl/parlamentares/forms.py
  3. 32
      sapl/parlamentares/migrations/0033_afastamentoparlamentar.py
  4. 21
      sapl/parlamentares/migrations/0034_auto_20190801_1408.py
  5. 16
      sapl/parlamentares/migrations/0035_merge_20190802_0954.py
  6. 38
      sapl/parlamentares/models.py
  7. 151
      sapl/parlamentares/tests/test_parlamentares.py
  8. 3
      sapl/parlamentares/urls.py
  9. 41
      sapl/parlamentares/views.py
  10. 1
      sapl/rules/map_rules.py
  11. 22
      sapl/sessao/forms.py
  12. 18
      sapl/sessao/views.py
  13. 16
      sapl/templates/base/RelatorioPresencaSessao_filter.html
  14. 6
      sapl/templates/parlamentares/layouts.yaml
  15. 2
      sapl/templates/parlamentares/subnav.yaml
  16. 11
      sapl/templates/parlamentares/tipoafastamento_form.html
  17. 119
      sapl/templates/sessao/presenca.html
  18. 7
      sapl/templates/sessao/presenca_ordemdia.html
  19. 17
      sapl/utils.py

40
sapl/base/views.py

@ -42,11 +42,12 @@ from sapl.materia.models import (Autoria, MateriaLegislativa, Proposicao, Anexad
TipoMateriaLegislativa, StatusTramitacao, UnidadeTramitacao,
DocumentoAcessorio, TipoDocumento)
from sapl.norma.models import (NormaJuridica, TipoNormaJuridica, NormaEstatisticas)
from sapl.parlamentares.models import Parlamentar, Legislatura, Mandato, Filiacao, SessaoLegislativa, Bancada
from sapl.parlamentares.models import (Parlamentar, Legislatura, Mandato, Filiacao,
SessaoLegislativa, Bancada, AfastamentoParlamentar)
from sapl.protocoloadm.models import (Protocolo, TipoDocumentoAdministrativo,
StatusTramitacaoAdministrativo,
DocumentoAdministrativo, Anexado)
from sapl.sessao.models import (PresencaOrdemDia, SessaoPlenaria,
from sapl.sessao.models import (PresencaOrdemDia, SessaoPlenaria, OrdemDia,
SessaoPlenariaPresenca, TipoSessaoPlenaria)
from sapl.utils import (parlamentares_ativos, gerar_hash_arquivo, SEPARADOR_HASH_PROPOSICAO,
show_results_filter_set, mail_service_configured,
@ -494,16 +495,35 @@ class RelatorioPresencaSessaoView(FilterView):
for i, p in enumerate(parlamentares_qs):
m = p.mandato_set.filter(Q(data_inicio_mandato__lte=_range[0], data_fim_mandato__gte=_range[1]) |
Q(data_inicio_mandato__lte=_range[0], data_fim_mandato__isnull=True) |
Q(data_inicio_mandato__gte=_range[0], data_fim_mandato__lte=_range[1]) |
# mandato suplente
Q(data_inicio_mandato__gte=_range[0], data_fim_mandato__lte=_range[1]))
afastamentos = AfastamentoParlamentar.objects.filter(Q(parlamentar=p) & (Q(data_inicio__range=_range) |
Q(data_inicio__gte=_range[0], data_fim__isnull=True)))
afast_parl_sessao = 0
afast_parl_ordem = 0
for afastamento in afastamentos:
if afastamento.data_fim:
afast_parl_sessao += SessaoPlenaria.objects.filter(data_inicio__range=[afastamento.data_inicio,
afastamento.data_fim]).count()
afast_parl_ordem += OrdemDia.objects.filter(sessao_plenaria__data_inicio__range=[afastamento.data_inicio,
afastamento.data_fim]).order_by('sessao_plenaria__id').\
distinct('sessao_plenaria__id').count()
else:
afast_parl_sessao += SessaoPlenaria.objects.filter(data_inicio__gte=afastamento.data_inicio).count()
afast_parl_ordem += OrdemDia.objects.filter(sessao_plenaria__data_inicio__gte=afastamento.data_inicio).\
order_by('sessao_plenaria__id').\
distinct('sessao_plenaria__id').count()
m = m.last()
parlamentares_presencas.append({
'parlamentar': p,
'titular': m.titular if m else False,
'sessao_porc': 0,
'ordemdia_porc': 0
'ordemdia_porc': 0,
'sessao_afast': afast_parl_sessao,
'ordem_afast': afast_parl_ordem
})
try:
self.logger.debug(
@ -529,13 +549,15 @@ class RelatorioPresencaSessaoView(FilterView):
})
if total_sessao != 0:
porc = round(
sessao_count * 100 / (total_sessao-afast_parl_sessao), 2)
parlamentares_presencas[i].update(
{'sessao_porc': round(
sessao_count * 100 / total_sessao, 2)})
{'sessao_porc': porc if porc <=100 else 100})
if total_ordemdia != 0:
porc = round(
ordemdia_count * 100 / (total_ordemdia-afast_parl_ordem), 2)
parlamentares_presencas[i].update(
{'ordemdia_porc': round(
ordemdia_count * 100 / total_ordemdia, 2)})
{'ordemdia_porc': porc if porc <=100.0 else 100.0})
context['date_range'] = _range
context['total_ordemdia'] = total_ordemdia

87
sapl/parlamentares/forms.py

@ -24,7 +24,7 @@ import django_filters
from .models import (ComposicaoColigacao, Filiacao, Frente, Legislatura,
Mandato, Parlamentar, Votante, Bloco, Bancada, CargoBloco,
CargoBlocoPartido)
CargoBlocoPartido, AfastamentoParlamentar, TipoAfastamento)
class ImageThumbnailFileInput(ClearableFileInput):
@ -87,6 +87,12 @@ class MandatoForm(ModelForm):
'tipo_afastamento', 'observacao', 'parlamentar']
widgets = {'parlamentar': forms.HiddenInput()}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['tipo_afastamento'].queryset = TipoAfastamento.objects.filter(indicador='F')
def clean(self):
super(MandatoForm, self).clean()
@ -673,6 +679,8 @@ class BancadaForm(ModelForm):
nome=bancada.nome
)
return bancada
class CargoBlocoForm(ModelForm):
class Meta:
model = CargoBloco
@ -737,3 +745,80 @@ class CargoBlocoPartidoForm(ModelForm):
if self.instance.pk and (cleaned_data['parlamentar'].id != self.instance.parlamentar.id):
raise ValidationError("Não é possivel alterar o parlamentar " + str(self.instance.parlamentar))
class AfastamentoParlamentarForm(ModelForm):
logger = logging.getLogger(__name__)
class Meta:
model = AfastamentoParlamentar
fields = ['data_inicio', 'data_fim', 'mandato',
'tipo_afastamento', 'observacao', 'parlamentar']
widgets = {'parlamentar': forms.HiddenInput()}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if not (self.instance and self.instance.pk):
parlamentar = kwargs['initial']['parlamentar']
self.fields['mandato'].queryset = Mandato.objects.filter(parlamentar=parlamentar)
self.fields['tipo_afastamento'].queryset = TipoAfastamento.objects.filter(indicador='A')
def clean(self):
super(AfastamentoParlamentarForm, self).clean()
if not self.is_valid():
return self.cleaned_data
data = self.cleaned_data
mandato = data['mandato']
data_inicio_mandato = mandato.data_inicio_mandato
data_fim_mandato = mandato.data_fim_mandato
data_inicio_afastamento = data['data_inicio']
data_fim_afastamento = data['data_fim']
if data_inicio_afastamento < data_inicio_mandato:
self.logger.error("Data início de afastamento ({}) anterior ao inicio"
" do mandato informado ({})."
.format(data_inicio_afastamento, data_inicio_mandato))
raise ValidationError(_("Data início do afastamento anterior ao início"
" do mandato informado."))
if data_fim_mandato and data_inicio_afastamento > data_fim_mandato:
self.logger.error("Data início de afastamento ({}) posterior ao fim"
" do mandato informado ({} a {})."
.format(data_inicio_afastamento, data_fim_mandato))
raise ValidationError(_("Data início do afastamento posterior ao fim"
" do mandato informado."))
if data_fim_afastamento:
if data_fim_afastamento < data_inicio_afastamento:
self.logger.error("Data fim de afastamento ({}) anterior à data início"
" do afastamento ({})."
.format(data_fim_afastamento, data_inicio_afastamento))
raise ValidationError(_("Data fim do afastamento anterior à data início do"
" afastamento."))
if data_fim_afastamento < data_inicio_mandato:
self.logger.error("Data fim de afastamento ({}) anterior ao início"
" do mandato informado ({} a {})."
.format(data_fim_afastamento, data_inicio_mandato))
raise ValidationError(_("Data fim do afastamento anterior ao início"
" do mandato informado."))
if data_fim_mandato and data_fim_afastamento > data_fim_mandato:
self.logger.error("Data fim de afastamento ({}) posterior ao fim"
" do mandato informado ({})."
.format(data_inicio_afastamento, data_fim_mandato))
raise ValidationError(_("Data fim do afastamento posterior ao fim"
" do mandato informado."))
ultimo_afastamento = AfastamentoParlamentar.objects.last()
if ultimo_afastamento and not ultimo_afastamento.data_fim \
and ultimo_afastamento != self.instance:
self.logger.error("Existe Afastamento sem Data Fim.")
raise ValidationError(_("Existe Afastamento sem Data Fim."))
return self.cleaned_data

32
sapl/parlamentares/migrations/0033_afastamentoparlamentar.py

@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.20 on 2019-08-01 15:35
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('parlamentares', '0032_auto_20190619_1509'),
]
operations = [
migrations.CreateModel(
name='AfastamentoParlamentar',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('data_inicio', models.DateField(null=True, verbose_name='Início do Afastamento')),
('data_fim', models.DateField(blank=True, null=True, verbose_name='Fim do Afastamento')),
('observacao', models.TextField(blank=True, verbose_name='Observação')),
('mandato', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='parlamentares.Mandato', verbose_name='Mandato')),
('parlamentar', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='parlamentares.Parlamentar')),
('tipo_afastamento', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='parlamentares.TipoAfastamento', verbose_name='Tipo de Afastamento')),
],
options={
'verbose_name': 'Afastamento',
'verbose_name_plural': 'Afastamentos',
},
),
]

21
sapl/parlamentares/migrations/0034_auto_20190801_1408.py

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.20 on 2019-08-01 17:08
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('parlamentares', '0033_afastamentoparlamentar'),
]
operations = [
migrations.AlterField(
model_name='mandato',
name='tipo_afastamento',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='parlamentares.TipoAfastamento', verbose_name='Tipo de Afastamento'),
),
]

16
sapl/parlamentares/migrations/0035_merge_20190802_0954.py

@ -0,0 +1,16 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.20 on 2019-08-02 12:54
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('parlamentares', '0033_auto_20190712_1132'),
('parlamentares', '0034_auto_20190801_1408'),
]
operations = [
]

38
sapl/parlamentares/models.py

@ -478,8 +478,12 @@ class TipoAfastamento(models.Model):
@reversion.register()
class Mandato(models.Model):
parlamentar = models.ForeignKey(Parlamentar, on_delete=models.CASCADE)
tipo_afastamento = models.ForeignKey(
TipoAfastamento, blank=True, null=True, on_delete=models.PROTECT)
tipo_afastamento = models.ForeignKey(TipoAfastamento,
blank=True,
null=True,
on_delete=models.PROTECT,
verbose_name=_('Tipo de Afastamento'))
legislatura = models.ForeignKey(Legislatura, on_delete=models.PROTECT,
verbose_name=_('Legislatura'))
coligacao = models.ForeignKey(
@ -727,7 +731,6 @@ class CargoBancada(models.Model):
def __str__(self):
return self.nome_cargo
class CargoBlocoPartido(models.Model):
class Meta:
verbose_name = _('Vinculo bloco parlamentar')
@ -748,3 +751,32 @@ class CargoBlocoPartido(models.Model):
data_inicio = models.DateField(verbose_name=_('Data Início'))
data_fim = models.DateField(blank=True, null=True, verbose_name=_('Data Fim'))
@reversion.register()
class AfastamentoParlamentar(models.Model):
parlamentar = models.ForeignKey(Parlamentar, on_delete=models.CASCADE)
mandato = models.ForeignKey(Mandato, on_delete=models.CASCADE,
verbose_name=_('Mandato'))
tipo_afastamento = models.ForeignKey(TipoAfastamento,
on_delete=models.PROTECT,
verbose_name=_('Tipo de Afastamento'),
blank=True,
null=True)
data_inicio = models.DateField(verbose_name=_('Início do Afastamento'),
blank=False,
null=True)
data_fim = models.DateField(verbose_name=_('Fim do Afastamento'),
blank=True,
null=True)
observacao = models.TextField(verbose_name=_('Observação'),
blank=True)
class Meta:
verbose_name = _('Afastamento')
verbose_name_plural = _('Afastamentos')
def __str__(self):
return _('%(parlamentar)s %(legislatura)s') % {
'parlamentar': self.parlamentar, 'legislatura': self.mandato.legislatura
}

151
sapl/parlamentares/tests/test_parlamentares.py

@ -5,10 +5,12 @@ from model_mommy import mommy
from datetime import datetime
from sapl.parlamentares import forms
from sapl.parlamentares.forms import FrenteForm, LegislaturaForm, MandatoForm
from sapl.parlamentares.forms import FrenteForm, LegislaturaForm, MandatoForm, AfastamentoParlamentarForm
from sapl.parlamentares.models import (Dependente, Filiacao, Legislatura,
Mandato, Parlamentar, Partido,
TipoDependente)
TipoDependente, TipoAfastamento,
AfastamentoParlamentar)
from sapl.utils import verifica_afastamento_parlamentar
@pytest.mark.django_db(transaction=False)
@ -408,3 +410,148 @@ def test_bancada_form_datas_invalidas():
'descricao': 'teste'
})
assert not form.is_valid()
@pytest.mark.django_db(transaction=False)
def test_afastamentos_form_datas_invalidas():
parlamentar = mommy.make(Parlamentar)
mandato = mommy.make(Mandato,
parlamentar = parlamentar,
data_inicio_mandato='2017-01-01',
data_fim_mandato='2021-12-31')
tipo_afastamento = mommy.make(TipoAfastamento,
indicador= 'A',
pk=1)
form = AfastamentoParlamentarForm(data={
'parlamentar': parlamentar.pk,
'mandato': mandato.pk,
'data_inicio': '2016-12-12',
'data_fim': '2019-10-09',
'tipo_afastamento': tipo_afastamento.pk
}, initial={'parlamentar': parlamentar.pk})
assert not form.is_valid()
assert form.errors['__all__'] == \
["Data início do afastamento anterior ao início do mandato informado."]
form = AfastamentoParlamentarForm(data={
'parlamentar': parlamentar.pk,
'mandato': mandato.pk,
'data_inicio': '2017-02-02',
'data_fim': '2022-10-09',
'tipo_afastamento': tipo_afastamento.pk
}, initial={'parlamentar': parlamentar.pk})
assert not form.is_valid()
assert form.errors['__all__'] == \
["Data fim do afastamento posterior ao fim do mandato informado."]
form = AfastamentoParlamentarForm(data={
'parlamentar': parlamentar.pk,
'mandato': mandato.pk,
'data_inicio': '2017-02-02',
'data_fim': '2015-10-09',
'tipo_afastamento': tipo_afastamento.pk
}, initial={'parlamentar': parlamentar.pk})
assert not form.is_valid()
assert form.errors['__all__'] == \
["Data fim do afastamento anterior à data início do afastamento."]
form = AfastamentoParlamentarForm(data={
'parlamentar': parlamentar.pk,
'mandato': mandato.pk,
'data_inicio': '2017-02-02',
'tipo_afastamento': tipo_afastamento.pk
}, initial={'parlamentar': parlamentar.pk})
assert form.is_valid()
form.save()
assert AfastamentoParlamentar.objects.all().count() == 1
form = AfastamentoParlamentarForm(data={
'parlamentar': parlamentar.pk,
'mandato': mandato.pk,
'data_inicio': '2018-04-02',
'data_fim': '2018-06-09',
'tipo_afastamento': tipo_afastamento.pk
}, initial={'parlamentar': parlamentar.pk})
assert not form.is_valid()
assert form.errors['__all__'] == \
["Existe Afastamento sem Data Fim."]
afastamento = AfastamentoParlamentar.objects.first()
afastamento.data_fim = '2017-04-02'
afastamento.save()
assert AfastamentoParlamentar.objects.first().data_fim == data('2017-04-02')
form = AfastamentoParlamentarForm(data={
'parlamentar': parlamentar.pk,
'mandato': mandato.pk,
'data_inicio': '2018-04-02',
'data_fim': '2018-06-09',
'tipo_afastamento': tipo_afastamento.pk
}, initial={'parlamentar': parlamentar.pk})
assert form.is_valid()
@pytest.mark.django_db(transaction=False)
def test_afastamentos_form_campos_invalidos():
parlamentar = mommy.make(Parlamentar)
mandato = mommy.make(Mandato,
parlamentar = parlamentar,
data_inicio_mandato='2017-01-01',
data_fim_mandato='2021-12-31')
tipo_afastamento = mommy.make(TipoAfastamento,
indicador= 'A',
pk=1)
form = AfastamentoParlamentarForm(data={}, initial={'parlamentar': parlamentar.pk})
assert not form.is_valid()
assert form.errors['data_inicio'] == ["Este campo é obrigatório."]
assert form.errors['mandato'] == ["Este campo é obrigatório."]
assert form.errors['parlamentar'] == ["Este campo é obrigatório."]
@pytest.mark.django_db(transaction=False)
def test_parlamentar_esta_afastado():
parlamentar = mommy.make(Parlamentar)
mandato = mommy.make(Mandato,
parlamentar = parlamentar,
data_inicio_mandato='2017-01-01',
data_fim_mandato='2021-12-31')
afastamento = AfastamentoParlamentar.objects.create(mandato=mandato,
parlamentar=parlamentar,
data_inicio='2017-12-05',
data_fim='2017-12-12')
assert verifica_afastamento_parlamentar(parlamentar, '2017-12-06')
assert not verifica_afastamento_parlamentar(parlamentar, '2017-12-13')
assert not verifica_afastamento_parlamentar(parlamentar, '2017-12-04')
assert verifica_afastamento_parlamentar(parlamentar, '2017-12-06', '2017-12-11')
assert verifica_afastamento_parlamentar(parlamentar, '2017-12-06', '2017-12-06')
assert not verifica_afastamento_parlamentar(parlamentar, '2017-12-03', '2017-12-04')
assert not verifica_afastamento_parlamentar(parlamentar, '2017-12-13', '2017-12-15')
assert not verifica_afastamento_parlamentar(parlamentar, '2017-12-15', '2017-12-15')
afastamento = AfastamentoParlamentar.objects.create(mandato=mandato,
parlamentar=parlamentar,
data_inicio='2017-12-05')
assert AfastamentoParlamentar.objects.all().count() == 2
assert verifica_afastamento_parlamentar(parlamentar, '2017-12-06')
assert not verifica_afastamento_parlamentar(parlamentar, '2017-12-13')
assert not verifica_afastamento_parlamentar(parlamentar, '2017-12-04')
assert verifica_afastamento_parlamentar(parlamentar, '2017-12-06', '2017-12-11')
assert verifica_afastamento_parlamentar(parlamentar, '2017-12-06', '2017-12-06')
assert not verifica_afastamento_parlamentar(parlamentar, '2017-12-03', '2017-12-04')
assert verifica_afastamento_parlamentar(parlamentar, '2017-12-13', '2017-12-15')

3
sapl/parlamentares/urls.py

@ -12,6 +12,7 @@ from sapl.parlamentares.views import (CargoMesaCrud, ColigacaoCrud,
RelatoriaParlamentarCrud,
SessaoLegislativaCrud,
TipoAfastamentoCrud, TipoDependenteCrud,
AfastamentoParlamentarCrud,
TipoMilitarCrud, VotanteView,
altera_field_mesa,
altera_field_mesa_public_view,
@ -42,7 +43,7 @@ urlpatterns = [
ParticipacaoParlamentarCrud.get_urls() +
ProposicaoParlamentarCrud.get_urls() +
RelatoriaParlamentarCrud.get_urls() + FrenteList.get_urls() +
VotanteView.get_urls()
VotanteView.get_urls() + AfastamentoParlamentarCrud.get_urls()
)),
url(r'^parlamentar/lista$', lista_parlamentares, name='lista_parlamentares'),

41
sapl/parlamentares/views.py

@ -38,13 +38,13 @@ from .forms import (FiliacaoForm, FrenteForm, LegislaturaForm, MandatoForm,
ParlamentarCreateForm, ParlamentarForm, VotanteForm,
ParlamentarFilterSet, VincularParlamentarForm,
BlocoForm, CargoBlocoForm, CargoBlocoPartidoForm,
BancadaForm)
BancadaForm, AfastamentoParlamentarForm)
from .models import (Bancada, CargoBancada, CargoMesa, Coligacao, ComposicaoColigacao, ComposicaoMesa,
Dependente, Filiacao, Frente, Legislatura, Mandato,
NivelInstrucao, Parlamentar, Partido, SessaoLegislativa,
SituacaoMilitar, TipoAfastamento, TipoDependente, Votante,
Bloco, CargoBlocoPartido, HistoricoPartido, CargoBloco)
Bloco, CargoBlocoPartido, HistoricoPartido, CargoBloco, AfastamentoParlamentar)
CargoBancadaCrud = CrudAux.build(CargoBancada, '')
@ -1298,3 +1298,40 @@ def deleta_vinculo_parlamentar_bloco(request,pk):
'sapl.parlamentares:bloco_detail',
kwargs={'pk': pk_bloco})
)
class AfastamentoParlamentarCrud(PermissionRequiredMixin, MasterDetailCrud):
model = AfastamentoParlamentar
parent_field = 'parlamentar'
public = [RP_DETAIL, RP_LIST]
list_field_names = ['mandato__legislatura',
'data_inicio',
'data_fim',
'tipo_afastamento']
class ListView(MasterDetailCrud.ListView):
ordering = ('-mandato__legislatura__numero', '-data_inicio')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
rows = context['rows']
coluna_coligacao = 2
coluna_votos_recebidos = 3
for row in rows:
if not row[coluna_coligacao][0]:
row[coluna_coligacao] = (' ', None)
if not row[coluna_votos_recebidos][0]:
row[coluna_votos_recebidos] = (' ', None)
return context
class CreateView(MasterDetailCrud.CreateView):
form_class = AfastamentoParlamentarForm
def get_initial(self):
parlamentar = Parlamentar.objects.get(pk=self.kwargs['pk'])
return {'parlamentar': parlamentar}
class UpdateView(MasterDetailCrud.UpdateView):
form_class = AfastamentoParlamentarForm

1
sapl/rules/map_rules.py

@ -287,6 +287,7 @@ rules_group_geral = {
(parlamentares.Bloco, __base__, __perms_publicas__),
(parlamentares.CargoBloco, __base__, __perms_publicas__),
(parlamentares.CargoBlocoPartido, __base__, __perms_publicas__),
(parlamentares.AfastamentoParlamentar, __base__, __perms_publicas__),
(sessao.TipoSessaoPlenaria, __base__, __perms_publicas__),
(sessao.TipoResultadoVotacao, __base__, __perms_publicas__),

22
sapl/sessao/forms.py

@ -22,7 +22,7 @@ from sapl.parlamentares.models import Parlamentar, Mandato
from sapl.utils import (RANGE_DIAS_MES, RANGE_MESES,
MateriaPesquisaOrderingFilter, autor_label,
autor_modal, timezone, choice_anos_com_sessaoplenaria,
FileFieldCheckMixin)
FileFieldCheckMixin, verifica_afastamento_parlamentar)
from .models import (ExpedienteMateria, JustificativaAusencia,
Orador, OradorExpediente, OrdemDia, PresencaOrdemDia, SessaoPlenaria,
@ -599,8 +599,14 @@ class AdicionarVariasMateriasFilterSet(MateriaLegislativaFilterSet):
class OradorForm(ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['parlamentar'].queryset = \
Parlamentar.objects.filter(ativo=True).order_by('nome_parlamentar')
sessao = SessaoPlenaria.objects.get(id=kwargs['initial']['id_sessao'])
parlamentares_ativos = Parlamentar.objects.filter(ativo=True).order_by('nome_parlamentar')
for p in parlamentares_ativos:
if verifica_afastamento_parlamentar(p, sessao.data_inicio, sessao.data_fim):
parlamentares_ativos = parlamentares_ativos.exclude(id=p.id)
self.fields['parlamentar'].queryset = parlamentares_ativos
def clean(self):
super(OradorForm, self).clean()
@ -641,9 +647,13 @@ class OradorExpedienteForm(ModelForm):
id_sessao = int(self.initial['id_sessao'])
sessao = SessaoPlenaria.objects.get(id=id_sessao)
legislatura_vigente = sessao.legislatura
self.fields['parlamentar'].queryset = \
Parlamentar.objects.filter(mandato__legislatura=legislatura_vigente,
ativo=True).order_by('nome_parlamentar')
parlamentares_ativos = Parlamentar.objects.filter(ativo=True).order_by('nome_parlamentar')
for p in parlamentares_ativos:
if verifica_afastamento_parlamentar(p, sessao.data_inicio, sessao.data_fim):
parlamentares_ativos = parlamentares_ativos.exclude(id=p.id)
self.fields['parlamentar'].queryset = parlamentares_ativos
def clean(self):
super(OradorExpedienteForm, self).clean()

18
sapl/sessao/views.py

@ -35,10 +35,12 @@ from sapl.materia.models import (Autoria, TipoMateriaLegislativa,
from sapl.materia.views import MateriaLegislativaPesquisaView
from sapl.painel.models import Cronometro, PainelConfig
from sapl.parlamentares.models import (Filiacao, Legislatura, Mandato,
Parlamentar, SessaoLegislativa)
Parlamentar, SessaoLegislativa,
AfastamentoParlamentar)
from sapl.sessao.apps import AppConfig
from sapl.sessao.forms import ExpedienteMateriaForm, OrdemDiaForm
from sapl.utils import show_results_filter_set, remover_acentos, get_client_ip, filiacao_data
from sapl.utils import (show_results_filter_set, remover_acentos, get_client_ip, filiacao_data,
verifica_afastamento_parlamentar)
from .forms import (AdicionarVariasMateriasFilterSet, ExpedienteForm,
JustificativaAusenciaForm, OcorrenciaSessaoForm, ListMateriaForm,
@ -461,10 +463,12 @@ def get_presencas_generic(model, sessao, legislatura):
legislatura=legislatura).order_by('parlamentar__nome_parlamentar')
for m in mandato:
if m.parlamentar in presentes:
yield (m.parlamentar, True)
parlamentar = m.parlamentar
p_afastado = verifica_afastamento_parlamentar(parlamentar, sessao.data_inicio, sessao.data_fim)
if parlamentar in presentes:
yield (parlamentar, True, p_afastado)
else:
yield (m.parlamentar, False)
yield (parlamentar, False, p_afastado)
class TipoExpedienteCrud(CrudAux):
@ -1287,7 +1291,9 @@ class MesaView(FormMixin, DetailView):
org_parlamentares_vagos.sort(
key=lambda x: remover_acentos(x.nome_parlamentar))
org_parlamentares_vagos = [
p for p in org_parlamentares_vagos if p.ativo]
p for p in org_parlamentares_vagos if (p.ativo and
not verifica_afastamento_parlamentar(p, sessao.data_inicio, sessao.data_fim)
)]
# Se todos os cargos estiverem ocupados, a listagem de parlamentares
# deve ser renderizada vazia
if not cargos_vagos:

16
sapl/templates/base/RelatorioPresencaSessao_filter.html

@ -25,20 +25,26 @@
<b>Tipo Sessão Plenária: {{tipo}}<br /> </b>
<b>TOTAIS NO PERÍODO - SESSÕES: {{total_sessao}} - ORDENS DO DIA: {{total_ordemdia}}</b><br />
<b>Exibir presença das Ordens do Dia: {% if exibir_ordem %} Sim {% else %} Não {% endif %}</b><br />
<br>
<h4><b>Atenção!</b> Este relatório leva em conta os afastamentos cadastrados em Parlamentar -> Afastamentos.
A porcentagem de presença exclui períodos em que o parlamentar estava afastado.</h4>
<br>
<table class="table table-bordered table-hover presenca_table">
<thead class="thead-default" align="center">
<tr class="active">
<th rowspan="2">Nome Parlamentar / Partido</th>
<th rowspan="2">Titular?</th>
<th rowspan="2">Ativo?</th>
<th colspan="2">Sessão</th>
{% if exibir_ordem %} <th colspan="2">Ordem do Dia</th> {% endif %}
<th colspan="3">Sessão</th>
{% if exibir_ordem %} <th colspan="3">Ordem do Dia</th> {% endif %}
</tr>
<tr class="active">
<th>(Qtd)</th>
<th>(Qtd Presente)</th>
<th>(Qtde Afastado)</th>
<th>( % )</th>
{% if exibir_ordem %}
<th>(Qtd)</th>
<th>(Qtd Presente)</th>
<th>(Qtde Afastado)</th>
<th>( % )</th>
{% endif %}
</tr>
@ -50,9 +56,11 @@
<td>{% if p.titular %} Sim {% else %} Não {% endif %}</td>
<td>{% if p.parlamentar.ativo %} Sim {% else %} Não {% endif %}</td>
<td>{{p.sessao_count}}</td>
<td>{{p.sessao_afast}}</td>
<td>{{p.sessao_porc}}</td>
{% if exibir_ordem %}
<td>{{p.ordemdia_count}}</td>
<td>{{p.ordem_afast}}</td>
<td>{{p.ordemdia_porc}}</td>
{% endif %}
</tr>

6
sapl/templates/parlamentares/layouts.yaml

@ -151,3 +151,9 @@ CargoBloco:
- nome
- unico
- descricao
AfastamentoParlamentar:
{% trans 'Afastamento' %}:
- data_inicio data_fim
- mandato tipo_afastamento
- observacao

2
sapl/templates/parlamentares/subnav.yaml

@ -3,6 +3,8 @@
url: parlamentar_detail
- title: {% trans 'Mandatos' %}
url: mandato_list
- title: {% trans 'Afastamentos' %}
url: afastamentoparlamentar_list
- title: {% trans 'Matérias' %}
url: parlamentar_materias
- title: {% trans 'Filiações Partidárias' %}

11
sapl/templates/parlamentares/tipoafastamento_form.html

@ -0,0 +1,11 @@
{% extends "crud/form.html" %}
{% load i18n %}
{% load crispy_forms_tags %}
{% block base_content %}
<br>
Atenção! Quando o indicador selecionado for <b>Fim de mandato</b>, este tipo será exibido em Mandatos do Parlamentar.
Quando o indicador selecionado for <b>Afastamento</b>, este tipo será exibido em Afastamentos do Parlamentar.
<br><br>
{{block.super}}
{% endblock base_content %}

119
sapl/templates/sessao/presenca.html

@ -5,71 +5,72 @@
{% block actions %}{% endblock %}
{% block detail_content %}
{% if perms|get_add_perm:view %}
<form method="POST">
{% csrf_token %}
<div class="controls">
<div class="checkbox">
<label for="id_check_all">
<input type="checkbox" id="id_check_all" onchange="checkAll(event)" /> Marcar/Desmarcar Todos
</label>
{% if perms|get_add_perm:view %}
<form method="POST">
{% csrf_token %}
<div class="controls">
<div class="checkbox">
<label for="id_check_all">
<input type="checkbox" id="id_check_all" onchange="checkAll(event)" /> Marcar/Desmarcar Todos
</label>
</div>
</div>
</div>
<br />
<div class="controls">
<div class="checkbox">
<label for="id_ativos">
<input type="checkbox" name="ativos" id="id_ativos" onchange="escondeInativos()" checked /> Exibir somente
parlamentares ativos
</label>
<br />
<div class="controls">
<div class="checkbox">
<label for="id_ativos">
<input type="checkbox" name="ativos" id="id_ativos" onchange="escondeInativos()" checked /> Exibir somente
parlamentares ativos
</label>
</div>
</div>
</div>
<br />
<div class="controls ">
{% for parlamentar, check in view.get_presencas %}
{% if parlamentar.ativo %}
<div class="checkbox">
<label for="id_presenca_{{forloop.counter}}">
<input type="checkbox" id="id_presenca_{{forloop.counter}}" name="presenca_ativos" value="{{ parlamentar.id }}"
{% if check %} checked {% endif %}>
{{ parlamentar.nome_parlamentar }} / {% if parlamentar|filiacao_data_filter:object.data_inicio %} {{ parlamentar|filiacao_data_filter:object.data_inicio }} {% else %} Sem partido {% endif %}
</label>
<br />
<div class="controls ">
{% for parlamentar, check, afastado in view.get_presencas %}
{% if parlamentar.ativo and not afastado %}
<div class="checkbox">
<label for="id_presenca_{{forloop.counter}}">
<input type="checkbox" id="id_presenca_{{forloop.counter}}" name="presenca_ativos" value="{{ parlamentar.id }}"
{% if check %} checked {% endif %}>
{{ parlamentar.nome_parlamentar }} / {% if parlamentar|filiacao_data_filter:object.data_inicio %} {{ parlamentar|filiacao_data_filter:object.data_inicio }} {% else %} Sem partido {% endif %}
</label>
</div>
{% else %}
<div class="checkbox inativos" style="display:none;">
<label for="id_presenca_{{forloop.counter}}" class="inativos" style="display:none;">
<input type="checkbox" id="id_presenca_{{forloop.counter}}" name="presenca_inativos" value="{{ parlamentar.id }}"
{% if check %} checked {% endif %}>
{{ parlamentar.nome_parlamentar }} / {% if parlamentar|filiacao_data_filter:object.data_inicio %} {{ parlamentar|filiacao_data_filter:object.data_inicio }} {% else %} Sem partido {% endif %}
{% if afastado %} (Afastado) {% endif %}
</label>
</div>
{% endif %}
{% endfor %}
</div>
{% else %}
<div class="checkbox inativos" style="display:none;">
<label for="id_presenca_{{forloop.counter}}" class="inativos" style="display:none;">
<input type="checkbox" id="id_presenca_{{forloop.counter}}" name="presenca_inativos" value="{{ parlamentar.id }}"
{% if check %} checked {% endif %}>
{{ parlamentar.nome_parlamentar }} / {% if parlamentar|filiacao_data_filter:object.data_inicio %} {{ parlamentar|filiacao_data_filter:object.data_inicio }} {% else %} Sem partido {% endif %}
</label>
</div>
{% endif %}
{% endfor %}
</div>
<br />
<input type="submit" value="Salvar" class="btn btn-primary" />
</form>
<br />
<input type="submit" value="Salvar" class="btn btn-primary" />
</form>
{% else %}
{% else %}
<div class="row">
<div class="col-md-6">
<h2>Parlamentares presentes
</div>
</div>
<br />
<div class="row">
<div class="col-md-6">
<h2>Parlamentares presentes
</div>
</div>
<br />
{% for parlamentar, check in view.get_presencas %}
{% if check %}
<div class="row">
<div class="col-md-6">
<label for="parlamentar"> - {{ parlamentar.nome_parlamentar }} / {% if parlamentar|filiacao_data_filter:object.data_inicio %} {{ parlamentar|filiacao_data_filter:object.data_inicio }} {% else %} Sem partido {% endif %}
</label>
</div>
</div>
{% endif %}
{% endfor %}
{% endif %}
{% for parlamentar, check, afastado in view.get_presencas %}
{% if check %}
<div class="row">
<div class="col-md-6">
<label for="parlamentar"> - {{ parlamentar.nome_parlamentar }} / {% if parlamentar|filiacao_data_filter:object.data_inicio %} {{ parlamentar|filiacao_data_filter:object.data_inicio }} {% else %} Sem partido {% endif %}
</label>
</div>
</div>
{% endif %}
{% endfor %}
{% endif %}
{% endblock detail_content %}
{% block extra_js %}

7
sapl/templates/sessao/presenca_ordemdia.html

@ -31,8 +31,8 @@
<br />
<div class="controls">
{% for parlamentar, check in view.get_presencas_ordem %}
{% if parlamentar.ativo %}
{% for parlamentar, check, afastado in view.get_presencas_ordem %}
{% if parlamentar.ativo and not afastado %}
<div class="checkbox">
<label for="id_presenca_{{forloop.counter}}">
<input type="checkbox" id="id_presenca_{{forloop.counter}}" name="presenca_ativos" value="{{ parlamentar.id }}"
@ -46,6 +46,7 @@
<input type="checkbox" id="id_presenca_{{forloop.counter}}" name="presenca_inativos" value="{{ parlamentar.id }}"
{% if check %} checked {% endif %} />
{{ parlamentar.nome_parlamentar }} / {% if parlamentar|filiacao_data_filter:object.data_inicio %} {{ parlamentar|filiacao_data_filter:object.data_inicio }} {% else %} Sem partido {% endif %}
{% if afastado %} (Afastado) {% endif %}
</label>
</div>
{% endif %}
@ -64,7 +65,7 @@
</div>
<br/>
{% for parlamentar, check in view.get_presencas_ordem %}
{% for parlamentar, check, afastado in view.get_presencas_ordem %}
{% if check %}
<div class="row">
<div class="col-md-6"><label for="parlamentar"> - {{ parlamentar.nome_parlamentar }} / {% if parlamentar|filiacao_data_filter:object.data_inicio %} {{ parlamentar|filiacao_data_filter:object.data_inicio }} {% else %} Sem partido {% endif %}</label></div>

17
sapl/utils.py

@ -998,3 +998,20 @@ class OverwriteStorage(FileSystemStorage):
if self.exists(name):
os.remove(os.path.join(settings.MEDIA_ROOT, name))
return name
def verifica_afastamento_parlamentar(parlamentar, data_inicio, data_fim=None):
from sapl.parlamentares.models import AfastamentoParlamentar
if data_fim:
existe_afastamento = AfastamentoParlamentar.objects.filter(Q(parlamentar=parlamentar) &
( (Q(data_inicio__lte=data_inicio) &
Q(data_fim__gte=data_fim)) |
( Q(data_inicio__lte=data_inicio) &
Q(data_fim__isnull=True))
) ).exists()
else:
existe_afastamento = AfastamentoParlamentar.objects.filter(parlamentar=parlamentar,
data_inicio__lte=data_inicio,
data_fim__gte=data_inicio).exists()
return existe_afastamento

Loading…
Cancel
Save