From 0585bd7130efaa95b5718c3bebdadd3da8a51bea Mon Sep 17 00:00:00 2001 From: Edward Date: Thu, 28 Mar 2019 17:29:13 -0300 Subject: [PATCH 01/18] =?UTF-8?q?HOT-FIX:=20remove=20refer=C3=AAncia=20dup?= =?UTF-8?q?la=20a=20openssh-client?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index d970a4e4c..cc8d62e22 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 From 7e37308042141836943cd9744275855a5c1f004a Mon Sep 17 00:00:00 2001 From: Edward Date: Thu, 28 Mar 2019 17:48:37 -0300 Subject: [PATCH 02/18] =?UTF-8?q?HOT-FIX:=20Remove=20coment=C3=A1rio=20de?= =?UTF-8?q?=20Dockerfile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 3 --- 1 file changed, 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index cc8d62e22..015357a40 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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 From c9d2295033cc70ad20f49185307ce3076f8c36ed Mon Sep 17 00:00:00 2001 From: Cesar Augusto de Carvalho Date: Fri, 29 Mar 2019 15:33:01 -0300 Subject: [PATCH 03/18] =?UTF-8?q?Fix=20Documento=20Acess=C3=B3rio=20em=20L?= =?UTF-8?q?ote=20para=20Arquivos=20Grandes=20(#2664)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migrations/0044_auto_20190327_1409.py | 22 +++++++++++++ sapl/materia/models.py | 1 + sapl/materia/views.py | 32 ++++++++++++++++--- 3 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 sapl/materia/migrations/0044_auto_20190327_1409.py 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/views.py b/sapl/materia/views.py index 696b1235a..846f367fa 100644 --- a/sapl/materia/views.py +++ b/sapl/materia/views.py @@ -1,5 +1,10 @@ -from datetime import datetime import logging +import os +import shutil +import tempfile +import weasyprint + +from datetime import datetime from random import choice from string import ascii_letters, digits @@ -45,6 +50,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, @@ -2035,17 +2041,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) From fb6832e7fb38bab0a9938fd8ef4ff9caaa7cfdc0 Mon Sep 17 00:00:00 2001 From: Cesar Augusto de Carvalho Date: Mon, 1 Apr 2019 14:29:06 -0300 Subject: [PATCH 04/18] =?UTF-8?q?Fix=20#2665=20-=20Tramitar=20mat=C3=A9ria?= =?UTF-8?q?s=20anexadas=20junto=20com=20as=20mat=C3=A9rias=20anexadoras=20?= =?UTF-8?q?(#2674)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sapl/materia/views.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/sapl/materia/views.py b/sapl/materia/views.py index 846f367fa..a2366c8d9 100644 --- a/sapl/materia/views.py +++ b/sapl/materia/views.py @@ -3,6 +3,7 @@ import os import shutil import tempfile import weasyprint +import itertools from datetime import datetime from random import choice @@ -2249,8 +2250,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")) @@ -2260,7 +2271,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, @@ -2294,7 +2305,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: From 2bcaedda76a8a99e4febac0595d2d4e565b2da22 Mon Sep 17 00:00:00 2001 From: Edward Ribeiro Date: Mon, 1 Apr 2019 14:48:45 -0300 Subject: [PATCH 05/18] =?UTF-8?q?Retira=20atributos=20class=20e=20espa?= =?UTF-8?q?=C3=A7os=20de=20tags=20

?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sapl/relatorios/views.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sapl/relatorios/views.py b/sapl/relatorios/views.py index dec1d50b7..9439b1578 100755 --- a/sapl/relatorios/views.py +++ b/sapl/relatorios/views.py @@ -578,6 +578,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 '&' From 4b35e6a4fe37ba10d6ac95ae02cc220a14c61e80 Mon Sep 17 00:00:00 2001 From: Ulysses Lara Date: Mon, 1 Apr 2019 15:09:50 -0300 Subject: [PATCH 06/18] Fix #2663 parte relacionada a turno (#2675) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix #2663 parte relacionada a turno * Retira parênteses desnecessários --- sapl/sessao/views.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/sapl/sessao/views.py b/sapl/sessao/views.py index 4e4ad02fa..9300ab9e1 100755 --- a/sapl/sessao/views.py +++ b/sapl/sessao/views.py @@ -1378,9 +1378,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 +1492,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) From 52de1510019bdfbc77a6bcb962868bdf9356686e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Rodrigues?= Date: Mon, 1 Apr 2019 15:11:33 -0300 Subject: [PATCH 07/18] Fix #2671 (#2673) * Fix #2671 * Atualizar sessao/forms.py * Atualizar sessao/forms.py --- sapl/sessao/forms.py | 22 ++++++++++++++++++++++ sapl/sessao/views.py | 1 + 2 files changed, 23 insertions(+) diff --git a/sapl/sessao/forms.py b/sapl/sessao/forms.py index c2532b4a1..172b37be5 100644 --- a/sapl/sessao/forms.py +++ b/sapl/sessao/forms.py @@ -692,7 +692,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'] diff --git a/sapl/sessao/views.py b/sapl/sessao/views.py index 9300ab9e1..c5a6620c4 100755 --- a/sapl/sessao/views.py +++ b/sapl/sessao/views.py @@ -620,6 +620,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 From 31e3cd7b4474c2273a8a8606110ec78bd772dc45 Mon Sep 17 00:00:00 2001 From: Victor Fabre Date: Mon, 1 Apr 2019 17:29:10 -0300 Subject: [PATCH 08/18] Fix #2679 (#2680) --- sapl/materia/forms.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sapl/materia/forms.py b/sapl/materia/forms.py index 4b75e134a..cf200447e 100644 --- a/sapl/materia/forms.py +++ b/sapl/materia/forms.py @@ -727,7 +727,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 +751,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) From 0b7ab48218e76a8cfb69e48499ab7b2779391e18 Mon Sep 17 00:00:00 2001 From: Victor Fabre Date: Mon, 1 Apr 2019 17:29:17 -0300 Subject: [PATCH 09/18] Fix #2678 (#2681) --- sapl/sessao/urls.py | 6 ++++++ sapl/sessao/views.py | 19 +++++++++++++++++++ .../sessao/expedientemateria_list.html | 5 ++++- sapl/templates/sessao/ordemdia_list.html | 5 ++++- 4 files changed, 33 insertions(+), 2 deletions(-) diff --git a/sapl/sessao/urls.py b/sapl/sessao/urls.py index 4da3a761a..ba0735ec6 100644 --- a/sapl/sessao/urls.py +++ b/sapl/sessao/urls.py @@ -28,6 +28,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) @@ -75,6 +77,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 c5a6620c4..550b231be 100755 --- a/sapl/sessao/views.py +++ b/sapl/sessao/views.py @@ -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__) 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/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 %} From a64e7dbb093d7beb9f617746fa3b403457500b7f Mon Sep 17 00:00:00 2001 From: Edward Ribeiro Date: Mon, 1 Apr 2019 17:30:17 -0300 Subject: [PATCH 10/18] HOT-FIX: adiciona assinaturas no extrato do PDF --- sapl/relatorios/views.py | 17 +++++++++-------- sapl/templates/relatorios/relatorio_ata.html | 5 +++-- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/sapl/relatorios/views.py b/sapl/relatorios/views.py index 9439b1578..deb1e885f 100755 --- a/sapl/relatorios/views.py +++ b/sapl/relatorios/views.py @@ -28,10 +28,10 @@ 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_explicações_pessoais, get_ocorrencias_da_sessão, get_assinaturas) from .templates import (pdf_capa_processo_gerar, pdf_documento_administrativo_gerar, pdf_espelho_gerar, @@ -1258,12 +1258,13 @@ def resumo_ata_pdf(request,pk): context.update(get_materias_ordem_do_dia(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/templates/relatorios/relatorio_ata.html b/sapl/templates/relatorios/relatorio_ata.html index 743e13c82..7d6c45d7b 100644 --- a/sapl/templates/relatorios/relatorio_ata.html +++ b/sapl/templates/relatorios/relatorio_ata.html @@ -82,12 +82,13 @@

{% else %}
_____________________
-

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

+

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




From 8068e64be4275300c57fdf9f357350198d9a1e1c Mon Sep 17 00:00:00 2001 From: Leandro Roberto Date: Tue, 2 Apr 2019 00:07:18 -0300 Subject: [PATCH 11/18] add endpoint get ultima_tramitacao --- sapl/api/views.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/sapl/api/views.py b/sapl/api/views.py index f2f146e24..f15232871 100644 --- a/sapl/api/views.py +++ b/sapl/api/views.py @@ -349,6 +349,23 @@ class _ProposicaoViewSet(SaplSetViews['materia']['proposicao']): return qs +class _MateriaLegislativaViewSet(SaplSetViews['materia']['materialegislativa']): + + @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 = SaplSetViews[ + 'materia']['tramitacao'].serializer_class(ultima_tramitacao) + + return Response(serializer_class.data) + + class _TipoMateriaLegislativaViewSet(SaplSetViews['materia']['tipomaterialegislativa']): @action(detail=True, methods=['POST']) @@ -443,6 +460,8 @@ class _SessaoPlenariaViewSet( SaplSetViews['base']['autor'] = _AutorViewSet.build_class_with_actions() + +SaplSetViews['materia']['materialegislativa'] = _MateriaLegislativaViewSet SaplSetViews['materia']['proposicao'] = _ProposicaoViewSet SaplSetViews['materia']['tipomaterialegislativa'] = _TipoMateriaLegislativaViewSet From e523e9b23cc0b29b1e9c74312d5c1dd1a87fbc19 Mon Sep 17 00:00:00 2001 From: Leandro Roberto Date: Tue, 2 Apr 2019 00:18:05 -0300 Subject: [PATCH 12/18] corrige classe BusinessRulesNotImplementedMixin --- sapl/api/views.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/sapl/api/views.py b/sapl/api/views.py index f15232871..5d2ca37eb 100644 --- a/sapl/api/views.py +++ b/sapl/api/views.py @@ -31,11 +31,8 @@ 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")) From dfe74c59f706f61547fa9cfb1f2d926b1d5b25f1 Mon Sep 17 00:00:00 2001 From: Leandro Roberto Date: Tue, 2 Apr 2019 02:02:23 -0300 Subject: [PATCH 13/18] =?UTF-8?q?simplifica=20customiza=C3=A7=C3=A3o=20de?= =?UTF-8?q?=20viewsets=20da=20api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sapl/api/urls.py | 7 +-- sapl/api/views.py | 110 ++++++++++++++++++++++++++++------------------ 2 files changed, 71 insertions(+), 46 deletions(-) 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 5d2ca37eb..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 @@ -38,10 +42,18 @@ class BusinessRulesNotImplementedMixin: 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 @@ -92,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 @@ -116,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 @@ -184,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 @@ -237,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) @@ -260,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): """ @@ -285,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 @@ -346,7 +384,8 @@ class _ProposicaoViewSet(SaplSetViews['materia']['proposicao']): return qs -class _MateriaLegislativaViewSet(SaplSetViews['materia']['materialegislativa']): +@customize(MateriaLegislativa) +class _MateriaLegislativaViewSet: @action(detail=True, methods=['GET']) def ultima_tramitacao(self, request, *args, **kwargs): @@ -357,13 +396,14 @@ class _MateriaLegislativaViewSet(SaplSetViews['materia']['materialegislativa']): ultima_tramitacao = materia.tramitacao_set.last() - serializer_class = SaplSetViews[ - 'materia']['tramitacao'].serializer_class(ultima_tramitacao) + serializer_class = SaplApiViewSetConstrutor.get_class_for_model( + Tramitacao).serializer_class(ultima_tramitacao) return Response(serializer_class.data) -class _TipoMateriaLegislativaViewSet(SaplSetViews['materia']['tipomaterialegislativa']): +@customize(TipoMateriaLegislativa) +class _TipoMateriaLegislativaViewSet: @action(detail=True, methods=['POST']) def change_position(self, request, *args, **kwargs): @@ -380,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): @@ -414,8 +455,8 @@ class _DocumentoAdministrativoViewSet(SaplSetViews['protocoloadm']['documentoadm return qs -class _DocumentoAcessorioAdministrativoViewSet( - SaplSetViews['protocoloadm']['documentoacessorioadministrativo']): +@customize(DocumentoAcessorioAdministrativo) +class _DocumentoAcessorioAdministrativoViewSet: permission_classes = ( _DocumentoAdministrativoViewSet.DocumentoAdministrativoPermission, ) @@ -428,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 = ( @@ -444,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): @@ -453,19 +493,3 @@ class _SessaoPlenariaViewSet( serializer = ChoiceSerializer(years, many=True) return Response(serializer.data) - - -SaplSetViews['base']['autor'] = _AutorViewSet.build_class_with_actions() - - -SaplSetViews['materia']['materialegislativa'] = _MateriaLegislativaViewSet -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 From 8ec54cfe37e47fa0400d7d34e1700abed5f2863d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Cantu=C3=A1ria?= Date: Wed, 3 Apr 2019 11:38:39 -0300 Subject: [PATCH 14/18] Fix #2653 (#2677) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implementa chamada REST Remove ipdb Atualiza testes Atualiza formatação de estilo do jquery Co-authored-by: Edward Ribeiro Co-authored-by: Victor Fabre --- sapl/comissoes/urls.py | 3 +- sapl/comissoes/views.py | 16 +++- sapl/materia/forms.py | 61 +++++++++++---- sapl/materia/tests/test_materia_form.py | 13 +++- sapl/materia/views.py | 86 +--------------------- sapl/templates/materia/relatoria_form.html | 19 +++++ 6 files changed, 94 insertions(+), 104 deletions(-) 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 cf200447e..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: @@ -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/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 a2366c8d9..73dcb3072 100644 --- a/sapl/materia/views.py +++ b/sapl/materia/views.py @@ -1111,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 @@ -1174,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 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 %} From a8a41ea78cabfe7c1d87789bf0b6fd1dd6ab3696 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Rodrigues?= Date: Wed, 3 Apr 2019 11:44:00 -0300 Subject: [PATCH 15/18] Fix #2403 Adiciona Oradores da Ordem do Dia (#2662) * Adicionar oradores da ordem do dia * Atualizar sessao/forms.py --- .../templates/pdf_sessao_plenaria_gerar.py | 24 +++++- sapl/relatorios/views.py | 44 +++++++++-- sapl/rules/map_rules.py | 1 + sapl/sessao/forms.py | 55 +++++++++++++- sapl/sessao/migrations/0034_oradorordemdia.py | 34 +++++++++ .../0035_resumoordenacao_decimo_quarto.py | 20 +++++ sapl/sessao/models.py | 9 +++ sapl/sessao/urls.py | 7 +- sapl/sessao/views.py | 76 +++++++++++++++++-- sapl/templates/relatorios/relatorio_ata.html | 1 + .../sessao/blocos_ata/oradores_ordemdia.html | 13 ++++ .../blocos_resumo/oradores_ordemdia.html | 15 ++++ sapl/templates/sessao/layouts.yaml | 6 ++ sapl/templates/sessao/resumo.html | 3 + sapl/templates/sessao/resumo_ata.html | 1 + sapl/templates/sessao/subnav.yaml | 2 + 16 files changed, 292 insertions(+), 19 deletions(-) create mode 100644 sapl/sessao/migrations/0034_oradorordemdia.py create mode 100644 sapl/sessao/migrations/0035_resumoordenacao_decimo_quarto.py create mode 100644 sapl/templates/sessao/blocos_ata/oradores_ordemdia.html create mode 100644 sapl/templates/sessao/blocos_resumo/oradores_ordemdia.html 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 deb1e885f..72ef64e45 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_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_assinaturas) + get_oradores_ordemdia, + get_oradores_explicações_pessoais, get_ocorrencias_da_sessão) from .templates import (pdf_capa_processo_gerar, pdf_documento_administrativo_gerar, pdf_espelho_gerar, @@ -780,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( @@ -830,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) @@ -886,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) @@ -910,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) @@ -1256,6 +1289,7 @@ 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(get_assinaturas(sessao_plenaria)) 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 172b37be5..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): @@ -758,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') @@ -789,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) @@ -819,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 ba0735ec6..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, @@ -44,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'), diff --git a/sapl/sessao/views.py b/sapl/sessao/views.py index 550b231be..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') @@ -618,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 @@ -1290,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): @@ -1309,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() @@ -1584,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( @@ -1698,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)) # ===================================================================== @@ -1718,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' } @@ -1737,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.") @@ -1753,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( @@ -1769,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/templates/relatorios/relatorio_ata.html b/sapl/templates/relatorios/relatorio_ata.html index 7d6c45d7b..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' %} 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/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/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 From 9405129d1874b01ac3e4874341d261f57ae62fd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Cantu=C3=A1ria?= Date: Wed, 3 Apr 2019 11:39:09 -0300 Subject: [PATCH 16/18] =?UTF-8?q?Separa=20cria=C3=A7=C3=A3o=20de=20mandato?= =?UTF-8?q?=20e=20parlamentar=20(#2579)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix #2536 Adiciona cadastro de parlamentar na tabelas auxiliares Remoção de mandato na criação do parlamentar Corrige testes Corrige título no CRUD de parlamentar Corrige identação Adiciona botão de criação de parlamentar após pesquisa Corrige botões em parlamentares Altera nomes Remove intermediário para vinculação de parlamentar Arruma bug Altera redirecionamento para detales de parlamentar Início da criação do form e view de vincular parlamentar Corrige VincularParlamentarForm Corrige VincularParlamentarView Adiciona html de vincular parlamentar Mostrar somente parlamentares ativos Cria variável data_expedicao_diploma Compara mandato por legislatura invés de data Corrige verificação de existencia de chave no dicionário Adiciona data fim do mandato na criação do mandato Adiciona restrição para usuário não logado Co-authored-by: UlyssesBML Co-authored-by: Ricardo Lima Canela --- sapl/parlamentares/forms.py | 62 +++++++++++++++++++ sapl/parlamentares/urls.py | 5 +- sapl/parlamentares/views.py | 30 ++++++++- sapl/templates/parlamentares/layouts.yaml | 8 +-- .../parlamentares/parlamentar_filter.html | 44 ++++++------- .../parlamentares/parlamentares_list.html | 9 +++ .../parlamentares/vincular_parlamentar.html | 8 +++ 7 files changed, 137 insertions(+), 29 deletions(-) create mode 100644 sapl/templates/parlamentares/vincular_parlamentar.html 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/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 From b9f7f06e00c3a451fc9968708e15e4b45fbad4e6 Mon Sep 17 00:00:00 2001 From: Edward Ribeiro Date: Wed, 3 Apr 2019 13:20:58 -0300 Subject: [PATCH 17/18] Release: 3.1.151 --- docker-compose.yml | 2 +- sapl/settings.py | 2 +- sapl/templates/base.html | 2 +- setup.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) 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/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/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', From f823cd56d70d131986694fd7b2e590d089be558e Mon Sep 17 00:00:00 2001 From: Edward Ribeiro Date: Wed, 3 Apr 2019 15:02:08 -0300 Subject: [PATCH 18/18] HOT-FIX: conserta PDF de extrato --- sapl/relatorios/views.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sapl/relatorios/views.py b/sapl/relatorios/views.py index 72ef64e45..117b7a172 100755 --- a/sapl/relatorios/views.py +++ b/sapl/relatorios/views.py @@ -28,11 +28,11 @@ 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_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_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,