diff --git a/Dockerfile b/Dockerfile index d970a4e4c..015357a40 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ FROM alpine:3.8 ENV BUILD_PACKAGES postgresql-dev graphviz-dev graphviz build-base git pkgconfig \ python3-dev libxml2-dev jpeg-dev libressl-dev libffi-dev libxslt-dev \ 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 @@ -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 -# 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 # Remove .env(fake) e sapl.db da imagem diff --git a/docker-compose.yml b/docker-compose.yml index 5ef3b240e..4be453668 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,7 +11,7 @@ sapldb: ports: - "5432:5432" sapl: - image: interlegis/sapl:3.1.150 + image: interlegis/sapl:3.1.151 restart: always environment: ADMIN_PASSWORD: interlegis diff --git a/sapl/api/urls.py b/sapl/api/urls.py index 3e874add2..27196146d 100644 --- a/sapl/api/urls.py +++ b/sapl/api/urls.py @@ -8,7 +8,7 @@ from rest_framework.routers import DefaultRouter from sapl.api.deprecated import MateriaLegislativaViewSet, SessaoPlenariaViewSet,\ AutoresProvaveisListView, AutoresPossiveisListView, AutorListView,\ ModelChoiceView -from sapl.api.views import SaplSetViews +from sapl.api.views import SaplApiViewSetConstrutor from .apps import AppConfig @@ -21,9 +21,10 @@ router.register(r'materia$', MateriaLegislativaViewSet) 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(): - router.register(app + '/' + view_prefix, viewset) + router.register(app.label + '/' + + view_prefix._meta.model_name, viewset) urlpatterns_router = router.urls diff --git a/sapl/api/views.py b/sapl/api/views.py index f2f146e24..1cfdd1d44 100644 --- a/sapl/api/views.py +++ b/sapl/api/views.py @@ -22,8 +22,12 @@ from sapl.api.forms import SaplFilterSetMixin from sapl.api.permissions import SaplModelPermissions from sapl.api.serializers import ChoiceSerializer 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.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 @@ -31,20 +35,25 @@ class BusinessRulesNotImplementedMixin: def create(self, request, *args, **kwargs): raise Exception(_("POST Create não implementado")) - def put(self, request, *args, **kwargs): - raise Exception(_("PUT Update não implementado")) - - def patch(self, request, *args, **kwargs): - raise Exception(_("PATCH Partial Update não implementado")) + def update(self, request, *args, **kwargs): + raise Exception(_("PUT and PATCH não implementado")) def delete(self, request, *args, **kwargs): raise Exception(_("DELETE Delete não implementado")) -class SaplApiViewSetConstrutor(ModelViewSet): - +class SaplApiViewSet(ModelViewSet): 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 def build_class(cls): import inspect @@ -95,7 +104,7 @@ class SaplApiViewSetConstrutor(ModelViewSet): model = _model # Define uma classe padrão ModelViewSet de DRF - class ModelSaplViewSet(cls): + class ModelSaplViewSet(SaplApiViewSet): queryset = _model.objects.all() # Utiliza o filtro customizado pela classe @@ -119,12 +128,12 @@ class SaplApiViewSetConstrutor(ModelViewSet): apps_sapl = [apps.apps.get_app_config( n[5:]) for n in settings.SAPL_APPS] for app in apps_sapl: - built_sets[app.label] = {} + cls._built_sets[app] = {} 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 @@ -187,15 +196,39 @@ class SaplApiViewSetConstrutor(ModelViewSet): } """ -SaplSetViews = SaplApiViewSetConstrutor.build_class() - # Toda Classe construida acima, pode ser redefinida e aplicado quaisquer # das possibilidades para uma classe normal criada a partir de # 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 -class _AutorViewSet(SaplSetViews['base']['autor']): + + +@customize(Autor) +class _AutorViewSet: """ Neste exemplo de customização do que foi criado em SaplApiViewSetConstrutor além do ofertado por @@ -240,7 +273,7 @@ class _AutorViewSet(SaplSetViews['base']['autor']): return Response(serializer.data) @classonlymethod - def build_class_with_actions(cls): + def build(cls): models_with_gr_for_autor = models_with_gr_for_model(Autor) @@ -263,7 +296,8 @@ class _AutorViewSet(SaplSetViews['base']['autor']): return cls -class _ParlamentarViewSet(SaplSetViews['parlamentares']['parlamentar']): +@customize(Parlamentar) +class _ParlamentarViewSet: @action(detail=True) def proposicoes(self, request, *args, **kwargs): """ @@ -288,15 +322,16 @@ class _ParlamentarViewSet(SaplSetViews['parlamentares']['parlamentar']): page = self.paginate_queryset(qs) if page is not None: - serializer = SaplSetViews[ - 'materia']['proposicao'].serializer_class(page, many=True) + serializer = SaplApiViewSetConstrutor.get_class_for_model( + Proposicao).serializer_class(page, many=True) return self.get_paginated_response(serializer.data) serializer = self.get_serializer(page, many=True) return Response(serializer.data) -class _ProposicaoViewSet(SaplSetViews['materia']['proposicao']): +@customize(Proposicao) +class _ProposicaoViewSet(): """ list: Retorna lista de Proposições @@ -349,7 +384,26 @@ class _ProposicaoViewSet(SaplSetViews['materia']['proposicao']): 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']) def change_position(self, request, *args, **kwargs): @@ -366,7 +420,8 @@ class _TipoMateriaLegislativaViewSet(SaplSetViews['materia']['tipomaterialegisla return Response(result) -class _DocumentoAdministrativoViewSet(SaplSetViews['protocoloadm']['documentoadministrativo']): +@customize(DocumentoAdministrativo) +class _DocumentoAdministrativoViewSet: class DocumentoAdministrativoPermission(SaplModelPermissions): def has_permission(self, request, view): @@ -400,8 +455,8 @@ class _DocumentoAdministrativoViewSet(SaplSetViews['protocoloadm']['documentoadm return qs -class _DocumentoAcessorioAdministrativoViewSet( - SaplSetViews['protocoloadm']['documentoacessorioadministrativo']): +@customize(DocumentoAcessorioAdministrativo) +class _DocumentoAcessorioAdministrativoViewSet: permission_classes = ( _DocumentoAdministrativoViewSet.DocumentoAdministrativoPermission, ) @@ -414,9 +469,8 @@ class _DocumentoAcessorioAdministrativoViewSet( return qs -class _TramitacaoAdministrativoViewSet( - SaplSetViews['protocoloadm']['tramitacaoadministrativo'], - BusinessRulesNotImplementedMixin): +@customize(TramitacaoAdministrativo) +class _TramitacaoAdministrativoViewSet(BusinessRulesNotImplementedMixin): # TODO: Implementar regras de manutenção das tramitações de docs adms permission_classes = ( @@ -430,8 +484,8 @@ class _TramitacaoAdministrativoViewSet( return qs -class _SessaoPlenariaViewSet( - SaplSetViews['sessao']['sessaoplenaria']): +@customize(SessaoPlenaria) +class _SessaoPlenariaViewSet: @action(detail=False) def years(self, request, *args, **kwargs): @@ -439,17 +493,3 @@ class _SessaoPlenariaViewSet( serializer = ChoiceSerializer(years, many=True) 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 diff --git a/sapl/comissoes/urls.py b/sapl/comissoes/urls.py index 72886f1f1..f22f32e1d 100644 --- a/sapl/comissoes/urls.py +++ b/sapl/comissoes/urls.py @@ -1,7 +1,7 @@ from django.conf.urls import include, url from sapl.comissoes.views import (CargoCrud, ComissaoCrud, ComposicaoCrud, DocumentoAcessorioCrud, MateriasTramitacaoListView, ParticipacaoCrud, - PeriodoComposicaoCrud, ReuniaoCrud, TipoComissaoCrud) + PeriodoComposicaoCrud, ReuniaoCrud, TipoComissaoCrud, get_participacoes_comissao) from .apps import AppConfig @@ -21,4 +21,5 @@ urlpatterns = [ url(r'^sistema/comissao/periodo-composicao/', include(PeriodoComposicaoCrud.get_urls())), url(r'^sistema/comissao/tipo/', include(TipoComissaoCrud.get_urls())), + url(r'^sistema/comissao/recupera-participacoes', get_participacoes_comissao), ] diff --git a/sapl/comissoes/views.py b/sapl/comissoes/views.py index d6d129b11..7b8a6fd1d 100644 --- a/sapl/comissoes/views.py +++ b/sapl/comissoes/views.py @@ -2,7 +2,7 @@ import logging from django.core.urlresolvers import reverse 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.generic import ListView from django.views.generic.base import RedirectView @@ -108,7 +108,7 @@ class ComposicaoCrud(MasterDetailCrud): paginate_by = None def take_composicao_pk(self): - + username = self.request.user.username try: self.logger.debug('user=' + username + '. Tentando obter pk da composição.') @@ -278,3 +278,15 @@ class DocumentoAcessorioCrud(MasterDetailCrud): return HttpResponseRedirect( reverse('sapl.comissoes:reuniao_detail', 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) diff --git a/sapl/materia/forms.py b/sapl/materia/forms.py index 4b75e134a..673dfd024 100644 --- a/sapl/materia/forms.py +++ b/sapl/materia/forms.py @@ -26,7 +26,7 @@ import django_filters import sapl 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, STATUS_TA_PRIVATE) from sapl.crispy_layout_mixin import (SaplFormLayout, form_actions, to_column, @@ -37,7 +37,7 @@ from sapl.materia.models import (AssuntoMateria, Autoria, MateriaAssunto, UnidadeTramitacao) from sapl.norma.models import (LegislacaoCitada, NormaJuridica, 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.settings import MAX_DOC_UPLOAD_SIZE from sapl.utils import (YES_NO_CHOICES, SEPARADOR_HASH_PROPOSICAO, @@ -345,7 +345,7 @@ class AcompanhamentoMateriaForm(ModelForm): self.helper = SaplFormHelper() self.helper.layout = Layout( Fieldset( - _('Acompanhamento de Matéria por e-mail'), + _('Acompanhamento de Matéria por e-mail'), row1, form_actions(label='Cadastrar') ) @@ -362,34 +362,63 @@ class DocumentoAcessorioForm(FileFieldCheckMixin, ModelForm): class RelatoriaForm(ModelForm): - logger = logging.getLogger(__name__) + composicao = forms.ModelChoiceField( + required=True, + empty_label='---------', + queryset=Composicao.objects.all(), + label=_('Composição') + ) + class Meta: model = Relatoria - fields = ['data_designacao_relator', 'comissao', 'parlamentar', - 'data_destituicao_relator', 'tipo_fim_relatoria'] + fields = [ + 'comissao', + 'data_designacao_relator', + 'data_destituicao_relator', + 'tipo_fim_relatoria', + 'composicao', + 'parlamentar' + ] widgets = {'comissao': forms.Select(attrs={'disabled': 'disabled'})} 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) + 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): - super(RelatoriaForm, self).clean() + self.fields['parlamentar'].choices = [('', '---------')] - if not self.is_valid(): - return self.cleaned_data + def clean(self): + super().clean() cleaned_data = self.cleaned_data + if not self.is_valid(): + return cleaned_data + try: self.logger.debug("Tentando obter objeto Comissao.") comissao = Comissao.objects.get(id=self.initial['comissao']) except ObjectDoesNotExist as e: - self.logger.error("Objeto Comissao não encontrado com id={} " - ".A localização atual deve ser uma comissão. " - .format(self.initial['comissao']) + str(e)) + self.logger.error( + "Objeto Comissao não encontrado com id={}. A localização atual deve ser uma comissão. ".format( + self.initial['comissao']) + str(e)) msg = _('A localização atual deve ser uma comissão.') raise ValidationError(msg) else: @@ -727,7 +756,7 @@ class AnexadaForm(ModelForm): 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) @@ -751,8 +780,8 @@ class AnexadaForm(ModelForm): ano=cleaned_data['ano'], tipo=cleaned_data['tipo']) except ObjectDoesNotExist: - msg = _('A MateriaLegislativa a ser anexada (numero={}, ano={}, tipo={}) não existe no cadastro' - ' de matérias legislativas.'.format(cleaned_data['numero'], cleaned_data['ano'], cleaned_data['tipo'])) + msg = _('A {} {}/{} não existe no cadastro de matérias legislativas.' + .format(cleaned_data['tipo'], cleaned_data['numero'], cleaned_data['ano'])) self.logger.error("A matéria a ser anexada não existe no cadastro" " de matérias legislativas.") raise ValidationError(msg) @@ -1537,7 +1566,7 @@ class ProposicaoForm(FileFieldCheckMixin, forms.ModelForm): tm, am, nm = (cd.get('tipo_materia', ''), cd.get('ano_materia', ''), cd.get('numero_materia', '')) - + if cd['numero_materia_futuro'] and \ 'tipo' in cd and \ MateriaLegislativa.objects.filter(tipo=cd['tipo'].tipo_conteudo_related, @@ -1755,7 +1784,7 @@ class ConfirmarProposicaoForm(ProposicaoForm): self._meta.fields.remove('regime_tramitacao') # esta chamada isola o __init__ de ProposicaoForm - super(ProposicaoForm, self).__init__(*args, **kwargs) + super(ProposicaoForm, self).__init__(*args, **kwargs) fields = [ Fieldset( @@ -1831,7 +1860,7 @@ class ConfirmarProposicaoForm(ProposicaoForm): self.fields['tipo_readonly'].initial = self.instance.tipo.descricao self.fields['autor_readonly'].initial = str(self.instance.autor) if self.instance.numero_materia_futuro: - self.fields['numero_materia_futuro'].initial = self.instance.numero_materia_futuro + self.fields['numero_materia_futuro'].initial = self.instance.numero_materia_futuro if self.instance.materia_de_vinculo: self.fields[ diff --git a/sapl/materia/migrations/0044_auto_20190327_1409.py b/sapl/materia/migrations/0044_auto_20190327_1409.py new file mode 100644 index 000000000..5322d7833 --- /dev/null +++ b/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'), + ), + ] diff --git a/sapl/materia/models.py b/sapl/materia/models.py index af4cb1231..c7aaaed15 100644 --- a/sapl/materia/models.py +++ b/sapl/materia/models.py @@ -497,6 +497,7 @@ class DocumentoAcessorio(models.Model): arquivo = models.FileField( blank=True, null=True, + max_length=255, upload_to=anexo_upload_path, verbose_name=_('Texto Integral'), validators=[restringe_tipos_de_arquivo_txt]) diff --git a/sapl/materia/tests/test_materia_form.py b/sapl/materia/tests/test_materia_form.py index b67ef2733..e29d89815 100644 --- a/sapl/materia/tests/test_materia_form.py +++ b/sapl/materia/tests/test_materia_form.py @@ -2,6 +2,7 @@ import pytest from django.utils.translation import ugettext as _ from model_mommy import mommy +from sapl.comissoes.models import Comissao, TipoComissao from sapl.materia import forms 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) 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() errors = form.errors + assert errors['parlamentar'] == [_('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) diff --git a/sapl/materia/views.py b/sapl/materia/views.py index 696b1235a..73dcb3072 100644 --- a/sapl/materia/views.py +++ b/sapl/materia/views.py @@ -1,5 +1,11 @@ -from datetime import datetime import logging +import os +import shutil +import tempfile +import weasyprint +import itertools + +from datetime import datetime from random import choice 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.parlamentares.models import Legislatura 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, gerar_hash_arquivo, get_base_url, get_mime_type_from_file_extension, montar_row_autor, @@ -1104,55 +1111,13 @@ class RelatoriaCrud(MasterDetailCrud): class CreateView(MasterDetailCrud.CreateView): form_class = RelatoriaForm + layout_key = None 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): materia = MateriaLegislativa.objects.get(id=self.kwargs['pk']) - loc_atual = Tramitacao.objects.filter( - materia=materia).last() + loc_atual = Tramitacao.objects.filter(materia=materia).last() if loc_atual is None: localizacao = 0 @@ -1167,47 +1132,9 @@ class RelatoriaCrud(MasterDetailCrud): class UpdateView(MasterDetailCrud.UpdateView): form_class = RelatoriaForm - + layout_key = None 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): model = Tramitacao @@ -2035,17 +1962,35 @@ class DocumentoAcessorioEmLoteView(PermissionRequiredMixin, FilterView): messages.add_message(request, messages.ERROR, msg) 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: doc = DocumentoAcessorio() doc.materia_id = materia_id doc.tipo = tipo - doc.arquivo = request.FILES['arquivo'] doc.nome = request.POST['nome'] - doc.data = tz.localize(datetime.strptime( - request.POST['data'], "%d/%m/%Y")) + doc.data = doc_data doc.autor = request.POST['autor'] doc.ementa = request.POST['ementa'] 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).') messages.add_message(request, messages.SUCCESS, msg) return self.get(request, self.kwargs) @@ -2225,8 +2170,18 @@ class PrimeiraTramitacaoEmLoteView(PermissionRequiredMixin, FilterView): # issue https://github.com/interlegis/sapl/issues/1123 # TODO: usar Form urgente = request.POST['urgente'] == 'True' - flag_error = False - for materia_id in marcadas: + flag_error = False + + 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: data_tramitacao = tz.localize(datetime.strptime( request.POST['data_tramitacao'], "%d/%m/%Y")) @@ -2236,7 +2191,7 @@ class PrimeiraTramitacaoEmLoteView(PermissionRequiredMixin, FilterView): return self.get(request, self.kwargs) t = Tramitacao( - materia_id=materia_id, + materia=materia, data_tramitacao=data_tramitacao, data_encaminhamento=data_encaminhamento, data_fim_prazo=data_fim_prazo, @@ -2270,7 +2225,7 @@ class PrimeiraTramitacaoEmLoteView(PermissionRequiredMixin, FilterView): 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': materia.em_tramitacao = False elif self.primeira_tramitacao: diff --git a/sapl/parlamentares/forms.py b/sapl/parlamentares/forms.py index c9d5d828d..e1e773d9b 100755 --- a/sapl/parlamentares/forms.py +++ b/sapl/parlamentares/forms.py @@ -525,3 +525,65 @@ class VotanteForm(ModelForm): votante.user = u votante.save() 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 diff --git a/sapl/parlamentares/urls.py b/sapl/parlamentares/urls.py index 668a65653..51fb856b4 100644 --- a/sapl/parlamentares/urls.py +++ b/sapl/parlamentares/urls.py @@ -19,7 +19,7 @@ from sapl.parlamentares.views import (CargoMesaCrud, ColigacaoCrud, parlamentares_frente_selected, remove_parlamentar_composicao, parlamentares_filiados, - PesquisarParlamentarView) + PesquisarParlamentarView, VincularParlamentarView) from .apps import AppConfig @@ -41,6 +41,9 @@ urlpatterns = [ url(r'^parlamentar/(?P\d+)/materias$', ParlamentarMateriasView.as_view(), name='parlamentar_materias'), + url(r'^parlamentar/vincular-parlamentar/$', + VincularParlamentarView.as_view(), name='vincular_parlamentar'), + url(r'^sistema/coligacao/', include(ColigacaoCrud.get_urls() + ComposicaoColigacaoCrud.get_urls())), diff --git a/sapl/parlamentares/views.py b/sapl/parlamentares/views.py index 772f06eaa..d5e8cc41a 100644 --- a/sapl/parlamentares/views.py +++ b/sapl/parlamentares/views.py @@ -3,6 +3,7 @@ import json import logging from django.contrib import messages +from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.contenttypes.models import ContentType from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist 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 .forms import (FiliacaoForm, FrenteForm, LegislaturaForm, MandatoForm, - ParlamentarCreateForm, ParlamentarForm, VotanteForm, ParlamentarFilterSet) + ParlamentarCreateForm, ParlamentarForm, VotanteForm, ParlamentarFilterSet, VincularParlamentarForm) from .models import (CargoMesa, Coligacao, ComposicaoColigacao, ComposicaoMesa, Dependente, Filiacao, Frente, Legislatura, Mandato, NivelInstrucao, Parlamentar, Partido, SessaoLegislativa, @@ -1132,3 +1133,30 @@ def altera_field_mesa_public_view(request): 'lista_fotos': lista_fotos, 'sessao_selecionada': sessao_selecionada, '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()) diff --git a/sapl/relatorios/templates/pdf_sessao_plenaria_gerar.py b/sapl/relatorios/templates/pdf_sessao_plenaria_gerar.py index 6a39fa1c1..4c388e59a 100644 --- a/sapl/relatorios/templates/pdf_sessao_plenaria_gerar.py +++ b/sapl/relatorios/templates/pdf_sessao_plenaria_gerar.py @@ -340,6 +340,24 @@ def votacao_vot_nom(lst_votacao_vot_nom): return tmp +def oradores_ordemdia(lst_oradores_ordemdia): + """ + + """ + tmp = '' + tmp += '\t\tOradores da Ordem do Dia\n' + tmp += '\t\t\n' + tmp += '\t\t\t
\n' + tmp += '\t\t
\n' + for orador_ordemdia in lst_oradores_ordemdia: + tmp += '\t\t' + \ + str(orador_ordemdia['num_ordem']) + ' - ' + \ + orador_ordemdia['nome_parlamentar'] + '/' + \ + str(orador_ordemdia['sigla']) + ' - ' + \ + str(orador_ordemdia['observacao']) + '\n' + return tmp + + def oradores(lst_oradores): """ @@ -374,7 +392,7 @@ def ocorrencias(lst_ocorrencias): 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" @@ -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), 'mesa_d': mesa(lst_mesa), 'oradores_exped': oradores_expediente(lst_oradores_expediente), + 'oradores_o_d': oradores_ordemdia(lst_oradores_ordemdia), 'oradores_expli': oradores(lst_oradores), '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_segundo] tmp += dict_ord_template[ordenacao.decimo_terceiro] + tmp += dict_ord_template[ordenacao.decimo_quarto] except KeyError as e: logger.error("KeyError: " + str(e) + ". Erro ao tentar utilizar " "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 += votacao(lst_votacao) tmp += votacao_vot_nom(lst_votacao_vot_nom) + tmp += oradores_ordemdia(lst_oradores_ordemdia) tmp += oradores(lst_oradores) 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 += votacao(lst_votacao) tmp += votacao_vot_nom(lst_votacao_vot_nom) + tmp += oradores_ordemdia(lst_oradores_ordemdia) tmp += oradores(lst_oradores) tmp += ocorrencias(lst_ocorrencias) diff --git a/sapl/relatorios/views.py b/sapl/relatorios/views.py index dec1d50b7..117b7a172 100755 --- a/sapl/relatorios/views.py +++ b/sapl/relatorios/views.py @@ -23,15 +23,16 @@ from sapl.sessao.models import (ExpedienteMateria, ExpedienteSessao, Orador, OradorExpediente, OrdemDia, PresencaOrdemDia, SessaoPlenaria, SessaoPlenariaPresenca, OcorrenciaSessao, - RegistroVotacao, VotoParlamentar) + RegistroVotacao, VotoParlamentar, OradorOrdemDia) from sapl.settings import STATIC_ROOT from sapl.utils import LISTA_DE_UFS, TrocaTag, filiacao_data from sapl.sessao.views import (get_identificação_basica, get_mesa_diretora, - get_presenca_sessao,get_expedientes, - get_materias_expediente,get_oradores_expediente, - get_presenca_ordem_do_dia,get_materias_ordem_do_dia, - get_oradores_explicações_pessoais, get_ocorrencias_da_sessão) + get_presenca_sessao, get_expedientes, + get_materias_expediente, get_oradores_expediente, + get_presenca_ordem_do_dia, get_materias_ordem_do_dia, + get_oradores_ordemdia, + get_oradores_explicações_pessoais, get_ocorrencias_da_sessão, get_assinaturas) from .templates import (pdf_capa_processo_gerar, pdf_documento_administrativo_gerar, pdf_espelho_gerar, @@ -578,6 +579,8 @@ def get_sessao_plenaria(sessao, casa): # unescape HTML codes # https://github.com/interlegis/sapl/issues/1046 conteudo = re.sub('style=".*?"', '', conteudo) + conteudo = re.sub('class=".*?"', '', conteudo) + conteudo = re.sub('', '

', conteudo) conteudo = html.unescape(conteudo) # escape special character '&' @@ -778,6 +781,35 @@ def get_sessao_plenaria(sessao, casa): } 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 lst_oradores = [] for orador in Orador.objects.filter( @@ -828,6 +860,7 @@ def get_sessao_plenaria(sessao, casa): lst_presenca_ordem_dia, lst_votacao, lst_votacao_vot_nom, + lst_oradores_ordemdia, lst_oradores, lst_ocorrencias) @@ -884,7 +917,8 @@ def relatorio_sessao_plenaria(request, pk): lst_oradores_expediente, lst_presenca_ordem_dia, lst_votacao, - lst_votacao_vot_nom, + lst_votacao_vot_nom, + lst_oradores_ordemdia, lst_oradores, lst_ocorrencias) = get_sessao_plenaria(sessao, casa) @@ -908,6 +942,7 @@ def relatorio_sessao_plenaria(request, pk): lst_presenca_ordem_dia, lst_votacao, lst_votacao_vot_nom, + lst_oradores_ordemdia, lst_oradores, lst_ocorrencias) @@ -1254,14 +1289,16 @@ def resumo_ata_pdf(request,pk): context.update(get_oradores_expediente(sessao_plenaria)) context.update(get_presenca_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_ocorrencias_da_sessão(sessao_plenaria)) - context.update({'object':sessao_plenaria}) + context.update(get_assinaturas(sessao_plenaria)) + context.update({'object': sessao_plenaria}) context.update({'data': dt.today().strftime('%d/%m/%Y')}) - context.update({'rodape':rodape}) - header_context = {"casa":casa, 'logotipo':casa.logotipo, 'MEDIA_URL': MEDIA_URL} + context.update({'rodape': rodape}) + header_context = {"casa": casa, 'logotipo':casa.logotipo, 'MEDIA_URL': MEDIA_URL} - html_template = render_to_string('relatorios/relatorio_ata.html',context) + html_template = render_to_string('relatorios/relatorio_ata.html', context) html_header = render_to_string('relatorios/header_ata.html', header_context) pdf_file = make_pdf(base_url=base_url,main_template=html_template,header_template=html_header) diff --git a/sapl/rules/map_rules.py b/sapl/rules/map_rules.py index 13c89a547..d09e65961 100644 --- a/sapl/rules/map_rules.py +++ b/sapl/rules/map_rules.py @@ -175,6 +175,7 @@ rules_group_sessao = { (sessao.ExpedienteSessao, __base__, __perms_publicas__), (sessao.Orador, __base__, __perms_publicas__), (sessao.OradorExpediente, __base__, __perms_publicas__), + (sessao.OradorOrdemDia, __base__, __perms_publicas__), (sessao.OrdemDia, __base__, __perms_publicas__), (sessao.PresencaOrdemDia, __base__, __perms_publicas__), (sessao.RegistroVotacao, __base__, __perms_publicas__), diff --git a/sapl/sessao/forms.py b/sapl/sessao/forms.py index c2532b4a1..373b1e951 100644 --- a/sapl/sessao/forms.py +++ b/sapl/sessao/forms.py @@ -26,7 +26,7 @@ from sapl.utils import (RANGE_DIAS_MES, RANGE_MESES, from .models import (Bancada, Bloco, ExpedienteMateria, JustificativaAusencia, Orador, OradorExpediente, OrdemDia, PresencaOrdemDia, SessaoPlenaria, SessaoPlenariaPresenca, TipoResultadoVotacao, - OcorrenciaSessao, RetiradaPauta, TipoRetiradaPauta) + OcorrenciaSessao, RetiradaPauta, TipoRetiradaPauta, OradorOrdemDia) MES_CHOICES = RANGE_MESES @@ -45,7 +45,8 @@ ORDENACAO_RESUMO = [('cont_mult', 'Conteúdo Multimídia'), ('oradores_expli', 'Oradores das Explicações Pessoais'), ('ocorr_sessao', 'Ocorrências da Sessão'), ('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): @@ -692,7 +693,29 @@ class OradorForm(ModelForm): self.fields['parlamentar'].queryset = Parlamentar.objects.filter( 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: model = Orador exclude = ['sessao_plenaria'] @@ -736,6 +759,49 @@ class OradorExpedienteForm(ModelForm): 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): titulo = _('Pesquisa de Pauta de Sessão') @@ -767,6 +833,8 @@ class ResumoOrdenacaoForm(forms.Form): choices=ORDENACAO_RESUMO) decimo_terceiro = forms.ChoiceField(label='13°', choices=ORDENACAO_RESUMO) + decimo_quarto = forms.ChoiceField(label='14°', + choices=ORDENACAO_RESUMO) def __init__(self, *args, **kwargs): super(ResumoOrdenacaoForm, self).__init__(*args, **kwargs) @@ -797,13 +865,16 @@ class ResumoOrdenacaoForm(forms.Form): [('decimo_segundo', 12)]) row13 = to_row( [('decimo_terceiro', 12)]) + row14 = to_row( + [('decimo_quarto', 12)] + ) self.helper = SaplFormHelper() self.helper.layout = Layout( Fieldset(_(''), row1, row2, row3, row4, row5, row6, row7, row8, row9, row10, - row11, row12, row13, + row11, row12, row13, row14, form_actions(label='Atualizar')) ) diff --git a/sapl/sessao/migrations/0034_oradorordemdia.py b/sapl/sessao/migrations/0034_oradorordemdia.py new file mode 100644 index 000000000..b0e5976fb --- /dev/null +++ b/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', + }, + ), + ] diff --git a/sapl/sessao/migrations/0035_resumoordenacao_decimo_quarto.py b/sapl/sessao/migrations/0035_resumoordenacao_decimo_quarto.py new file mode 100644 index 000000000..f8e00489e --- /dev/null +++ b/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), + ), + ] diff --git a/sapl/sessao/models.py b/sapl/sessao/models.py index 34ed20cca..7267e78fc 100644 --- a/sapl/sessao/models.py +++ b/sapl/sessao/models.py @@ -378,6 +378,14 @@ class OradorExpediente(AbstractOrador): # OradoresExpediente 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() 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_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_quarto = models.CharField(max_length=30, default="Oradores da Ordem do Dia") class Meta: verbose_name = _('Ordenação do Resumo de uma Sessão') diff --git a/sapl/sessao/urls.py b/sapl/sessao/urls.py index 4da3a761a..00add810a 100644 --- a/sapl/sessao/urls.py +++ b/sapl/sessao/urls.py @@ -4,7 +4,8 @@ from sapl.sessao.views import (AdicionarVariasMateriasExpediente, AdicionarVariasMateriasOrdemDia, BancadaCrud, BlocoCrud, CargoBancadaCrud, ExpedienteMateriaCrud, ExpedienteView, JustificativaAusenciaCrud, - OcorrenciaSessaoView, MateriaOrdemDiaCrud, MesaView, OradorCrud, + OcorrenciaSessaoView, MateriaOrdemDiaCrud, OradorOrdemDiaCrud, + MesaView, OradorCrud, OradorExpedienteCrud, PainelView, PautaSessaoDetailView, PautaSessaoView, PesquisarPautaSessaoView, @@ -28,6 +29,8 @@ from sapl.sessao.views import (AdicionarVariasMateriasExpediente, remove_parlamentar_composicao, reordernar_materias_expediente, reordernar_materias_ordem, + renumerar_materias_ordem, + renumerar_materias_expediente, sessao_legislativa_legislatura_ajax, VotacaoEmBlocoOrdemDia, VotacaoEmBlocoExpediente, VotacaoEmBlocoSimbolicaView, VotacaoEmBlocoNominalView) @@ -42,7 +45,9 @@ urlpatterns = [ OradorExpedienteCrud.get_urls() + ExpedienteMateriaCrud.get_urls() + JustificativaAusenciaCrud.get_urls() + - MateriaOrdemDiaCrud.get_urls() + RetiradaPautaCrud.get_urls())), + MateriaOrdemDiaCrud.get_urls() + + OradorOrdemDiaCrud.get_urls() + + RetiradaPautaCrud.get_urls())), url(r'^sessao/(?P\d+)/mesa$', MesaView.as_view(), name='mesa'), @@ -75,6 +80,10 @@ urlpatterns = [ name="reordenar_expediente"), url(r'^sessao/(?P\d+)/reordenar-ordem$', reordernar_materias_ordem, name="reordenar_ordem"), + url(r'^sessao/(?P\d+)/renumerar-ordem$', renumerar_materias_ordem, + name="renumerar_ordem"), + url(r'^sessao/(?P\d+)/renumerar-materias-expediente$', renumerar_materias_expediente, + name="renumerar_materias_expediente"), url(r'^sistema/sessao-plenaria/tipo/', include(TipoSessaoCrud.get_urls())), url(r'^sistema/sessao-plenaria/tipo-resultado-votacao/', diff --git a/sapl/sessao/views.py b/sapl/sessao/views.py index 4e4ad02fa..08adf1fa0 100755 --- a/sapl/sessao/views.py +++ b/sapl/sessao/views.py @@ -42,14 +42,14 @@ from .forms import (AdicionarVariasMateriasFilterSet, BancadaForm, BlocoForm, MesaForm, OradorExpedienteForm, OradorForm, PautaSessaoFilterSet, PresencaForm, ResumoOrdenacaoForm, SessaoPlenariaFilterSet, SessaoPlenariaForm, VotacaoEditForm, VotacaoForm, - VotacaoNominalForm, RetiradaPautaForm) + VotacaoNominalForm, RetiradaPautaForm, OradorOrdemDiaForm) from .models import (Bancada, Bloco, CargoBancada, CargoMesa, ExpedienteMateria, ExpedienteSessao, OcorrenciaSessao, IntegranteMesa, MateriaLegislativa, Orador, OradorExpediente, OrdemDia, PresencaOrdemDia, RegistroVotacao, ResumoOrdenacao, SessaoPlenaria, SessaoPlenariaPresenca, TipoExpediente, TipoResultadoVotacao, TipoSessaoPlenaria, VotoParlamentar, TipoRetiradaPauta, - RetiradaPauta, TipoJustificativa, JustificativaAusencia) + RetiradaPauta, TipoJustificativa, JustificativaAusencia, OradorOrdemDia) TipoSessaoCrud = CrudAux.build(TipoSessaoPlenaria, 'tipo_sessao_plenaria') @@ -93,6 +93,25 @@ def reordernar_materias_ordem(request, pk): return HttpResponseRedirect( 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): logger = logging.getLogger(__name__) @@ -599,6 +618,31 @@ class OradorExpedienteCrud(OradorCrud): '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): model = Orador @@ -620,6 +664,7 @@ class OradorCrud(OradorCrud): 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 @@ -1270,7 +1315,8 @@ class ResumoOrdenacaoView(PermissionRequiredMixin, FormView): 'decimo': ordenacao.decimo, 'decimo_primeiro': ordenacao.decimo_primeiro, 'decimo_segundo': ordenacao.decimo_segundo, - 'decimo_terceiro': ordenacao.decimo_terceiro}) + 'decimo_terceiro': ordenacao.decimo_terceiro, + 'decimo_quarto': ordenacao.decimo_quarto}) return initial def form_valid(self, form): @@ -1289,6 +1335,7 @@ class ResumoOrdenacaoView(PermissionRequiredMixin, FormView): ordenacao.decimo_primeiro = form.cleaned_data['decimo_primeiro'] ordenacao.decimo_segundo = form.cleaned_data['decimo_segundo'] ordenacao.decimo_terceiro = form.cleaned_data['decimo_terceiro'] + ordenacao.decimo_quarto = form.cleaned_data['decimo_quarto'] ordenacao.save() @@ -1378,9 +1425,15 @@ def get_materias_expediente(sessao_plenaria): ementa = m.materia.ementa titulo = m.materia 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: turno = get_turno(tramitacao.turno) @@ -1486,7 +1539,14 @@ def get_materias_ordem_do_dia(sessao_plenaria): ementa_observacao = o.observacao titulo = o.materia 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 if tramitacao: turno = get_turno(tramitacao.turno) @@ -1551,6 +1611,32 @@ def get_materias_ordem_do_dia(sessao_plenaria): 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): oradores_explicacoes = [] for orador in Orador.objects.filter( @@ -1665,6 +1751,9 @@ class ResumoView(DetailView): 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 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', 'mesa_d': 'mesa_diretora.html', 'oradores_exped': 'oradores_expediente.html', + 'oradores_o_d': 'oradores_ordemdia.html', 'oradores_expli': 'oradores_explicacoes.html', 'ocorr_sessao': 'ocorrencias_da_sessao.html' } @@ -1704,7 +1794,8 @@ class ResumoView(DetailView): 'decimo_ordenacao': dict_ord_template[ordenacao.decimo], 'decimo_primeiro_ordenacao': dict_ord_template[ordenacao.decimo_primeiro], '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: 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.") @@ -1720,8 +1811,9 @@ class ResumoView(DetailView): 'nono_ordenacao': dict_ord_template['lista_p_o_d'], 'decimo_ordenacao': dict_ord_template['mat_o_d'], 'decimo_primeiro_ordenacao': dict_ord_template['v_n_mat_o_d'], - 'decimo_segundo_ordenacao': dict_ord_template['oradores_expli'], - 'decimo_terceiro_ordenacao': dict_ord_template['ocorr_sessao'] + 'decimo_segundo_ordenacao': dict_ord_template['oradores_o_d'], + 'decimo_terceiro_ordenacao': dict_ord_template['oradores_expli'], + 'decimo_quarto_ordenacao': dict_ord_template['ocorr_sessao'] }) else: context.update( @@ -1736,8 +1828,9 @@ class ResumoView(DetailView): 'nono_ordenacao': dict_ord_template['lista_p_o_d'], 'decimo_ordenacao': dict_ord_template['mat_o_d'], 'decimo_primeiro_ordenacao': dict_ord_template['v_n_mat_o_d'], - 'decimo_segundo_ordenacao': dict_ord_template['oradores_expli'], - 'decimo_terceiro_ordenacao': dict_ord_template['ocorr_sessao'] + 'decimo_segundo_ordenacao': dict_ord_template['oradores_o_d'], + 'decimo_terceiro_ordenacao': dict_ord_template['oradores_expli'], + 'decimo_quarto_ordenacao': dict_ord_template['ocorr_sessao'] }) return context diff --git a/sapl/settings.py b/sapl/settings.py index 8bad6c92e..7d414cdbf 100644 --- a/sapl/settings.py +++ b/sapl/settings.py @@ -41,7 +41,7 @@ ALLOWED_HOSTS = ['*'] LOGIN_REDIRECT_URL = '/' LOGIN_URL = '/login/?next=' -SAPL_VERSION = '3.1.150' +SAPL_VERSION = '3.1.151' if DEBUG: EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' diff --git a/sapl/templates/base.html b/sapl/templates/base.html index 2fa19c1cd..1944d53ea 100644 --- a/sapl/templates/base.html +++ b/sapl/templates/base.html @@ -179,7 +179,7 @@ Desenvolvido pelo Interlegis em software livre e aberto. - Release: 3.1.150 + Release: 3.1.151

diff --git a/sapl/templates/materia/relatoria_form.html b/sapl/templates/materia/relatoria_form.html index 5dc3ca9c9..02f9a904a 100644 --- a/sapl/templates/materia/relatoria_form.html +++ b/sapl/templates/materia/relatoria_form.html @@ -16,5 +16,24 @@ {% block extra_js %} {% endblock extra_js %} diff --git a/sapl/templates/parlamentares/layouts.yaml b/sapl/templates/parlamentares/layouts.yaml index ded7ca647..030e32758 100644 --- a/sapl/templates/parlamentares/layouts.yaml +++ b/sapl/templates/parlamentares/layouts.yaml @@ -30,7 +30,7 @@ SessaoLegislativa: - data_inicio_intervalo data_fim_intervalo Parlamentar: - {% trans 'Cadastro do Parlamentar' %}: + {% trans 'Dados do Parlamentar' %}: - nome_parlamentar:8 ativo - nome_completo - nivel_instrucao sexo data_nascimento @@ -47,7 +47,7 @@ Parlamentar: - biografia ParlamentarUpdate: - {% trans 'Cadastro do Parlamentar' %}: + {% trans 'Dados do Parlamentar' %}: - nome_parlamentar:8 ativo - nome_completo - nivel_instrucao sexo data_nascimento @@ -64,9 +64,7 @@ ParlamentarUpdate: - biografia ParlamentarCreate: - {% trans 'Dados do Mandato' %}: - - legislatura data_expedicao_diploma - {% trans 'Cadastro do Parlamentar' %}: + {% trans 'Dados do Parlamentar' %}: - nome_parlamentar:8 ativo - nome_completo - nivel_instrucao sexo data_nascimento diff --git a/sapl/templates/parlamentares/parlamentar_filter.html b/sapl/templates/parlamentares/parlamentar_filter.html index 9a3ea54f7..dd6af7034 100644 --- a/sapl/templates/parlamentares/parlamentar_filter.html +++ b/sapl/templates/parlamentares/parlamentar_filter.html @@ -5,44 +5,44 @@ {% block base_content %} {% if not show_results %} {% crispy filter.form %} - {% endif %} - - {% if show_results %} -
+ {% else %} + -

+ {% if not request.user.is_anonymous %} + Cadastrar Parlamentar + {% endif %} +
+
{% if numero_res > 0 %} {% if numero_res == 1 %}

Foi encontrado {{ numero_res }} resultado

{% else %}

Foram encontrados {{ numero_res }} resultados

{% endif %} - - - - +
Nome do Parlamentar
+ + + - - - - {% for usuario in page_obj %} - - + + + {% for usuario in page_obj %} + + - - {% endfor %} - -
Nome do Parlamentar Nome Completo Ativo
+
{{usuario.nome_parlamentar}} {{ usuario.nome_completo }} {% if usuario.ativo %} Sim {% else %} Não {% endif %}
+ + {% endfor %} + + {% else %}

{{ NO_ENTRIES_MSG }}

- {% endif %} + {% endif %} {% endif %} -
{% include 'paginacao.html'%}


diff --git a/sapl/templates/parlamentares/parlamentares_list.html b/sapl/templates/parlamentares/parlamentares_list.html index 88ad4acfe..a91d50da9 100644 --- a/sapl/templates/parlamentares/parlamentares_list.html +++ b/sapl/templates/parlamentares/parlamentares_list.html @@ -1,6 +1,15 @@ {% extends "crud/list.html" %} {% load i18n %} {% load crispy_forms_tags cropping%} +{% block actions %} +
+ Pesquisar Parlamentar + {% if not request.user.is_anonymous %} + Vincular Parlamentar + {% endif %} + {% block more_buttons %}{% endblock more_buttons %} +
+{% endblock actions %} {% block extra_content %}
Selecione o Período diff --git a/sapl/templates/parlamentares/vincular_parlamentar.html b/sapl/templates/parlamentares/vincular_parlamentar.html new file mode 100644 index 000000000..6ba8948dd --- /dev/null +++ b/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 %} \ No newline at end of file diff --git a/sapl/templates/relatorios/relatorio_ata.html b/sapl/templates/relatorios/relatorio_ata.html index 743e13c82..05c5355c4 100644 --- a/sapl/templates/relatorios/relatorio_ata.html +++ b/sapl/templates/relatorios/relatorio_ata.html @@ -46,6 +46,7 @@ {% include 'sessao/blocos_ata/oradores_expediente.html' %} {% include 'sessao/blocos_ata/lista_presenca_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/ocorrencias_da_sessao.html' %} @@ -82,12 +83,13 @@
_____________________
-

{{p.cargo}}: {{p.parlamentar.nome_completo}} / {{ p.parlamentar|filiacao_data_filter:object.data_inicio }}

+

+ {{p.nome_completo}} / {{ p|filiacao_data_filter:object.data_inicio }}




{% else %}
_____________________
-

{{p.parlamentar.nome_completo}} / {{ p.parlamentar|filiacao_data_filter:object.data_inicio }}

+

{{p.nome_completo}} / {{ p|filiacao_data_filter:object.data_inicio }}




diff --git a/sapl/templates/sessao/blocos_ata/oradores_ordemdia.html b/sapl/templates/sessao/blocos_ata/oradores_ordemdia.html new file mode 100644 index 000000000..62ec92e81 --- /dev/null +++ b/sapl/templates/sessao/blocos_ata/oradores_ordemdia.html @@ -0,0 +1,13 @@ +
+

+ {% if oradores_ordemdia %} + Oradores da Ordem do Dia: + {% for orador in oradores_ordemdia %} +

{{ orador.numero_ordem }} - {{ orador.parlamentar.nome_completo }}
+
{{ orador.url_discurso }}
+
{{ orador.observacao }}
+
+ {% endfor %} + {% endif %} +

+
\ No newline at end of file diff --git a/sapl/templates/sessao/blocos_resumo/oradores_ordemdia.html b/sapl/templates/sessao/blocos_resumo/oradores_ordemdia.html new file mode 100644 index 000000000..a1898cce0 --- /dev/null +++ b/sapl/templates/sessao/blocos_resumo/oradores_ordemdia.html @@ -0,0 +1,15 @@ +
+ Oradores da Ordem do Dia +
+
Parlamentar
+
Discurso
+
Observação
+
+
+ {% for orador in oradores_ordemdia %} +
{{ orador.numero_ordem }} - {{ orador.parlamentar }}
+
{{ orador.url_discurso }}
+
{{ orador.observacao }}
+ {% endfor %} +
+
\ No newline at end of file diff --git a/sapl/templates/sessao/expedientemateria_list.html b/sapl/templates/sessao/expedientemateria_list.html index a2ce5e2ab..e1bd0f781 100644 --- a/sapl/templates/sessao/expedientemateria_list.html +++ b/sapl/templates/sessao/expedientemateria_list.html @@ -7,7 +7,10 @@ {% if perms|get_add_perm:view %} - {% blocktrans with verbose_name=view.verbose_name %} Ajustar Ordenação {% endblocktrans %} + {% blocktrans with verbose_name=view.verbose_name %} Reordenar pela precedência {% endblocktrans %} + + + {% blocktrans with verbose_name=view.verbose_name %} Renumerar Expediente {% endblocktrans %} {% blocktrans with verbose_name=view.verbose_name %} Adicionar Várias Matérias {% endblocktrans %} diff --git a/sapl/templates/sessao/layouts.yaml b/sapl/templates/sessao/layouts.yaml index 55e0db570..98307d74d 100644 --- a/sapl/templates/sessao/layouts.yaml +++ b/sapl/templates/sessao/layouts.yaml @@ -39,6 +39,12 @@ OradorExpediente: - url_discurso observacao - upload_anexo +OradorOrdemDia: + {% trans 'Orador da Ordem do Dia' %}: + - numero_ordem parlamentar + - url_discurso observacao + - upload_anexo + ExpedienteMateria: {% trans 'Matéria do Expediente' %}: - data_ordem numero_ordem diff --git a/sapl/templates/sessao/ordemdia_list.html b/sapl/templates/sessao/ordemdia_list.html index d12e3f356..bc95cf1f1 100644 --- a/sapl/templates/sessao/ordemdia_list.html +++ b/sapl/templates/sessao/ordemdia_list.html @@ -7,7 +7,10 @@ {% if perms|get_add_perm:view %} - {% blocktrans with verbose_name=view.verbose_name %} Ajustar Ordenação {% endblocktrans %} + {% blocktrans with verbose_name=view.verbose_name %} Reordenar pela precedência {% endblocktrans %} + + + {% blocktrans with verbose_name=view.verbose_name %} Renumerar Ordem {% endblocktrans %} {% blocktrans with verbose_name=view.verbose_name %} Adicionar Várias Matérias {% endblocktrans %} diff --git a/sapl/templates/sessao/resumo.html b/sapl/templates/sessao/resumo.html index bcedeedb8..0be605eb8 100644 --- a/sapl/templates/sessao/resumo.html +++ b/sapl/templates/sessao/resumo.html @@ -58,5 +58,8 @@ {% include 'sessao/blocos_resumo/'|add:decimo_terceiro_ordenacao %}


+ + {% include 'sessao/blocos_resumo/'|add:decimo_quarto_ordenacao %} +


{% endblock detail_content %} diff --git a/sapl/templates/sessao/resumo_ata.html b/sapl/templates/sessao/resumo_ata.html index 12507b79a..2f450b97e 100644 --- a/sapl/templates/sessao/resumo_ata.html +++ b/sapl/templates/sessao/resumo_ata.html @@ -32,5 +32,6 @@ {% include 'sessao/blocos_ata/'|add:decimo_primeiro_ordenacao %} {% include 'sessao/blocos_ata/'|add:decimo_segundo_ordenacao %} {% include 'sessao/blocos_ata/'|add:decimo_terceiro_ordenacao %} + {% include 'sessao/blocos_ata/'|add:decimo_quarto_ordenacao %} {% include 'sessao/blocos_ata/assinaturas.html' %} {% endblock detail_content %} diff --git a/sapl/templates/sessao/subnav.yaml b/sapl/templates/sessao/subnav.yaml index 1decc0ded..28717e4ab 100644 --- a/sapl/templates/sessao/subnav.yaml +++ b/sapl/templates/sessao/subnav.yaml @@ -35,6 +35,8 @@ url: ordemdia_list - title: {% trans 'Presença Ordem do Dia' %} url: presencaordemdia + - title: {% trans 'Oradores da Ordem do Dia' %} + url: oradorordemdia_list - title: {% trans 'Votação em Bloco' %} url: votacao_bloco_ordemdia check_permission: sessao.add_sessaoplenaria diff --git a/setup.py b/setup.py index 1edc89a91..57429e5a0 100644 --- a/setup.py +++ b/setup.py @@ -43,7 +43,7 @@ install_requires = [ ] setup( name='interlegis-sapl', - version='3.1.150', + version='3.1.151', packages=find_packages(), include_package_data=True, license='GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007',