Browse Source

Merge tag '3.1.151' into migracao

migracao
Marcio Mazza 6 years ago
parent
commit
31392d9442
  1. 5
      Dockerfile
  2. 2
      docker-compose.yml
  3. 7
      sapl/api/urls.py
  4. 126
      sapl/api/views.py
  5. 3
      sapl/comissoes/urls.py
  6. 14
      sapl/comissoes/views.py
  7. 59
      sapl/materia/forms.py
  8. 22
      sapl/materia/migrations/0044_auto_20190327_1409.py
  9. 1
      sapl/materia/models.py
  10. 13
      sapl/materia/tests/test_materia_form.py
  11. 135
      sapl/materia/views.py
  12. 62
      sapl/parlamentares/forms.py
  13. 5
      sapl/parlamentares/urls.py
  14. 30
      sapl/parlamentares/views.py
  15. 24
      sapl/relatorios/templates/pdf_sessao_plenaria_gerar.py
  16. 41
      sapl/relatorios/views.py
  17. 1
      sapl/rules/map_rules.py
  18. 77
      sapl/sessao/forms.py
  19. 34
      sapl/sessao/migrations/0034_oradorordemdia.py
  20. 20
      sapl/sessao/migrations/0035_resumoordenacao_decimo_quarto.py
  21. 9
      sapl/sessao/models.py
  22. 13
      sapl/sessao/urls.py
  23. 115
      sapl/sessao/views.py
  24. 2
      sapl/settings.py
  25. 2
      sapl/templates/base.html
  26. 19
      sapl/templates/materia/relatoria_form.html
  27. 8
      sapl/templates/parlamentares/layouts.yaml
  28. 12
      sapl/templates/parlamentares/parlamentar_filter.html
  29. 9
      sapl/templates/parlamentares/parlamentares_list.html
  30. 8
      sapl/templates/parlamentares/vincular_parlamentar.html
  31. 6
      sapl/templates/relatorios/relatorio_ata.html
  32. 13
      sapl/templates/sessao/blocos_ata/oradores_ordemdia.html
  33. 15
      sapl/templates/sessao/blocos_resumo/oradores_ordemdia.html
  34. 5
      sapl/templates/sessao/expedientemateria_list.html
  35. 6
      sapl/templates/sessao/layouts.yaml
  36. 5
      sapl/templates/sessao/ordemdia_list.html
  37. 3
      sapl/templates/sessao/resumo.html
  38. 1
      sapl/templates/sessao/resumo_ata.html
  39. 2
      sapl/templates/sessao/subnav.yaml
  40. 2
      setup.py

5
Dockerfile

@ -3,7 +3,7 @@ FROM alpine:3.8
ENV BUILD_PACKAGES postgresql-dev graphviz-dev graphviz build-base git pkgconfig \ ENV BUILD_PACKAGES postgresql-dev graphviz-dev graphviz build-base git pkgconfig \
python3-dev libxml2-dev jpeg-dev libressl-dev libffi-dev libxslt-dev \ python3-dev libxml2-dev jpeg-dev libressl-dev libffi-dev libxslt-dev \
nodejs py3-lxml py3-magic postgresql-client poppler-utils antiword \ nodejs py3-lxml py3-magic postgresql-client poppler-utils antiword \
curl jq openssh-client vim openssh-client bash curl jq openssh-client vim bash
RUN apk update --update-cache && apk upgrade RUN apk update --update-cache && apk upgrade
@ -32,9 +32,6 @@ RUN pip install -r /var/interlegis/sapl/requirements/dev-requirements.txt --upgr
COPY config/env_dockerfile /var/interlegis/sapl/sapl/.env COPY config/env_dockerfile /var/interlegis/sapl/sapl/.env
# Configura timezone para BRT
# RUN cp /usr/share/zoneinfo/America/Sao_Paulo /etc/localtime && echo "America/Sao_Paulo" > /etc/timezone
RUN python3 manage.py collectstatic --noinput --clear RUN python3 manage.py collectstatic --noinput --clear
# Remove .env(fake) e sapl.db da imagem # Remove .env(fake) e sapl.db da imagem

2
docker-compose.yml

@ -11,7 +11,7 @@ sapldb:
ports: ports:
- "5432:5432" - "5432:5432"
sapl: sapl:
image: interlegis/sapl:3.1.150 image: interlegis/sapl:3.1.151
restart: always restart: always
environment: environment:
ADMIN_PASSWORD: interlegis ADMIN_PASSWORD: interlegis

7
sapl/api/urls.py

@ -8,7 +8,7 @@ from rest_framework.routers import DefaultRouter
from sapl.api.deprecated import MateriaLegislativaViewSet, SessaoPlenariaViewSet,\ from sapl.api.deprecated import MateriaLegislativaViewSet, SessaoPlenariaViewSet,\
AutoresProvaveisListView, AutoresPossiveisListView, AutorListView,\ AutoresProvaveisListView, AutoresPossiveisListView, AutorListView,\
ModelChoiceView ModelChoiceView
from sapl.api.views import SaplSetViews from sapl.api.views import SaplApiViewSetConstrutor
from .apps import AppConfig from .apps import AppConfig
@ -21,9 +21,10 @@ router.register(r'materia$', MateriaLegislativaViewSet)
router.register(r'sessao-plenaria', SessaoPlenariaViewSet) router.register(r'sessao-plenaria', SessaoPlenariaViewSet)
for app, built_sets in SaplSetViews.items(): for app, built_sets in SaplApiViewSetConstrutor._built_sets.items():
for view_prefix, viewset in built_sets.items(): for view_prefix, viewset in built_sets.items():
router.register(app + '/' + view_prefix, viewset) router.register(app.label + '/' +
view_prefix._meta.model_name, viewset)
urlpatterns_router = router.urls urlpatterns_router = router.urls

126
sapl/api/views.py

@ -22,8 +22,12 @@ from sapl.api.forms import SaplFilterSetMixin
from sapl.api.permissions import SaplModelPermissions from sapl.api.permissions import SaplModelPermissions
from sapl.api.serializers import ChoiceSerializer from sapl.api.serializers import ChoiceSerializer
from sapl.base.models import Autor, AppConfig, DOC_ADM_OSTENSIVO from sapl.base.models import Autor, AppConfig, DOC_ADM_OSTENSIVO
from sapl.materia.models import Proposicao, TipoMateriaLegislativa from sapl.materia.models import Proposicao, TipoMateriaLegislativa,\
MateriaLegislativa, Tramitacao
from sapl.parlamentares.models import Parlamentar from sapl.parlamentares.models import Parlamentar
from sapl.protocoloadm.models import DocumentoAdministrativo,\
DocumentoAcessorioAdministrativo, TramitacaoAdministrativo
from sapl.sessao.models import SessaoPlenaria
from sapl.utils import models_with_gr_for_model, choice_anos_com_sessaoplenaria from sapl.utils import models_with_gr_for_model, choice_anos_com_sessaoplenaria
@ -31,20 +35,25 @@ class BusinessRulesNotImplementedMixin:
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
raise Exception(_("POST Create não implementado")) raise Exception(_("POST Create não implementado"))
def put(self, request, *args, **kwargs): def update(self, request, *args, **kwargs):
raise Exception(_("PUT Update não implementado")) raise Exception(_("PUT and PATCH não implementado"))
def patch(self, request, *args, **kwargs):
raise Exception(_("PATCH Partial Update não implementado"))
def delete(self, request, *args, **kwargs): def delete(self, request, *args, **kwargs):
raise Exception(_("DELETE Delete não implementado")) raise Exception(_("DELETE Delete não implementado"))
class SaplApiViewSetConstrutor(ModelViewSet): class SaplApiViewSet(ModelViewSet):
filter_backends = (DjangoFilterBackend,) filter_backends = (DjangoFilterBackend,)
class SaplApiViewSetConstrutor():
_built_sets = {}
@classonlymethod
def get_class_for_model(cls, model):
return cls._built_sets[model._meta.app_config][model]
@classonlymethod @classonlymethod
def build_class(cls): def build_class(cls):
import inspect import inspect
@ -95,7 +104,7 @@ class SaplApiViewSetConstrutor(ModelViewSet):
model = _model model = _model
# Define uma classe padrão ModelViewSet de DRF # Define uma classe padrão ModelViewSet de DRF
class ModelSaplViewSet(cls): class ModelSaplViewSet(SaplApiViewSet):
queryset = _model.objects.all() queryset = _model.objects.all()
# Utiliza o filtro customizado pela classe # Utiliza o filtro customizado pela classe
@ -119,12 +128,12 @@ class SaplApiViewSetConstrutor(ModelViewSet):
apps_sapl = [apps.apps.get_app_config( apps_sapl = [apps.apps.get_app_config(
n[5:]) for n in settings.SAPL_APPS] n[5:]) for n in settings.SAPL_APPS]
for app in apps_sapl: for app in apps_sapl:
built_sets[app.label] = {} cls._built_sets[app] = {}
for model in app.get_models(): for model in app.get_models():
built_sets[app.label][model._meta.model_name] = build(model) cls._built_sets[app][model] = build(model)
return built_sets
SaplApiViewSetConstrutor.build_class()
""" """
1. Constroi uma rest_framework.viewsets.ModelViewSet para 1. Constroi uma rest_framework.viewsets.ModelViewSet para
@ -187,15 +196,39 @@ class SaplApiViewSetConstrutor(ModelViewSet):
} }
""" """
SaplSetViews = SaplApiViewSetConstrutor.build_class()
# Toda Classe construida acima, pode ser redefinida e aplicado quaisquer # Toda Classe construida acima, pode ser redefinida e aplicado quaisquer
# das possibilidades para uma classe normal criada a partir de # das possibilidades para uma classe normal criada a partir de
# rest_framework.viewsets.ModelViewSet conforme exemplo para a classe autor # rest_framework.viewsets.ModelViewSet conforme exemplo para a classe autor
# decorator para recuperar e transformar o default
class customize(object):
def __init__(self, model):
self.model = model
def __call__(self, cls):
class _SaplApiViewSet(
cls,
SaplApiViewSetConstrutor._built_sets[
self.model._meta.app_config][self.model]
):
pass
if hasattr(_SaplApiViewSet, 'build'):
_SaplApiViewSet = _SaplApiViewSet.build()
SaplApiViewSetConstrutor._built_sets[
self.model._meta.app_config][self.model] = _SaplApiViewSet
return _SaplApiViewSet
# Customização para AutorViewSet com implementação de actions específicas # Customização para AutorViewSet com implementação de actions específicas
class _AutorViewSet(SaplSetViews['base']['autor']):
@customize(Autor)
class _AutorViewSet:
""" """
Neste exemplo de customização do que foi criado em Neste exemplo de customização do que foi criado em
SaplApiViewSetConstrutor além do ofertado por SaplApiViewSetConstrutor além do ofertado por
@ -240,7 +273,7 @@ class _AutorViewSet(SaplSetViews['base']['autor']):
return Response(serializer.data) return Response(serializer.data)
@classonlymethod @classonlymethod
def build_class_with_actions(cls): def build(cls):
models_with_gr_for_autor = models_with_gr_for_model(Autor) models_with_gr_for_autor = models_with_gr_for_model(Autor)
@ -263,7 +296,8 @@ class _AutorViewSet(SaplSetViews['base']['autor']):
return cls return cls
class _ParlamentarViewSet(SaplSetViews['parlamentares']['parlamentar']): @customize(Parlamentar)
class _ParlamentarViewSet:
@action(detail=True) @action(detail=True)
def proposicoes(self, request, *args, **kwargs): def proposicoes(self, request, *args, **kwargs):
""" """
@ -288,15 +322,16 @@ class _ParlamentarViewSet(SaplSetViews['parlamentares']['parlamentar']):
page = self.paginate_queryset(qs) page = self.paginate_queryset(qs)
if page is not None: if page is not None:
serializer = SaplSetViews[ serializer = SaplApiViewSetConstrutor.get_class_for_model(
'materia']['proposicao'].serializer_class(page, many=True) Proposicao).serializer_class(page, many=True)
return self.get_paginated_response(serializer.data) return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(page, many=True) serializer = self.get_serializer(page, many=True)
return Response(serializer.data) return Response(serializer.data)
class _ProposicaoViewSet(SaplSetViews['materia']['proposicao']): @customize(Proposicao)
class _ProposicaoViewSet():
""" """
list: list:
Retorna lista de Proposições Retorna lista de Proposições
@ -349,7 +384,26 @@ class _ProposicaoViewSet(SaplSetViews['materia']['proposicao']):
return qs return qs
class _TipoMateriaLegislativaViewSet(SaplSetViews['materia']['tipomaterialegislativa']): @customize(MateriaLegislativa)
class _MateriaLegislativaViewSet:
@action(detail=True, methods=['GET'])
def ultima_tramitacao(self, request, *args, **kwargs):
materia = self.get_object()
if not materia.tramitacao_set.exists():
return Response({})
ultima_tramitacao = materia.tramitacao_set.last()
serializer_class = SaplApiViewSetConstrutor.get_class_for_model(
Tramitacao).serializer_class(ultima_tramitacao)
return Response(serializer_class.data)
@customize(TipoMateriaLegislativa)
class _TipoMateriaLegislativaViewSet:
@action(detail=True, methods=['POST']) @action(detail=True, methods=['POST'])
def change_position(self, request, *args, **kwargs): def change_position(self, request, *args, **kwargs):
@ -366,7 +420,8 @@ class _TipoMateriaLegislativaViewSet(SaplSetViews['materia']['tipomaterialegisla
return Response(result) return Response(result)
class _DocumentoAdministrativoViewSet(SaplSetViews['protocoloadm']['documentoadministrativo']): @customize(DocumentoAdministrativo)
class _DocumentoAdministrativoViewSet:
class DocumentoAdministrativoPermission(SaplModelPermissions): class DocumentoAdministrativoPermission(SaplModelPermissions):
def has_permission(self, request, view): def has_permission(self, request, view):
@ -400,8 +455,8 @@ class _DocumentoAdministrativoViewSet(SaplSetViews['protocoloadm']['documentoadm
return qs return qs
class _DocumentoAcessorioAdministrativoViewSet( @customize(DocumentoAcessorioAdministrativo)
SaplSetViews['protocoloadm']['documentoacessorioadministrativo']): class _DocumentoAcessorioAdministrativoViewSet:
permission_classes = ( permission_classes = (
_DocumentoAdministrativoViewSet.DocumentoAdministrativoPermission, ) _DocumentoAdministrativoViewSet.DocumentoAdministrativoPermission, )
@ -414,9 +469,8 @@ class _DocumentoAcessorioAdministrativoViewSet(
return qs return qs
class _TramitacaoAdministrativoViewSet( @customize(TramitacaoAdministrativo)
SaplSetViews['protocoloadm']['tramitacaoadministrativo'], class _TramitacaoAdministrativoViewSet(BusinessRulesNotImplementedMixin):
BusinessRulesNotImplementedMixin):
# TODO: Implementar regras de manutenção das tramitações de docs adms # TODO: Implementar regras de manutenção das tramitações de docs adms
permission_classes = ( permission_classes = (
@ -430,8 +484,8 @@ class _TramitacaoAdministrativoViewSet(
return qs return qs
class _SessaoPlenariaViewSet( @customize(SessaoPlenaria)
SaplSetViews['sessao']['sessaoplenaria']): class _SessaoPlenariaViewSet:
@action(detail=False) @action(detail=False)
def years(self, request, *args, **kwargs): def years(self, request, *args, **kwargs):
@ -439,17 +493,3 @@ class _SessaoPlenariaViewSet(
serializer = ChoiceSerializer(years, many=True) serializer = ChoiceSerializer(years, many=True)
return Response(serializer.data) return Response(serializer.data)
SaplSetViews['base']['autor'] = _AutorViewSet.build_class_with_actions()
SaplSetViews['materia']['proposicao'] = _ProposicaoViewSet
SaplSetViews['materia']['tipomaterialegislativa'] = _TipoMateriaLegislativaViewSet
SaplSetViews['parlamentares']['parlamentar'] = _ParlamentarViewSet
SaplSetViews['protocoloadm']['documentoadministrativo'] = _DocumentoAdministrativoViewSet
SaplSetViews['protocoloadm']['documentoacessorioadministrativo'] = _DocumentoAcessorioAdministrativoViewSet
SaplSetViews['protocoloadm']['tramitacaoadministrativo'] = _TramitacaoAdministrativoViewSet
SaplSetViews['sessao']['sessaoplenaria'] = _SessaoPlenariaViewSet

3
sapl/comissoes/urls.py

@ -1,7 +1,7 @@
from django.conf.urls import include, url from django.conf.urls import include, url
from sapl.comissoes.views import (CargoCrud, ComissaoCrud, ComposicaoCrud, from sapl.comissoes.views import (CargoCrud, ComissaoCrud, ComposicaoCrud,
DocumentoAcessorioCrud, MateriasTramitacaoListView, ParticipacaoCrud, DocumentoAcessorioCrud, MateriasTramitacaoListView, ParticipacaoCrud,
PeriodoComposicaoCrud, ReuniaoCrud, TipoComissaoCrud) PeriodoComposicaoCrud, ReuniaoCrud, TipoComissaoCrud, get_participacoes_comissao)
from .apps import AppConfig from .apps import AppConfig
@ -21,4 +21,5 @@ urlpatterns = [
url(r'^sistema/comissao/periodo-composicao/', url(r'^sistema/comissao/periodo-composicao/',
include(PeriodoComposicaoCrud.get_urls())), include(PeriodoComposicaoCrud.get_urls())),
url(r'^sistema/comissao/tipo/', include(TipoComissaoCrud.get_urls())), url(r'^sistema/comissao/tipo/', include(TipoComissaoCrud.get_urls())),
url(r'^sistema/comissao/recupera-participacoes', get_participacoes_comissao),
] ]

14
sapl/comissoes/views.py

@ -2,7 +2,7 @@ import logging
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.db.models import F from django.db.models import F
from django.http.response import HttpResponseRedirect from django.http.response import HttpResponseRedirect, JsonResponse
from django.views.decorators.clickjacking import xframe_options_exempt from django.views.decorators.clickjacking import xframe_options_exempt
from django.views.generic import ListView from django.views.generic import ListView
from django.views.generic.base import RedirectView from django.views.generic.base import RedirectView
@ -278,3 +278,15 @@ class DocumentoAcessorioCrud(MasterDetailCrud):
return HttpResponseRedirect( return HttpResponseRedirect(
reverse('sapl.comissoes:reuniao_detail', reverse('sapl.comissoes:reuniao_detail',
kwargs={'pk': obj.reuniao.pk})) kwargs={'pk': obj.reuniao.pk}))
def get_participacoes_comissao(request):
parlamentares = []
composicao_id = request.GET.get('composicao_id')
if composicao_id:
parlamentares = [{'nome': p.parlamentar.nome_parlamentar, 'id': p.parlamentar.id} for p in
Participacao.objects.filter(composicao_id=composicao_id).order_by(
'parlamentar__nome_parlamentar')]
return JsonResponse(parlamentares, safe=False)

59
sapl/materia/forms.py

@ -26,7 +26,7 @@ import django_filters
import sapl import sapl
from sapl.base.models import AppConfig, Autor, TipoAutor from sapl.base.models import AppConfig, Autor, TipoAutor
from sapl.comissoes.models import Comissao, Participacao from sapl.comissoes.models import Comissao, Participacao, Composicao
from sapl.compilacao.models import (STATUS_TA_IMMUTABLE_PUBLIC, from sapl.compilacao.models import (STATUS_TA_IMMUTABLE_PUBLIC,
STATUS_TA_PRIVATE) STATUS_TA_PRIVATE)
from sapl.crispy_layout_mixin import (SaplFormLayout, form_actions, to_column, from sapl.crispy_layout_mixin import (SaplFormLayout, form_actions, to_column,
@ -37,7 +37,7 @@ from sapl.materia.models import (AssuntoMateria, Autoria, MateriaAssunto,
UnidadeTramitacao) UnidadeTramitacao)
from sapl.norma.models import (LegislacaoCitada, NormaJuridica, from sapl.norma.models import (LegislacaoCitada, NormaJuridica,
TipoNormaJuridica) TipoNormaJuridica)
from sapl.parlamentares.models import Legislatura, Partido from sapl.parlamentares.models import Legislatura, Partido, Parlamentar
from sapl.protocoloadm.models import Protocolo, DocumentoAdministrativo from sapl.protocoloadm.models import Protocolo, DocumentoAdministrativo
from sapl.settings import MAX_DOC_UPLOAD_SIZE from sapl.settings import MAX_DOC_UPLOAD_SIZE
from sapl.utils import (YES_NO_CHOICES, SEPARADOR_HASH_PROPOSICAO, from sapl.utils import (YES_NO_CHOICES, SEPARADOR_HASH_PROPOSICAO,
@ -362,34 +362,63 @@ class DocumentoAcessorioForm(FileFieldCheckMixin, ModelForm):
class RelatoriaForm(ModelForm): class RelatoriaForm(ModelForm):
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
composicao = forms.ModelChoiceField(
required=True,
empty_label='---------',
queryset=Composicao.objects.all(),
label=_('Composição')
)
class Meta: class Meta:
model = Relatoria model = Relatoria
fields = ['data_designacao_relator', 'comissao', 'parlamentar', fields = [
'data_destituicao_relator', 'tipo_fim_relatoria'] 'comissao',
'data_designacao_relator',
'data_destituicao_relator',
'tipo_fim_relatoria',
'composicao',
'parlamentar'
]
widgets = {'comissao': forms.Select(attrs={'disabled': 'disabled'})} widgets = {'comissao': forms.Select(attrs={'disabled': 'disabled'})}
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
row1 = to_row([('comissao', 12)])
row2 = to_row([('data_designacao_relator', 4),
('data_destituicao_relator', 4),
('tipo_fim_relatoria', 4)])
row3 = to_row([('composicao', 4),
('parlamentar', 8)])
self.helper = SaplFormHelper()
self.helper.layout = SaplFormLayout(
Fieldset(_('Relatoria'), row1, row2, row3))
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
comissao_pk = kwargs['initial']['comissao']
composicoes = Composicao.objects.filter(comissao_id=comissao_pk)
self.fields['composicao'].choices = [('', '---------')] + \
[(c.pk, c) for c in composicoes]
def clean(self): self.fields['parlamentar'].choices = [('', '---------')]
super(RelatoriaForm, self).clean()
if not self.is_valid(): def clean(self):
return self.cleaned_data super().clean()
cleaned_data = self.cleaned_data cleaned_data = self.cleaned_data
if not self.is_valid():
return cleaned_data
try: try:
self.logger.debug("Tentando obter objeto Comissao.") self.logger.debug("Tentando obter objeto Comissao.")
comissao = Comissao.objects.get(id=self.initial['comissao']) comissao = Comissao.objects.get(id=self.initial['comissao'])
except ObjectDoesNotExist as e: except ObjectDoesNotExist as e:
self.logger.error("Objeto Comissao não encontrado com id={} " self.logger.error(
".A localização atual deve ser uma comissão. " "Objeto Comissao não encontrado com id={}. A localização atual deve ser uma comissão. ".format(
.format(self.initial['comissao']) + str(e)) self.initial['comissao']) + str(e))
msg = _('A localização atual deve ser uma comissão.') msg = _('A localização atual deve ser uma comissão.')
raise ValidationError(msg) raise ValidationError(msg)
else: else:
@ -727,7 +756,7 @@ class AnexadaForm(ModelForm):
empty_label='Selecione', empty_label='Selecione',
) )
numero = forms.CharField(label='Número', required=True) numero = forms.IntegerField(label='Número', required=True)
ano = forms.CharField(label='Ano', required=True) ano = forms.CharField(label='Ano', required=True)
@ -751,8 +780,8 @@ class AnexadaForm(ModelForm):
ano=cleaned_data['ano'], ano=cleaned_data['ano'],
tipo=cleaned_data['tipo']) tipo=cleaned_data['tipo'])
except ObjectDoesNotExist: except ObjectDoesNotExist:
msg = _('A MateriaLegislativa a ser anexada (numero={}, ano={}, tipo={}) não existe no cadastro' msg = _('A {} {}/{} não existe no cadastro de matérias legislativas.'
' de matérias legislativas.'.format(cleaned_data['numero'], cleaned_data['ano'], cleaned_data['tipo'])) .format(cleaned_data['tipo'], cleaned_data['numero'], cleaned_data['ano']))
self.logger.error("A matéria a ser anexada não existe no cadastro" self.logger.error("A matéria a ser anexada não existe no cadastro"
" de matérias legislativas.") " de matérias legislativas.")
raise ValidationError(msg) raise ValidationError(msg)

22
sapl/materia/migrations/0044_auto_20190327_1409.py

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.20 on 2019-03-27 17:09
from __future__ import unicode_literals
from django.db import migrations, models
import sapl.materia.models
import sapl.utils
class Migration(migrations.Migration):
dependencies = [
('materia', '0043_auto_20190320_1749'),
]
operations = [
migrations.AlterField(
model_name='documentoacessorio',
name='arquivo',
field=models.FileField(blank=True, max_length=255, null=True, upload_to=sapl.materia.models.anexo_upload_path, validators=[sapl.utils.restringe_tipos_de_arquivo_txt], verbose_name='Texto Integral'),
),
]

1
sapl/materia/models.py

@ -497,6 +497,7 @@ class DocumentoAcessorio(models.Model):
arquivo = models.FileField( arquivo = models.FileField(
blank=True, blank=True,
null=True, null=True,
max_length=255,
upload_to=anexo_upload_path, upload_to=anexo_upload_path,
verbose_name=_('Texto Integral'), verbose_name=_('Texto Integral'),
validators=[restringe_tipos_de_arquivo_txt]) validators=[restringe_tipos_de_arquivo_txt])

13
sapl/materia/tests/test_materia_form.py

@ -2,6 +2,7 @@ import pytest
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from model_mommy import mommy from model_mommy import mommy
from sapl.comissoes.models import Comissao, TipoComissao
from sapl.materia import forms from sapl.materia import forms
from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa
@ -172,15 +173,23 @@ def test_valida_campos_obrigatorios_devolver_proposicao_form():
@pytest.mark.django_db(transaction=False) @pytest.mark.django_db(transaction=False)
def test_valida_campos_obrigatorios_relatoria_form(): def test_valida_campos_obrigatorios_relatoria_form():
form = forms.RelatoriaForm(data={}) tipo_comissao = mommy.make(TipoComissao)
comissao = mommy.make(Comissao,
tipo=tipo_comissao,
nome='Comissao Teste',
sigla='T',
data_criacao='2016-03-21')
form = forms.RelatoriaForm(initial={'comissao':comissao}, data={})
assert not form.is_valid() assert not form.is_valid()
errors = form.errors errors = form.errors
assert errors['parlamentar'] == [_('Este campo é obrigatório.')] assert errors['parlamentar'] == [_('Este campo é obrigatório.')]
assert errors['data_designacao_relator'] == [_('Este campo é obrigatório.')] assert errors['data_designacao_relator'] == [_('Este campo é obrigatório.')]
assert errors['composicao'] == [_('Este campo é obrigatório.')]
assert len(errors) == 2 assert len(errors) == 3
@pytest.mark.django_db(transaction=False) @pytest.mark.django_db(transaction=False)

135
sapl/materia/views.py

@ -1,5 +1,11 @@
from datetime import datetime
import logging import logging
import os
import shutil
import tempfile
import weasyprint
import itertools
from datetime import datetime
from random import choice from random import choice
from string import ascii_letters, digits from string import ascii_letters, digits
@ -45,6 +51,7 @@ from sapl.materia.forms import (AnexadaForm, AutoriaForm,
from sapl.norma.models import LegislacaoCitada from sapl.norma.models import LegislacaoCitada
from sapl.parlamentares.models import Legislatura from sapl.parlamentares.models import Legislatura
from sapl.protocoloadm.models import Protocolo from sapl.protocoloadm.models import Protocolo
from sapl.settings import MEDIA_ROOT
from sapl.utils import (YES_NO_CHOICES, autor_label, autor_modal, SEPARADOR_HASH_PROPOSICAO, from sapl.utils import (YES_NO_CHOICES, autor_label, autor_modal, SEPARADOR_HASH_PROPOSICAO,
gerar_hash_arquivo, get_base_url, gerar_hash_arquivo, get_base_url,
get_mime_type_from_file_extension, montar_row_autor, get_mime_type_from_file_extension, montar_row_autor,
@ -1104,55 +1111,13 @@ class RelatoriaCrud(MasterDetailCrud):
class CreateView(MasterDetailCrud.CreateView): class CreateView(MasterDetailCrud.CreateView):
form_class = RelatoriaForm form_class = RelatoriaForm
layout_key = None
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
username = self.request.user.username
try:
self.logger.debug("user=" + username + ". Tentando obter objeto Comissao de pk={}.".format(
context['form'].initial['comissao']))
comissao = Comissao.objects.get(
pk=context['form'].initial['comissao'])
except:
self.logger.error("user=" + username + ". Objeto Comissão de pk={} não encontrado.".format(
context['form'].initial['comissao']))
pass
else:
self.logger.info("user=" + username + ". Objeto Comissao de pk={} obtido com sucesso.".format(
context['form'].initial['comissao']))
materia = MateriaLegislativa.objects.get(
pk=self.kwargs.get('pk'))
data_materia = materia.data_apresentacao
comissao = Comissao.objects.get(
pk=context['form'].initial['comissao'])
composicao = comissao.composicao_set.filter(
Q(periodo__data_fim__isnull=False,
periodo__data_inicio__lte=data_materia,
periodo__data_fim__gte=data_materia) |
Q(periodo__data_fim__isnull=True,
periodo__data_inicio__lte=data_materia)
)
participacoes = Participacao.objects.select_related().filter(composicao=composicao)
parlamentares = [('', '---------')] + [
(participacao.parlamentar.id, participacao.parlamentar.nome_parlamentar) for participacao in
participacoes if participacao.titular]
context['form'].fields['parlamentar'].choices = parlamentares
return context
def get_initial(self): def get_initial(self):
materia = MateriaLegislativa.objects.get(id=self.kwargs['pk']) materia = MateriaLegislativa.objects.get(id=self.kwargs['pk'])
loc_atual = Tramitacao.objects.filter( loc_atual = Tramitacao.objects.filter(materia=materia).last()
materia=materia).last()
if loc_atual is None: if loc_atual is None:
localizacao = 0 localizacao = 0
@ -1167,47 +1132,9 @@ class RelatoriaCrud(MasterDetailCrud):
class UpdateView(MasterDetailCrud.UpdateView): class UpdateView(MasterDetailCrud.UpdateView):
form_class = RelatoriaForm form_class = RelatoriaForm
layout_key = None
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
username = self.request.user.username
try:
self.logger.debug("user=" + username + ". Tentando obter objeto Comissao de pk={}.".format(
context['form'].initial['comissao']))
comissao = Comissao.objects.get(
pk=context['form'].initial['comissao'])
except ObjectDoesNotExist:
self.logger.error("user=" + username + ". Objeto Comissão de pk={} não encontrado.".format(
context['form'].initial['comissao']))
pass
else:
self.logger.info("user=" + username + ". Objeto Comissao de pk={} obtido com sucesso.".format(
context['form'].initial['comissao']))
relatoria = Relatoria.objects.select_related(
'materia').get(pk=self.kwargs.get('pk'))
ano_materia = relatoria.materia.ano
comissao = Comissao.objects.get(
pk=context['form'].initial['comissao'])
composicoes = comissao.composicao_set.all()
composicao = comissao.composicao_set.filter(
periodo__data_inicio__year=ano_materia)
participacoes = Participacao.objects.select_related().filter(composicao=composicao)
parlamentares = [('', '---------')] + [
(participacao.parlamentar.id, participacao.parlamentar.nome_parlamentar) for participacao in
participacoes if participacao.titular]
context['form'].fields['parlamentar'].choices = parlamentares
return context
class TramitacaoCrud(MasterDetailCrud): class TramitacaoCrud(MasterDetailCrud):
model = Tramitacao model = Tramitacao
@ -2035,17 +1962,35 @@ class DocumentoAcessorioEmLoteView(PermissionRequiredMixin, FilterView):
messages.add_message(request, messages.ERROR, msg) messages.add_message(request, messages.ERROR, msg)
return self.get(request, self.kwargs) return self.get(request, self.kwargs)
tmp_name = os.path.join(tempfile.gettempdir(), request.FILES['arquivo'].name)
with open(tmp_name, 'wb') as destination:
for chunk in request.FILES['arquivo'].chunks():
destination.write(chunk)
doc_data = tz.localize(datetime.strptime(
request.POST['data'], "%d/%m/%Y"))
for materia_id in marcadas: for materia_id in marcadas:
doc = DocumentoAcessorio() doc = DocumentoAcessorio()
doc.materia_id = materia_id doc.materia_id = materia_id
doc.tipo = tipo doc.tipo = tipo
doc.arquivo = request.FILES['arquivo']
doc.nome = request.POST['nome'] doc.nome = request.POST['nome']
doc.data = tz.localize(datetime.strptime( doc.data = doc_data
request.POST['data'], "%d/%m/%Y"))
doc.autor = request.POST['autor'] doc.autor = request.POST['autor']
doc.ementa = request.POST['ementa'] doc.ementa = request.POST['ementa']
doc.save() doc.save()
diretorio = os.path.join(MEDIA_ROOT,
'sapl/public/documentoacessorio',
str(doc_data.year),
str(doc.id))
if not os.path.exists(diretorio):
os.makedirs(diretorio)
file_path = os.path.join(diretorio,
request.FILES['arquivo'].name)
shutil.copy2(tmp_name, file_path)
doc.arquivo.name = file_path.split(MEDIA_ROOT)[1] # Retira MEDIA_ROOT do nome
doc.save()
os.remove(tmp_name)
msg = _('Documento(s) criado(s).') msg = _('Documento(s) criado(s).')
messages.add_message(request, messages.SUCCESS, msg) messages.add_message(request, messages.SUCCESS, msg)
return self.get(request, self.kwargs) return self.get(request, self.kwargs)
@ -2226,7 +2171,17 @@ class PrimeiraTramitacaoEmLoteView(PermissionRequiredMixin, FilterView):
# TODO: usar Form # TODO: usar Form
urgente = request.POST['urgente'] == 'True' urgente = request.POST['urgente'] == 'True'
flag_error = False flag_error = False
for materia_id in marcadas:
materias_principais = [m for m in MateriaLegislativa.objects.filter(id__in=marcadas)]
materias_anexadas = [m.anexadas.all() for m in MateriaLegislativa.objects.filter(id__in=marcadas) if m.anexadas.all()]
materias_anexadas = list(itertools.chain.from_iterable(materias_anexadas))
tramitacao_local = int(request.POST['unidade_tramitacao_local'])
materias_anexadas = list(filter(lambda ma : not ma.tramitacao_set.all() or \
ma.tramitacao_set.last().unidade_tramitacao_destino.id == tramitacao_local,
materias_anexadas))
materias = set(materias_principais + materias_anexadas)
for materia in materias:
try: try:
data_tramitacao = tz.localize(datetime.strptime( data_tramitacao = tz.localize(datetime.strptime(
request.POST['data_tramitacao'], "%d/%m/%Y")) request.POST['data_tramitacao'], "%d/%m/%Y"))
@ -2236,7 +2191,7 @@ class PrimeiraTramitacaoEmLoteView(PermissionRequiredMixin, FilterView):
return self.get(request, self.kwargs) return self.get(request, self.kwargs)
t = Tramitacao( t = Tramitacao(
materia_id=materia_id, materia=materia,
data_tramitacao=data_tramitacao, data_tramitacao=data_tramitacao,
data_encaminhamento=data_encaminhamento, data_encaminhamento=data_encaminhamento,
data_fim_prazo=data_fim_prazo, data_fim_prazo=data_fim_prazo,
@ -2270,7 +2225,7 @@ class PrimeiraTramitacaoEmLoteView(PermissionRequiredMixin, FilterView):
status = StatusTramitacao.objects.get(id=request.POST['status']) status = StatusTramitacao.objects.get(id=request.POST['status'])
for materia in MateriaLegislativa.objects.filter(id__in=marcadas): for materia in materias:
if status.indicador == 'F': if status.indicador == 'F':
materia.em_tramitacao = False materia.em_tramitacao = False
elif self.primeira_tramitacao: elif self.primeira_tramitacao:

62
sapl/parlamentares/forms.py

@ -525,3 +525,65 @@ class VotanteForm(ModelForm):
votante.user = u votante.user = u
votante.save() votante.save()
return votante return votante
class VincularParlamentarForm(forms.Form):
logger = logging.getLogger(__name__)
parlamentar = forms.ModelChoiceField(
label=Parlamentar._meta.verbose_name,
queryset=Parlamentar.objects.filter(ativo=True),
required=True,
empty_label='Selecione'
)
legislatura = forms.ModelChoiceField(
label=Legislatura._meta.verbose_name,
queryset=Legislatura.objects.all(),
required=True,
empty_label='Selecione'
)
data_expedicao_diploma = forms.DateField(
label='Data de Expedição do Diploma',
required=False,
widget=forms.DateInput(format='%d/%m/%Y')
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
row1 = to_row([
('parlamentar', 6),
('legislatura', 3),
('data_expedicao_diploma', 3)
])
self.helper = SaplFormHelper()
self.helper.layout = Layout(
Fieldset(
'Vincular Parlamentar',
row1,
form_actions(label='Vincular')
)
)
def clean(self):
super().clean()
if not self.is_valid():
return self.cleaned_data
cleaned_data = self.cleaned_data
parlamentar = cleaned_data['parlamentar']
legislatura = cleaned_data['legislatura']
data_expedicao_diploma = cleaned_data['data_expedicao_diploma']
if parlamentar.mandato_set.filter(legislatura=legislatura):
self.logger.error('Parlamentar já está vinculado a legislatura informada.')
raise ValidationError(_('Parlamentar já está vinculado a legislatura informada.'))
elif data_expedicao_diploma and legislatura.data_inicio <= data_expedicao_diploma:
self.logger.error('Data da Expedição do Diploma deve ser anterior a data de início da Legislatura.')
raise ValidationError(_('Data da Expedição do Diploma deve ser anterior a data de início da Legislatura.'))
return cleaned_data

5
sapl/parlamentares/urls.py

@ -19,7 +19,7 @@ from sapl.parlamentares.views import (CargoMesaCrud, ColigacaoCrud,
parlamentares_frente_selected, parlamentares_frente_selected,
remove_parlamentar_composicao, remove_parlamentar_composicao,
parlamentares_filiados, parlamentares_filiados,
PesquisarParlamentarView) PesquisarParlamentarView, VincularParlamentarView)
from .apps import AppConfig from .apps import AppConfig
@ -41,6 +41,9 @@ urlpatterns = [
url(r'^parlamentar/(?P<pk>\d+)/materias$', url(r'^parlamentar/(?P<pk>\d+)/materias$',
ParlamentarMateriasView.as_view(), name='parlamentar_materias'), ParlamentarMateriasView.as_view(), name='parlamentar_materias'),
url(r'^parlamentar/vincular-parlamentar/$',
VincularParlamentarView.as_view(), name='vincular_parlamentar'),
url(r'^sistema/coligacao/', url(r'^sistema/coligacao/',
include(ColigacaoCrud.get_urls() + include(ColigacaoCrud.get_urls() +
ComposicaoColigacaoCrud.get_urls())), ComposicaoColigacaoCrud.get_urls())),

30
sapl/parlamentares/views.py

@ -3,6 +3,7 @@ import json
import logging import logging
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist
from django.core.urlresolvers import reverse, reverse_lazy from django.core.urlresolvers import reverse, reverse_lazy
@ -33,7 +34,7 @@ from sapl.parlamentares.apps import AppConfig
from sapl.utils import (parlamentares_ativos, show_results_filter_set) from sapl.utils import (parlamentares_ativos, show_results_filter_set)
from .forms import (FiliacaoForm, FrenteForm, LegislaturaForm, MandatoForm, from .forms import (FiliacaoForm, FrenteForm, LegislaturaForm, MandatoForm,
ParlamentarCreateForm, ParlamentarForm, VotanteForm, ParlamentarFilterSet) ParlamentarCreateForm, ParlamentarForm, VotanteForm, ParlamentarFilterSet, VincularParlamentarForm)
from .models import (CargoMesa, Coligacao, ComposicaoColigacao, ComposicaoMesa, from .models import (CargoMesa, Coligacao, ComposicaoColigacao, ComposicaoMesa,
Dependente, Filiacao, Frente, Legislatura, Mandato, Dependente, Filiacao, Frente, Legislatura, Mandato,
NivelInstrucao, Parlamentar, Partido, SessaoLegislativa, NivelInstrucao, Parlamentar, Partido, SessaoLegislativa,
@ -1132,3 +1133,30 @@ def altera_field_mesa_public_view(request):
'lista_fotos': lista_fotos, 'lista_fotos': lista_fotos,
'sessao_selecionada': sessao_selecionada, 'sessao_selecionada': sessao_selecionada,
'msg': ('', 1)}) 'msg': ('', 1)})
class VincularParlamentarView(PermissionRequiredMixin, FormView):
logger = logging.getLogger(__name__)
form_class = VincularParlamentarForm
template_name = 'parlamentares/vincular_parlamentar.html'
permission_required = ('parlamentares.add_parlamentar', )
def get_success_url(self):
return reverse('sapl.parlamentares:parlamentar_list')
def form_valid(self, form):
kwargs = {
'parlamentar': form.cleaned_data['parlamentar'],
'legislatura': form.cleaned_data['legislatura'],
'data_inicio_mandato': form.cleaned_data['legislatura'].data_inicio,
'data_fim_mandato': form.cleaned_data['legislatura'].data_fim
}
data_expedicao_diploma = form.cleaned_data.get('data_expedicao_diploma')
if data_expedicao_diploma:
kwargs.update({'data_expedicao_diploma': data_expedicao_diploma})
mandato = Mandato.objects.create(**kwargs)
mandato.save()
return HttpResponseRedirect(self.get_success_url())

24
sapl/relatorios/templates/pdf_sessao_plenaria_gerar.py

@ -340,6 +340,24 @@ def votacao_vot_nom(lst_votacao_vot_nom):
return tmp return tmp
def oradores_ordemdia(lst_oradores_ordemdia):
"""
"""
tmp = ''
tmp += '\t\t<para style="P1">Oradores da Ordem do Dia</para>\n'
tmp += '\t\t<para style="P2">\n'
tmp += '\t\t\t<font color="white"> <br/></font>\n'
tmp += '\t\t</para>\n'
for orador_ordemdia in lst_oradores_ordemdia:
tmp += '\t\t<para style="P2" spaceAfter="5"><b>' + \
str(orador_ordemdia['num_ordem']) + '</b> - ' + \
orador_ordemdia['nome_parlamentar'] + '/' + \
str(orador_ordemdia['sigla']) + ' - ' + \
str(orador_ordemdia['observacao']) + '</para>\n'
return tmp
def oradores(lst_oradores): def oradores(lst_oradores):
""" """
@ -374,7 +392,7 @@ def ocorrencias(lst_ocorrencias):
return tmp return tmp
def principal(rodape_dic, imagem, inf_basicas_dic, lst_mesa, lst_presenca_sessao, lst_ausencia_sessao, lst_expedientes, lst_expediente_materia, lst_expediente_materia_vot_nom, lst_oradores_expediente, lst_presenca_ordem_dia, lst_votacao, lst_votacao_vot_nom, lst_oradores, lst_ocorrencias): def principal(rodape_dic, imagem, inf_basicas_dic, lst_mesa, lst_presenca_sessao, lst_ausencia_sessao, lst_expedientes, lst_expediente_materia, lst_expediente_materia_vot_nom, lst_oradores_expediente, lst_presenca_ordem_dia, lst_votacao, lst_votacao_vot_nom, lst_oradores_ordemdia, lst_oradores, lst_ocorrencias):
""" """
""" """
arquivoPdf = str(int(time.time() * 100)) + ".pdf" arquivoPdf = str(int(time.time() * 100)) + ".pdf"
@ -409,6 +427,7 @@ def principal(rodape_dic, imagem, inf_basicas_dic, lst_mesa, lst_presenca_sessao
'v_n_mat_o_d': votacao_vot_nom(lst_votacao_vot_nom), 'v_n_mat_o_d': votacao_vot_nom(lst_votacao_vot_nom),
'mesa_d': mesa(lst_mesa), 'mesa_d': mesa(lst_mesa),
'oradores_exped': oradores_expediente(lst_oradores_expediente), 'oradores_exped': oradores_expediente(lst_oradores_expediente),
'oradores_o_d': oradores_ordemdia(lst_oradores_ordemdia),
'oradores_expli': oradores(lst_oradores), 'oradores_expli': oradores(lst_oradores),
'ocorr_sessao': ocorrencias(lst_ocorrencias) 'ocorr_sessao': ocorrencias(lst_ocorrencias)
} }
@ -428,6 +447,7 @@ def principal(rodape_dic, imagem, inf_basicas_dic, lst_mesa, lst_presenca_sessao
tmp += dict_ord_template[ordenacao.decimo_primeiro] tmp += dict_ord_template[ordenacao.decimo_primeiro]
tmp += dict_ord_template[ordenacao.decimo_segundo] tmp += dict_ord_template[ordenacao.decimo_segundo]
tmp += dict_ord_template[ordenacao.decimo_terceiro] tmp += dict_ord_template[ordenacao.decimo_terceiro]
tmp += dict_ord_template[ordenacao.decimo_quarto]
except KeyError as e: except KeyError as e:
logger.error("KeyError: " + str(e) + ". Erro ao tentar utilizar " logger.error("KeyError: " + str(e) + ". Erro ao tentar utilizar "
"configuração de ordenação. Utilizando ordenação padrão.") "configuração de ordenação. Utilizando ordenação padrão.")
@ -441,6 +461,7 @@ def principal(rodape_dic, imagem, inf_basicas_dic, lst_mesa, lst_presenca_sessao
tmp += presenca_ordem_dia(lst_presenca_ordem_dia) tmp += presenca_ordem_dia(lst_presenca_ordem_dia)
tmp += votacao(lst_votacao) tmp += votacao(lst_votacao)
tmp += votacao_vot_nom(lst_votacao_vot_nom) tmp += votacao_vot_nom(lst_votacao_vot_nom)
tmp += oradores_ordemdia(lst_oradores_ordemdia)
tmp += oradores(lst_oradores) tmp += oradores(lst_oradores)
tmp += ocorrencias(lst_ocorrencias) tmp += ocorrencias(lst_ocorrencias)
@ -455,6 +476,7 @@ def principal(rodape_dic, imagem, inf_basicas_dic, lst_mesa, lst_presenca_sessao
tmp += presenca_ordem_dia(lst_presenca_ordem_dia) tmp += presenca_ordem_dia(lst_presenca_ordem_dia)
tmp += votacao(lst_votacao) tmp += votacao(lst_votacao)
tmp += votacao_vot_nom(lst_votacao_vot_nom) tmp += votacao_vot_nom(lst_votacao_vot_nom)
tmp += oradores_ordemdia(lst_oradores_ordemdia)
tmp += oradores(lst_oradores) tmp += oradores(lst_oradores)
tmp += ocorrencias(lst_ocorrencias) tmp += ocorrencias(lst_ocorrencias)

41
sapl/relatorios/views.py

@ -23,7 +23,7 @@ from sapl.sessao.models import (ExpedienteMateria, ExpedienteSessao,
Orador, OradorExpediente, Orador, OradorExpediente,
OrdemDia, PresencaOrdemDia, SessaoPlenaria, OrdemDia, PresencaOrdemDia, SessaoPlenaria,
SessaoPlenariaPresenca, OcorrenciaSessao, SessaoPlenariaPresenca, OcorrenciaSessao,
RegistroVotacao, VotoParlamentar) RegistroVotacao, VotoParlamentar, OradorOrdemDia)
from sapl.settings import STATIC_ROOT from sapl.settings import STATIC_ROOT
from sapl.utils import LISTA_DE_UFS, TrocaTag, filiacao_data from sapl.utils import LISTA_DE_UFS, TrocaTag, filiacao_data
@ -31,7 +31,8 @@ from sapl.sessao.views import (get_identificação_basica, get_mesa_diretora,
get_presenca_sessao, get_expedientes, get_presenca_sessao, get_expedientes,
get_materias_expediente, get_oradores_expediente, get_materias_expediente, get_oradores_expediente,
get_presenca_ordem_do_dia, get_materias_ordem_do_dia, get_presenca_ordem_do_dia, get_materias_ordem_do_dia,
get_oradores_explicações_pessoais, get_ocorrencias_da_sessão) get_oradores_ordemdia,
get_oradores_explicações_pessoais, get_ocorrencias_da_sessão, get_assinaturas)
from .templates import (pdf_capa_processo_gerar, from .templates import (pdf_capa_processo_gerar,
pdf_documento_administrativo_gerar, pdf_espelho_gerar, pdf_documento_administrativo_gerar, pdf_espelho_gerar,
@ -578,6 +579,8 @@ def get_sessao_plenaria(sessao, casa):
# unescape HTML codes # unescape HTML codes
# https://github.com/interlegis/sapl/issues/1046 # https://github.com/interlegis/sapl/issues/1046
conteudo = re.sub('style=".*?"', '', conteudo) conteudo = re.sub('style=".*?"', '', conteudo)
conteudo = re.sub('class=".*?"', '', conteudo)
conteudo = re.sub('<p\s+>', '<p>', conteudo)
conteudo = html.unescape(conteudo) conteudo = html.unescape(conteudo)
# escape special character '&' # escape special character '&'
@ -778,6 +781,35 @@ def get_sessao_plenaria(sessao, casa):
} }
lst_votacao_vot_nom.append(dic_votacao_vot_nom) lst_votacao_vot_nom.append(dic_votacao_vot_nom)
# Lista dos oradores da Ordem do Dia
lst_oradores_ordemdia = []
oradores_ordem_dia = OradorOrdemDia.objects.filter(
sessao_plenaria=sessao
).order_by('numero_ordem')
for orador_ordemdia in oradores_ordem_dia:
parlamentar_orador = Parlamentar.objects.get(
id=orador_ordemdia.parlamentar.id
)
sigla_partido = Filiacao.objects.filter(
parlamentar=parlamentar_orador
).first()
if not sigla_partido:
sigla_p = ""
else:
sigla_p = sigla_partido.partido.sigla
dic_oradores_ordemdia = {
'num_ordem': orador_ordemdia.numero_ordem,
'nome_parlamentar': parlamentar_orador.nome_parlamentar,
'observacao': orador_ordemdia.observacao,
'sigla': sigla_p
}
lst_oradores_ordemdia.append(dic_oradores_ordemdia)
# Lista dos oradores nas Explicações Pessoais # Lista dos oradores nas Explicações Pessoais
lst_oradores = [] lst_oradores = []
for orador in Orador.objects.filter( for orador in Orador.objects.filter(
@ -828,6 +860,7 @@ def get_sessao_plenaria(sessao, casa):
lst_presenca_ordem_dia, lst_presenca_ordem_dia,
lst_votacao, lst_votacao,
lst_votacao_vot_nom, lst_votacao_vot_nom,
lst_oradores_ordemdia,
lst_oradores, lst_oradores,
lst_ocorrencias) lst_ocorrencias)
@ -885,6 +918,7 @@ def relatorio_sessao_plenaria(request, pk):
lst_presenca_ordem_dia, lst_presenca_ordem_dia,
lst_votacao, lst_votacao,
lst_votacao_vot_nom, lst_votacao_vot_nom,
lst_oradores_ordemdia,
lst_oradores, lst_oradores,
lst_ocorrencias) = get_sessao_plenaria(sessao, casa) lst_ocorrencias) = get_sessao_plenaria(sessao, casa)
@ -908,6 +942,7 @@ def relatorio_sessao_plenaria(request, pk):
lst_presenca_ordem_dia, lst_presenca_ordem_dia,
lst_votacao, lst_votacao,
lst_votacao_vot_nom, lst_votacao_vot_nom,
lst_oradores_ordemdia,
lst_oradores, lst_oradores,
lst_ocorrencias) lst_ocorrencias)
@ -1254,8 +1289,10 @@ def resumo_ata_pdf(request,pk):
context.update(get_oradores_expediente(sessao_plenaria)) context.update(get_oradores_expediente(sessao_plenaria))
context.update(get_presenca_ordem_do_dia(sessao_plenaria)) context.update(get_presenca_ordem_do_dia(sessao_plenaria))
context.update(get_materias_ordem_do_dia(sessao_plenaria)) context.update(get_materias_ordem_do_dia(sessao_plenaria))
context.update(get_oradores_ordemdia(sessao_plenaria))
context.update(get_oradores_explicações_pessoais(sessao_plenaria)) context.update(get_oradores_explicações_pessoais(sessao_plenaria))
context.update(get_ocorrencias_da_sessão(sessao_plenaria)) context.update(get_ocorrencias_da_sessão(sessao_plenaria))
context.update(get_assinaturas(sessao_plenaria))
context.update({'object': sessao_plenaria}) context.update({'object': sessao_plenaria})
context.update({'data': dt.today().strftime('%d/%m/%Y')}) context.update({'data': dt.today().strftime('%d/%m/%Y')})
context.update({'rodape': rodape}) context.update({'rodape': rodape})

1
sapl/rules/map_rules.py

@ -175,6 +175,7 @@ rules_group_sessao = {
(sessao.ExpedienteSessao, __base__, __perms_publicas__), (sessao.ExpedienteSessao, __base__, __perms_publicas__),
(sessao.Orador, __base__, __perms_publicas__), (sessao.Orador, __base__, __perms_publicas__),
(sessao.OradorExpediente, __base__, __perms_publicas__), (sessao.OradorExpediente, __base__, __perms_publicas__),
(sessao.OradorOrdemDia, __base__, __perms_publicas__),
(sessao.OrdemDia, __base__, __perms_publicas__), (sessao.OrdemDia, __base__, __perms_publicas__),
(sessao.PresencaOrdemDia, __base__, __perms_publicas__), (sessao.PresencaOrdemDia, __base__, __perms_publicas__),
(sessao.RegistroVotacao, __base__, __perms_publicas__), (sessao.RegistroVotacao, __base__, __perms_publicas__),

77
sapl/sessao/forms.py

@ -26,7 +26,7 @@ from sapl.utils import (RANGE_DIAS_MES, RANGE_MESES,
from .models import (Bancada, Bloco, ExpedienteMateria, JustificativaAusencia, from .models import (Bancada, Bloco, ExpedienteMateria, JustificativaAusencia,
Orador, OradorExpediente, OrdemDia, PresencaOrdemDia, SessaoPlenaria, Orador, OradorExpediente, OrdemDia, PresencaOrdemDia, SessaoPlenaria,
SessaoPlenariaPresenca, TipoResultadoVotacao, SessaoPlenariaPresenca, TipoResultadoVotacao,
OcorrenciaSessao, RetiradaPauta, TipoRetiradaPauta) OcorrenciaSessao, RetiradaPauta, TipoRetiradaPauta, OradorOrdemDia)
MES_CHOICES = RANGE_MESES MES_CHOICES = RANGE_MESES
@ -45,7 +45,8 @@ ORDENACAO_RESUMO = [('cont_mult', 'Conteúdo Multimídia'),
('oradores_expli', 'Oradores das Explicações Pessoais'), ('oradores_expli', 'Oradores das Explicações Pessoais'),
('ocorr_sessao', 'Ocorrências da Sessão'), ('ocorr_sessao', 'Ocorrências da Sessão'),
('v_n_mat_exp', 'Votações Nominais - Matérias do Expediente'), ('v_n_mat_exp', 'Votações Nominais - Matérias do Expediente'),
('v_n_mat_o_d', 'Votações Nominais - Matérias da Ordem do Dia')] ('v_n_mat_o_d', 'Votações Nominais - Matérias da Ordem do Dia'),
('oradores_o_d', 'Oradores da Ordem do Dia')]
class SessaoPlenariaForm(FileFieldCheckMixin, ModelForm): class SessaoPlenariaForm(FileFieldCheckMixin, ModelForm):
@ -693,6 +694,28 @@ class OradorForm(ModelForm):
self.fields['parlamentar'].queryset = Parlamentar.objects.filter( self.fields['parlamentar'].queryset = Parlamentar.objects.filter(
id__in=ids).order_by('nome_parlamentar') id__in=ids).order_by('nome_parlamentar')
def clean(self):
super(OradorForm, self).clean()
cleaned_data = self.cleaned_data
if not self.is_valid():
return self.cleaned_data
sessao_id = self.initial['id_sessao']
numero = self.initial.get('numero')
numero_ordem = cleaned_data['numero_ordem']
ordem = Orador.objects.filter(
sessao_plenaria_id=sessao_id,
numero_ordem=numero_ordem
).exists()
if ordem and numero_ordem != numero:
raise ValidationError(_(
"Já existe orador nesta posição de ordem de pronunciamento"
))
return self.cleaned_data
class Meta: class Meta:
model = Orador model = Orador
exclude = ['sessao_plenaria'] exclude = ['sessao_plenaria']
@ -736,6 +759,49 @@ class OradorExpedienteForm(ModelForm):
exclude = ['sessao_plenaria'] exclude = ['sessao_plenaria']
class OradorOrdemDiaForm(ModelForm):
def __init__(self, *args, **kwargs):
super(OradorOrdemDiaForm, self).__init__(*args, **kwargs)
id_sessao = int(self.initial['id_sessao'])
ids = [p.parlamentar.id for p in PresencaOrdemDia.objects.filter(
sessao_plenaria_id=id_sessao
)]
self.fields['parlamentar'].queryset = Parlamentar.objects.filter(
id__in=ids
).order_by('nome_parlamentar')
def clean(self):
super(OradorOrdemDiaForm, self).clean()
cleaned_data = self.cleaned_data
if not self.is_valid():
return self.cleaned_data
sessao_id = self.initial['id_sessao']
numero = self.initial.get('numero')
numero_ordem = cleaned_data['numero_ordem']
ordem = OradorOrdemDia.objects.filter(
sessao_plenaria_id=sessao_id,
numero_ordem=numero_ordem
).exists()
if ordem and numero_ordem != numero:
raise ValidationError(_(
"Já existe orador nesta posição de ordem de pronunciamento"
))
return self.cleaned_data
class Meta:
model = OradorOrdemDia
exclude = ['sessao_plenaria']
class PautaSessaoFilterSet(SessaoPlenariaFilterSet): class PautaSessaoFilterSet(SessaoPlenariaFilterSet):
titulo = _('Pesquisa de Pauta de Sessão') titulo = _('Pesquisa de Pauta de Sessão')
@ -767,6 +833,8 @@ class ResumoOrdenacaoForm(forms.Form):
choices=ORDENACAO_RESUMO) choices=ORDENACAO_RESUMO)
decimo_terceiro = forms.ChoiceField(label='13°', decimo_terceiro = forms.ChoiceField(label='13°',
choices=ORDENACAO_RESUMO) choices=ORDENACAO_RESUMO)
decimo_quarto = forms.ChoiceField(label='14°',
choices=ORDENACAO_RESUMO)
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(ResumoOrdenacaoForm, self).__init__(*args, **kwargs) super(ResumoOrdenacaoForm, self).__init__(*args, **kwargs)
@ -797,13 +865,16 @@ class ResumoOrdenacaoForm(forms.Form):
[('decimo_segundo', 12)]) [('decimo_segundo', 12)])
row13 = to_row( row13 = to_row(
[('decimo_terceiro', 12)]) [('decimo_terceiro', 12)])
row14 = to_row(
[('decimo_quarto', 12)]
)
self.helper = SaplFormHelper() self.helper = SaplFormHelper()
self.helper.layout = Layout( self.helper.layout = Layout(
Fieldset(_(''), Fieldset(_(''),
row1, row2, row3, row4, row5, row1, row2, row3, row4, row5,
row6, row7, row8, row9, row10, row6, row7, row8, row9, row10,
row11, row12, row13, row11, row12, row13, row14,
form_actions(label='Atualizar')) form_actions(label='Atualizar'))
) )

34
sapl/sessao/migrations/0034_oradorordemdia.py

@ -0,0 +1,34 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.20 on 2019-03-26 16:14
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
import sapl.sessao.models
class Migration(migrations.Migration):
dependencies = [
('parlamentares', '0025_auto_20180924_1724'),
('sessao', '0033_auto_20190228_1803'),
]
operations = [
migrations.CreateModel(
name='OradorOrdemDia',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('numero_ordem', models.PositiveIntegerField(verbose_name='Ordem de pronunciamento')),
('url_discurso', models.URLField(blank=True, max_length=150, verbose_name='URL Vídeo')),
('observacao', models.CharField(blank=True, max_length=150, verbose_name='Observação')),
('upload_anexo', models.FileField(blank=True, null=True, upload_to=sapl.sessao.models.anexo_upload_path, verbose_name='Anexo do Orador')),
('parlamentar', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='parlamentares.Parlamentar', verbose_name='Parlamentar')),
('sessao_plenaria', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sessao.SessaoPlenaria')),
],
options={
'verbose_name_plural': 'Oradores da Ordem do Dia',
'verbose_name': 'Orador da Ordem do Dia',
},
),
]

20
sapl/sessao/migrations/0035_resumoordenacao_decimo_quarto.py

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.20 on 2019-03-26 18:14
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('sessao', '0034_oradorordemdia'),
]
operations = [
migrations.AddField(
model_name='resumoordenacao',
name='decimo_quarto',
field=models.CharField(default='Oradores da Ordem do Dia', max_length=30),
),
]

9
sapl/sessao/models.py

@ -378,6 +378,14 @@ class OradorExpediente(AbstractOrador): # OradoresExpediente
verbose_name_plural = _('Oradores do Expediente') verbose_name_plural = _('Oradores do Expediente')
@reversion.register()
class OradorOrdemDia(AbstractOrador): # OradoresOrdemDia
class Meta:
verbose_name = _('Orador da Ordem do Dia')
verbose_name_plural = _('Oradores da Ordem do Dia')
@reversion.register() @reversion.register()
class OrdemDia(AbstractOrdemDia): class OrdemDia(AbstractOrdemDia):
@ -579,6 +587,7 @@ class ResumoOrdenacao(models.Model):
decimo_primeiro = models.CharField(max_length=30,default="Ocorrências da Sessão") decimo_primeiro = models.CharField(max_length=30,default="Ocorrências da Sessão")
decimo_segundo = models.CharField(max_length=30, default="Votos Nominais Mat Expediente") decimo_segundo = models.CharField(max_length=30, default="Votos Nominais Mat Expediente")
decimo_terceiro = models.CharField(max_length=30, default="Votos Nominais Mat Ordem Dia") decimo_terceiro = models.CharField(max_length=30, default="Votos Nominais Mat Ordem Dia")
decimo_quarto = models.CharField(max_length=30, default="Oradores da Ordem do Dia")
class Meta: class Meta:
verbose_name = _('Ordenação do Resumo de uma Sessão') verbose_name = _('Ordenação do Resumo de uma Sessão')

13
sapl/sessao/urls.py

@ -4,7 +4,8 @@ from sapl.sessao.views import (AdicionarVariasMateriasExpediente,
AdicionarVariasMateriasOrdemDia, BancadaCrud, AdicionarVariasMateriasOrdemDia, BancadaCrud,
BlocoCrud, CargoBancadaCrud, BlocoCrud, CargoBancadaCrud,
ExpedienteMateriaCrud, ExpedienteView, JustificativaAusenciaCrud, ExpedienteMateriaCrud, ExpedienteView, JustificativaAusenciaCrud,
OcorrenciaSessaoView, MateriaOrdemDiaCrud, MesaView, OradorCrud, OcorrenciaSessaoView, MateriaOrdemDiaCrud, OradorOrdemDiaCrud,
MesaView, OradorCrud,
OradorExpedienteCrud, PainelView, OradorExpedienteCrud, PainelView,
PautaSessaoDetailView, PautaSessaoView, PautaSessaoDetailView, PautaSessaoView,
PesquisarPautaSessaoView, PesquisarPautaSessaoView,
@ -28,6 +29,8 @@ from sapl.sessao.views import (AdicionarVariasMateriasExpediente,
remove_parlamentar_composicao, remove_parlamentar_composicao,
reordernar_materias_expediente, reordernar_materias_expediente,
reordernar_materias_ordem, reordernar_materias_ordem,
renumerar_materias_ordem,
renumerar_materias_expediente,
sessao_legislativa_legislatura_ajax, sessao_legislativa_legislatura_ajax,
VotacaoEmBlocoOrdemDia, VotacaoEmBlocoExpediente, VotacaoEmBlocoOrdemDia, VotacaoEmBlocoExpediente,
VotacaoEmBlocoSimbolicaView, VotacaoEmBlocoNominalView) VotacaoEmBlocoSimbolicaView, VotacaoEmBlocoNominalView)
@ -42,7 +45,9 @@ urlpatterns = [
OradorExpedienteCrud.get_urls() + OradorExpedienteCrud.get_urls() +
ExpedienteMateriaCrud.get_urls() + ExpedienteMateriaCrud.get_urls() +
JustificativaAusenciaCrud.get_urls() + JustificativaAusenciaCrud.get_urls() +
MateriaOrdemDiaCrud.get_urls() + RetiradaPautaCrud.get_urls())), MateriaOrdemDiaCrud.get_urls() +
OradorOrdemDiaCrud.get_urls() +
RetiradaPautaCrud.get_urls())),
url(r'^sessao/(?P<pk>\d+)/mesa$', MesaView.as_view(), name='mesa'), url(r'^sessao/(?P<pk>\d+)/mesa$', MesaView.as_view(), name='mesa'),
@ -75,6 +80,10 @@ urlpatterns = [
name="reordenar_expediente"), name="reordenar_expediente"),
url(r'^sessao/(?P<pk>\d+)/reordenar-ordem$', reordernar_materias_ordem, url(r'^sessao/(?P<pk>\d+)/reordenar-ordem$', reordernar_materias_ordem,
name="reordenar_ordem"), name="reordenar_ordem"),
url(r'^sessao/(?P<pk>\d+)/renumerar-ordem$', renumerar_materias_ordem,
name="renumerar_ordem"),
url(r'^sessao/(?P<pk>\d+)/renumerar-materias-expediente$', renumerar_materias_expediente,
name="renumerar_materias_expediente"),
url(r'^sistema/sessao-plenaria/tipo/', url(r'^sistema/sessao-plenaria/tipo/',
include(TipoSessaoCrud.get_urls())), include(TipoSessaoCrud.get_urls())),
url(r'^sistema/sessao-plenaria/tipo-resultado-votacao/', url(r'^sistema/sessao-plenaria/tipo-resultado-votacao/',

115
sapl/sessao/views.py

@ -42,14 +42,14 @@ from .forms import (AdicionarVariasMateriasFilterSet, BancadaForm, BlocoForm,
MesaForm, OradorExpedienteForm, OradorForm, PautaSessaoFilterSet, MesaForm, OradorExpedienteForm, OradorForm, PautaSessaoFilterSet,
PresencaForm, ResumoOrdenacaoForm, SessaoPlenariaFilterSet, PresencaForm, ResumoOrdenacaoForm, SessaoPlenariaFilterSet,
SessaoPlenariaForm, VotacaoEditForm, VotacaoForm, SessaoPlenariaForm, VotacaoEditForm, VotacaoForm,
VotacaoNominalForm, RetiradaPautaForm) VotacaoNominalForm, RetiradaPautaForm, OradorOrdemDiaForm)
from .models import (Bancada, Bloco, CargoBancada, CargoMesa, from .models import (Bancada, Bloco, CargoBancada, CargoMesa,
ExpedienteMateria, ExpedienteSessao, OcorrenciaSessao, IntegranteMesa, ExpedienteMateria, ExpedienteSessao, OcorrenciaSessao, IntegranteMesa,
MateriaLegislativa, Orador, OradorExpediente, OrdemDia, MateriaLegislativa, Orador, OradorExpediente, OrdemDia,
PresencaOrdemDia, RegistroVotacao, ResumoOrdenacao, PresencaOrdemDia, RegistroVotacao, ResumoOrdenacao,
SessaoPlenaria, SessaoPlenariaPresenca, TipoExpediente, SessaoPlenaria, SessaoPlenariaPresenca, TipoExpediente,
TipoResultadoVotacao, TipoSessaoPlenaria, VotoParlamentar, TipoRetiradaPauta, TipoResultadoVotacao, TipoSessaoPlenaria, VotoParlamentar, TipoRetiradaPauta,
RetiradaPauta, TipoJustificativa, JustificativaAusencia) RetiradaPauta, TipoJustificativa, JustificativaAusencia, OradorOrdemDia)
TipoSessaoCrud = CrudAux.build(TipoSessaoPlenaria, 'tipo_sessao_plenaria') TipoSessaoCrud = CrudAux.build(TipoSessaoPlenaria, 'tipo_sessao_plenaria')
@ -93,6 +93,25 @@ def reordernar_materias_ordem(request, pk):
return HttpResponseRedirect( return HttpResponseRedirect(
reverse('sapl.sessao:ordemdia_list', kwargs={'pk': pk})) reverse('sapl.sessao:ordemdia_list', kwargs={'pk': pk}))
def renumerar_materias_ordem(request, pk):
ordens = OrdemDia.objects.filter(sessao_plenaria_id=pk)
for ordem_num, o in enumerate(ordens, 1):
o.numero_ordem = ordem_num
o.save()
return HttpResponseRedirect(
reverse('sapl.sessao:ordemdia_list', kwargs={'pk': pk}))
def renumerar_materias_expediente(request, pk):
expedientes = ExpedienteMateria.objects.filter(sessao_plenaria_id=pk)
for exp_num, e in enumerate(expedientes, 1):
e.numero_ordem = exp_num
e.save()
return HttpResponseRedirect(
reverse('sapl.sessao:expedientemateria_list', kwargs={'pk': pk}))
def verifica_presenca(request, model, spk): def verifica_presenca(request, model, spk):
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -599,6 +618,31 @@ class OradorExpedienteCrud(OradorCrud):
'numero': self.object.numero_ordem} 'numero': self.object.numero_ordem}
class OradorOrdemDiaCrud(OradorCrud):
model = OradorOrdemDia
class CreateView(MasterDetailCrud.CreateView):
form_class = OradorOrdemDiaForm
def get_initial(self):
return {'id_sessao': self.kwargs['pk']}
def get_success_url(self):
return reverse('sapl.sessao:oradorordemdia_list',
kwargs={'pk': self.kwargs['pk']})
class UpdateView(MasterDetailCrud.UpdateView):
form_class = OradorOrdemDiaForm
def get_initial(self):
initial = super(UpdateView, self).get_initial()
initial.update({'id_sessao': self.object.sessao_plenaria.id})
initial.update({'numero': self.object.numero_ordem})
return initial
class OradorCrud(OradorCrud): class OradorCrud(OradorCrud):
model = Orador model = Orador
@ -620,6 +664,7 @@ class OradorCrud(OradorCrud):
def get_initial(self): def get_initial(self):
initial = super(UpdateView, self).get_initial() initial = super(UpdateView, self).get_initial()
initial.update({'id_sessao': self.object.sessao_plenaria.id}) initial.update({'id_sessao': self.object.sessao_plenaria.id})
initial.update({'numero':self.object.numero_ordem})
return initial return initial
@ -1270,7 +1315,8 @@ class ResumoOrdenacaoView(PermissionRequiredMixin, FormView):
'decimo': ordenacao.decimo, 'decimo': ordenacao.decimo,
'decimo_primeiro': ordenacao.decimo_primeiro, 'decimo_primeiro': ordenacao.decimo_primeiro,
'decimo_segundo': ordenacao.decimo_segundo, 'decimo_segundo': ordenacao.decimo_segundo,
'decimo_terceiro': ordenacao.decimo_terceiro}) 'decimo_terceiro': ordenacao.decimo_terceiro,
'decimo_quarto': ordenacao.decimo_quarto})
return initial return initial
def form_valid(self, form): def form_valid(self, form):
@ -1289,6 +1335,7 @@ class ResumoOrdenacaoView(PermissionRequiredMixin, FormView):
ordenacao.decimo_primeiro = form.cleaned_data['decimo_primeiro'] ordenacao.decimo_primeiro = form.cleaned_data['decimo_primeiro']
ordenacao.decimo_segundo = form.cleaned_data['decimo_segundo'] ordenacao.decimo_segundo = form.cleaned_data['decimo_segundo']
ordenacao.decimo_terceiro = form.cleaned_data['decimo_terceiro'] ordenacao.decimo_terceiro = form.cleaned_data['decimo_terceiro']
ordenacao.decimo_quarto = form.cleaned_data['decimo_quarto']
ordenacao.save() ordenacao.save()
@ -1378,9 +1425,15 @@ def get_materias_expediente(sessao_plenaria):
ementa = m.materia.ementa ementa = m.materia.ementa
titulo = m.materia titulo = m.materia
numero = m.numero_ordem numero = m.numero_ordem
tramitacao = m.materia.tramitacao_set.last()
turno = None
tramitacao = ''
tramitacoes = Tramitacao.objects.filter(materia=m.materia).order_by('-pk')
for aux_tramitacao in tramitacoes:
if aux_tramitacao.turno:
tramitacao = aux_tramitacao
break
turno = None
if tramitacao: if tramitacao:
turno = get_turno(tramitacao.turno) turno = get_turno(tramitacao.turno)
@ -1486,7 +1539,14 @@ def get_materias_ordem_do_dia(sessao_plenaria):
ementa_observacao = o.observacao ementa_observacao = o.observacao
titulo = o.materia titulo = o.materia
numero = o.numero_ordem numero = o.numero_ordem
tramitacao = o.materia.tramitacao_set.last()
tramitacao = ''
tramitacoes = Tramitacao.objects.filter(materia=o.materia).order_by('-pk')
for aux_tramitacao in tramitacoes:
if aux_tramitacao.turno:
tramitacao = aux_tramitacao
break
turno = None turno = None
if tramitacao: if tramitacao:
turno = get_turno(tramitacao.turno) turno = get_turno(tramitacao.turno)
@ -1551,6 +1611,32 @@ def get_materias_ordem_do_dia(sessao_plenaria):
return context return context
def get_oradores_ordemdia(sessao_plenaria):
oradores = []
oradores_ordem_dia = OradorOrdemDia.objects.filter(
sessao_plenaria_id=sessao_plenaria.id
).order_by('numero_ordem')
for orador in oradores_ordem_dia:
numero_ordem = orador.numero_ordem
url_discurso = orador.url_discurso
observacao = orador.observacao
parlamentar = Parlamentar.objects.get(
id=orador.parlamentar_id
)
o = {
'numero_ordem': numero_ordem,
'url_discurso': url_discurso,
'parlamentar': parlamentar,
'observacao': observacao
}
oradores.append(o)
context = {'oradores_ordemdia': oradores}
return context
def get_oradores_explicações_pessoais(sessao_plenaria): def get_oradores_explicações_pessoais(sessao_plenaria):
oradores_explicacoes = [] oradores_explicacoes = []
for orador in Orador.objects.filter( for orador in Orador.objects.filter(
@ -1665,6 +1751,9 @@ class ResumoView(DetailView):
context.update(get_materias_ordem_do_dia(self.object)) context.update(get_materias_ordem_do_dia(self.object))
# ===================================================================== # =====================================================================
# Oradores Ordem do Dia
context.update(get_oradores_ordemdia(self.object))
# =====================================================================
# Oradores nas Explicações Pessoais # Oradores nas Explicações Pessoais
context.update(get_oradores_explicações_pessoais(self.object)) context.update(get_oradores_explicações_pessoais(self.object))
# ===================================================================== # =====================================================================
@ -1685,6 +1774,7 @@ class ResumoView(DetailView):
'v_n_mat_o_d': 'votos_nominais_materias_ordem_dia.html', 'v_n_mat_o_d': 'votos_nominais_materias_ordem_dia.html',
'mesa_d': 'mesa_diretora.html', 'mesa_d': 'mesa_diretora.html',
'oradores_exped': 'oradores_expediente.html', 'oradores_exped': 'oradores_expediente.html',
'oradores_o_d': 'oradores_ordemdia.html',
'oradores_expli': 'oradores_explicacoes.html', 'oradores_expli': 'oradores_explicacoes.html',
'ocorr_sessao': 'ocorrencias_da_sessao.html' 'ocorr_sessao': 'ocorrencias_da_sessao.html'
} }
@ -1704,7 +1794,8 @@ class ResumoView(DetailView):
'decimo_ordenacao': dict_ord_template[ordenacao.decimo], 'decimo_ordenacao': dict_ord_template[ordenacao.decimo],
'decimo_primeiro_ordenacao': dict_ord_template[ordenacao.decimo_primeiro], 'decimo_primeiro_ordenacao': dict_ord_template[ordenacao.decimo_primeiro],
'decimo_segundo_ordenacao': dict_ord_template[ordenacao.decimo_segundo], 'decimo_segundo_ordenacao': dict_ord_template[ordenacao.decimo_segundo],
'decimo_terceiro_ordenacao': dict_ord_template[ordenacao.decimo_terceiro]}) 'decimo_terceiro_ordenacao': dict_ord_template[ordenacao.decimo_terceiro],
'decimo_quarto_ordenacao': dict_ord_template[ordenacao.decimo_quarto]})
except KeyError as e: except KeyError as e:
self.logger.error('user=' + self.request.user.username + '. ' + "KeyError: " + str(e) + ". Erro " self.logger.error('user=' + self.request.user.username + '. ' + "KeyError: " + str(e) + ". Erro "
"ao tentar utilizar configuração de ordenação. Utilizando ordenação padrão.") "ao tentar utilizar configuração de ordenação. Utilizando ordenação padrão.")
@ -1720,8 +1811,9 @@ class ResumoView(DetailView):
'nono_ordenacao': dict_ord_template['lista_p_o_d'], 'nono_ordenacao': dict_ord_template['lista_p_o_d'],
'decimo_ordenacao': dict_ord_template['mat_o_d'], 'decimo_ordenacao': dict_ord_template['mat_o_d'],
'decimo_primeiro_ordenacao': dict_ord_template['v_n_mat_o_d'], 'decimo_primeiro_ordenacao': dict_ord_template['v_n_mat_o_d'],
'decimo_segundo_ordenacao': dict_ord_template['oradores_expli'], 'decimo_segundo_ordenacao': dict_ord_template['oradores_o_d'],
'decimo_terceiro_ordenacao': dict_ord_template['ocorr_sessao'] 'decimo_terceiro_ordenacao': dict_ord_template['oradores_expli'],
'decimo_quarto_ordenacao': dict_ord_template['ocorr_sessao']
}) })
else: else:
context.update( context.update(
@ -1736,8 +1828,9 @@ class ResumoView(DetailView):
'nono_ordenacao': dict_ord_template['lista_p_o_d'], 'nono_ordenacao': dict_ord_template['lista_p_o_d'],
'decimo_ordenacao': dict_ord_template['mat_o_d'], 'decimo_ordenacao': dict_ord_template['mat_o_d'],
'decimo_primeiro_ordenacao': dict_ord_template['v_n_mat_o_d'], 'decimo_primeiro_ordenacao': dict_ord_template['v_n_mat_o_d'],
'decimo_segundo_ordenacao': dict_ord_template['oradores_expli'], 'decimo_segundo_ordenacao': dict_ord_template['oradores_o_d'],
'decimo_terceiro_ordenacao': dict_ord_template['ocorr_sessao'] 'decimo_terceiro_ordenacao': dict_ord_template['oradores_expli'],
'decimo_quarto_ordenacao': dict_ord_template['ocorr_sessao']
}) })
return context return context

2
sapl/settings.py

@ -41,7 +41,7 @@ ALLOWED_HOSTS = ['*']
LOGIN_REDIRECT_URL = '/' LOGIN_REDIRECT_URL = '/'
LOGIN_URL = '/login/?next=' LOGIN_URL = '/login/?next='
SAPL_VERSION = '3.1.150' SAPL_VERSION = '3.1.151'
if DEBUG: if DEBUG:
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

2
sapl/templates/base.html

@ -179,7 +179,7 @@
<small> <small>
Desenvolvido pelo <a href="http://www.interlegis.leg.br/">Interlegis</a> em software livre e aberto. Desenvolvido pelo <a href="http://www.interlegis.leg.br/">Interlegis</a> em software livre e aberto.
</small> </small>
<span>Release: 3.1.150</span> <span>Release: 3.1.151</span>
</p> </p>
</div> </div>
<div class="col-md-4"> <div class="col-md-4">

19
sapl/templates/materia/relatoria_form.html

@ -16,5 +16,24 @@
{% block extra_js %} {% block extra_js %}
<script language="Javascript"> <script language="Javascript">
$("#id_comissao").attr("disabled", true); $("#id_comissao").attr("disabled", true);
$(document).ready(function() {
$("#id_composicao").change(function() {
var composicao_id = $("#id_composicao").val();
var json_data = { composicao_id: composicao_id };
var parlamentar_field = $("#id_parlamentar");
$.getJSON("/sistema/comissao/recupera-participacoes", json_data, function(data) {
parlamentar_field.children().remove();
parlamentar_field.append($("<option></option>").attr("value", '').text('---------'));
if (data) {
$.each(data, function(idx, parlamentar) {
parlamentar_field.append($("<option></option>").attr("value", parlamentar.id).text(parlamentar.nome));
});
}
});
});
});
</script> </script>
{% endblock extra_js %} {% endblock extra_js %}

8
sapl/templates/parlamentares/layouts.yaml

@ -30,7 +30,7 @@ SessaoLegislativa:
- data_inicio_intervalo data_fim_intervalo - data_inicio_intervalo data_fim_intervalo
Parlamentar: Parlamentar:
{% trans 'Cadastro do Parlamentar' %}: {% trans 'Dados do Parlamentar' %}:
- nome_parlamentar:8 ativo - nome_parlamentar:8 ativo
- nome_completo - nome_completo
- nivel_instrucao sexo data_nascimento - nivel_instrucao sexo data_nascimento
@ -47,7 +47,7 @@ Parlamentar:
- biografia - biografia
ParlamentarUpdate: ParlamentarUpdate:
{% trans 'Cadastro do Parlamentar' %}: {% trans 'Dados do Parlamentar' %}:
- nome_parlamentar:8 ativo - nome_parlamentar:8 ativo
- nome_completo - nome_completo
- nivel_instrucao sexo data_nascimento - nivel_instrucao sexo data_nascimento
@ -64,9 +64,7 @@ ParlamentarUpdate:
- biografia - biografia
ParlamentarCreate: ParlamentarCreate:
{% trans 'Dados do Mandato' %}: {% trans 'Dados do Parlamentar' %}:
- legislatura data_expedicao_diploma
{% trans 'Cadastro do Parlamentar' %}:
- nome_parlamentar:8 ativo - nome_parlamentar:8 ativo
- nome_completo - nome_completo
- nivel_instrucao sexo data_nascimento - nivel_instrucao sexo data_nascimento

12
sapl/templates/parlamentares/parlamentar_filter.html

@ -5,13 +5,14 @@
{% block base_content %} {% block base_content %}
{% if not show_results %} {% if not show_results %}
{% crispy filter.form %} {% crispy filter.form %}
{% endif %} {% else %}
<div class="actions btn-group float-right btn-group-sm" role="group">
{% if show_results %}
<div class="actions btn-group float-right" role="group">
<a href="{% url 'sapl.parlamentares:pesquisar_parlamentar' %}" class="btn btn-outline-primary">{% trans 'Fazer nova pesquisa' %}</a> <a href="{% url 'sapl.parlamentares:pesquisar_parlamentar' %}" class="btn btn-outline-primary">{% trans 'Fazer nova pesquisa' %}</a>
{% if not request.user.is_anonymous %}
<a href="{% url 'sapl.parlamentares:parlamentar_create' %}"class="btn btn-outline-primary">Cadastrar Parlamentar</a>
{% endif %}
</div> </div>
<br /><br /> <br>
{% if numero_res > 0 %} {% if numero_res > 0 %}
{% if numero_res == 1 %} {% if numero_res == 1 %}
<p>Foi encontrado {{ numero_res }} resultado</p> <p>Foi encontrado {{ numero_res }} resultado</p>
@ -42,7 +43,6 @@
<font size="4"><p align="center">{{ NO_ENTRIES_MSG }}</p></font> <font size="4"><p align="center">{{ NO_ENTRIES_MSG }}</p></font>
{% endif %} {% endif %}
{% endif %} {% endif %}
<br/> <br/>
{% include 'paginacao.html'%} {% include 'paginacao.html'%}
<br /><br /><br /> <br /><br /><br />

9
sapl/templates/parlamentares/parlamentares_list.html

@ -1,6 +1,15 @@
{% extends "crud/list.html" %} {% extends "crud/list.html" %}
{% load i18n %} {% load i18n %}
{% load crispy_forms_tags cropping%} {% load crispy_forms_tags cropping%}
{% block actions %}
<div class="actions btn-group float-right btn-group-sm" role="group">
<a href="{% url 'sapl.parlamentares:pesquisar_parlamentar' %}" class="btn btn-outline-primary">Pesquisar Parlamentar</a>
{% if not request.user.is_anonymous %}
<a href="{% url 'sapl.parlamentares:vincular_parlamentar' %}" class="btn btn-outline-primary">Vincular Parlamentar</a>
{% endif %}
{% block more_buttons %}{% endblock more_buttons %}
</div>
{% endblock actions %}
{% block extra_content %} {% block extra_content %}
<fieldset class="form-group"> <fieldset class="form-group">
<legend>Selecione o Período</legend> <legend>Selecione o Período</legend>

8
sapl/templates/parlamentares/vincular_parlamentar.html

@ -0,0 +1,8 @@
{% extends "crud/form.html" %}
{% load i18n %}
{% load crispy_forms_tags %}
{% load common_tags %}
{% block detail_content %}
{% crispy form %}
{% endblock detail_content %}

6
sapl/templates/relatorios/relatorio_ata.html

@ -46,6 +46,7 @@
{% include 'sessao/blocos_ata/oradores_expediente.html' %} {% include 'sessao/blocos_ata/oradores_expediente.html' %}
{% include 'sessao/blocos_ata/lista_presenca_ordem_dia.html' %} {% include 'sessao/blocos_ata/lista_presenca_ordem_dia.html' %}
{% include 'sessao/blocos_ata/materias_ordem_dia.html' %} {% include 'sessao/blocos_ata/materias_ordem_dia.html' %}
{% include 'sessao/blocos_ata/oradores_ordemdia.html' %}
{% include 'sessao/blocos_ata/oradores_explicacoes.html' %} {% include 'sessao/blocos_ata/oradores_explicacoes.html' %}
{% include 'sessao/blocos_ata/ocorrencias_da_sessao.html' %} {% include 'sessao/blocos_ata/ocorrencias_da_sessao.html' %}
@ -82,12 +83,13 @@
<tr style="margin-top:20px"> <tr style="margin-top:20px">
<td> <td>
<div style="float: left; position: relative;top: -50px; left: 8px; width: 120px;">_____________________</br> <div style="float: left; position: relative;top: -50px; left: 8px; width: 120px;">_____________________</br>
<p style="font-size:8pt"><b>{{p.cargo}}: </b> {{p.parlamentar.nome_completo}} / {{ p.parlamentar|filiacao_data_filter:object.data_inicio }}</p> <p style="font-size:8pt">
{{p.nome_completo}} / {{ p|filiacao_data_filter:object.data_inicio }}</p>
</br></br></br> </br></br></br>
</div> </div>
{% else %} {% else %}
<div style="float: left; position: relative; top: -50px;left: 142px; width: 120px; margin-right:-220px;">_____________________ </br> <div style="float: left; position: relative; top: -50px;left: 142px; width: 120px; margin-right:-220px;">_____________________ </br>
<p style="font-size:8pt">{{p.parlamentar.nome_completo}} / {{ p.parlamentar|filiacao_data_filter:object.data_inicio }}</p> <p style="font-size:8pt">{{p.nome_completo}} / {{ p|filiacao_data_filter:object.data_inicio }}</p>
</br></br></br> </br></br></br>
</div> </div>
</td> </td>

13
sapl/templates/sessao/blocos_ata/oradores_ordemdia.html

@ -0,0 +1,13 @@
<fieldset>
<p aling="justify">
{% if oradores_ordemdia %}
<strong>Oradores da Ordem do Dia: </strong>
{% for orador in oradores_ordemdia %}
<div><b>{{ orador.numero_ordem }}</b> - {{ orador.parlamentar.nome_completo }}</div>
<div>{{ orador.url_discurso }}</div>
<div>{{ orador.observacao }}</div>
</br>
{% endfor %}
{% endif %}
</p>
</fieldset>

15
sapl/templates/sessao/blocos_resumo/oradores_ordemdia.html

@ -0,0 +1,15 @@
<fieldset>
<legend>Oradores da Ordem do Dia</legend>
<div class="row">
<div class="col-md-4">Parlamentar</div>
<div class="col-md-4">Discurso</div>
<div class="col-md-4">Observação</div>
</div>
<div class="row">
{% for orador in oradores_ordemdia %}
<div class="col-md-4"><b>{{ orador.numero_ordem }}</b> - {{ orador.parlamentar }}</div>
<div class="col-md-4">{{ orador.url_discurso }}</div>
<div class="col-md-4">{{ orador.observacao }}</div>
{% endfor %}
</div>
</fieldset>

5
sapl/templates/sessao/expedientemateria_list.html

@ -7,7 +7,10 @@
{% if perms|get_add_perm:view %} {% if perms|get_add_perm:view %}
<a href="{% url 'sapl.sessao:reordenar_expediente' root_pk %}" class="btn btn-outline-primary"> <a href="{% url 'sapl.sessao:reordenar_expediente' root_pk %}" class="btn btn-outline-primary">
{% blocktrans with verbose_name=view.verbose_name %} Ajustar Ordenação {% endblocktrans %} {% blocktrans with verbose_name=view.verbose_name %} Reordenar pela precedência {% endblocktrans %}
</a>
<a href="{% url 'sapl.sessao:renumerar_materias_expediente' root_pk %}" class="btn btn-outline-primary">
{% blocktrans with verbose_name=view.verbose_name %} Renumerar Expediente {% endblocktrans %}
</a> </a>
<a href="{% url 'sapl.sessao:adicionar_varias_materias_expediente' root_pk %}" class="btn btn-outline-primary"> <a href="{% url 'sapl.sessao:adicionar_varias_materias_expediente' root_pk %}" class="btn btn-outline-primary">
{% blocktrans with verbose_name=view.verbose_name %} Adicionar Várias Matérias {% endblocktrans %} {% blocktrans with verbose_name=view.verbose_name %} Adicionar Várias Matérias {% endblocktrans %}

6
sapl/templates/sessao/layouts.yaml

@ -39,6 +39,12 @@ OradorExpediente:
- url_discurso observacao - url_discurso observacao
- upload_anexo - upload_anexo
OradorOrdemDia:
{% trans 'Orador da Ordem do Dia' %}:
- numero_ordem parlamentar
- url_discurso observacao
- upload_anexo
ExpedienteMateria: ExpedienteMateria:
{% trans 'Matéria do Expediente' %}: {% trans 'Matéria do Expediente' %}:
- data_ordem numero_ordem - data_ordem numero_ordem

5
sapl/templates/sessao/ordemdia_list.html

@ -7,7 +7,10 @@
{% if perms|get_add_perm:view %} {% if perms|get_add_perm:view %}
<a href="{% url 'sapl.sessao:reordenar_ordem' root_pk %}" class="btn btn-outline-primary"> <a href="{% url 'sapl.sessao:reordenar_ordem' root_pk %}" class="btn btn-outline-primary">
{% blocktrans with verbose_name=view.verbose_name %} Ajustar Ordenação {% endblocktrans %} {% blocktrans with verbose_name=view.verbose_name %} Reordenar pela precedência {% endblocktrans %}
</a>
<a href="{% url 'sapl.sessao:renumerar_ordem' root_pk %}" class="btn btn-outline-primary">
{% blocktrans with verbose_name=view.verbose_name %} Renumerar Ordem {% endblocktrans %}
</a> </a>
<a href="{% url 'sapl.sessao:adicionar_varias_materias_ordem_dia' root_pk %}" class="btn btn-outline-primary"> <a href="{% url 'sapl.sessao:adicionar_varias_materias_ordem_dia' root_pk %}" class="btn btn-outline-primary">
{% blocktrans with verbose_name=view.verbose_name %} Adicionar Várias Matérias {% endblocktrans %} {% blocktrans with verbose_name=view.verbose_name %} Adicionar Várias Matérias {% endblocktrans %}

3
sapl/templates/sessao/resumo.html

@ -59,4 +59,7 @@
{% include 'sessao/blocos_resumo/'|add:decimo_terceiro_ordenacao %} {% include 'sessao/blocos_resumo/'|add:decimo_terceiro_ordenacao %}
<br /><br /><br /> <br /><br /><br />
{% include 'sessao/blocos_resumo/'|add:decimo_quarto_ordenacao %}
<br /><br /><br />
{% endblock detail_content %} {% endblock detail_content %}

1
sapl/templates/sessao/resumo_ata.html

@ -32,5 +32,6 @@
{% include 'sessao/blocos_ata/'|add:decimo_primeiro_ordenacao %} {% include 'sessao/blocos_ata/'|add:decimo_primeiro_ordenacao %}
{% include 'sessao/blocos_ata/'|add:decimo_segundo_ordenacao %} {% include 'sessao/blocos_ata/'|add:decimo_segundo_ordenacao %}
{% include 'sessao/blocos_ata/'|add:decimo_terceiro_ordenacao %} {% include 'sessao/blocos_ata/'|add:decimo_terceiro_ordenacao %}
{% include 'sessao/blocos_ata/'|add:decimo_quarto_ordenacao %}
{% include 'sessao/blocos_ata/assinaturas.html' %} {% include 'sessao/blocos_ata/assinaturas.html' %}
{% endblock detail_content %} {% endblock detail_content %}

2
sapl/templates/sessao/subnav.yaml

@ -35,6 +35,8 @@
url: ordemdia_list url: ordemdia_list
- title: {% trans 'Presença Ordem do Dia' %} - title: {% trans 'Presença Ordem do Dia' %}
url: presencaordemdia url: presencaordemdia
- title: {% trans 'Oradores da Ordem do Dia' %}
url: oradorordemdia_list
- title: {% trans 'Votação em Bloco' %} - title: {% trans 'Votação em Bloco' %}
url: votacao_bloco_ordemdia url: votacao_bloco_ordemdia
check_permission: sessao.add_sessaoplenaria check_permission: sessao.add_sessaoplenaria

2
setup.py

@ -43,7 +43,7 @@ install_requires = [
] ]
setup( setup(
name='interlegis-sapl', name='interlegis-sapl',
version='3.1.150', version='3.1.151',
packages=find_packages(), packages=find_packages(),
include_package_data=True, include_package_data=True,
license='GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007', license='GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007',

Loading…
Cancel
Save