diff --git a/docker-compose.yml b/docker-compose.yml index cb57ad36e..6c668d80f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,8 +11,7 @@ sapldb: ports: - "5432:5432" sapl: - image: interlegis/sapl:3.1.158 -# build: . + image: interlegis/sapl:3.1.159 restart: always environment: ADMIN_PASSWORD: interlegis @@ -24,27 +23,11 @@ sapl: EMAIL_HOST_USER: usuariosmtp EMAIL_SEND_USER: usuariosmtp EMAIL_HOST_PASSWORD: senhasmtp -# USE_SOLR: 'True' -# SOLR_COLLECTION: sapl -# SOLR_URL: http://saplsolr:8983 TZ: America/Sao_Paulo volumes: - sapl_data:/var/interlegis/sapl/data - sapl_media:/var/interlegis/sapl/media links: - sapldb -# - saplsolr ports: - - "80:80" - -#saplsolr: -# image: solr:7.4-alpine -# restart: always -# command: bin/solr start -c -f -# volumes: -# - solr_data:/opt/solr/server/solr -# - solr_configsets:/opt/solr/server/solr/configsets -# ports: -# - "8983:8983" - - + - "80:80" \ No newline at end of file diff --git a/release.sh b/release.sh index 833e74cdf..5bfd27fef 100755 --- a/release.sh +++ b/release.sh @@ -1,4 +1,4 @@ -#/bin/bash +#!/bin/bash ## ## Versioning info: [major].[minor].[patch][-RC[num]], example: 3.1.159, 3.1.159-RC1 @@ -72,19 +72,21 @@ function commit_and_push { git commit -m "Release: $FINAL_VERSION" git tag $FINAL_VERSION - echo "sending to github..." - git push origin - git push origin $FINAL_VERSION + echo "Para enviar pro github execute..." + echo "git push origin 3.1.x" + echo "git push origin "$FINAL_VERSION echo "done." } case "$1" in --latest) + git fetch echo $LATEST_VERSION exit 0 ;; --major) + git fetch set_major_version echo "generating major release: "$FINAL_VERSION # git tag $FINAL_VERSION @@ -93,6 +95,7 @@ case "$1" in exit 0 ;; --rc) + git fetch set_rc_version echo "generating release candidate: "$FINAL_VERSION # git tag $FINAL_VERSION @@ -100,10 +103,6 @@ case "$1" in commit_and_push exit 0 ;; - --undo) - git tag -d $LATEST_VERSION - exit 0 - ;; --top) git tag | sort --version-sort | tail "-$2" exit 0 diff --git a/requirements/requirements.txt b/requirements/requirements.txt index 05789db35..6000f1575 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -15,6 +15,7 @@ django-extensions==2.1.4 django-image-cropping==1.2 django-webpack-loader==0.6.0 drf-yasg==1.13.0 +ruamel.yaml>=0.15.34,<0.16.0 easy-thumbnails==2.5 python-decouple==3.1 psycopg2-binary==2.7.6.1 diff --git a/sapl/audiencia/forms.py b/sapl/audiencia/forms.py index 56f4d1285..d62900df6 100755 --- a/sapl/audiencia/forms.py +++ b/sapl/audiencia/forms.py @@ -1,6 +1,7 @@ import logging from django import forms +from sapl.settings import MAX_DOC_UPLOAD_SIZE from django.core.exceptions import ObjectDoesNotExist, ValidationError from django.db import transaction from django.utils.translation import ugettext_lazy as _ @@ -106,8 +107,6 @@ class AudienciaForm(FileFieldCheckMixin, forms.ModelForm): else: cleaned_data['numero'] = 1 - - if self.cleaned_data['hora_inicio'] and self.cleaned_data['hora_fim']: if (self.cleaned_data['hora_fim'] < self.cleaned_data['hora_inicio']): @@ -116,6 +115,22 @@ class AudienciaForm(FileFieldCheckMixin, forms.ModelForm): self.logger.error('Hora de fim anterior à hora de início.') raise ValidationError(msg) + upload_pauta = self.cleaned_data.get('upload_pauta', False) + upload_ata = self.cleaned_data.get('upload_ata', False) + upload_anexo = self.cleaned_data.get('upload_anexo', False) + + if upload_pauta and upload_pauta.size > MAX_DOC_UPLOAD_SIZE: + raise ValidationError("O arquivo Pauta da Audiência Pública deve ser menor que {0:.1f} mb, o tamanho atual desse arquivo é {1:.1f} mb" \ + .format((MAX_DOC_UPLOAD_SIZE/1024)/1024, (upload_pauta.size/1024)/1024)) + + if upload_ata and upload_ata.size > MAX_DOC_UPLOAD_SIZE: + raise ValidationError("O arquivo Ata da Audiência Pública deve ser menor que {0:.1f} mb, o tamanho atual desse arquivo é {1:.1f} mb" \ + .format((MAX_DOC_UPLOAD_SIZE/1024)/1024, (upload_ata.size/1024)/1024)) + + if upload_anexo and upload_anexo.size > MAX_DOC_UPLOAD_SIZE: + raise ValidationError("O arquivo Anexo da Audiência Pública deve ser menor que {0:.1f} mb, o tamanho atual desse arquivo é {1:.1f} mb" \ + .format((MAX_DOC_UPLOAD_SIZE/1024)/1024, (upload_anexo.size/1024)/1024)) + return cleaned_data @@ -140,3 +155,17 @@ class AnexoAudienciaPublicaForm(forms.ModelForm): row1, row2)) super(AnexoAudienciaPublicaForm, self).__init__( *args, **kwargs) + + def clean(self): + super(AnexoAudienciaPublicaForm, self).clean() + + if not self.is_valid(): + return self.cleaned_data + + arquivo = self.cleaned_data.get('arquivo', False) + + if arquivo and arquivo.size > MAX_DOC_UPLOAD_SIZE: + raise ValidationError("O arquivo deve ser menor que {0:.1f} mb, o tamanho atual desse arquivo é {1:.1f} mb" \ + .format((MAX_DOC_UPLOAD_SIZE/1024)/1024, (arquivo.size/1024)/1024)) + + return self.cleaned_data diff --git a/sapl/audiencia/tests/test_audiencia.py b/sapl/audiencia/tests/test_audiencia.py index 5640b3d0d..b7c2e26a8 100644 --- a/sapl/audiencia/tests/test_audiencia.py +++ b/sapl/audiencia/tests/test_audiencia.py @@ -1,11 +1,62 @@ import pytest -from django.utils.translation import ugettext as _ +import datetime from model_mommy import mommy +from django.utils.translation import ugettext as _ from sapl.audiencia import forms -from sapl.audiencia.models import TipoAudienciaPublica +from sapl.audiencia.models import AnexoAudienciaPublica +from sapl.audiencia.models import TipoAudienciaPublica, AudienciaPublica from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa + +@pytest.mark.django_db(transaction=False) +def test_tipo_audiencia_publica_model(): + mommy.make(TipoAudienciaPublica, + nome='Teste_Nome_Tipo_Audiencia_Publica', + tipo='A') + + tipo_audiencia_publica = TipoAudienciaPublica.objects.first() + assert tipo_audiencia_publica.nome == 'Teste_Nome_Tipo_Audiencia_Publica' + assert tipo_audiencia_publica.tipo == 'A' + + +@pytest.mark.django_db(transaction=False) +def test_audiencia_publica_model(): + mommy.make(AudienciaPublica, + numero=1, + nome='Teste_Nome_Audiencia_Publica', + tema='Teste_Tema_Audiencia_Publica', + data='2016-03-21', + hora_inicio='16:03') + + audiencia_publica = AudienciaPublica.objects.first() + + data = '2016-03-21' + teste_data = datetime.datetime.strptime(data, "%Y-%m-%d").date() + + assert audiencia_publica.numero == 1 + assert audiencia_publica.nome == 'Teste_Nome_Audiencia_Publica' + assert audiencia_publica.tema == 'Teste_Tema_Audiencia_Publica' + assert audiencia_publica.data == teste_data + assert audiencia_publica.hora_inicio == '16:03' + + +@pytest.mark.django_db(transaction=False) +def test_anexo_audiencia_publica_model(): + audiencia = mommy.make(AudienciaPublica, + numero=2, + nome='Nome_Audiencia_Publica', + tema='Tema_Audiencia_Publica', + data='2017-04-22', + hora_inicio='17:04') + + mommy.make(AnexoAudienciaPublica, + audiencia=audiencia) + + anexo_audiencia_publica = AnexoAudienciaPublica.objects.first() + assert anexo_audiencia_publica.audiencia == audiencia + + @pytest.mark.django_db(transaction=False) def test_valida_campos_obrigatorios_audiencia_form(): form = forms.AudienciaForm(data={}) @@ -35,7 +86,5 @@ def test_audiencia_form_hora_invalida(): 'data': '2016-10-01', 'hora_inicio': '10:00', 'hora_fim': '9:00', - }) + }) assert not form.is_valid() - - \ No newline at end of file diff --git a/sapl/base/forms.py b/sapl/base/forms.py index d78f4a5e4..d9594511b 100644 --- a/sapl/base/forms.py +++ b/sapl/base/forms.py @@ -2,7 +2,6 @@ import logging import os from crispy_forms.bootstrap import FieldWithButtons, InlineRadios, StrictButton -from sapl.crispy_layout_mixin import SaplFormHelper from crispy_forms.layout import HTML, Button, Div, Field, Fieldset, Layout, Row from django import forms from django.conf import settings @@ -26,6 +25,7 @@ from sapl.comissoes.models import Reuniao, Comissao from sapl.comissoes.models import Reuniao, Comissao from sapl.crispy_layout_mixin import (SaplFormLayout, form_actions, to_column, to_row) +from sapl.crispy_layout_mixin import SaplFormHelper from sapl.materia.models import ( MateriaLegislativa, UnidadeTramitacao, StatusTramitacao) from sapl.norma.models import (NormaJuridica, NormaEstatisticas) @@ -39,6 +39,7 @@ from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES, models_with_gr_for_model, qs_override_django_filter, choice_anos_com_normas, choice_anos_com_materias, FilterOverridesMetaMixin, FileFieldCheckMixin) + from .models import AppConfig, CasaLegislativa @@ -65,11 +66,6 @@ def get_roles(): class UsuarioCreateForm(ModelForm): logger = logging.getLogger(__name__) - username = forms.CharField( - required=True, - label="Nome de usuário", - max_length=30 - ) firstname = forms.CharField( required=True, label="Nome", @@ -108,8 +104,11 @@ class UsuarioCreateForm(ModelForm): class Meta: model = get_user_model() - fields = ['username', 'firstname', 'lastname', 'email', - 'password1', 'password2', 'user_active', 'roles'] + fields = [ + get_user_model().USERNAME_FIELD, 'firstname', 'lastname', + 'password1', 'password2', 'user_active', 'roles' + ] + (['email'] + if get_user_model().USERNAME_FIELD != 'email' else []) def clean(self): super().clean() @@ -151,9 +150,9 @@ class UsuarioCreateForm(ModelForm): class UsuarioFilterSet(django_filters.FilterSet): - + username = django_filters.CharFilter( - label=_('Nome de Usuário'), + label=_('Nome de Usuário'), lookup_expr='icontains') class Meta: @@ -164,7 +163,7 @@ class UsuarioFilterSet(django_filters.FilterSet): super(UsuarioFilterSet, self).__init__(*args, **kwargs) row0 = to_row([('username', 12)]) - + self.form.helper = SaplFormHelper() self.form.helper.form_method = 'GET' self.form.helper.layout = Layout( @@ -190,24 +189,30 @@ class UsuarioEditForm(ModelForm): class Meta: model = get_user_model() - fields = ['email', 'password1', 'password2', 'user_active', 'roles'] + fields = [ + get_user_model().USERNAME_FIELD, 'password1', + 'password2', 'user_active', 'roles' + ] + (['email'] + if get_user_model().USERNAME_FIELD != 'email' else []) def __init__(self, *args, **kwargs): super(UsuarioEditForm, self).__init__(*args, **kwargs) - row1 = to_row([('email', 6), + row1 = to_row([('username', 12)]) + row2 = to_row([('email', 6), ('user_active', 6)]) - row2 = to_row( + row3 = to_row( [('password1', 6), ('password2', 6)]) - row3 = to_row([(form_actions(label='Salvar Alterações'), 6)]) + row4 = to_row([(form_actions(label='Salvar Alterações'), 6)]) self.helper = SaplFormHelper() self.helper.layout = Layout( row1, row2, + row3, 'roles', form_actions(label='Salvar Alterações')) @@ -867,22 +872,36 @@ class RelatorioPresencaSessaoFilterSet(django_filters.FilterSet): class Meta(FilterOverridesMetaMixin): model = SessaoPlenaria - fields = ['data_inicio'] + fields = ['data_inicio', + 'sessao_legislativa', + 'tipo', + 'legislatura'] def __init__(self, *args, **kwargs): super(RelatorioPresencaSessaoFilterSet, self).__init__( *args, **kwargs) + self.form.fields['exibir_ordem_dia'] = forms.BooleanField(required=False, + label='Exibir presença das Ordens do Dia') + self.form.initial['exibir_ordem_dia'] = True + self.filters['data_inicio'].label = 'Período (Inicial - Final)' - self.form.fields['data_inicio'].required = True + + tipo_sessao_ordinaria = self.filters['tipo'].queryset.filter(nome='Ordinária') + if tipo_sessao_ordinaria: + self.form.initial['tipo'] = tipo_sessao_ordinaria.first() row1 = to_row([('data_inicio', 12)]) + row2 = to_row([('legislatura', 4), + ('sessao_legislativa', 4), + ('tipo', 4)]) + row3 = to_row([('exibir_ordem_dia', 12)]) self.form.helper = SaplFormHelper() self.form.helper.form_method = 'GET' self.form.helper.layout = Layout( Fieldset(_('Presença dos parlamentares nas sessões plenárias'), - row1, form_actions(label='Pesquisar')) + row1, row2, row3, form_actions(label='Pesquisar')) ) @property @@ -900,7 +919,7 @@ class RelatorioHistoricoTramitacaoFilterSet(django_filters.FilterSet): class Meta(FilterOverridesMetaMixin): model = MateriaLegislativa fields = ['tipo', 'tramitacao__status', 'tramitacao__data_tramitacao', - 'tramitacao__unidade_tramitacao_local', 'tramitacao__unidade_tramitacao_destino'] + 'tramitacao__unidade_tramitacao_local', 'tramitacao__unidade_tramitacao_destino'] def __init__(self, *args, **kwargs): super(RelatorioHistoricoTramitacaoFilterSet, self).__init__( @@ -908,8 +927,10 @@ class RelatorioHistoricoTramitacaoFilterSet(django_filters.FilterSet): self.filters['tipo'].label = 'Tipo de Matéria' self.filters['tramitacao__status'].label = _('Status') - self.filters['tramitacao__unidade_tramitacao_local'].label = _('Unidade Local (Origem)') - self.filters['tramitacao__unidade_tramitacao_destino'].label = _('Unidade Destino') + self.filters['tramitacao__unidade_tramitacao_local'].label = _( + 'Unidade Local (Origem)') + self.filters['tramitacao__unidade_tramitacao_destino'].label = _( + 'Unidade Destino') row1 = to_row([('tramitacao__data_tramitacao', 12)]) row2 = to_row([('tramitacao__unidade_tramitacao_local', 6), @@ -927,7 +948,6 @@ class RelatorioHistoricoTramitacaoFilterSet(django_filters.FilterSet): ) - class RelatorioDataFimPrazoTramitacaoFilterSet(django_filters.FilterSet): @property @@ -946,7 +966,8 @@ class RelatorioDataFimPrazoTramitacaoFilterSet(django_filters.FilterSet): *args, **kwargs) self.filters['tipo'].label = 'Tipo de Matéria' - self.filters['tramitacao__unidade_tramitacao_local'].label = 'Unidade Local (Origem)' + self.filters[ + 'tramitacao__unidade_tramitacao_local'].label = 'Unidade Local (Origem)' self.filters['tramitacao__unidade_tramitacao_destino'].label = 'Unidade Destino' self.filters['tramitacao__status'].label = 'Status de tramitação' @@ -960,7 +981,7 @@ class RelatorioDataFimPrazoTramitacaoFilterSet(django_filters.FilterSet): self.form.helper = SaplFormHelper() self.form.helper.form_method = 'GET' self.form.helper.layout = Layout( - Fieldset(_('Tramitações por fim de prazo'), + Fieldset(_('Tramitações'), row1, row2, row3, form_actions(label='Pesquisar')) ) @@ -1409,7 +1430,7 @@ class PartidoForm(FileFieldCheckMixin, ModelForm): [('sigla', 2), ('nome', 6), ('data_criacao', 2), - ('data_extincao', 2),]) + ('data_extincao', 2), ]) row2 = to_row([('observacao', 12)]) row3 = to_row([('logo_partido', 12)]) @@ -1424,10 +1445,11 @@ class PartidoForm(FileFieldCheckMixin, ModelForm): if not self.is_valid(): return cleaned_data - + if cleaned_data['data_criacao'] and cleaned_data['data_extincao']: if cleaned_data['data_criacao'] > cleaned_data['data_extincao']: - raise ValidationError("Certifique-se de que a data de criação seja anterior à data de extinção.") + raise ValidationError( + "Certifique-se de que a data de criação seja anterior à data de extinção.") return cleaned_data @@ -1441,9 +1463,9 @@ class RelatorioHistoricoTramitacaoAdmFilterSet(django_filters.FilterSet): class Meta(FilterOverridesMetaMixin): model = DocumentoAdministrativo - fields = ['tipo', 'tramitacaoadministrativo__status', + fields = ['tipo', 'tramitacaoadministrativo__status', 'tramitacaoadministrativo__data_tramitacao', - 'tramitacaoadministrativo__unidade_tramitacao_local', + 'tramitacaoadministrativo__unidade_tramitacao_local', 'tramitacaoadministrativo__unidade_tramitacao_destino'] def __init__(self, *args, **kwargs): @@ -1452,8 +1474,10 @@ class RelatorioHistoricoTramitacaoAdmFilterSet(django_filters.FilterSet): self.filters['tipo'].label = 'Tipo de Documento' self.filters['tramitacaoadministrativo__status'].label = _('Status') - self.filters['tramitacaoadministrativo__unidade_tramitacao_local'].label = _('Unidade Local (Origem)') - self.filters['tramitacaoadministrativo__unidade_tramitacao_destino'].label = _('Unidade Destino') + self.filters['tramitacaoadministrativo__unidade_tramitacao_local'].label = _( + 'Unidade Local (Origem)') + self.filters['tramitacaoadministrativo__unidade_tramitacao_destino'].label = _( + 'Unidade Destino') row1 = to_row([('tramitacaoadministrativo__data_tramitacao', 12)]) row2 = to_row([('tramitacaoadministrativo__unidade_tramitacao_local', 6), diff --git a/sapl/base/templatetags/common_tags.py b/sapl/base/templatetags/common_tags.py index 26fd54f4d..12192ab6f 100644 --- a/sapl/base/templatetags/common_tags.py +++ b/sapl/base/templatetags/common_tags.py @@ -223,8 +223,34 @@ def audio_url(value): @register.filter -def video_url(value): - return True if url(value) and value.endswith("mp4") else False +def is_video_url(value): + video_extensions = ["mp4", "ogg", "webm", "3gp", "ogv"] + has_ext = any([value.endswith(i) for i in video_extensions]) + return url(value) and has_ext + + +@register.filter +def youtube_url(value): + # Test if YouTube video + # tested on https://pythex.org/ + value = value.lower() + youtube_pattern = "^((https?://)?(www\.)?youtube\.com\/watch\?v=)" + r = re.findall(youtube_pattern, value) + return True if r else False + +@register.filter +def facebook_url(value): + value = value.lower() + facebook_pattern = "^((https?://)?((www|pt-br)\.)?facebook\.com(\/.+)?\/videos(\/.*)?)" + r = re.findall(facebook_pattern, value) + return True if r else False + +@register.filter +def youtube_id(value): + from urllib.parse import urlparse, parse_qs + u_pars = urlparse(value) + quer_v = parse_qs(u_pars.query).get('v')[0] + return quer_v @register.filter @@ -272,12 +298,20 @@ def urldetail(obj): @register.filter def filiacao_data_filter(parlamentar, data_inicio): - return filiacao_data(parlamentar, data_inicio) + try: + filiacao = filiacao_data(parlamentar, data_inicio) + except Exception: + filiacao = '' + return filiacao @register.filter def filiacao_intervalo_filter(parlamentar, date_range): - return filiacao_data(parlamentar, date_range[0], date_range[1]) + try: + filiacao = filiacao_data(parlamentar, date_range[0], date_range[1]) + except Exception: + filiacao = '' + return filiacao @register.simple_tag diff --git a/sapl/base/tests/test_base.py b/sapl/base/tests/test_base.py new file mode 100644 index 000000000..296a241af --- /dev/null +++ b/sapl/base/tests/test_base.py @@ -0,0 +1,24 @@ +import pytest +from model_mommy import mommy + +from sapl.base.models import CasaLegislativa + + +@pytest.mark.django_db(transaction=False) +def test_casa_legislativa_model(): + mommy.make(CasaLegislativa, + nome='Teste_Nome_Casa_Legislativa', + sigla='TSCL', + endereco='Teste_Endereço_Casa_Legislativa', + cep='12345678', + municipio='Teste_Municipio_Casa_Legislativa', + uf='DF') + + casa_legislativa = CasaLegislativa.objects.first() + + assert casa_legislativa.nome == 'Teste_Nome_Casa_Legislativa' + assert casa_legislativa.sigla == 'TSCL' + assert casa_legislativa.endereco == 'Teste_Endereço_Casa_Legislativa' + assert casa_legislativa.cep == '12345678' + assert casa_legislativa.municipio == 'Teste_Municipio_Casa_Legislativa' + assert casa_legislativa.uf == 'DF' diff --git a/sapl/base/tests/test_view_base.py b/sapl/base/tests/test_view_base.py index fc61ac160..6655317b3 100644 --- a/sapl/base/tests/test_view_base.py +++ b/sapl/base/tests/test_view_base.py @@ -1,6 +1,635 @@ import pytest +from model_mommy import mommy +import datetime from django.core.urlresolvers import reverse from django.utils.translation import ugettext_lazy as _ +from model_mommy import mommy + +from sapl.base.models import Autor, TipoAutor +from sapl.comissoes.models import Comissao, TipoComissao +from sapl.sessao.models import Bancada +from sapl.protocoloadm.models import (Protocolo, DocumentoAdministrativo, + TipoDocumentoAdministrativo, Anexado) +from sapl.materia.models import (TipoMateriaLegislativa, RegimeTramitacao, + MateriaLegislativa, Anexada) +from sapl.parlamentares.models import (Parlamentar, Partido, Filiacao, + Legislatura, Mandato) + +from sapl.base.views import (protocolos_duplicados, protocolos_com_materias, + materias_protocolo_inexistente, + mandato_sem_data_inicio, parlamentares_duplicados, + parlamentares_mandatos_intersecao, + parlamentares_filiacoes_intersecao, + autores_duplicados, + bancada_comissao_autor_externo, anexados_ciclicos) + + +@pytest.mark.django_db(transaction=False) +def test_lista_protocolos_duplicados(): + mommy.make( + Protocolo, + numero=15, + ano=2031 + ) + mommy.make( + Protocolo, + numero=15, + ano=2031 + ) + mommy.make( + Protocolo, + numero=33, + ano=2033 + ) + + lista_protocolos_duplicados = protocolos_duplicados() + + assert len(lista_protocolos_duplicados) == 1 + assert lista_protocolos_duplicados[0][1] == 2 + assert lista_protocolos_duplicados[0][0].numero == 15 + assert lista_protocolos_duplicados[0][0].ano == 2031 + + +@pytest.mark.django_db(transaction=False) +def test_lista_protocolos_com_materias(): + mommy.make( + Protocolo, + numero=15, + ano=2035 + ) + mommy.make( + Protocolo, + numero=33, + ano=2035 + ) + + tipo_materia = mommy.make( + TipoMateriaLegislativa, + descricao="Tipo_Materia_Teste" + ) + regime_tramitacao = mommy.make( + RegimeTramitacao, + descricao="Regime_Tramitacao_Teste" + ) + mommy.make( + MateriaLegislativa, + numero=16, + ano=2035, + data_apresentacao='2035-06-02', + regime_tramitacao=regime_tramitacao, + tipo=tipo_materia, + numero_protocolo=15 + ) + mommy.make( + MateriaLegislativa, + numero=17, + ano=2035, + data_apresentacao='2035-06-05', + regime_tramitacao=regime_tramitacao, + tipo=tipo_materia, + numero_protocolo=15 + ) + + lista_protocolos_com_materias = protocolos_com_materias() + + assert len(lista_protocolos_com_materias) == 1 + assert lista_protocolos_com_materias[0][1] == 2 + assert lista_protocolos_com_materias[0][0].numero_protocolo == 15 + assert lista_protocolos_com_materias[0][0].ano == 2035 + + +@pytest.mark.django_db(transaction=False) +def test_lista_materias_protocolo_inexistente(): + protocolo_a = mommy.make( + Protocolo, + numero=15, + ano=2031 + ) + + tipo_materia = mommy.make( + TipoMateriaLegislativa, + descricao="Tipo_Materia_Teste" + ) + regime_tramitacao = mommy.make( + RegimeTramitacao, + descricao="Regime_Tramitacao_Teste" + ) + mommy.make( + MateriaLegislativa, + numero=16, + ano=2031, + data_apresentacao='2031-06-02', + regime_tramitacao=regime_tramitacao, + tipo=tipo_materia, + numero_protocolo=15 + ) + materia = mommy.make( + MateriaLegislativa, + numero=17, + ano=2031, + data_apresentacao='2031-06-02', + regime_tramitacao=regime_tramitacao, + tipo=tipo_materia, + numero_protocolo=16 + ) + + lista_materias_protocolo_inexistente = materias_protocolo_inexistente() + + assert len(lista_materias_protocolo_inexistente) == 1 + assert lista_materias_protocolo_inexistente == [(materia, 2031, 16)] + + +@pytest.mark.django_db(transaction=False) +def test_lista_mandatos_sem_data_inicio(): + parlamentar = mommy.make( + Parlamentar, + nome_completo="Nome_Completo_Parlamentar_Teste", + nome_parlamentar="Nome_Parlamentar_Teste", + sexo='M' + ) + legislatura = mommy.make( + Legislatura, + numero=1, + data_inicio='2015-05-02', + data_fim='2024-02-04', + data_eleicao='2015-02-05' + ) + + mandato_a = mommy.make( + Mandato, + parlamentar=parlamentar, + legislatura=legislatura + ) + mommy.make( + Mandato, + parlamentar=parlamentar, + legislatura=legislatura, + data_inicio_mandato='2015-05-27' + ) + + lista_mandatos_sem_data_inicio = mandato_sem_data_inicio() + + assert len(lista_mandatos_sem_data_inicio) == 1 + assert lista_mandatos_sem_data_inicio[0] == mandato_a + + +@pytest.mark.django_db(transaction=False) +def test_lista_parlamentares_duplicados(): + mommy.make( + Parlamentar, + nome_completo="Nome_Completo_Parlamentar_Teste", + nome_parlamentar="Nome_Parlamentar_Teste", + sexo='M' + ) + mommy.make( + Parlamentar, + nome_completo="Nome_Completo_Parlamentar_Teste", + nome_parlamentar="Nome_Parlamentar_Teste", + sexo='M' + ) + mommy.make( + Parlamentar, + nome_completo="Nome_Completo_Parlamentar_Teste-1", + nome_parlamentar="Nome_Parlamentar_Teste-1", + sexo='M' + ) + + lista_dict_values_parlamentares_duplicados = parlamentares_duplicados() + parlamentar_duplicado = list( + lista_dict_values_parlamentares_duplicados[0] + ) + parlamentar_duplicado.sort(key=str) + + assert len(lista_dict_values_parlamentares_duplicados) == 1 + assert parlamentar_duplicado == [2, "Nome_Parlamentar_Teste"] + + +@pytest.mark.django_db(transaction=False) +def test_lista_parlamentares_mandatos_intersecao(): + legislatura = mommy.make( + Legislatura, + numero=1, + data_inicio='2017-07-04', + data_fim='2170-05-01', + data_eleicao='2017-04-07' + ) + parlamentar_a = mommy.make( + Parlamentar, + nome_completo="Nome_Completo_Parlamentar_Teste", + nome_parlamentar="Nome_Parlamentar_Teste", + sexo='M' + ) + parlamentar_b = mommy.make( + Parlamentar, + nome_completo="Nome_Completo_Parlamentar_Teste-1", + nome_parlamentar="Nome_Parlamentar_Teste-1", + sexo='M' + ) + + mandato_a = mommy.make( + Mandato, + parlamentar=parlamentar_a, + legislatura=legislatura, + data_inicio_mandato='2017-07-08', + data_fim_mandato='2018-01-07' + ) + mandato_b = mommy.make( + Mandato, + parlamentar=parlamentar_a, + legislatura=legislatura, + data_inicio_mandato='2017-07-09' + ) + mommy.make( + Mandato, + parlamentar=parlamentar_b, + legislatura=legislatura, + data_inicio_mandato='2017-11-17', + data_fim_mandato='2018-08-02' + ) + mommy.make( + Mandato, + parlamentar=parlamentar_b, + legislatura=legislatura, + data_inicio_mandato='2018-08-03' + ) + + lista_parlamentares = parlamentares_mandatos_intersecao() + + assert len(lista_parlamentares) == 1 + assert lista_parlamentares == [(parlamentar_a, mandato_a, mandato_b)] + + +@pytest.mark.django_db(transaction=False) +def test_lista_parlamentares_filiacoes_intersecao(): + partido = mommy.make( + Partido, + sigla="ST", + nome="Nome_Partido_Teste" + ) + parlamentar_a = mommy.make( + Parlamentar, + nome_completo="Nome_Completo_Parlamentar_Teste", + nome_parlamentar="Nome_Parlamentar_Teste", + sexo='M' + ) + parlamentar_b = mommy.make( + Parlamentar, + nome_completo="Nome_Completo_Parlamentar_Teste-1", + nome_parlamentar="Nome_Parlamentar_Teste-1", + sexo='M' + ) + + filiacao_a = mommy.make( + Filiacao, + parlamentar=parlamentar_a, + partido=partido, + data='2018-02-02', + data_desfiliacao='2019-08-01' + ) + filiacao_b = mommy.make( + Filiacao, + parlamentar=parlamentar_a, + partido=partido, + data='2018-02-23', + data_desfiliacao='2020-02-04' + ) + mommy.make( + Filiacao, + parlamentar=parlamentar_b, + partido=partido, + data='2018-02-07', + data_desfiliacao='2018-02-27' + ) + mommy.make( + Filiacao, + parlamentar=parlamentar_b, + partido=partido, + data='2018-02-28' + ) + + lista_parlamentares = parlamentares_filiacoes_intersecao() + + assert len(lista_parlamentares) == 1 + assert lista_parlamentares == [(parlamentar_a, filiacao_b, filiacao_a)] + + +@pytest.mark.django_db(transaction=False) +def test_lista_autores_duplicados(): + tipo_autor = mommy.make( + TipoAutor, + descricao="Tipo_Autor_Teste" + ) + + mommy.make( + Autor, + tipo=tipo_autor, + nome="Nome_Autor_Teste" + ) + mommy.make( + Autor, + tipo=tipo_autor, + nome="Nome_Autor_Teste" + ) + mommy.make( + Autor, + tipo=tipo_autor, + nome="Nome_Autor_Teste-1" + ) + + lista_autores_duplicados = autores_duplicados() + + assert len(lista_autores_duplicados) == 1 + assert lista_autores_duplicados[0]['count'] == 2 + assert lista_autores_duplicados[0]['nome'] == "Nome_Autor_Teste" + + +@pytest.mark.django_db(transaction=False) +def test_lista_bancada_comissao_autor_externo(): + tipo_autor = mommy.make( + TipoAutor, + descricao="Tipo_Autor_Teste" + ) + tipo_autor_externo = mommy.make( + TipoAutor, + descricao="Externo" + ) + + legislatura = mommy.make( + Legislatura, + numero=1, + data_inicio='2012-01-03', + data_fim='2013-01-02', + data_eleicao='2011-10-04' + ) + + bancada_a = mommy.make( + Bancada, + legislatura=legislatura, + nome="Bancada_Teste", + data_criacao='2012-01-08', + ) + bancada_a.autor.create( + nome="Nome_Autor_Teste", + tipo=tipo_autor + ) + + bancada_b = mommy.make( + Bancada, + legislatura=legislatura, + nome="Bancada_Teste-1", + data_criacao='2012-02-02' + ) + autor_bancada_b = bancada_b.autor.create( + nome="Nome_Autor_Externo_Teste", + tipo=tipo_autor_externo + ) + + tipo_comissao = mommy.make( + TipoComissao, + nome="Tipo_Comissao_Teste", + natureza='T', + sigla="TCT" + ) + + comissao_a = mommy.make( + Comissao, + nome="Comissao_Teste", + sigla="CT", + data_criacao='2012-03-08', + ) + comissao_a.autor.create( + nome="Nome_Autor_Teste", + tipo=tipo_autor + ) + + comissao_b = mommy.make( + Comissao, + nome="Comissao_Teste-1", + sigla="CT1", + data_criacao='2012-04-01', + ) + autor_comissao_b = comissao_b.autor.create( + nome="Nome_Autor_Externo_Teste", + tipo=tipo_autor_externo + ) + + lista_bancada_comissao = bancada_comissao_autor_externo() + + assert len(lista_bancada_comissao) == 2 + assert lista_bancada_comissao[0][0:2] == (autor_bancada_b, bancada_b) + assert lista_bancada_comissao[0][2:4] == ('Bancada', 'sistema/bancada') + assert lista_bancada_comissao[1][0:2] == (autor_comissao_b, comissao_b) + assert lista_bancada_comissao[1][2:4] == ('Comissão', 'comissao') + + +@pytest.mark.django_db(transaction=False) +def test_lista_anexados_ciclicas(): + ## DocumentoAdministrativo + tipo_documento = mommy.make( + TipoDocumentoAdministrativo, + sigla="TT", + descricao="Tipo_Teste" + ) + + documento_a = mommy.make( + DocumentoAdministrativo, + tipo=tipo_documento, + numero=26, + ano=2019, + data='2019-05-15', + ) + documento_b = mommy.make( + DocumentoAdministrativo, + tipo=tipo_documento, + numero=27, + ano=2019, + data='2019-05-16', + ) + documento_c = mommy.make( + DocumentoAdministrativo, + tipo=tipo_documento, + numero=28, + ano=2019, + data='2019-05-17', + ) + documento_a1 = mommy.make( + DocumentoAdministrativo, + tipo=tipo_documento, + numero=29, + ano=2019, + data='2019-05-18', + ) + documento_b1 = mommy.make( + DocumentoAdministrativo, + tipo=tipo_documento, + numero=30, + ano=2019, + data='2019-05-19', + ) + documento_c1 = mommy.make( + DocumentoAdministrativo, + tipo=tipo_documento, + numero=31, + ano=2019, + data='2019-05-20', + ) + + mommy.make( + Anexado, + documento_principal=documento_a, + documento_anexado=documento_b, + data_anexacao='2019-05-21' + ) + mommy.make( + Anexado, + documento_principal=documento_a, + documento_anexado=documento_c, + data_anexacao='2019-05-22' + ) + mommy.make( + Anexado, + documento_principal=documento_b, + documento_anexado=documento_c, + data_anexacao='2019-05-23' + ) + mommy.make( + Anexado, + documento_principal=documento_a1, + documento_anexado=documento_b1, + data_anexacao='2019-05-24' + ) + mommy.make( + Anexado, + documento_principal=documento_a1, + documento_anexado=documento_c1, + data_anexacao='2019-05-25' + ) + mommy.make( + Anexado, + documento_principal=documento_b1, + documento_anexado=documento_c1, + data_anexacao='2019-05-26' + ) + mommy.make( + Anexado, + documento_principal=documento_c1, + documento_anexado=documento_b1, + data_anexacao='2019-05-27' + ) + + lista_documento_ciclicos = anexados_ciclicos(False) + + ## Matéria + tipo_materia = mommy.make( + TipoMateriaLegislativa, + descricao="Tipo_Teste" + ) + regime_tramitacao = mommy.make( + RegimeTramitacao, + descricao="Regime_Teste" + ) + + materia_a = mommy.make( + MateriaLegislativa, + numero=20, + ano=2018, + data_apresentacao="2018-01-04", + regime_tramitacao=regime_tramitacao, + tipo=tipo_materia + ) + materia_b = mommy.make( + MateriaLegislativa, + numero=21, + ano=2019, + data_apresentacao="2019-05-04", + regime_tramitacao=regime_tramitacao, + tipo=tipo_materia + ) + materia_c = mommy.make( + MateriaLegislativa, + numero=22, + ano=2019, + data_apresentacao="2019-05-05", + regime_tramitacao=regime_tramitacao, + tipo=tipo_materia + ) + materia_a1 = mommy.make( + MateriaLegislativa, + numero=23, + ano=2018, + data_apresentacao="2019-05-06", + regime_tramitacao=regime_tramitacao, + tipo=tipo_materia + ) + materia_b1 = mommy.make( + MateriaLegislativa, + numero=24, + ano=2019, + data_apresentacao="2019-05-07", + regime_tramitacao=regime_tramitacao, + tipo=tipo_materia + ) + materia_c1 = mommy.make( + MateriaLegislativa, + numero=25, + ano=2019, + data_apresentacao="2019-05-08", + regime_tramitacao=regime_tramitacao, + tipo=tipo_materia + ) + + mommy.make( + Anexada, + materia_principal=materia_a, + materia_anexada=materia_b, + data_anexacao='2019-05-11' + ) + mommy.make( + Anexada, + materia_principal=materia_a, + materia_anexada=materia_c, + data_anexacao='2019-05-12' + ) + mommy.make( + Anexada, + materia_principal=materia_b, + materia_anexada=materia_c, + data_anexacao='2019-05-13' + ) + mommy.make( + Anexada, + materia_principal=materia_a1, + materia_anexada=materia_b1, + data_anexacao='2019-05-11' + ) + mommy.make( + Anexada, + materia_principal=materia_a1, + materia_anexada=materia_c1, + data_anexacao='2019-05-12' + ) + mommy.make( + Anexada, + materia_principal=materia_b1, + materia_anexada=materia_c1, + data_anexacao='2019-05-13' + ) + mommy.make( + Anexada, + materia_principal=materia_c1, + materia_anexada=materia_b1, + data_anexacao='2019-05-14' + ) + + lista_materias_ciclicas = anexados_ciclicos(True) + + assert len(lista_materias_ciclicas) == 2 + assert lista_materias_ciclicas[0] == (datetime.date(2019,5,13), materia_b1, materia_c1) + assert lista_materias_ciclicas[1] == (datetime.date(2019,5,14), materia_c1, materia_b1) + + assert len(lista_documento_ciclicos) == 2 + assert lista_documento_ciclicos[0] == (datetime.date(2019,5,26), documento_b1, documento_c1) + assert lista_documento_ciclicos[1] == (datetime.date(2019,5,27), documento_c1, documento_b1) @pytest.mark.django_db(transaction=False) diff --git a/sapl/base/urls.py b/sapl/base/urls.py index 8fbeb43b7..d9a1c5a94 100644 --- a/sapl/base/urls.py +++ b/sapl/base/urls.py @@ -29,17 +29,13 @@ from .views import (AlterarSenha, AppConfigCrud, CasaLegislativaCrud, EstatisticasAcessoNormas, RelatoriosListView, ListarInconsistenciasView, ListarProtocolosDuplicadosView, - ListarProtocolosComMateriasView, - ListarMatProtocoloInexistenteView, + ListarProtocolosComMateriasView, ListarMatProtocoloInexistenteView, ListarParlamentaresDuplicadosView, - ListarFiliacoesSemDataFiliacaoView, - ListarMandatoSemDataInicioView, - ListarParlMandatosIntersecaoView, - ListarParlFiliacoesIntersecaoView, - ListarAutoresDuplicadosView, - ListarBancadaComissaoAutorExternoView, - ListarLegislaturaInfindavelView, - pesquisa_textual, + ListarFiliacoesSemDataFiliacaoView, ListarMandatoSemDataInicioView, + ListarParlMandatosIntersecaoView, ListarParlFiliacoesIntersecaoView, + ListarAutoresDuplicadosView, ListarBancadaComissaoAutorExternoView, + ListarLegislaturaInfindavelView, ListarAnexadasCiclicasView, + ListarAnexadosCiclicosView, pesquisa_textual, RelatorioHistoricoTramitacaoAdmView) @@ -180,6 +176,13 @@ urlpatterns = [ url(r'^sistema/inconsistencias/legislatura_infindavel$', ListarLegislaturaInfindavelView.as_view(), name='lista_legislatura_infindavel'), + url(r'sistema/inconsistencias/anexadas_ciclicas$', + ListarAnexadasCiclicasView.as_view(), + name='lista_anexadas_ciclicas'), + url(r'sistema/inconsistencias/anexados_ciclicos$', + ListarAnexadosCiclicosView.as_view(), + name='lista_anexados_ciclicos'), + url(r'^sistema/pesquisa-textual', pesquisa_textual, name='pesquisa_textual'), diff --git a/sapl/base/views.py b/sapl/base/views.py index c56b5e880..f3b1c553d 100644 --- a/sapl/base/views.py +++ b/sapl/base/views.py @@ -35,15 +35,15 @@ from sapl.base.forms import AutorForm, AutorFormForAdmin, TipoAutorForm from sapl.base.models import Autor, TipoAutor from sapl.comissoes.models import Reuniao, Comissao from sapl.crud.base import CrudAux, make_pagination -from sapl.materia.models import (Autoria, MateriaLegislativa, Proposicao, +from sapl.materia.models import (Autoria, MateriaLegislativa, Proposicao, Anexada, TipoMateriaLegislativa, StatusTramitacao, UnidadeTramitacao) from sapl.norma.models import (NormaJuridica, NormaEstatisticas) -from sapl.parlamentares.models import Parlamentar, Legislatura, Mandato, Filiacao +from sapl.parlamentares.models import Parlamentar, Legislatura, Mandato, Filiacao, SessaoLegislativa from sapl.protocoloadm.models import (Protocolo, TipoDocumentoAdministrativo, StatusTramitacaoAdministrativo, - DocumentoAdministrativo) + DocumentoAdministrativo, Anexado) from sapl.sessao.models import (PresencaOrdemDia, SessaoPlenaria, - SessaoPlenariaPresenca, Bancada) + SessaoPlenariaPresenca, Bancada, TipoSessaoPlenaria) from sapl.utils import (parlamentares_ativos, gerar_hash_arquivo, SEPARADOR_HASH_PROPOSICAO, show_results_filter_set, mail_service_configured, intervalos_tem_intersecao, remover_acentos) @@ -342,14 +342,61 @@ class RelatorioPresencaSessaoView(FilterView): # Verifica se os campos foram preenchidos if not self.filterset.form.is_valid(): return context + + cd = self.filterset.form.cleaned_data + if not cd['data_inicio'] and not cd['sessao_legislativa'] \ + and not cd['legislatura']: + msg = _("Formulário inválido! Preencha pelo menos algum dos campos Período, Legislatura ou Sessão Legislativa.") + messages.error(self.request, msg) + return context + + # Caso a data tenha sido preenchida, verifica se foi preenchida corretamente + if self.request.GET.get('data_inicio_0') and not self.request.GET.get('data_inicio_1'): + msg = _("Formulário inválido! Preencha a data do Período Final.") + messages.error(self.request, msg) + return context + + if not self.request.GET.get('data_inicio_0') and self.request.GET.get('data_inicio_1'): + msg = _("Formulário inválido! Preencha a data do Período Inicial.") + messages.error(self.request, msg) + return context + + param0 = {} + + legislatura_pk = self.request.GET.get('legislatura') + if legislatura_pk: + param0['sessao_plenaria__legislatura_id'] = legislatura_pk + legislatura = Legislatura.objects.get(id=legislatura_pk) + context['legislatura'] = legislatura - # if 'salvar' not in self.request.GET: - where = context['object_list'].query.where - _range = where.children[0].rhs + sessao_legislativa_pk = self.request.GET.get('sessao_legislativa') + if sessao_legislativa_pk: + param0['sessao_plenaria__sessao_legislativa_id'] = sessao_legislativa_pk + sessao_legislativa = SessaoLegislativa.objects.get(id=sessao_legislativa_pk) + context['sessao_legislativa'] = sessao_legislativa - sufixo = 'sessao_plenaria__data_inicio__range' - param0 = {'%s' % sufixo: _range} + tipo_sessao_plenaria_pk = self.request.GET.get('tipo') + context['tipo'] = '' + if tipo_sessao_plenaria_pk: + param0['sessao_plenaria__tipo_id'] = tipo_sessao_plenaria_pk + context['tipo'] = TipoSessaoPlenaria.objects.get(id=tipo_sessao_plenaria_pk) + _range = [] + + if ('data_inicio_0' in self.request.GET) and self.request.GET['data_inicio_0'] and \ + ('data_inicio_1' in self.request.GET) and self.request.GET['data_inicio_1']: + where = context['object_list'].query.where + _range = where.children[0].rhs + + elif legislatura_pk and not sessao_legislativa_pk: + _range = [legislatura.data_inicio, legislatura.data_fim] + + elif sessao_legislativa_pk: + _range = [sessao_legislativa.data_inicio, sessao_legislativa.data_fim] + + param0.update({'sessao_plenaria__data_inicio__range': _range}) + + # Parlamentares com Mandato no intervalo de tempo (Ativos) parlamentares_qs = parlamentares_ativos( _range[0], _range[1]).order_by('nome_parlamentar') @@ -357,16 +404,12 @@ class RelatorioPresencaSessaoView(FilterView): 'id', flat=True) # Presenças de cada Parlamentar em Sessões - presenca_sessao = SessaoPlenariaPresenca.objects.filter( - parlamentar_id__in=parlamentares_id, - sessao_plenaria__data_inicio__range=_range).values_list( + presenca_sessao = SessaoPlenariaPresenca.objects.filter(**param0).values_list( 'parlamentar_id').annotate( sessao_count=Count('id')) # Presenças de cada Ordem do Dia - presenca_ordem = PresencaOrdemDia.objects.filter( - parlamentar_id__in=parlamentares_id, - sessao_plenaria__data_inicio__range=_range).values_list( + presenca_ordem = PresencaOrdemDia.objects.filter(**param0).values_list( 'parlamentar_id').annotate( sessao_count=Count('id')) @@ -433,6 +476,14 @@ class RelatorioPresencaSessaoView(FilterView): context['periodo'] = ( self.request.GET['data_inicio_0'] + ' - ' + self.request.GET['data_inicio_1']) + context['sessao_legislativa'] = '' + context['legislatura'] = '' + context['exibir_ordem'] = self.request.GET.get('exibir_ordem_dia') == 'on' + + if sessao_legislativa_pk: + context['sessao_legislativa'] = SessaoLegislativa.objects.get(id=sessao_legislativa_pk) + if legislatura_pk: + context['legislatura'] = Legislatura.objects.get(id=legislatura_pk) # ===================================================================== qr = self.request.GET.copy() context['filter_url'] = ('&' + qr.urlencode()) if len(qr) > 0 else '' @@ -498,7 +549,7 @@ class RelatorioDataFimPrazoTramitacaoView(FilterView): def get_context_data(self, **kwargs): context = super(RelatorioDataFimPrazoTramitacaoView, self).get_context_data(**kwargs) - context['title'] = _('Fim de Prazo de Tramitações') + context['title'] = _('Relatório de Tramitações') if not self.filterset.form.is_valid(): return context qr = self.request.GET.copy() @@ -1016,10 +1067,128 @@ class ListarInconsistenciasView(PermissionRequiredMixin, ListView): len(legislatura_infindavel()) ) ) - + tabela.append( + ('anexadas_ciclicas', + 'Matérias Anexadas cíclicas', + len(anexados_ciclicos(True)) + ) + ) + tabela.append( + ('anexados_ciclicos', + 'Documentos Anexados cíclicos', + len(anexados_ciclicos(False)) + ) + ) return tabela +def anexados_ciclicos(ofMateriaLegislativa): + ciclicos = [] + + if ofMateriaLegislativa: + principais = Anexada.objects.values( + 'materia_principal' + ).annotate( + count=Count('materia_principal') + ).filter(count__gt=0).order_by('-data_anexacao') + else: + principais = Anexado.objects.values( + 'documento_principal' + ).annotate( + count=Count('documento_principal') + ).filter(count__gt=0).order_by('-data_anexacao') + + for principal in principais: + anexados_total = [] + + if ofMateriaLegislativa: + anexados = Anexada.objects.filter( + materia_principal=principal['materia_principal'] + ).order_by('-data_anexacao') + else: + anexados = Anexado.objects.filter( + documento_principal=principal['documento_principal'] + ).order_by('-data_anexacao') + + anexados_temp = list(anexados) + while anexados_temp: + anexado = anexados_temp.pop() + if ofMateriaLegislativa: + if anexado.materia_anexada not in anexados_total: + if not principal['materia_principal'] == anexado.materia_anexada.pk: + anexados_total.append(anexado.materia_anexada) + anexados_anexado = Anexada.objects.filter( + materia_principal=anexado.materia_anexada + ) + anexados_temp.extend(anexados_anexado) + else: + ciclicos.append((anexado.data_anexacao, anexado.materia_principal, anexado.materia_anexada)) + else: + if anexado.documento_anexado not in anexados_total: + if not principal['documento_principal'] == anexado.documento_anexado.pk: + anexados_total.append(anexado.documento_anexado) + anexados_anexado = Anexado.objects.filter( + documento_principal=anexado.documento_anexado + ) + anexados_temp.extend(anexados_anexado) + else: + ciclicos.append((anexado.data_anexacao, anexado.documento_principal, anexado.documento_anexado)) + + return ciclicos + + +class ListarAnexadosCiclicosView(PermissionRequiredMixin, ListView): + model = get_user_model() + template_name = 'base/anexados_ciclicos.html' + context_object_name = 'anexados_ciclicos' + permission_required = ('base.list_appconfig',) + paginate_by = 10 + + def get_queryset(self): + return anexados_ciclicos(False) + + def get_context_data(self, **kwargs): + context = super( + ListarAnexadosCiclicosView, self + ).get_context_data(**kwargs) + + paginator = context['paginator'] + page_obj = context['page_obj'] + + context['page_range'] = make_pagination( + page_obj.number, paginator.num_pages + ) + context['NO_ENTRIES_MSG'] = 'Nenhum encontrado.' + + return context + + +class ListarAnexadasCiclicasView(PermissionRequiredMixin, ListView): + model = get_user_model() + template_name = 'base/anexadas_ciclicas.html' + context_object_name = 'anexadas_ciclicas' + permission_required = ('base.list_appconfig',) + paginate_by = 10 + + def get_queryset(self): + return anexados_ciclicos(True) + + def get_context_data(self, **kwargs): + context = super( + ListarAnexadasCiclicasView, self + ).get_context_data(**kwargs) + + paginator = context['paginator'] + page_obj = context['page_obj'] + + context['page_range'] = make_pagination( + page_obj.number, paginator.num_pages + ) + context['NO_ENTRIES_MSG'] = 'Nenhuma encontrada.' + + return context + + def legislatura_infindavel(): return Legislatura.objects.filter(data_fim__isnull=True).order_by('-numero') diff --git a/sapl/comissoes/forms.py b/sapl/comissoes/forms.py index e999f0b8d..9f7c449f8 100644 --- a/sapl/comissoes/forms.py +++ b/sapl/comissoes/forms.py @@ -1,6 +1,7 @@ import logging from django import forms +from sapl.settings import MAX_DOC_UPLOAD_SIZE from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ValidationError from django.db import transaction @@ -381,6 +382,23 @@ class ReuniaoForm(ModelForm): self.logger.error("A hora de término da reunião ({}) não pode ser menor que a de início ({})." .format(self.cleaned_data['hora_fim'], self.cleaned_data['hora_inicio'])) raise ValidationError(msg) + + upload_pauta = self.cleaned_data.get('upload_pauta', False) + upload_ata = self.cleaned_data.get('upload_ata', False) + upload_anexo = self.cleaned_data.get('upload_anexo', False) + + if upload_pauta and upload_pauta.size > MAX_DOC_UPLOAD_SIZE: + raise ValidationError("O arquivo Pauta da Reunião deve ser menor que {0:.1f} mb, o tamanho atual desse arquivo é {1:.1f} mb" \ + .format((MAX_DOC_UPLOAD_SIZE/1024)/1024, (upload_pauta.size/1024)/1024)) + + if upload_ata and upload_ata.size > MAX_DOC_UPLOAD_SIZE: + raise ValidationError("O arquivo Ata da Reunião deve ser menor que {0:.1f} mb, o tamanho atual desse arquivo é {1:.1f} mb" \ + .format((MAX_DOC_UPLOAD_SIZE/1024)/1024, (upload_ata.size/1024)/1024)) + + if upload_anexo and upload_anexo.size > MAX_DOC_UPLOAD_SIZE: + raise ValidationError("O arquivo Anexo da Reunião deve ser menor que {0:.1f} mb, o tamanho atual desse arquivo é {1:.1f} mb" \ + .format((MAX_DOC_UPLOAD_SIZE/1024)/1024, (upload_anexo.size/1024)/1024)) + return self.cleaned_data @@ -412,6 +430,20 @@ class DocumentoAcessorioCreateForm(FileFieldCheckMixin, forms.ModelForm): def create_documentoacessorio(self): reuniao = Reuniao.objects.get(id=self.initial['parent_pk']) + def clean(self): + super(DocumentoAcessorioCreateForm, self).clean() + + if not self.is_valid(): + return self.cleaned_data + + arquivo = self.cleaned_data.get('arquivo', False) + + if arquivo and arquivo.size > MAX_DOC_UPLOAD_SIZE: + raise ValidationError("O arquivo Texto Integral deve ser menor que {0:.1f} mb, o tamanho atual desse arquivo é {1:.1f} mb" \ + .format((MAX_DOC_UPLOAD_SIZE/1024)/1024, (arquivo.size/1024)/1024)) + + return self.cleaned_data + class DocumentoAcessorioEditForm(FileFieldCheckMixin, forms.ModelForm): @@ -424,3 +456,17 @@ class DocumentoAcessorioEditForm(FileFieldCheckMixin, forms.ModelForm): def __init__(self, user=None, **kwargs): super(DocumentoAcessorioEditForm, self).__init__(**kwargs) + + def clean(self): + super(DocumentoAcessorioEditForm, self).clean() + + if not self.is_valid(): + return self.cleaned_data + + arquivo = self.cleaned_data.get('arquivo', False) + + if arquivo and arquivo.size > MAX_DOC_UPLOAD_SIZE: + raise ValidationError("O arquivo Texto Integral deve ser menor que {0:.1f} mb, o tamanho atual desse arquivo é {1:.1f} mb" \ + .format((MAX_DOC_UPLOAD_SIZE/1024)/1024, (arquivo.size/1024)/1024)) + + return self.cleaned_data diff --git a/sapl/comissoes/tests/test_comissoes.py b/sapl/comissoes/tests/test_comissoes.py index 3b45bf337..274e36adb 100644 --- a/sapl/comissoes/tests/test_comissoes.py +++ b/sapl/comissoes/tests/test_comissoes.py @@ -3,7 +3,8 @@ from django.core.urlresolvers import reverse from django.utils.translation import ugettext as _ from model_mommy import mommy -from sapl.comissoes.models import Comissao, Composicao, Periodo, TipoComissao, Reuniao +from sapl.comissoes.models import Comissao, Composicao, Periodo +from sapl.comissoes.models import TipoComissao, Reuniao from sapl.parlamentares.models import Filiacao, Parlamentar, Partido from sapl.comissoes import forms @@ -45,6 +46,20 @@ def make_filiacao(): return Filiacao.objects.first() +@pytest.mark.django_db(transaction=False) +def test_tipo_comissao_model(): + mommy.make(TipoComissao, + nome='Teste_Nome_Tipo_Comissao', + natureza='T', + sigla='TSTC') + + tipo_comissao = TipoComissao.objects.first() + + assert tipo_comissao.nome == 'Teste_Nome_Tipo_Comissao' + assert tipo_comissao.natureza == 'T' + assert tipo_comissao.sigla == 'TSTC' + + @pytest.mark.django_db(transaction=False) def test_incluir_parlamentar_errors(admin_client): comissao = make_comissao() @@ -106,7 +121,7 @@ def test_periodo_invalidas(): form = forms.PeriodoForm(data={'data_inicio': '10/11/2017', 'data_fim': '09/11/2017' - }) + }) assert not form.is_valid() assert form.errors['__all__'] == [_('A Data Final não pode ser menor que ' 'a Data Inicial')] @@ -123,7 +138,7 @@ def test_valida_campos_obrigatorios_periodo_form(): assert errors['data_inicio'] == [_('Este campo é obrigatório.')] assert len(errors) == 1 - + @pytest.mark.django_db(transaction=False) def test_valida_campos_obrigatorios_reuniao_form(): @@ -141,4 +156,3 @@ def test_valida_campos_obrigatorios_reuniao_form(): assert errors['hora_inicio'] == [_('Este campo é obrigatório.')] assert len(errors) == 6 - diff --git a/sapl/compilacao/tests/test_compilacao.py b/sapl/compilacao/tests/test_compilacao.py new file mode 100644 index 000000000..4eacb8cb1 --- /dev/null +++ b/sapl/compilacao/tests/test_compilacao.py @@ -0,0 +1,112 @@ +import pytest +from model_mommy import mommy + +from sapl.compilacao.models import PerfilEstruturalTextoArticulado +from sapl.compilacao.models import TipoTextoArticulado +from sapl.compilacao.models import TextoArticulado, TipoNota +from sapl.compilacao.models import TipoVide, TipoDispositivo +from sapl.compilacao.models import TipoDispositivoRelationship + + +@pytest.mark.django_db(transaction=False) +def test_perfil_estrutural_texto_articulado_model(): + perfil_estrutural_texto_articulado = mommy.make( + PerfilEstruturalTextoArticulado, + nome='Teste_Nome_Perfil', + sigla='TSPETA') + + assert perfil_estrutural_texto_articulado.nome == 'Teste_Nome_Perfil' + assert perfil_estrutural_texto_articulado.sigla == 'TSPETA' + + +@pytest.mark.django_db(transaction=False) +def test_tipo_texto_articulado_model(): + tipo_texto_articulado = mommy.make( + TipoTextoArticulado, + sigla='TTP', + descricao='T_Desc_Tipo_Texto_Articulado' + ) + + assert tipo_texto_articulado.sigla == 'TTP' + assert tipo_texto_articulado.descricao == 'T_Desc_Tipo_Texto_Articulado' + + +@pytest.mark.django_db(transaction=False) +def test_texto_articulado_model(): + texto_articulado = mommy.make( + TextoArticulado, + ementa='Teste_Ementa_Texto_Articulado', + numero='12345678', + ano=2016, + ) + + assert texto_articulado.ementa == 'Teste_Ementa_Texto_Articulado' + assert texto_articulado.numero == '12345678' + assert texto_articulado.ano == 2016 + + +@pytest.mark.django_db(transaction=False) +def test_tipo_nota_model(): + tipo_nota = mommy.make( + TipoNota, + sigla='TTN', + nome='Teste_Nome_Tipo_Nota' + ) + + assert tipo_nota.sigla == 'TTN' + assert tipo_nota.nome == 'Teste_Nome_Tipo_Nota' + + +@pytest.mark.django_db(transaction=False) +def test_tipo_vide_model(): + tipo_vide = mommy.make( + TipoVide, + sigla='TTV', + nome='Teste_Nome_Tipo_Vide' + ) + + assert tipo_vide.sigla == 'TTV' + assert tipo_vide.nome == 'Teste_Nome_Tipo_Vide' + + +@pytest.mark.django_db(transaction=False) +def test_tipo_dispositivo_model(): + tipo_dispositivo = mommy.make( + TipoDispositivo, + nome='Teste_Nome_Tipo_Dispositivo', + rotulo_ordinal=0 + ) + + assert tipo_dispositivo.nome == 'Teste_Nome_Tipo_Dispositivo' + assert tipo_dispositivo.rotulo_ordinal == 0 + + +@pytest.mark.django_db(transaction=False) +def test_tipo_dispositivo_relationship_model(): + tipo_dispositivo_pai = mommy.make( + TipoDispositivo, + nome='Tipo_Dispositivo_Pai', + rotulo_ordinal=0 + ) + + t_dispositivo_filho = mommy.make( + TipoDispositivo, + nome='Tipo_Dispositivo_Filho', + rotulo_ordinal=0 + ) + + p_e_texto_articulado = mommy.make( + PerfilEstruturalTextoArticulado, + nome='Teste_Nome_Perfil', + sigla='TSPETA') + + tipo_dispositivo_relationship = mommy.make( + TipoDispositivoRelationship, + pai=tipo_dispositivo_pai, + filho_permitido=t_dispositivo_filho, + perfil=p_e_texto_articulado, + ) + + assert tipo_dispositivo_relationship.pai == tipo_dispositivo_pai + assert tipo_dispositivo_relationship.perfil == p_e_texto_articulado + assert tipo_dispositivo_relationship.filho_permitido == t_dispositivo_filho diff --git a/sapl/materia/forms.py b/sapl/materia/forms.py index 8955ebc31..85e202139 100644 --- a/sapl/materia/forms.py +++ b/sapl/materia/forms.py @@ -3,7 +3,7 @@ import logging import os from crispy_forms.bootstrap import Alert, InlineRadios -from crispy_forms.layout import (HTML, Button, Column, Div, Field, Fieldset, +from crispy_forms.layout import (HTML, Button, Field, Fieldset, Layout, Row) from django import forms from django.contrib.contenttypes.models import ContentType @@ -156,25 +156,6 @@ class MateriaSimplificadaForm(FileFieldCheckMixin, ModelForm): ) super(MateriaSimplificadaForm, self).__init__(*args, **kwargs) - def clean(self): - super(MateriaSimplificadaForm, self).clean() - - if not self.is_valid(): - return self.cleaned_data - - cleaned_data = self.cleaned_data - - data_apresentacao = cleaned_data['data_apresentacao'] - ano = cleaned_data['ano'] - - if data_apresentacao.year != ano: - self.logger.error("O ano da matéria ({}) é diferente" - " do ano na data de apresentação ({}).".format(ano, data_apresentacao.year)) - raise ValidationError("O ano da matéria não pode ser " - "diferente do ano na data de apresentação") - - return cleaned_data - class MateriaLegislativaForm(FileFieldCheckMixin, ModelForm): @@ -256,13 +237,6 @@ class MateriaLegislativaForm(FileFieldCheckMixin, ModelForm): raise ValidationError( _('Tipo do Protocolo deve ser o mesmo do Tipo Matéria')) - if data_apresentacao.year != ano: - self.logger.error("O ano da matéria ({}) é diferente " - "do ano na data de apresentação ({})." - .format(ano, data_apresentacao.year)) - raise ValidationError(_("O ano da matéria não pode ser " - "diferente do ano na data de apresentação")) - ano_origem_externa = cleaned_data['ano_origem_externa'] data_origem_externa = cleaned_data['data_origem_externa'] @@ -275,6 +249,12 @@ class MateriaLegislativaForm(FileFieldCheckMixin, ModelForm): "pode ser diferente do ano na data de " "origem externa")) + texto_original = self.cleaned_data.get('texto_original', False) + + if texto_original and texto_original.size > MAX_DOC_UPLOAD_SIZE: + raise ValidationError("O arquivo Texto Original deve ser menor que {0:.1f} mb, o tamanho atual desse arquivo é {1:.1f} mb" \ + .format((MAX_DOC_UPLOAD_SIZE/1024)/1024, (texto_original.size/1024)/1024)) + return cleaned_data def save(self, commit=False): @@ -285,7 +265,7 @@ class MateriaLegislativaForm(FileFieldCheckMixin, ModelForm): materia = super(MateriaLegislativaForm, self).save(commit) materia.save() - + if self.cleaned_data['autor']: autoria = Autoria() autoria.primeiro_autor = primeiro_autor @@ -364,6 +344,20 @@ class DocumentoAcessorioForm(FileFieldCheckMixin, ModelForm): class Meta: model = DocumentoAcessorio fields = ['tipo', 'nome', 'data', 'autor', 'ementa', 'arquivo'] + + def clean(self): + super(DocumentoAcessorioForm, self).clean() + + if not self.is_valid(): + return self.cleaned_data + + arquivo = self.cleaned_data.get('arquivo', False) + + if arquivo and arquivo.size > MAX_DOC_UPLOAD_SIZE: + raise ValidationError("O arquivo Texto Integral deve ser menor que {0:.1f} mb, o tamanho atual desse arquivo é {1:.1f} mb" \ + .format((MAX_DOC_UPLOAD_SIZE/1024)/1024, (arquivo.size/1024)/1024)) + + return self.cleaned_data class RelatoriaForm(ModelForm): @@ -561,7 +555,8 @@ class TramitacaoForm(ModelForm): materia.em_tramitacao = False if tramitacao.status.indicador == "F" else True materia.save() - tramitar_anexadas = sapl.base.models.AppConfig.attr('tramitacao_materia') + tramitar_anexadas = sapl.base.models.AppConfig.attr( + 'tramitacao_materia') if tramitar_anexadas: lista_tramitacao = [] anexadas_list = lista_anexados(materia) @@ -571,35 +566,38 @@ class TramitacaoForm(ModelForm): ma.em_tramitacao = False if tramitacao.status.indicador == "F" else True ma.save() lista_tramitacao.append(Tramitacao( - status=tramitacao.status, - materia=ma, - data_tramitacao=tramitacao.data_tramitacao, - unidade_tramitacao_local=tramitacao.unidade_tramitacao_local, - data_encaminhamento=tramitacao.data_encaminhamento, - unidade_tramitacao_destino=tramitacao.unidade_tramitacao_destino, - urgente=tramitacao.urgente, - turno=tramitacao.turno, - texto=tramitacao.texto, - data_fim_prazo=tramitacao.data_fim_prazo, - user=tramitacao.user, - ip=tramitacao.ip - )) + status=tramitacao.status, + materia=ma, + data_tramitacao=tramitacao.data_tramitacao, + unidade_tramitacao_local=tramitacao.unidade_tramitacao_local, + data_encaminhamento=tramitacao.data_encaminhamento, + unidade_tramitacao_destino=tramitacao.unidade_tramitacao_destino, + urgente=tramitacao.urgente, + turno=tramitacao.turno, + texto=tramitacao.texto, + data_fim_prazo=tramitacao.data_fim_prazo, + user=tramitacao.user, + ip=tramitacao.ip + )) Tramitacao.objects.bulk_create(lista_tramitacao) return tramitacao -# Compara se os campos de duas tramitações são iguais, +# Compara se os campos de duas tramitações são iguais, # exceto os campos id, documento_id e timestamp def compara_tramitacoes_mat(tramitacao1, tramitacao2): if not tramitacao1 or not tramitacao2: return False lst_items = ['id', 'materia_id', 'timestamp'] - values = [(k,v) for k,v in tramitacao1.__dict__.items() if ((k not in lst_items) and (k[0] != '_'))] - other_values = [(k,v) for k,v in tramitacao2.__dict__.items() if (k not in lst_items and k[0] != '_')] + values = [(k, v) for k, v in tramitacao1.__dict__.items() + if ((k not in lst_items) and (k[0] != '_'))] + other_values = [(k, v) for k, v in tramitacao2.__dict__.items() + if (k not in lst_items and k[0] != '_')] return values == other_values + class TramitacaoUpdateForm(TramitacaoForm): unidade_tramitacao_local = forms.ModelChoiceField( queryset=UnidadeTramitacao.objects.all(), @@ -673,7 +671,8 @@ class TramitacaoUpdateForm(TramitacaoForm): materia.em_tramitacao = False if nova_tram_principal.status.indicador == "F" else True materia.save() - tramitar_anexadas = sapl.base.models.AppConfig.attr('tramitacao_materia') + tramitar_anexadas = sapl.base.models.AppConfig.attr( + 'tramitacao_materia') if tramitar_anexadas: anexadas_list = lista_anexados(materia) for ma in anexadas_list: @@ -696,6 +695,7 @@ class TramitacaoUpdateForm(TramitacaoForm): ma.save() return nova_tram_principal + class LegislacaoCitadaForm(ModelForm): tipo = forms.ModelChoiceField( @@ -869,8 +869,10 @@ class AnexadaForm(ModelForm): data_desanexacao = cleaned_data['data_desanexacao'] if cleaned_data['data_desanexacao'] else data_anexacao if data_anexacao > data_desanexacao: - self.logger.error("Data de anexação posterior à data de desanexação.") - raise ValidationError(_("Data de anexação posterior à data de desanexação.")) + self.logger.error( + "Data de anexação posterior à data de desanexação.") + raise ValidationError( + _("Data de anexação posterior à data de desanexação.")) try: self.logger.info("Tentando obter objeto MateriaLegislativa (numero={}, ano={}, tipo={})." @@ -899,9 +901,10 @@ class AnexadaForm(ModelForm): if is_anexada: self.logger.error("Matéria já se encontra anexada.") raise ValidationError(_('Matéria já se encontra anexada')) - + ciclico = False - anexadas_anexada = Anexada.objects.filter(materia_principal=materia_anexada) + anexadas_anexada = Anexada.objects.filter( + materia_principal=materia_anexada) while anexadas_anexada and not ciclico: anexadas = [] @@ -910,15 +913,17 @@ class AnexadaForm(ModelForm): if materia_principal == anexa.materia_anexada: ciclico = True - else: + else: for a in Anexada.objects.filter(materia_principal=anexa.materia_anexada): anexadas.append(a) anexadas_anexada = anexadas - + if ciclico: - self.logger.error("A matéria não pode ser anexada por uma de suas anexadas.") - raise ValidationError(_("A matéria não pode ser anexada por uma de suas anexadas.")) + self.logger.error( + "A matéria não pode ser anexada por uma de suas anexadas.") + raise ValidationError( + _("A matéria não pode ser anexada por uma de suas anexadas.")) cleaned_data['materia_anexada'] = materia_anexada @@ -1079,9 +1084,6 @@ class MateriaLegislativaFilterSet(django_filters.FilterSet): HTML(autor_modal), row4, row6, row7, row9, form_actions(label=_('Pesquisar'))) - - - ) @property @@ -1140,9 +1142,50 @@ def filtra_tramitacao_destino_and_status(status, destino): 'materia_id', flat=True) +class DespachoInicialCreateForm(forms.Form): + comissao = forms.ModelMultipleChoiceField( + queryset=Comissao.objects.filter(ativa=True), + widget=forms.CheckboxSelectMultiple(), + label=Comissao._meta.verbose_name_plural) + + def __init__(self, *args, **kwargs): + row1 = to_row( + [('comissao', 12), ]) + + self.helper = SaplFormHelper() + self.helper.form_method = 'POST' + self.helper.layout = SaplFormLayout(row1) + super().__init__(*args, **kwargs) + + def clean(self): + super().clean() + + comissoes = self.cleaned_data.get('comissao') + if not comissoes: + msg = _('Você deve escolher pelo menos uma comissão.') + raise ValidationError(msg) + + if not self.is_valid(): + return self.cleaned_data + + errors = [] + for comissao in comissoes: + if DespachoInicial.objects.filter( + materia=self.initial['materia'], + comissao=comissao, + ).exists(): + msg = _('Já existe um Despacho cadastrado para %s' % + comissao) + errors.append(msg) + if errors: + raise ValidationError(errors) + + return self.cleaned_data + + class DespachoInicialForm(ModelForm): comissao = forms.ModelChoiceField( - queryset=Comissao.objects.filter(ativa=True)) + queryset=Comissao.objects.filter(ativa=True), label=_('Comissão')) class Meta: model = DespachoInicial @@ -1184,7 +1227,8 @@ class AutoriaForm(ModelForm): if 'initial' in kwargs and 'materia' in kwargs['initial']: materia = kwargs['initial']['materia'] - self.fields['primeiro_autor'].initial = Autoria.objects.filter(materia=materia).count() == 0 + self.fields['primeiro_autor'].initial = Autoria.objects.filter( + materia=materia).count() == 0 row1 = to_row([('tipo_autor', 4), ('autor', 4), @@ -1254,8 +1298,9 @@ class AutoriaMultiCreateForm(Form): super().__init__(*args, **kwargs) if 'initial' in kwargs and 'autores' in kwargs['initial']: - self.fields['primeiro_autor'].initial = kwargs['initial']['autores'].count() == 0 - + self.fields['primeiro_autor'].initial = kwargs['initial']['autores'].count( + ) == 0 + row1 = to_row([('tipo_autor', 10), ('primeiro_autor', 2)]) row2 = to_row([('autor', 12), ]) @@ -1456,7 +1501,8 @@ class TipoProposicaoForm(ModelForm): self.fields['content_type'].choices = [ (ct.pk, ct) for k, ct in content_types.items()] - self.fields['content_type'].choices.sort(key=lambda x: str(x[1])) + # Ordena por id + self.fields['content_type'].choices.sort(key=lambda x: x[0]) if self.instance.pk: self.fields[ @@ -1536,6 +1582,197 @@ class TipoProposicaoSelect(Select): return option +class TramitacaoEmLoteForm(ModelForm): + logger = logging.getLogger(__name__) + + class Meta: + model = Tramitacao + fields = ['data_tramitacao', + 'unidade_tramitacao_local', + 'status', + 'urgente', + 'turno', + 'unidade_tramitacao_destino', + 'data_encaminhamento', + 'data_fim_prazo', + 'texto', + 'user', + 'ip'] + widgets = {'user': forms.HiddenInput(), + 'ip': forms.HiddenInput()} + + + def __init__(self, *args, **kwargs): + super(TramitacaoEmLoteForm, self).__init__(*args, **kwargs) + self.fields['data_tramitacao'].initial = timezone.now().date() + ust = UnidadeTramitacao.objects.select_related().all() + unidade_tramitacao_destino = [('', '---------')] + [(ut.pk, ut) + for ut in ust if ut.comissao and ut.comissao.ativa] + unidade_tramitacao_destino.extend( + [(ut.pk, ut) for ut in ust if ut.orgao]) + unidade_tramitacao_destino.extend( + [(ut.pk, ut) for ut in ust if ut.parlamentar]) + self.fields['unidade_tramitacao_destino'].choices = unidade_tramitacao_destino + self.fields['urgente'].label = "Urgente? *" + + row1 = to_row([ + ('data_tramitacao', 4), + ('data_encaminhamento', 4), + ('data_fim_prazo', 4) + ]) + row2 = to_row([ + ('unidade_tramitacao_local', 6), + ('unidade_tramitacao_destino', 6), + ]) + row3 = to_row([ + ('status', 4), + ('urgente', 4), + ('turno', 4) + ]) + row4 = to_row([ + ('texto', 12) + ]) + + documentos_checkbox_HTML = ''' + +
+ Selecione as matérias para tramitação: + +
+
+ +
+
+ + + + + {% for materia in object_list %} + + + + {% endfor %} + +
Matéria
+ + + {{materia.tipo.sigla}} {{materia.tipo.descricao}} {{materia.numero}}/{{materia.ano}} + +
+
+ ''' + + self.helper = SaplFormHelper() + self.helper.layout = Layout( + Fieldset( + 'Detalhes da tramitação:', + row1, row2, row3, row4, + HTML(documentos_checkbox_HTML), + form_actions(label='Salvar') + ) + ) + + + def clean(self): + cleaned_data = super(TramitacaoEmLoteForm, self).clean() + + if not self.is_valid(): + return self.cleaned_data + + if 'data_encaminhamento' in cleaned_data: + data_enc_form = cleaned_data['data_encaminhamento'] + if 'data_fim_prazo' in cleaned_data: + data_prazo_form = cleaned_data['data_fim_prazo'] + if 'data_tramitacao' in cleaned_data: + data_tram_form = cleaned_data['data_tramitacao'] + + if not self.instance.data_tramitacao: + + if cleaned_data['data_tramitacao'] > timezone.now().date(): + self.logger.error('A data de tramitação ({}) deve ser ' + 'menor ou igual a data de hoje ({})!' + .format(cleaned_data['data_tramitacao'], timezone.now().date())) + msg = _( + 'A data de tramitação deve ser ' + + 'menor ou igual a data de hoje!') + raise ValidationError(msg) + + if data_enc_form: + if data_enc_form < data_tram_form: + self.logger.error('A data de encaminhamento ({}) deve ser ' + 'maior que a data de tramitação ({})!' + .format(data_enc_form, data_tram_form)) + msg = _('A data de encaminhamento deve ser ' + + 'maior que a data de tramitação!') + raise ValidationError(msg) + + if data_prazo_form: + if data_prazo_form < data_tram_form: + self.logger.error('A data fim de prazo ({}) deve ser ' + 'maior que a data de tramitação ({})!' + .format(data_prazo_form, data_tram_form)) + msg = _('A data fim de prazo deve ser ' + + 'maior que a data de tramitação!') + raise ValidationError(msg) + + return cleaned_data + + @transaction.atomic + def save(self, commit=True): + cd = self.cleaned_data + materias = self.initial['materias'] + user = self.initial['user'] if 'user' in self.initial else None + ip = self.initial['ip'] if 'ip' in self.initial else '' + tramitar_anexadas = AppConfig.attr('tramitacao_materia') + for mat_id in materias: + mat = MateriaLegislativa.objects.get(id=mat_id) + tramitacao = Tramitacao.objects.create( + status=cd['status'], + materia=mat, + data_tramitacao=cd['data_tramitacao'], + unidade_tramitacao_local=cd['unidade_tramitacao_local'], + unidade_tramitacao_destino=cd['unidade_tramitacao_destino'], + data_encaminhamento=cd['data_encaminhamento'], + urgente=cd['urgente'], + turno=cd['turno'], + texto=cd['texto'], + data_fim_prazo=cd['data_fim_prazo'], + user=user, + ip=ip + ) + mat.em_tramitacao = False if tramitacao.status.indicador == "F" else True + mat.save() + + if tramitar_anexadas: + lista_tramitacao = [] + anexadas = lista_anexados(mat) + for ml in anexadas: + if not ml.tramitacao_set.all() \ + or ml.tramitacao_set.last() \ + .unidade_tramitacao_destino == tramitacao.unidade_tramitacao_local: + ml.em_tramitacao = False if tramitacao.status.indicador == "F" else True + ml.save() + lista_tramitacao.append(Tramitacao( + status=tramitacao.status, + materia=ml, + data_tramitacao=tramitacao.data_tramitacao, + unidade_tramitacao_local=tramitacao.unidade_tramitacao_local, + data_encaminhamento=tramitacao.data_encaminhamento, + unidade_tramitacao_destino=tramitacao.unidade_tramitacao_destino, + urgente=tramitacao.urgente, + turno=tramitacao.turno, + texto=tramitacao.texto, + data_fim_prazo=tramitacao.data_fim_prazo, + user=tramitacao.user, + ip=tramitacao.ip + )) + Tramitacao.objects.bulk_create(lista_tramitacao) + + return tramitacao + + class ProposicaoForm(FileFieldCheckMixin, forms.ModelForm): logger = logging.getLogger(__name__) @@ -1584,7 +1821,6 @@ class ProposicaoForm(FileFieldCheckMixin, forms.ModelForm): 'observacao', 'texto_original', 'materia_de_vinculo', - 'tipo_materia', 'numero_materia', 'ano_materia', @@ -1672,12 +1908,11 @@ class ProposicaoForm(FileFieldCheckMixin, forms.ModelForm): def clean_texto_original(self): texto_original = self.cleaned_data.get('texto_original', False) + if texto_original and texto_original.size > MAX_DOC_UPLOAD_SIZE: - max_size = str(MAX_DOC_UPLOAD_SIZE / (1024 * 1024)) - self.logger.error( - "- Arquivo muito grande. ( > {0}MB )".format(max_size)) - raise ValidationError( - "Arquivo muito grande. ( > {0}MB )".format(max_size)) + raise ValidationError("O arquivo Texto Original deve ser menor que {0:.1f} mb, o tamanho atual desse arquivo é {1:.1f} mb" \ + .format((MAX_DOC_UPLOAD_SIZE/1024)/1024, (texto_original.size/1024)/1024)) + return texto_original def gerar_hash(self, inst, receber_recibo): @@ -1731,6 +1966,7 @@ class ProposicaoForm(FileFieldCheckMixin, forms.ModelForm): self.logger.info("MateriaLegislativa vinculada (tipo_id={}, ano={}, numero={}) com sucesso." .format(tm, am, nm)) cd['materia_de_vinculo'] = materia_de_vinculo + return cd def save(self, commit=True): @@ -1766,7 +2002,7 @@ class ProposicaoForm(FileFieldCheckMixin, forms.ModelForm): ano=timezone.now().year).aggregate(Max('numero_proposicao')) numero__max = numero__max['numero_proposicao__max'] inst.numero_proposicao = ( - numero__max + 1) if numero__max else 1 + numero__max + 1) if numero__max else 1 self.gerar_hash(inst, receber_recibo) @@ -1864,7 +2100,7 @@ class ConfirmarProposicaoForm(ProposicaoForm): attrs={'readonly': 'readonly'})) regime_tramitacao = forms.ModelChoiceField(label="Regime de tramitação", - required=False, queryset=RegimeTramitacao.objects.all()) + required=False, queryset=RegimeTramitacao.objects.all()) gerar_protocolo = forms.ChoiceField( required=False, diff --git a/sapl/materia/migrations/0051_auto_20190703_1414.py b/sapl/materia/migrations/0051_auto_20190703_1414.py new file mode 100644 index 000000000..fad0e1933 --- /dev/null +++ b/sapl/materia/migrations/0051_auto_20190703_1414.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-07-03 17:14 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('materia', '0050_auto_20190521_1148'), + ] + + operations = [ + migrations.AlterField( + model_name='despachoinicial', + name='comissao', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='comissoes.Comissao', verbose_name='Comissão'), + ), + ] diff --git a/sapl/materia/models.py b/sapl/materia/models.py index 096aabc97..d9a5e344a 100644 --- a/sapl/materia/models.py +++ b/sapl/materia/models.py @@ -485,7 +485,7 @@ class AssuntoMateria(models.Model): @reversion.register() class DespachoInicial(models.Model): materia = models.ForeignKey(MateriaLegislativa, on_delete=models.CASCADE) - comissao = models.ForeignKey(Comissao, on_delete=models.CASCADE) + comissao = models.ForeignKey(Comissao, on_delete=models.CASCADE, verbose_name="Comissão") class Meta: verbose_name = _('Despacho Inicial') diff --git a/sapl/materia/urls.py b/sapl/materia/urls.py index 962dc2cff..2fe9145de 100644 --- a/sapl/materia/urls.py +++ b/sapl/materia/urls.py @@ -26,9 +26,11 @@ from sapl.materia.views import (AcompanhamentoConfirmarView, TramitacaoEmLoteView, UnidadeTramitacaoCrud, proposicao_texto, recuperar_materia, ExcluirTramitacaoEmLoteView, RetornarProposicao, - MateriaPesquisaSimplesView) + MateriaPesquisaSimplesView, + DespachoInicialMultiCreateView) from sapl.norma.views import NormaPesquisaSimplesView -from sapl.protocoloadm.views import (FichaPesquisaAdmView, FichaSelecionaAdmView) +from sapl.protocoloadm.views import ( + FichaPesquisaAdmView, FichaSelecionaAdmView) from .apps import AppConfig @@ -55,13 +57,20 @@ urlpatterns_impressos = [ name='impressos_materia_pesquisa'), url(r'^materia/impressos/ficha-pesquisa-adm/$', FichaPesquisaAdmView.as_view(), - name= 'impressos_ficha_pesquisa_adm'), + name='impressos_ficha_pesquisa_adm'), url(r'^materia/impressos/ficha-seleciona-adm/$', FichaSelecionaAdmView.as_view(), - name= 'impressos_ficha_seleciona_adm'), + name='impressos_ficha_seleciona_adm'), ] urlpatterns_materia = [ + + # Esta customização substitui a url do crud desque que ela permaneça antes + # da inclusão das urls de DespachoInicialCrud + url(r'^materia/(?P\d+)/despachoinicial/create', + DespachoInicialMultiCreateView.as_view(), + name='despacho-inicial-multi'), + url(r'^materia/', include(MateriaLegislativaCrud.get_urls() + AnexadaCrud.get_urls() + AutoriaCrud.get_urls() + @@ -76,7 +85,8 @@ urlpatterns_materia = [ url(r'^materia/(?P[0-9]+)/create_simplificado$', CriarProtocoloMateriaView.as_view(), name='materia_create_simplificado'), - url(r'^materia/recuperar-materia', recuperar_materia, name='recuperar_materia'), + url(r'^materia/recuperar-materia', + recuperar_materia, name='recuperar_materia'), url(r'^materia/(?P[0-9]+)/ta$', MateriaTaView.as_view(), name='materia_ta'), @@ -96,6 +106,7 @@ urlpatterns_materia = [ AutoriaMultiCreateView.as_view(), name='autoria_multicreate'), + url(r'^materia/acessorio-em-lote', DocumentoAcessorioEmLoteView.as_view(), name='acessorio_em_lote'), url(r'^materia/(?P\d+)/anexada-em-lote', MateriaAnexadaEmLoteView.as_view(), @@ -136,6 +147,7 @@ urlpatterns_proposicao = [ name='proposicao_texto'), url(r'^proposicao/(?P\d+)/retornar', RetornarProposicao.as_view(), name='retornar-proposicao'), + ] urlpatterns_sistema = [ diff --git a/sapl/materia/views.py b/sapl/materia/views.py index 739f4c486..21c590349 100644 --- a/sapl/materia/views.py +++ b/sapl/materia/views.py @@ -1,13 +1,11 @@ +from datetime import datetime +import itertools import logging import os -import shutil -import tempfile -import weasyprint -import itertools - -from datetime import datetime from random import choice +import shutil from string import ascii_letters, digits +import tempfile from crispy_forms.layout import HTML from django.conf import settings @@ -28,6 +26,7 @@ from django.views.generic.base import RedirectView from django.views.generic.edit import FormView from django_filters.views import FilterView import weasyprint +import weasyprint import sapl from sapl.base.email_utils import do_envia_email_confirmacao @@ -47,11 +46,12 @@ from sapl.materia.forms import (AnexadaForm, AutoriaForm, ConfirmarProposicaoForm, DevolverProposicaoForm, LegislacaoCitadaForm, OrgaoForm, ProposicaoForm, TipoProposicaoForm, - TramitacaoForm, TramitacaoUpdateForm, MateriaPesquisaSimplesForm) + TramitacaoForm, TramitacaoUpdateForm, MateriaPesquisaSimplesForm, + DespachoInicialCreateForm) 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.settings import MEDIA_ROOT, MAX_DOC_UPLOAD_SIZE from sapl.utils import (YES_NO_CHOICES, autor_label, autor_modal, SEPARADOR_HASH_PROPOSICAO, gerar_hash_arquivo, get_base_url, get_client_ip, get_mime_type_from_file_extension, montar_row_autor, @@ -69,7 +69,8 @@ from .forms import (AcessorioEmLoteFilterSet, AcompanhamentoMateriaForm, filtra_tramitacao_destino, filtra_tramitacao_destino_and_status, filtra_tramitacao_status, - ExcluirTramitacaoEmLote, compara_tramitacoes_mat) + ExcluirTramitacaoEmLote, compara_tramitacoes_mat, + TramitacaoEmLoteForm) from .models import (AcompanhamentoMateria, Anexada, AssuntoMateria, Autoria, DespachoInicial, DocumentoAcessorio, MateriaAssunto, MateriaLegislativa, Numeracao, Orgao, Origem, Proposicao, @@ -1140,9 +1141,9 @@ class RelatoriaCrud(MasterDetailCrud): parlamentar = relatoria.parlamentar comissao = relatoria.comissao composicoes = [p.composicao for p in - Participacao.objects.filter( - parlamentar=parlamentar, - composicao__comissao=comissao)] + Participacao.objects.filter( + parlamentar=parlamentar, + composicao__comissao=comissao)] data_designacao = relatoria.data_designacao_relator composicao = '' for c in composicoes: @@ -1203,7 +1204,8 @@ class TramitacaoCrud(MasterDetailCrud): '-timestamp', '-id').first() - #TODO: Esta checagem foi inserida na issue #2027, mas é mesmo necessária? + # TODO: Esta checagem foi inserida na issue #2027, mas é mesmo + # necessária? if ultima_tramitacao: if ultima_tramitacao.unidade_tramitacao_destino: context['form'].fields[ @@ -1257,7 +1259,8 @@ class TramitacaoCrud(MasterDetailCrud): layout_key = 'TramitacaoUpdate' def form_valid(self, form): - dict_objeto_antigo = Tramitacao.objects.get(pk=self.kwargs['pk']).__dict__ + dict_objeto_antigo = Tramitacao.objects.get( + pk=self.kwargs['pk']).__dict__ self.object = form.save() dict_objeto_novo = self.object.__dict__ @@ -1269,7 +1272,8 @@ class TramitacaoCrud(MasterDetailCrud): 'data_encaminhamento', 'data_fim_prazo', 'urgente', 'turno' ] - # Se não houve qualquer alteração em um dos dados, mantém o usuário e ip + # Se não houve qualquer alteração em um dos dados, mantém o usuário + # e ip for atributo in atributos: if dict_objeto_antigo[atributo] != dict_objeto_novo[atributo]: self.object.user = user @@ -1312,7 +1316,7 @@ class TramitacaoCrud(MasterDetailCrud): materia = tramitacao.materia url = reverse('sapl.materia:tramitacao_list', kwargs={'pk': materia.id}) - + ultima_tramitacao = materia.tramitacao_set.order_by( '-data_tramitacao', '-timestamp', @@ -1331,7 +1335,8 @@ class TramitacaoCrud(MasterDetailCrud): if materia.tramitacao_set.count() == 0: materia.em_tramitacao = False materia.save() - tramitar_anexadas = sapl.base.models.AppConfig.attr('tramitacao_materia') + tramitar_anexadas = sapl.base.models.AppConfig.attr( + 'tramitacao_materia') if tramitar_anexadas: mat_anexadas = lista_anexados(materia) for ma in mat_anexadas: @@ -1353,7 +1358,7 @@ class TramitacaoCrud(MasterDetailCrud): context = super().get_context_data(**kwargs) context['user'] = self.request.user return context - + def montar_helper_documento_acessorio(self): autor_row = montar_row_autor('autor') @@ -1484,20 +1489,58 @@ class AutoriaMultiCreateView(PermissionRequiredForAppCrudMixin, FormView): autores_selecionados = form.cleaned_data['autor'] primeiro_autor = form.cleaned_data['primeiro_autor'] for autor in autores_selecionados: - Autoria.objects.create(materia=self.materia, autor=autor, primeiro_autor=primeiro_autor) + Autoria.objects.create(materia=self.materia, + autor=autor, primeiro_autor=primeiro_autor) return FormView.form_valid(self, form) +class DespachoInicialMultiCreateView(PermissionRequiredForAppCrudMixin, FormView): + app_label = sapl.materia.apps.AppConfig.label + form_class = DespachoInicialCreateForm + template_name = 'materia/despachoinicial_multicreate_form.html' + + def get_initial(self): + initial = super().get_initial() + self.materia = MateriaLegislativa.objects.get(id=self.kwargs['pk']) + initial['materia'] = self.materia + return initial + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context['title'] = '%s (%s)' % ( + _('Adicionar Vários Despachos'), self.materia) + context['root_pk'] = self.kwargs['pk'] + context['subnav_template_name'] = 'materia/subnav.yaml' + return context + + def get_success_url(self): + messages.add_message( + self.request, messages.SUCCESS, + _('Despachos adicionados com sucesso.')) + return reverse( + 'sapl.materia:despachoinicial_list', kwargs={'pk': self.materia.pk}) + + def form_valid(self, form): + comissoes_selecionadas = form.cleaned_data['comissao'] + for comissao in comissoes_selecionadas: + DespachoInicial.objects.create( + materia=self.materia, comissao=comissao) + + return FormView.form_valid(self, form) + + @property + def cancel_url(self): + return reverse( + 'sapl.materia:despachoinicial_list', kwargs={'pk': self.materia.pk}) + + class DespachoInicialCrud(MasterDetailCrud): model = DespachoInicial parent_field = 'materia' help_topic = 'despacho_autoria' public = [RP_LIST, RP_DETAIL] - class CreateView(MasterDetailCrud.CreateView): - form_class = DespachoInicialForm - class UpdateView(MasterDetailCrud.UpdateView): form_class = DespachoInicialForm @@ -1620,7 +1663,7 @@ class MateriaLegislativaCrud(Crud): form_class = MateriaLegislativaForm def get_initial(self): - initial = super(CreateView, self).get_initial() + initial = super().get_initial() initial['user'] = self.request.user initial['ip'] = get_client_ip(self.request) @@ -1639,7 +1682,7 @@ class MateriaLegislativaCrud(Crud): dict_objeto_antigo = MateriaLegislativa.objects.get( pk=self.kwargs['pk'] ).__dict__ - + self.object = form.save() dict_objeto_novo = self.object.__dict__ @@ -1647,7 +1690,7 @@ class MateriaLegislativaCrud(Crud): 'tipo_id', 'ano', 'numero', 'data_apresentacao', 'numero_protocolo', 'tipo_apresentacao', 'texto_original', 'apelido', 'dias_prazo', 'polemica', 'objeto', 'regime_tramitacao_id', 'em_tramitacao', 'data_fim_prazo', - 'data_publicacao', 'complementar', 'tipo_origem_externa_id', + 'data_publicacao', 'complementar', 'tipo_origem_externa_id', 'numero_origem_externa', 'ano_origem_externa', 'local_origem_externa_id', 'data_origem_externa', 'ementa', 'indexacao', 'observacao' ] @@ -1664,7 +1707,7 @@ class MateriaLegislativaCrud(Crud): anexadas = lista_anexados(materia) for anexada in anexadas: - anexada.em_tramitacao = True if form.instance.em_tramitacao else False + anexada.em_tramitacao = True if form.instance.em_tramitacao else False anexada.save() return super().form_valid(form) @@ -1686,7 +1729,8 @@ class MateriaLegislativaCrud(Crud): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['user'] = self.request.user - context['materia'] = MateriaLegislativa.objects.get(pk=self.kwargs['pk']) + context['materia'] = MateriaLegislativa.objects.get( + pk=self.kwargs['pk']) return context class ListView(Crud.ListView, RedirectView): @@ -2073,20 +2117,29 @@ class DocumentoAcessorioEmLoteView(PermissionRequiredMixin, FilterView): msg = _('Autor tem que ter menos do que 50 caracteres.') messages.add_message(request, messages.ERROR, msg) return self.get(request, self.kwargs) - + + if request.FILES['arquivo'].size > MAX_DOC_UPLOAD_SIZE: + msg = _("O arquivo Anexo de Texto Integral deve ser menor que {0:.1f} MB, \ + o tamanho atual desse arquivo é {1:.1f} MB" \ + .format((MAX_DOC_UPLOAD_SIZE/1024)/1024, (request.FILES['arquivo'].size/1024)/1024)) + messages.add_message(request, messages.ERROR, msg) + return self.get(request, self.kwargs) + tmp_name = os.path.join(MEDIA_ROOT, request.FILES['arquivo'].name) with open(tmp_name, 'wb') as destination: for chunk in request.FILES['arquivo'].chunks(): destination.write(chunk) try: doc_data = tz.localize(datetime.strptime( - request.POST['data'], "%d/%m/%Y")) + request.POST['data'], "%d/%m/%Y")) except Exception as e: - msg = _('Formato da data incorreto. O formato deve ser da forma dd/mm/aaaa.') - messages.add_message(request, messages.ERROR, msg) - self.logger.error("User={}. {}. Data inserida: {}".format(username, str(msg), request.POST['data'])) - os.remove(tmp_name) - return self.get(request, self.kwargs) + msg = _( + 'Formato da data incorreto. O formato deve ser da forma dd/mm/aaaa.') + messages.add_message(request, messages.ERROR, msg) + self.logger.error("User={}. {}. Data inserida: {}".format( + username, str(msg), request.POST['data'])) + os.remove(tmp_name) + return self.get(request, self.kwargs) for materia_id in marcadas: doc = DocumentoAcessorio() @@ -2100,25 +2153,27 @@ class DocumentoAcessorioEmLoteView(PermissionRequiredMixin, FilterView): try: doc.clean_fields() except ValidationError as e: - for m in [ '%s: %s' % (DocumentoAcessorio()._meta.get_field(k).verbose_name, '
'.join(v)) - for k,v in e.message_dict.items() ]: + for m in ['%s: %s' % (DocumentoAcessorio()._meta.get_field(k).verbose_name, '
'.join(v)) + for k, v in e.message_dict.items()]: # Insere as mensagens de erro no formato: # 'verbose_name do nome do campo': 'mensagem de erro' messages.add_message(request, messages.ERROR, m) - self.logger.error("User={}. {}. Nome do arquivo: {}.".format(username, str(msg), request.FILES['arquivo'].name)) + self.logger.error("User={}. {}. Nome do arquivo: {}.".format( + username, str(msg), request.FILES['arquivo'].name)) os.remove(tmp_name) return self.get(request, self.kwargs) doc.save() - diretorio = os.path.join(MEDIA_ROOT, - 'sapl/public/documentoacessorio', - str(doc_data.year), - str(doc.id)) + 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, + 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.arquivo.name = file_path.split( + MEDIA_ROOT + "/")[1] # Retira MEDIA_ROOT do nome doc.save() os.remove(tmp_name) @@ -2144,17 +2199,17 @@ class MateriaAnexadaEmLoteView(PermissionRequiredMixin, FilterView): # Verifica se os campos foram preenchidos if not self.request.GET.get('tipo', " "): - msg =_('Por favor, selecione um tipo de matéria.') + msg = _('Por favor, selecione um tipo de matéria.') messages.add_message(self.request, messages.ERROR, msg) if not self.request.GET.get('data_apresentacao_0', " ") or not self.request.GET.get('data_apresentacao_1', " "): - msg =_('Por favor, preencha as datas.') + msg = _('Por favor, preencha as datas.') messages.add_message(self.request, messages.ERROR, msg) return context if not self.request.GET.get('data_apresentacao_0', " ") or not self.request.GET.get('data_apresentacao_1', " "): - msg =_('Por favor, preencha as datas.') + msg = _('Por favor, preencha as datas.') messages.add_message(self.request, messages.ERROR, msg) return context @@ -2163,8 +2218,10 @@ class MateriaAnexadaEmLoteView(PermissionRequiredMixin, FilterView): 'numero', '-ano') principal = MateriaLegislativa.objects.get(pk=self.kwargs['pk']) not_list = [self.kwargs['pk']] + \ - [m for m in principal.materia_principal_set.all().values_list('materia_anexada_id', flat=True)] - context['object_list'] = context['object_list'].exclude(pk__in=not_list) + [m for m in principal.materia_principal_set.all( + ).values_list('materia_anexada_id', flat=True)] + context['object_list'] = context['object_list'].exclude( + pk__in=not_list) context['temp_object_list'] = context['object_list'] context['object_list'] = [] @@ -2172,12 +2229,12 @@ class MateriaAnexadaEmLoteView(PermissionRequiredMixin, FilterView): materia_anexada = obj ciclico = False anexadas_anexada = Anexada.objects.filter( - materia_principal = materia_anexada + materia_principal=materia_anexada ) while anexadas_anexada and not ciclico: anexadas = [] - + for anexa in anexadas_anexada: if principal == anexa.materia_anexada: @@ -2185,7 +2242,7 @@ class MateriaAnexadaEmLoteView(PermissionRequiredMixin, FilterView): else: for a in Anexada.objects.filter(materia_principal=anexa.materia_anexada): anexadas.append(a) - + anexadas_anexada = anexadas if not ciclico: @@ -2216,7 +2273,7 @@ class MateriaAnexadaEmLoteView(PermissionRequiredMixin, FilterView): if len(marcadas) == 0: msg = _('Nenhuma máteria foi selecionada.') messages.add_message(request, messages.ERROR, msg) - + if data_anexacao > v_data_desanexacao: msg = _('Data de anexação posterior à data de desanexação.') messages.add_message(request, messages.ERROR, msg) @@ -2241,7 +2298,8 @@ class MateriaAnexadaEmLoteView(PermissionRequiredMixin, FilterView): msg = _('Matéria(s) anexada(s).') messages.add_message(request, messages.SUCCESS, msg) - success_url = reverse('sapl.materia:anexada_list', kwargs={'pk': kwargs['pk']}) + success_url = reverse('sapl.materia:anexada_list', + kwargs={'pk': kwargs['pk']}) return HttpResponseRedirect(success_url) @@ -2254,39 +2312,34 @@ class PrimeiraTramitacaoEmLoteView(PermissionRequiredMixin, FilterView): logger = logging.getLogger(__name__) + def get_context_data(self, **kwargs): context = super(PrimeiraTramitacaoEmLoteView, self).get_context_data(**kwargs) context['subnav_template_name'] = 'materia/em_lote/subnav_em_lote.yaml' + context['primeira_tramitacao'] = self.primeira_tramitacao # Verifica se os campos foram preenchidos if not self.filterset.form.is_valid(): return context - context['title'] = _('Primeira Tramitação em Lote') - + context['object_list'] = context['object_list'].order_by( + 'ano', 'numero') qr = self.request.GET.copy() - context['unidade_destino'] = UnidadeTramitacao.objects.all() - context['status_tramitacao'] = StatusTramitacao.objects.all() - context['turnos_tramitacao'] = Tramitacao.TURNO_CHOICES - context['urgente_tramitacao'] = YES_NO_CHOICES - context['unidade_local'] = UnidadeTramitacao.objects.all() - context['primeira_tramitacao'] = True + form = TramitacaoEmLoteForm() + context['form'] = form - # Pega somente matéria que não possuem tramitação - if (type(self.__dict__['filterset']).__name__ == - 'PrimeiraTramitacaoEmLoteFilterSet'): - context['object_list'] = context['object_list'].filter( - tramitacao__isnull=True) + if self.primeira_tramitacao: + context['title'] = _('Primeira Tramitação em Lote') + # Pega somente documentos que não possuem tramitação + context['object_list'] = [obj for obj in context['object_list'] + if obj.tramitacao_set.all().count() == 0] else: context['title'] = _('Tramitação em Lote') - context['unidade_local'] = [UnidadeTramitacao.objects.get( - id=qr['tramitacao__unidade_tramitacao_destino'])] - - context['object_list'] = context['object_list'].order_by( - 'ano', 'numero') + context['form'].fields['unidade_tramitacao_local'].initial = UnidadeTramitacao.objects.get( + id=qr['tramitacao__unidade_tramitacao_destino']) context['filter_url'] = ('&' + qr.urlencode()) if len(qr) > 0 else '' @@ -2295,127 +2348,43 @@ class PrimeiraTramitacaoEmLoteView(PermissionRequiredMixin, FilterView): return context def post(self, request, *args, **kwargs): + user = request.user + ip = get_client_ip(request) - marcadas = request.POST.getlist('materia_id') - - tz = timezone.get_current_timezone() - - username = request.user.username - - if len(marcadas) == 0: - msg = _('Nenhuma máteria foi selecionada.') + materias_ids = request.POST.getlist('materias') + if not materias_ids: + msg = _("Escolha alguma matéria para ser tramitada.") messages.add_message(request, messages.ERROR, msg) return self.get(request, self.kwargs) - obrigatorios = {'data_tramitacao': 'Data da Tramitação', - 'unidade_tramitacao_local': 'Unidade Local', - 'unidade_tramitacao_destino': 'Unidade Destino', - 'status': 'Status', - 'urgente': 'Urgente', - 'texto': 'Texto da Ação'} - for field, nome in obrigatorios.items(): - if not request.POST[field]: - msg = _('Campo {} deve ser preenchido.'.format(nome)) - messages.add_message(request, messages.ERROR, msg) - return self.get(request, self.kwargs) - if not request.POST['data_encaminhamento']: - data_encaminhamento = None - else: - try: - data_encaminhamento = tz.localize(datetime.strptime( - request.POST['data_encaminhamento'], "%d/%m/%Y")) - except ValueError: - msg = _('Formato da data de encaminhamento incorreto.') - messages.add_message(request, messages.ERROR, msg) - return self.get(request, self.kwargs) + form = TramitacaoEmLoteForm(request.POST, + initial= {'materias': materias_ids, + 'user': user, 'ip':ip}) - if request.POST['data_fim_prazo'] == '': - data_fim_prazo = None - else: - try: - data_fim_prazo = tz.localize(datetime.strptime( - request.POST['data_fim_prazo'], "%d/%m/%Y")) - except ValueError: - msg = _('Formato da data fim do prazo incorreto.') - messages.add_message(request, messages.ERROR, msg) - return self.get(request, self.kwargs) + if form.is_valid(): + form.save() - # issue https://github.com/interlegis/sapl/issues/1123 - # TODO: usar Form - urgente = request.POST['urgente'] == 'True' - flag_error = False + msg = _('Tramitação completa.') + self.logger.info('user=' + user.username + '. Tramitação completa.') + messages.add_message(request, messages.SUCCESS, msg) + return self.get_success_url() - materias_principais = [m for m in MateriaLegislativa.objects.filter(id__in=marcadas)] - tramitar_anexadas = sapl.base.models.AppConfig.attr('tramitacao_materia') - materias_anexadas = [] - if tramitar_anexadas: - for materia in materias_principais: - materias_anexadas = materias_anexadas + lista_anexados(materia) + return self.form_invalid(form) - materias = set(materias_principais + materias_anexadas) + + def get_success_url(self): + return HttpResponseRedirect(reverse('sapl.materia:primeira_tramitacao_em_lote')) - for materia in materias: - try: - data_tramitacao = tz.localize(datetime.strptime( - request.POST['data_tramitacao'], "%d/%m/%Y")) - except ValueError: - msg = _('Formato da data da tramitação incorreto.') - messages.add_message(request, messages.ERROR, msg) - return self.get(request, self.kwargs) - user = request.user - ip = get_client_ip(request) - t = Tramitacao( - materia=materia, - data_tramitacao=data_tramitacao, - data_encaminhamento=data_encaminhamento, - data_fim_prazo=data_fim_prazo, - unidade_tramitacao_local_id=request.POST[ - 'unidade_tramitacao_local'], - unidade_tramitacao_destino_id=request.POST[ - 'unidade_tramitacao_destino'], - urgente=urgente, - status_id=request.POST['status'], - turno=request.POST['turno'], - texto=request.POST['texto'], - user=user, - ip=ip - ) - t.save() - try: - self.logger.debug("user=" + username + - ". Tentando enviar tramitação.") - tramitacao_signal.send(sender=Tramitacao, - post=t, - request=self.request) + def form_invalid(self, form, *args, **kwargs): + for key, erros in form.errors.items(): + if not key=='__all__': + [messages.add_message(self.request, messages.ERROR, form.fields[key].label + ": " + e) for e in erros] + else: + [messages.add_message(self.request, messages.ERROR, e) for e in erros] + return self.get(self.request, kwargs, {'form':form}) + - except Exception as e: - self.logger.error('user=' + username + '. Tramitação criada , mas e-mail de acompanhamento ' - 'de matéria não enviado. Há problemas na configuração ' - 'do e-mail. ' + str(e)) - flag_error = True - if flag_error: - msg = _('Tramitação criada, mas e-mail de acompanhamento ' - 'de matéria não enviado. A não configuração do servidor de e-mail ' - 'impede o envio de aviso de tramitação') - messages.add_message(self.request, messages.WARNING, msg) - - status = StatusTramitacao.objects.get(id=request.POST['status']) - - for materia in materias: - if status.indicador == 'F': - materia.em_tramitacao = False - elif self.primeira_tramitacao: - materia.em_tramitacao = True - materia.save() - - msg = _('Tramitação completa. ' + "Foram tramitadas " + str(len(materias)) + " matéria(s).") - self.logger.info('user=' + username + '. Tramitação completa.') - messages.add_message(request, messages.SUCCESS, msg) - - if self.primeira_tramitacao: - return HttpResponseRedirect(reverse('sapl.materia:primeira_tramitacao_em_lote')) - return HttpResponseRedirect(reverse('sapl.materia:tramitacao_em_lote')) class TramitacaoEmLoteView(PrimeiraTramitacaoEmLoteView): filterset_class = TramitacaoEmLoteFilterSet @@ -2428,7 +2397,7 @@ class TramitacaoEmLoteView(PrimeiraTramitacaoEmLoteView): qr = self.request.GET.copy() - context['primeira_tramitacao'] = False + context['primeira_tramitacao'] = self.primeira_tramitacao if ('tramitacao__status' in qr and 'tramitacao__unidade_tramitacao_destino' in qr and diff --git a/sapl/norma/forms.py b/sapl/norma/forms.py index 366b7d544..803cd2751 100644 --- a/sapl/norma/forms.py +++ b/sapl/norma/forms.py @@ -132,8 +132,12 @@ class NormaJuridicaForm(FileFieldCheckMixin, ModelForm): 'indexacao', 'observacao', 'texto_integral', - 'assuntos'] - widgets = {'assuntos': widgets.CheckboxSelectMultiple} + 'assuntos', + 'user', + 'ip'] + widgets = {'assuntos': widgets.CheckboxSelectMultiple, + 'user': forms.HiddenInput(), + 'ip': forms.HiddenInput()} def clean(self): @@ -189,27 +193,17 @@ class NormaJuridicaForm(FileFieldCheckMixin, ModelForm): else: cleaned_data['materia'] = None - ano = cleaned_data['ano'] - data = cleaned_data['data'] - - if data.year != ano: - self.logger.error("O ano da norma ({}) é diferente " - "do ano no campo data ({}).".format(ano, data.year)) - raise ValidationError("O ano da norma não pode ser " - "diferente do ano no campo data") return cleaned_data def clean_texto_integral(self): super(NormaJuridicaForm, self).clean() texto_integral = self.cleaned_data.get('texto_integral', False) + if texto_integral and texto_integral.size > MAX_DOC_UPLOAD_SIZE: - max_size = str(MAX_DOC_UPLOAD_SIZE / (1024 * 1024)) - tam_fornecido = str(texto_integral.size / (1024 * 1024)) - self.logger.error("Arquivo muito grande ({}MB). ( Tamanho máximo permitido: {}MB )".format( - tam_fornecido, max_size)) - raise ValidationError( - "Arquivo muito grande. ( > {0}MB )".format(max_size)) + raise ValidationError("O arquivo Texto Integral deve ser menor que {0:.1f} mb, o tamanho atual desse arquivo é {1:.1f} mb" \ + .format((MAX_DOC_UPLOAD_SIZE/1024)/1024, (texto_integral.size/1024)/1024)) + return texto_integral def save(self, commit=False): @@ -283,16 +277,16 @@ class AnexoNormaJuridicaForm(FileFieldCheckMixin, ModelForm): def clean(self): cleaned_data = super(AnexoNormaJuridicaForm, self).clean() + if not self.is_valid(): return cleaned_data + anexo_arquivo = self.cleaned_data.get('anexo_arquivo', False) + if anexo_arquivo and anexo_arquivo.size > MAX_DOC_UPLOAD_SIZE: - max_size = str(MAX_DOC_UPLOAD_SIZE / (1024 * 1024)) - tam_fornecido = str(anexo_arquivo.size / (1024 * 1024)) - self.logger.error("Arquivo muito grande ({}MB). ( Tamanho máximo permitido: {}MB )".format( - tam_fornecido, max_size)) - raise ValidationError( - "Arquivo muito grande. ( > {0}MB )".format(max_size)) + raise ValidationError("O Arquivo Anexo deve ser menor que {0:.1f} mb, o tamanho atual desse arquivo é {1:.1f} mb" \ + .format((MAX_DOC_UPLOAD_SIZE/1024)/1024, (anexo_arquivo.size/1024)/1024)) + return cleaned_data def save(self, commit=False): diff --git a/sapl/norma/migrations/0025_auto_20190704_1403.py b/sapl/norma/migrations/0025_auto_20190704_1403.py new file mode 100644 index 000000000..15db2793b --- /dev/null +++ b/sapl/norma/migrations/0025_auto_20190704_1403.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-07-04 17:03 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('norma', '0024_auto_20190425_0917'), + ] + + operations = [ + migrations.AddField( + model_name='normajuridica', + name='ip', + field=models.CharField(blank=True, default='', max_length=30, verbose_name='IP'), + ), + migrations.AddField( + model_name='normajuridica', + name='user', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL, verbose_name='Usuário'), + ), + ] diff --git a/sapl/norma/models.py b/sapl/norma/models.py index 6b3a18a1f..cd90e4930 100644 --- a/sapl/norma/models.py +++ b/sapl/norma/models.py @@ -10,7 +10,9 @@ from sapl.base.models import Autor from sapl.compilacao.models import TextoArticulado from sapl.materia.models import MateriaLegislativa from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES, - restringe_tipos_de_arquivo_txt, texto_upload_path) + restringe_tipos_de_arquivo_txt, + texto_upload_path, + get_settings_auth_user_model) @reversion.register() @@ -138,6 +140,20 @@ class NormaJuridica(models.Model): through_fields=('norma', 'autor'), symmetrical=False) + user = models.ForeignKey( + get_settings_auth_user_model(), + verbose_name=_('Usuário'), + on_delete=models.PROTECT, + null=True, + blank=True + ) + ip = models.CharField( + verbose_name=_('IP'), + max_length=30, + blank=True, + default='' + ) + class Meta: verbose_name = _('Norma Jurídica') verbose_name_plural = _('Normas Jurídicas') diff --git a/sapl/norma/views.py b/sapl/norma/views.py index 735d595a3..8d9a62eaa 100644 --- a/sapl/norma/views.py +++ b/sapl/norma/views.py @@ -20,7 +20,7 @@ from sapl.base.models import AppConfig from sapl.compilacao.views import IntegracaoTaView from sapl.crud.base import (RP_DETAIL, RP_LIST, Crud, CrudAux, MasterDetailCrud, make_pagination) -from sapl.utils import show_results_filter_set +from sapl.utils import show_results_filter_set, get_client_ip from .forms import (AnexoNormaJuridicaForm, NormaFilterSet, NormaJuridicaForm, NormaPesquisaSimplesForm, NormaRelacionadaForm, AutoriaNormaForm) @@ -217,20 +217,24 @@ class NormaCrud(Crud): return self.search_url def get_initial(self): - username = self.request.user.username + initial = super().get_initial() + initial['user'] = self.request.user + initial['ip'] = get_client_ip(self.request) + + username = self.request.user.username try: self.logger.debug( 'user=' + username + '. Tentando obter objeto de modelo da esfera da federação.') esfera = sapl.base.models.AppConfig.objects.last( ).esfera_federacao - self.initial['esfera_federacao'] = esfera + initial['esfera_federacao'] = esfera except: self.logger.error( 'user=' + username + '. Erro ao obter objeto de modelo da esfera da federação.') pass - self.initial['complemento'] = False - return self.initial + initial['complemento'] = False + return initial layout_key = 'NormaJuridicaCreate' @@ -249,7 +253,7 @@ class NormaCrud(Crud): layout_key = 'NormaJuridicaCreate' def get_initial(self): - initial = super(UpdateView, self).get_initial() + initial = super().get_initial() norma = NormaJuridica.objects.get(id=self.kwargs['pk']) if norma.materia: initial['tipo_materia'] = norma.materia.tipo @@ -258,6 +262,41 @@ class NormaCrud(Crud): initial['esfera_federacao'] = norma.esfera_federacao return initial + def form_valid(self, form): + norma_antiga = NormaJuridica.objects.get( + pk=self.kwargs['pk'] + ) + + # Feito desta forma para que sejam materializados os assuntos antigos + assuntos_antigos = set(norma_antiga.assuntos.all()) + + dict_objeto_antigo = norma_antiga.__dict__ + self.object = form.save() + dict_objeto_novo = self.object.__dict__ + + atributos = ['tipo_id', 'numero', 'ano', 'data', 'esfera_federacao', + 'complemento', 'materia_id', 'numero', + 'data_publicacao', 'data_vigencia', + 'veiculo_publicacao', 'pagina_inicio_publicacao', + 'pagina_fim_publicacao', 'ementa', 'indexacao', + 'observacao', 'texto_integral'] + + for atributo in atributos: + if dict_objeto_antigo[atributo] != dict_objeto_novo[atributo]: + self.object.user = self.request.user + self.object.ip = get_client_ip(self.request) + self.object.save() + break + + # Campo Assuntos não veio no __dict__, então é comparado separadamente + assuntos_novos = set(self.object.assuntos.all()) + if assuntos_antigos != assuntos_novos: + self.object.user = self.request.user + self.object.ip = get_client_ip(self.request) + self.object.save() + + return super().form_valid(form) + def recuperar_norma(request): logger = logging.getLogger(__name__) diff --git a/sapl/painel/views.py b/sapl/painel/views.py index 333c3771e..5cadba9a1 100644 --- a/sapl/painel/views.py +++ b/sapl/painel/views.py @@ -523,11 +523,14 @@ def get_dados_painel(request, pk): if casa and app_config and (bool(casa.logotipo)): brasao = casa.logotipo.url \ if app_config.mostrar_brasao_painel else None - + response = { 'sessao_plenaria': str(sessao), 'sessao_plenaria_data': sessao.data_inicio.strftime('%d/%m/%Y'), 'sessao_plenaria_hora_inicio': sessao.hora_inicio, + 'sessao_solene': sessao.tipo.nome == "Solene", + 'sessao_finalizada': sessao.finalizada, + 'tema_solene': sessao.tema_solene, 'cronometro_aparte': get_cronometro_status(request, 'aparte'), 'cronometro_discurso': get_cronometro_status(request, 'discurso'), 'cronometro_ordem': get_cronometro_status(request, 'ordem'), diff --git a/sapl/parlamentares/urls.py b/sapl/parlamentares/urls.py index 4c1434333..6e175bafe 100644 --- a/sapl/parlamentares/urls.py +++ b/sapl/parlamentares/urls.py @@ -19,7 +19,8 @@ from sapl.parlamentares.views import (CargoMesaCrud, ColigacaoCrud, parlamentares_frente_selected, remove_parlamentar_composicao, parlamentares_filiados, BlocoCrud, - PesquisarParlamentarView, VincularParlamentarView) + PesquisarParlamentarView, VincularParlamentarView, + get_sessoes_legislatura) from .apps import AppConfig @@ -90,5 +91,8 @@ urlpatterns = [ url(r'^mesa-diretora/remove-parlamentar-composicao/$', remove_parlamentar_composicao, name='remove_parlamentar_composicao'), + + url(r'^parlamentar/get-sessoes-legislatura/$', + get_sessoes_legislatura, name='get_sessoes_legislatura'), ] diff --git a/sapl/parlamentares/views.py b/sapl/parlamentares/views.py index 874f53385..c81ed46bb 100644 --- a/sapl/parlamentares/views.py +++ b/sapl/parlamentares/views.py @@ -1176,4 +1176,15 @@ class BlocoCrud(CrudAux): form_class = BlocoForm def get_success_url(self): - return reverse('sapl.parlamentares:bloco_list') \ No newline at end of file + return reverse('sapl.parlamentares:bloco_list') + + +def get_sessoes_legislatura(request): + + legislatura_id = request.GET['legislatura'] + + json_response = {'sessoes_legislativas': []} + for s in SessaoLegislativa.objects.filter(legislatura_id=legislatura_id): + json_response['sessoes_legislativas'].append( (s.id, str(s)) ) + + return JsonResponse(json_response) \ No newline at end of file diff --git a/sapl/protocoloadm/forms.py b/sapl/protocoloadm/forms.py index f56409c0a..859de2850 100644 --- a/sapl/protocoloadm/forms.py +++ b/sapl/protocoloadm/forms.py @@ -5,6 +5,7 @@ from crispy_forms.bootstrap import InlineRadios, Alert, FormActions from sapl.crispy_layout_mixin import SaplFormHelper from crispy_forms.layout import HTML, Button, Column, Fieldset, Layout, Div, Submit from django import forms +from sapl.settings import MAX_DOC_UPLOAD_SIZE from django.core.exceptions import (MultipleObjectsReturned, ObjectDoesNotExist, ValidationError) from django.db import models, transaction @@ -657,6 +658,20 @@ class DocumentoAcessorioAdministrativoForm(FileFieldCheckMixin, ModelForm): 'data': forms.DateInput(format='%d/%m/%Y') } + def clean(self): + super(DocumentoAcessorioAdministrativoForm, self).clean() + + if not self.is_valid(): + return self.cleaned_data + + arquivo = self.cleaned_data.get('arquivo', False) + + if arquivo and arquivo.size > MAX_DOC_UPLOAD_SIZE: + raise ValidationError("O arquivo deve ser menor que {0:.1f} mb, o tamanho atual desse arquivo é {1:.1f} mb" \ + .format((MAX_DOC_UPLOAD_SIZE/1024)/1024, (arquivo.size/1024)/1024)) + + return self.cleaned_data + class TramitacaoAdmForm(ModelForm): @@ -1142,6 +1157,12 @@ class DocumentoAdministrativoForm(FileFieldCheckMixin, ModelForm): ' documento vinculado' % (numero_protocolo, ano_protocolo))) + texto_integral = self.cleaned_data.get('texto_integral', False) + + if texto_integral and texto_integral.size > MAX_DOC_UPLOAD_SIZE: + raise ValidationError("O arquivo Texto Integral deve ser menor que {0:.1f} mb, o tamanho atual desse arquivo é {1:.1f} mb" \ + .format((MAX_DOC_UPLOAD_SIZE/1024)/1024, (texto_integral.size/1024)/1024)) + return self.cleaned_data def save(self, commit=True): @@ -1587,12 +1608,6 @@ class TramitacaoEmLoteAdmForm(ModelForm): 'maior que a data de tramitação!') raise ValidationError(msg) - if cleaned_data['unidade_tramitacao_local'] == cleaned_data['unidade_tramitacao_destino']: - msg = _('Unidade tramitação local deve ser diferente da unidade tramitação destino.') - self.logger.error('Unidade tramitação local ({}) deve ser diferente da unidade tramitação destino' - .format(cleaned_data['unidade_tramitacao_local'])) - raise ValidationError(msg) - return cleaned_data @transaction.atomic diff --git a/sapl/protocoloadm/migrations/0022_auto_20190715_1101.py b/sapl/protocoloadm/migrations/0022_auto_20190715_1101.py new file mode 100644 index 000000000..c7bb30444 --- /dev/null +++ b/sapl/protocoloadm/migrations/0022_auto_20190715_1101.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-07-15 14:01 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('protocoloadm', '0021_merge_20190429_1531'), + ] + + operations = [ + migrations.AlterField( + model_name='protocolo', + name='ip_data_hora_manual', + field=models.CharField(blank=True, help_text='Endereço IP da estação de trabalho do usuário que está realizando Protocolo e informando data e hora manualmente.', max_length=256, verbose_name='IP'), + ), + migrations.AlterField( + model_name='protocolo', + name='user_data_hora_manual', + field=models.CharField(blank=True, help_text='Usuário que está realizando Protocolo e informando data e hora manualmente.', max_length=256, verbose_name='IP'), + ), + ] diff --git a/sapl/protocoloadm/models.py b/sapl/protocoloadm/models.py index 8396349a8..f0e40f9c7 100644 --- a/sapl/protocoloadm/models.py +++ b/sapl/protocoloadm/models.py @@ -57,7 +57,6 @@ class Protocolo(models.Model): null=False, choices=RANGE_ANOS, verbose_name=_('Ano do Protocolo')) - data = models.DateField(null=True, blank=True, verbose_name=_('Data do Protocolo'), help_text=_('Informado manualmente')) @@ -66,12 +65,12 @@ class Protocolo(models.Model): help_text=_('Informado manualmente')) timestamp_data_hora_manual = models.DateTimeField(default=timezone.now) user_data_hora_manual = models.CharField( - max_length=20, blank=True, + max_length=256, blank=True, verbose_name=_('IP'), help_text=_('Usuário que está realizando Protocolo e informando ' 'data e hora manualmente.')) ip_data_hora_manual = models.CharField( - max_length=15, blank=True, + max_length=256, blank=True, verbose_name=_('IP'), help_text=_('Endereço IP da estação de trabalho ' 'do usuário que está realizando Protocolo e informando ' @@ -346,12 +345,12 @@ class TramitacaoAdministrativo(models.Model): class Anexado(models.Model): documento_principal = models.ForeignKey( DocumentoAdministrativo, related_name='documento_principal_set', - on_delete = models.CASCADE, + on_delete=models.CASCADE, verbose_name=_('Documento Principal') ) documento_anexado = models.ForeignKey( DocumentoAdministrativo, related_name='documento_anexado_set', - on_delete = models.CASCADE, + on_delete=models.CASCADE, verbose_name=_('Documento Anexado') ) data_anexacao = models.DateField(verbose_name=_('Data Anexação')) @@ -369,7 +368,7 @@ class Anexado(models.Model): 'documento_anexado_tipo': self.documento_anexado.tipo, 'documento_anexado_numero': self.documento_anexado.numero, 'documento_anexado_ano': self.documento_anexado.ano - } + } @reversion.register() diff --git a/sapl/protocoloadm/tests/test_protocoloadm.py b/sapl/protocoloadm/tests/test_protocoloadm.py index bd9b823dc..a1166f81b 100644 --- a/sapl/protocoloadm/tests/test_protocoloadm.py +++ b/sapl/protocoloadm/tests/test_protocoloadm.py @@ -881,20 +881,6 @@ def test_tramitacao_lote_documentos_form(admin_client): ["A data fim de prazo deve ser maior que a data de tramitação!"] assert not form.is_valid() - form = TramitacaoEmLoteAdmForm(initial={'documentos': documentos}, data={}) - form.data = {'data_tramitacao': '2019-05-14', - 'data_encaminhamento' : '2019-05-15', - 'data_fim_prazo': '2019-05-18', - 'unidade_tramitacao_local': unidade_tramitacao_local_1.id, - 'unidade_tramitacao_destino': unidade_tramitacao_local_1.id, - 'status': status.id, - 'urgente': False, - 'texto': 'aaaa'} - - assert form.errors['__all__'] == \ - ["Unidade tramitação local deve ser diferente da unidade tramitação destino."] - assert not form.is_valid() - form = TramitacaoEmLoteAdmForm(initial={'documentos': documentos}, data={}) form.data = {'data_tramitacao': '2019-05-14', 'data_encaminhamento' : '2019-05-15', @@ -1037,22 +1023,6 @@ def test_tramitacao_lote_documentos_views(admin_client): assert 'Status: Este campo é obrigatório.' in msgs assert 'Unidade Destino: Este campo é obrigatório.' in msgs assert 'Texto da Ação: Este campo é obrigatório.' in msgs - - response = admin_client.post(url_lote, - {'documentos': documentos, - 'data_tramitacao': date(2019, 5, 15), - 'unidade_tramitacao_local': unidade_tramitacao_destino_1.id, - 'unidade_tramitacao_destino': unidade_tramitacao_destino_1.id, - 'status': status.id, - 'urgente': False, - 'texto': 'aaaa', - 'salvar':'salvar'}, - follow=True) - assert response.status_code == 200 - - msgs = [m.message for m in response.context['messages']] - - assert 'Unidade tramitação local deve ser diferente da unidade tramitação destino.' in msgs response = admin_client.post(url_lote, {'documentos': documentos, diff --git a/sapl/protocoloadm/views.py b/sapl/protocoloadm/views.py index 32f6748ad..eabccca90 100755 --- a/sapl/protocoloadm/views.py +++ b/sapl/protocoloadm/views.py @@ -11,6 +11,7 @@ from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ObjectDoesNotExist from django.core.urlresolvers import reverse +from django.db import transaction from django.db.models import Max, Q from django.http import Http404, HttpResponse, JsonResponse from django.http.response import HttpResponseRedirect @@ -539,6 +540,7 @@ class ProtocoloDocumentoView(PermissionRequiredMixin, initial['hora'] = timezone.localtime(timezone.now()) return initial + @transaction.atomic def form_valid(self, form): protocolo = form.save(commit=False) username = self.request.user.username @@ -726,6 +728,7 @@ class ProtocoloMateriaView(PermissionRequiredMixin, CreateView): initial['hora'] = timezone.localtime(timezone.now()) return initial + @transaction.atomic def form_valid(self, form): protocolo = form.save(commit=False) username = self.request.user.username @@ -983,7 +986,7 @@ class DocumentoAnexadoEmLoteView(PermissionRequiredMixin, FilterView): def get_context_data(self, **kwargs): context = super( - DocumentoAnexadoEmLoteView,self + DocumentoAnexadoEmLoteView, self ).get_context_data(**kwargs) context['root_pk'] = self.kwargs['pk'] @@ -1545,7 +1548,7 @@ class TramitacaoEmLoteAdmView(PrimeiraTramitacaoEmLoteAdmView): qr = self.request.GET.copy() - context['primeira_tramitacao'] = False + context['primeira_tramitacao'] = self.primeira_tramitacao if ('tramitacao__status' in qr and 'tramitacao__unidade_tramitacao_destino' in qr and diff --git a/sapl/relatorios/templates/pdf_sessao_plenaria_gerar.py b/sapl/relatorios/templates/pdf_sessao_plenaria_gerar.py index 805731a70..0ede61f97 100644 --- a/sapl/relatorios/templates/pdf_sessao_plenaria_gerar.py +++ b/sapl/relatorios/templates/pdf_sessao_plenaria_gerar.py @@ -128,6 +128,9 @@ def inf_basicas(inf_basicas_dic): if dat_fim_sessao or hr_fim_sessao: tmp += '\t\tEncerramento: ' + \ dat_fim_sessao + ' - ' + hr_fim_sessao + '\n' + if inf_basicas_dic.get('tema_solene'): + tmp += '\t\tTema da Sessão Solene: ' + \ + inf_basicas_dic['tema_solene'] + '\n' return tmp diff --git a/sapl/relatorios/urls.py b/sapl/relatorios/urls.py index 9ca2284a0..05ad22671 100644 --- a/sapl/relatorios/urls.py +++ b/sapl/relatorios/urls.py @@ -6,7 +6,7 @@ from .views import (relatorio_capa_processo, relatorio_etiqueta_protocolo, relatorio_materia, relatorio_ordem_dia, relatorio_pauta_sessao, relatorio_protocolo, relatorio_sessao_plenaria, - resumo_ata_pdf) + resumo_ata_pdf, relatorio_sessao_plenaria_pdf) app_name = AppConfig.name @@ -31,4 +31,6 @@ urlpatterns = [ relatorio_pauta_sessao, name='relatorio_pauta_sessao'), url(r'^relatorios/(?P\d+)/resumo_ata$', resumo_ata_pdf, name='resumo_ata_pdf'), + url(r'^relatorios/(?P\d+)/sessao-plenaria-pdf$', + relatorio_sessao_plenaria_pdf, name='relatorio_sessao_plenaria_pdf'), ] diff --git a/sapl/relatorios/views.py b/sapl/relatorios/views.py index cb1d05cca..be65f7b93 100755 --- a/sapl/relatorios/views.py +++ b/sapl/relatorios/views.py @@ -519,6 +519,9 @@ def get_sessao_plenaria(sessao, casa): inf_basicas_dic["dat_fim_sessao"] = '' inf_basicas_dic["hr_fim_sessao"] = sessao.hora_fim inf_basicas_dic["nom_camara"] = casa.nome + + if sessao.tipo.nome == 'Solene': + inf_basicas_dic["tema_solene"] = sessao.tema_solene # Conteudo multimidia cont_mult_dic = {} @@ -1350,3 +1353,79 @@ def relatorio_doc_administrativos(request, context): return response +def relatorio_sessao_plenaria_pdf(request, pk): + base_url=request.build_absolute_uri() + logger = logging.getLogger(__name__) + username = request.user.username + casa = CasaLegislativa.objects.first() + if not casa: + raise Http404 + + rodape = get_rodape(casa) + rodape = ' '.join(rodape) + + try: + logger.debug("user=" + username + + ". Tentando obter SessaoPlenaria com id={}.".format(pk)) + sessao = SessaoPlenaria.objects.get(id=pk) + except ObjectDoesNotExist as e: + logger.error("user=" + username + + ". Essa SessaoPlenaria não existe (pk={}). ".format(pk) + str(e)) + raise Http404('Essa página não existe') + + (inf_basicas_dic, + cont_mult_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) = get_sessao_plenaria(sessao, casa) + + html_template = render_to_string('relatorios/relatorio_sessao_plenaria.html', + { + "inf_basicas_dic":inf_basicas_dic, + "lst_mesa":lst_mesa, + "lst_presenca_sessao":lst_presenca_sessao, + "lst_ausencia_sessao":lst_ausencia_sessao, + "lst_expedientes":lst_expedientes, + "lst_expediente_materia":lst_expediente_materia, + "lst_oradores_expediente":lst_oradores_expediente, + "lst_presenca_ordem_dia":lst_presenca_ordem_dia, + "lst_votacao":lst_votacao, + "lst_oradores":lst_oradores, + "lst_ocorrencias":lst_ocorrencias, + "rodape":rodape, + "data": dt.today().strftime('%d/%m/%Y') + }) + + info = "Resumo da {}ª Reunião {} \ + da {}ª Sessão Legislativa da {} \ + Legislatura".format(inf_basicas_dic['num_sessao_plen'], + inf_basicas_dic['nom_sessao'], + inf_basicas_dic['num_sessao_leg'], + inf_basicas_dic['num_legislatura'], + inf_basicas_dic['num_legislatura'] + ) + + html_header = render_to_string('relatorios/header_ata.html',{"casa":casa, + "MEDIA_URL": MEDIA_URL, + "logotipo": casa.logotipo, + "info":info}) + + pdf_file = make_pdf(base_url=base_url, main_template=html_template, header_template=html_header) + + response = HttpResponse(content_type='application/pdf;') + response['Content-Disposition'] = 'inline; filename=relatorio.pdf' + response['Content-Transfer-Encoding'] = 'binary' + response.write(pdf_file) + + return response + diff --git a/sapl/rules/apps.py b/sapl/rules/apps.py index c0dc19fd8..2247e5c94 100644 --- a/sapl/rules/apps.py +++ b/sapl/rules/apps.py @@ -84,18 +84,7 @@ def create_proxy_permissions( ctypes.add(ctype) - # FIXME: Retirar try except quando sapl passar a usar django 1.11 - try: - logger.info("_get_all_permissions") - # Função não existe mais em Django 1.11 - # como sapl ainda não foi para Django 1.11 - # esta excessão foi adicionada para caso o - # Sapl esteja rodando em um projeto 1.11 não ocorra erros - _all_perms_of_klass = _get_all_permissions(klass._meta, ctype) - except Exception as e: - logger.error(str(e)) - # Nova função usada em projetos com Django 1.11 e o sapl é uma app - _all_perms_of_klass = _get_all_permissions(klass._meta) + _all_perms_of_klass = _get_all_permissions(klass._meta) for perm in _all_perms_of_klass: searched_perms.append((ctype, perm)) diff --git a/sapl/sessao/forms.py b/sapl/sessao/forms.py index 9b3fc1f90..01f0c3636 100644 --- a/sapl/sessao/forms.py +++ b/sapl/sessao/forms.py @@ -11,6 +11,7 @@ from django.forms.widgets import CheckboxSelectMultiple from django.utils.translation import ugettext_lazy as _ import django_filters +from sapl.settings import MAX_DOC_UPLOAD_SIZE from sapl.base.models import Autor, TipoAutor from sapl.crispy_layout_mixin import SaplFormHelper from sapl.crispy_layout_mixin import form_actions, to_row, SaplFormLayout @@ -149,6 +150,24 @@ class SessaoPlenariaForm(FileFieldCheckMixin, ModelForm): "entre a data de início e fim tanto da " "Legislatura quanto da Sessão Legislativa.") + + upload_pauta = self.cleaned_data.get('upload_pauta', False) + upload_ata = self.cleaned_data.get('upload_ata', False) + upload_anexo = self.cleaned_data.get('upload_anexo', False) + + if upload_pauta and upload_pauta.size > MAX_DOC_UPLOAD_SIZE: + raise ValidationError("O arquivo Pauta da Sessão deve ser menor que {0:.1f} mb, o tamanho atual desse arquivo é {1:.1f} mb" \ + .format((MAX_DOC_UPLOAD_SIZE/1024)/1024, (upload_pauta.size/1024)/1024)) + + if upload_ata and upload_ata.size > MAX_DOC_UPLOAD_SIZE: + raise ValidationError("O arquivo Ata da Sessão deve ser menor que {0:.1f} mb, o tamanho atual desse arquivo é {1:.1f} mb" \ + .format((MAX_DOC_UPLOAD_SIZE/1024)/1024, (upload_ata.size/1024)/1024)) + + if upload_anexo and upload_anexo.size > MAX_DOC_UPLOAD_SIZE: + raise ValidationError("O arquivo Anexo da Sessão deve ser menor que {0:.1f} mb, o tamanho atual desse arquivo é {1:.1f} mb" \ + .format((MAX_DOC_UPLOAD_SIZE/1024)/1024, (upload_anexo.size/1024)/1024)) + + return self.cleaned_data @@ -656,6 +675,12 @@ class OradorForm(ModelForm): "Já existe orador nesta posição de ordem de pronunciamento" )) + upload_anexo = self.cleaned_data.get('upload_anexo', False) + + if upload_anexo and upload_anexo.size > MAX_DOC_UPLOAD_SIZE: + raise ValidationError("O arquivo Anexo do Orador deve ser menor que {0:.1f} mb, o tamanho atual desse arquivo é {1:.1f} mb" \ + .format((MAX_DOC_UPLOAD_SIZE/1024)/1024, (upload_anexo.size/1024)/1024)) + return self.cleaned_data class Meta: @@ -691,6 +716,12 @@ class OradorExpedienteForm(ModelForm): raise ValidationError(_( 'Já existe orador nesta posição da ordem de pronunciamento')) + upload_anexo = self.cleaned_data.get('upload_anexo', False) + + if upload_anexo and upload_anexo.size > MAX_DOC_UPLOAD_SIZE: + raise ValidationError("O arquivo Anexo do Orador deve ser menor que {0:.1f} mb, o tamanho atual desse arquivo é {1:.1f} mb" \ + .format((MAX_DOC_UPLOAD_SIZE/1024)/1024, (upload_anexo.size/1024)/1024)) + return self.cleaned_data class Meta: @@ -728,6 +759,12 @@ class OradorOrdemDiaForm(ModelForm): "Já existe orador nesta posição de ordem de pronunciamento" )) + upload_anexo = self.cleaned_data.get('upload_anexo', False) + + if upload_anexo and upload_anexo.size > MAX_DOC_UPLOAD_SIZE: + raise ValidationError("O arquivo Anexo do Orador deve ser menor que {0:.1f} mb, o tamanho atual desse arquivo é {1:.1f} mb" \ + .format((MAX_DOC_UPLOAD_SIZE/1024)/1024, (upload_anexo.size/1024)/1024)) + return self.cleaned_data class Meta: @@ -966,9 +1003,15 @@ class JustificativaAusenciaForm(ModelForm): sessao_plenaria = self.instance.sessao_plenaria + upload_anexo = self.cleaned_data.get('upload_anexo', False) + + if upload_anexo and upload_anexo.size > MAX_DOC_UPLOAD_SIZE: + raise ValidationError("O arquivo Anexo de Justificativa deve ser menor que {0:.1f} mb, o tamanho atual desse arquivo é {1:.1f} mb" \ + .format((MAX_DOC_UPLOAD_SIZE/1024)/1024, (upload_anexo.size/1024)/1024)) + if not sessao_plenaria.finalizada or sessao_plenaria.finalizada is None: raise ValidationError( - "A sessão deve está finalizada para registrar uma Ausência") + "A sessão deve estar finalizada para registrar uma Ausência") else: return self.cleaned_data diff --git a/sapl/sessao/migrations/0041_sessaoplenaria_tema_solene.py b/sapl/sessao/migrations/0041_sessaoplenaria_tema_solene.py new file mode 100644 index 000000000..8219d2c3c --- /dev/null +++ b/sapl/sessao/migrations/0041_sessaoplenaria_tema_solene.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-05-31 12:37 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('sessao', '0040_auto_20190523_1130'), + ] + + operations = [ + migrations.AddField( + model_name='sessaoplenaria', + name='tema_solene', + field=models.TextField(blank=True, max_length=500, verbose_name='Tema da Sessão Solene'), + ), + ] diff --git a/sapl/sessao/migrations/0042_merge_20190612_0925.py b/sapl/sessao/migrations/0042_merge_20190612_0925.py new file mode 100644 index 000000000..3ca598ee3 --- /dev/null +++ b/sapl/sessao/migrations/0042_merge_20190612_0925.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-06-12 12:25 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('sessao', '0041_auto_20190610_1300'), + ('sessao', '0041_sessaoplenaria_tema_solene'), + ] + + operations = [ + ] diff --git a/sapl/sessao/models.py b/sapl/sessao/models.py index 8ab65ebfd..d438caa9a 100644 --- a/sapl/sessao/models.py +++ b/sapl/sessao/models.py @@ -208,6 +208,8 @@ class SessaoPlenaria(models.Model): interativa = models.NullBooleanField(blank=True, choices=YES_NO_CHOICES, verbose_name=_('Sessão interativa')) + tema_solene = models.TextField( + blank=True, max_length=500, verbose_name=_('Tema da Sessão Solene')) class Meta: verbose_name = _('Sessão Plenária') diff --git a/sapl/sessao/urls.py b/sapl/sessao/urls.py index c570dfde8..730469c29 100644 --- a/sapl/sessao/urls.py +++ b/sapl/sessao/urls.py @@ -33,7 +33,8 @@ from sapl.sessao.views import (AdicionarVariasMateriasExpediente, renumerar_materias_expediente, sessao_legislativa_legislatura_ajax, VotacaoEmBlocoOrdemDia, VotacaoEmBlocoExpediente, - VotacaoEmBlocoSimbolicaView, VotacaoEmBlocoNominalView) + VotacaoEmBlocoSimbolicaView, VotacaoEmBlocoNominalView, + recuperar_nome_tipo_sessao) from .apps import AppConfig @@ -68,6 +69,9 @@ urlpatterns = [ recuperar_numero_sessao_view, name='recuperar_numero_sessao_view' ), + url(r'^sessao/recuperar-nome-tipo-sessao/', + recuperar_nome_tipo_sessao, + name='recuperar_nome_tipo_sessao'), url(r'^sessao/sessao-legislativa-legislatura-ajax/', sessao_legislativa_legislatura_ajax, name='sessao_legislativa_legislatura_ajax_view'), @@ -173,7 +177,7 @@ urlpatterns = [ name='votacaonominalexpdetail'), url(r'^sessao/(?P\d+)/matexp/votsimb/(?P\d+)/(?P\d+)$', VotacaoExpedienteView.as_view(), name='votacaosimbolicaexp'), - url(r'^sessao/(?P\d+)/matexp/votsec/view/(?P\d+)/(?P\d+)$', + url(r'^sessao/(?P\d+)/matexp/votsimb/view/(?P\d+)/(?P\d+)$', VotacaoExpedienteEditView.as_view(), name='votacaosimbolicaexpedit'), url(r'^sessao/(?P\d+)/matexp/votsec/(?P\d+)/(?P\d+)$', VotacaoExpedienteView.as_view(), name='votacaosecretaexp'), diff --git a/sapl/sessao/views.py b/sapl/sessao/views.py index 295f22543..271f0b3d3 100755 --- a/sapl/sessao/views.py +++ b/sapl/sessao/views.py @@ -515,7 +515,7 @@ class MateriaOrdemDiaCrud(MasterDetailCrud): form_class = OrdemDiaForm def get_initial(self): - initial = super(UpdateView, self).get_initial() + initial = super().get_initial() initial['tipo_materia'] = self.object.materia.tipo.id initial['numero_materia'] = self.object.materia.numero initial['ano_materia'] = self.object.materia.ano @@ -576,7 +576,7 @@ class ExpedienteMateriaCrud(MasterDetailCrud): form_class = ExpedienteMateriaForm def get_initial(self): - initial = super(CreateView, self).get_initial() + initial = super().get_initial() initial['data_ordem'] = SessaoPlenaria.objects.get( pk=self.kwargs['pk']).data_inicio.strftime('%d/%m/%Y') max_numero_ordem = ExpedienteMateria.objects.filter( @@ -594,7 +594,7 @@ class ExpedienteMateriaCrud(MasterDetailCrud): form_class = ExpedienteMateriaForm def get_initial(self): - initial = super(UpdateView, self).get_initial() + initial = super().get_initial() initial['tipo_materia'] = self.object.materia.tipo.id initial['numero_materia'] = self.object.materia.numero initial['ano_materia'] = self.object.materia.ano @@ -614,6 +614,15 @@ class OradorCrud(MasterDetailCrud): class ListView(MasterDetailCrud.ListView): ordering = ['numero_ordem', 'parlamentar'] + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + sessao_pk = context['root_pk'] + sessao = SessaoPlenaria.objects.get(id=sessao_pk) + tipo_sessao = sessao.tipo + if tipo_sessao.nome == "Solene": + context.update({'subnav_template_name': 'sessao/subnav-solene.yaml'}) + return context + class CreateView(MasterDetailCrud.CreateView): form_class = OradorForm @@ -625,18 +634,87 @@ class OradorCrud(MasterDetailCrud): return reverse('sapl.sessao:orador_list', kwargs={'pk': self.kwargs['pk']}) - class UpdateView(MasterDetailCrud.UpdateView): +class UpdateView(MasterDetailCrud.UpdateView): form_class = OradorForm def get_initial(self): - initial = super(UpdateView, self).get_initial() + initial = super().get_initial() initial.update({'id_sessao': self.object.sessao_plenaria.id}) initial.update({'numero': self.object.numero_ordem}) return initial +class OradorExpedienteCrud(OradorCrud): + model = OradorExpediente + + class CreateView(MasterDetailCrud.CreateView): + + form_class = OradorForm + + def get_initial(self): + return {'id_sessao': self.kwargs['pk']} + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + sessao_pk = context['root_pk'] + sessao = SessaoPlenaria.objects.get(id=sessao_pk) + tipo_sessao = sessao.tipo + if tipo_sessao.nome == "Solene": + context.update({'subnav_template_name': 'sessao/subnav-solene.yaml'}) + return context + + def get_success_url(self): + return reverse('sapl.sessao:orador_list', + kwargs={'pk': self.kwargs['pk']}) + + + class UpdateView(MasterDetailCrud.UpdateView): + + form_class = OradorForm + + def get_initial(self): + initial = super().get_initial() + initial.update({'id_sessao': self.object.sessao_plenaria.id}) + initial.update({'numero':self.object.numero_ordem}) + + return initial + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + sessao_pk = context['root_pk'] + sessao = SessaoPlenaria.objects.get(id=sessao_pk) + tipo_sessao = sessao.tipo + if tipo_sessao.nome == "Solene": + context.update({'subnav_template_name': 'sessao/subnav-solene.yaml'}) + return context + + + class DetailView(MasterDetailCrud.DetailView): + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + sessao_pk = context['root_pk'] + sessao = SessaoPlenaria.objects.get(id=sessao_pk) + tipo_sessao = sessao.tipo + if tipo_sessao.nome == "Solene": + context.update({'subnav_template_name': 'sessao/subnav-solene.yaml'}) + return context + + + class DeleteView(MasterDetailCrud.DeleteView): + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + sessao_pk = context['root_pk'] + sessao = SessaoPlenaria.objects.get(id=sessao_pk) + tipo_sessao = sessao.tipo + if tipo_sessao.nome == "Solene": + context.update({'subnav_template_name': 'sessao/subnav-solene.yaml'}) + return context + + class OradorExpedienteCrud(OradorCrud): model = OradorExpediente @@ -647,6 +725,17 @@ class OradorExpedienteCrud(OradorCrud): def get_initial(self): return {'id_sessao': self.kwargs['pk']} + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + pk = context['root_pk'] + sessao = SessaoPlenaria.objects.get(id=pk) + tipo_sessao = sessao.tipo + if tipo_sessao.nome == "Solene": + context.update({'subnav_template_name': 'sessao/subnav-solene.yaml'}) + return context + + def get_success_url(self): return reverse('sapl.sessao:oradorexpediente_list', kwargs={'pk': self.kwargs['pk']}) @@ -659,6 +748,40 @@ class OradorExpedienteCrud(OradorCrud): 'numero': self.object.numero_ordem} + class ListView(MasterDetailCrud.ListView): + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + pk = context['root_pk'] + sessao = SessaoPlenaria.objects.get(id=pk) + tipo_sessao = sessao.tipo + if tipo_sessao.nome == "Solene": + context.update({'subnav_template_name': 'sessao/subnav-solene.yaml'}) + return context + + class DetailView(MasterDetailCrud.DetailView): + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + pk = context['root_pk'] + sessao = SessaoPlenaria.objects.get(id=pk) + tipo_sessao = sessao.tipo + if tipo_sessao.nome == "Solene": + context.update({'subnav_template_name': 'sessao/subnav-solene.yaml'}) + return context + + + class UpdateView(MasterDetailCrud.UpdateView): + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + pk = context['root_pk'] + sessao = SessaoPlenaria.objects.get(id=pk) + tipo_sessao = sessao.tipo + if tipo_sessao.nome == "Solene": + context.update({'subnav_template_name': 'sessao/subnav-solene.yaml'}) + return context + class OradorOrdemDiaCrud(OradorCrud): model = OradorOrdemDia @@ -676,7 +799,7 @@ class OradorOrdemDiaCrud(OradorCrud): form_class = OradorOrdemDiaForm def get_initial(self): - initial = super(UpdateView, self).get_initial() + initial = super().get_initial() initial.update({'id_sessao': self.object.sessao_plenaria.id}) initial.update({'numero': self.object.numero_ordem}) @@ -735,6 +858,16 @@ def sessao_legislativa_legislatura_ajax(request): return JsonResponse({'sessao_legislativa': lista_sessoes}) +def recuperar_nome_tipo_sessao(request): + try: + tipo = TipoSessaoPlenaria.objects.get(pk=request.GET['tipo']) + tipo_nome = tipo.nome + except ObjectDoesNotExist: + tipo_nome = '' + + return JsonResponse({'nome_tipo': tipo_nome}) + + class SessaoCrud(Crud): model = SessaoPlenaria help_topic = 'sessao_legislativa' @@ -764,6 +897,18 @@ class SessaoCrud(Crud): form_class = SessaoPlenariaForm + @property + def layout_key(self): + return 'SessaoSolene' + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + sessao = context['object'] + tipo_sessao = sessao.tipo + if tipo_sessao.nome == "Solene": + context.update({'subnav_template_name': 'sessao/subnav-solene.yaml'}) + return context + def get_initial(self): return {'sessao_legislativa': self.object.sessao_legislativa} @@ -772,6 +917,10 @@ class SessaoCrud(Crud): form_class = SessaoPlenariaForm logger = logging.getLogger(__name__) + @property + def layout_key(self): + return 'SessaoSolene' + @property def cancel_url(self): return self.search_url @@ -804,6 +953,26 @@ class SessaoCrud(Crud): namespace = self.model._meta.app_config.name return reverse('%s:%s' % (namespace, 'sessaoplenaria_list')) + class DetailView(Crud.DetailView): + + @property + def layout_key(self): + sessao = self.object + tipo_sessao = sessao.tipo + if tipo_sessao.nome == "Solene": + return 'SessaoSolene' + return 'SessaoPlenaria' + + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + sessao = context['object'] + tipo_sessao = sessao.tipo + if tipo_sessao.nome == "Solene": + context.update({'subnav_template_name': 'sessao/subnav-solene.yaml'}) + # self.layout_key = 'SessaoSolene' + return context + class SessaoPermissionMixin(PermissionRequiredForAppCrudMixin, FormMixin, @@ -837,6 +1006,10 @@ class PresencaView(FormMixin, PresencaMixin, DetailView): context = FormMixin.get_context_data(self, **kwargs) context['title'] = '%s (%s)' % ( _('Presença'), self.object) + sessao = context['object'] + tipo_sessao = sessao.tipo + if tipo_sessao.nome == "Solene": + context.update({'subnav_template_name': 'sessao/subnav-solene.yaml'}) return context @method_decorator(permission_required( @@ -921,17 +1094,23 @@ class PainelView(PermissionRequiredForAppCrudMixin, TemplateView): cronometro_ordem = cronometro_ordem.seconds cronometro_consideracoes = cronometro_consideracoes.seconds + sessao_pk = kwargs['pk'] + sessao = SessaoPlenaria.objects.get(pk=sessao_pk) context = TemplateView.get_context_data(self, **kwargs) context.update({ 'head_title': str(_('Painel Plenário')), - 'sessao_id': kwargs['pk'], - 'root_pk': kwargs['pk'], - 'sessaoplenaria': SessaoPlenaria.objects.get(pk=kwargs['pk']), + 'sessao_id': sessao_pk, + 'root_pk': sessao_pk, + 'sessaoplenaria': sessao, 'cronometro_discurso': cronometro_discurso, 'cronometro_aparte': cronometro_aparte, 'cronometro_ordem': cronometro_ordem, 'cronometro_consideracoes': cronometro_consideracoes}) + tipo_sessao = sessao.tipo + if tipo_sessao.nome == "Solene": + context.update({'subnav_template_name': 'sessao/subnav-solene.yaml'}) + return context @@ -1154,8 +1333,12 @@ class MesaView(FormMixin, DetailView): context = FormMixin.get_context_data(self, **kwargs) context['title'] = '%s (%s)' % ( _('Mesa Diretora'), self.object) + sessao = context['object'] + tipo_sessao = sessao.tipo + if tipo_sessao.nome == "Solene": + context.update({'subnav_template_name': 'sessao/subnav-solene.yaml'}) return context - + def get_success_url(self): pk = self.kwargs['pk'] return reverse('sapl.sessao:mesa', kwargs={'pk': pk}) @@ -1363,14 +1546,18 @@ def get_identificação_basica(sessao_plenaria): abertura = data_inicio.strftime('%d/%m/%Y') if data_inicio else '' data_fim = sessao_plenaria.data_fim encerramento = data_fim.strftime('%d/%m/%Y') + ' -' if data_fim else '' - return({'basica': [ + tema_solene = sessao_plenaria.tema_solene + context = {'basica': [ _('Tipo de Sessão: %(tipo)s') % {'tipo': sessao_plenaria.tipo}, _('Abertura: %(abertura)s - %(hora_inicio)s') % { 'abertura': abertura, 'hora_inicio': sessao_plenaria.hora_inicio}, _('Encerramento: %(encerramento)s %(hora_fim)s') % { - 'encerramento': encerramento, 'hora_fim': sessao_plenaria.hora_fim} - ], - 'sessaoplenaria': sessao_plenaria}) + 'encerramento': encerramento, 'hora_fim': sessao_plenaria.hora_fim}, + ], + 'sessaoplenaria': sessao_plenaria} + if sessao_plenaria.tipo.nome == "Solene" and tema_solene: + context.update({'tema_solene': 'Tema da Sessão Solene: %s' % tema_solene}) + return context def get_conteudo_multimidia(sessao_plenaria): @@ -1531,10 +1718,11 @@ def get_assinaturas(sessao_plenaria): context.update( {'texto_assinatura': 'Assinatura da Mesa Diretora da Sessão'}) context.update({'assinatura_mesa': mesa_dia}) - elif config_assinatura_ata == 'P' and presidente_dia: + elif config_assinatura_ata == 'P' and presidente_dia and presidente_dia[0]: context.update( {'texto_assinatura': 'Assinatura do Presidente da Sessão'}) - context.update({'assinatura_mesa': presidente_dia}) + assinatura_presidente = [{'parlamentar': presidente_dia[0], 'cargo': "Presidente"}] + context.update({'assinatura_mesa': assinatura_presidente}) return context @@ -1824,6 +2012,11 @@ class ResumoView(DetailView): 'decimo_terceiro_ordenacao': 'oradores_explicacoes.html', 'decimo_quarto_ordenacao': 'ocorrencias_da_sessao.html' }) + + sessao = context['object'] + tipo_sessao = sessao.tipo + if tipo_sessao.nome == "Solene": + context.update({'subnav_template_name': 'sessao/subnav-solene.yaml'}) return context def get(self, request, *args, **kwargs): @@ -1848,6 +2041,10 @@ class ExpedienteView(FormMixin, DetailView): context = FormMixin.get_context_data(self, **kwargs) context['title'] = '%s (%s)' % ( _('Expediente Diversos'), self.object) + sessao = context['object'] + tipo_sessao = sessao.tipo + if tipo_sessao.nome == "Solene": + context.update({'subnav_template_name': 'sessao/subnav-solene.yaml'}) return context @method_decorator(permission_required('sessao.add_expedientesessao')) @@ -1937,6 +2134,10 @@ class OcorrenciaSessaoView(FormMixin, DetailView): context = FormMixin.get_context_data(self, **kwargs) context['title'] = 'Ocorrências da Sessão (%s)' % ( self.object) + sessao = context['object'] + tipo_sessao = sessao.tipo + if tipo_sessao.nome == "Solene": + context.update({'subnav_template_name': 'sessao/subnav-solene.yaml'}) return context def delete(self): @@ -2662,8 +2863,7 @@ class VotacaoNominalTransparenciaDetailView(TemplateView): template_name = 'sessao/votacao/nominal_transparencia.html' def get_context_data(self, **kwargs): - context = super(VotacaoNominalTransparenciaDetailView, - self).get_context_data(**kwargs) + context = super().get_context_data(**kwargs) materia_votacao = self.request.GET.get('materia', None) @@ -2747,8 +2947,7 @@ class VotacaoSimbolicaTransparenciaDetailView(TemplateView): template_name = 'sessao/votacao/simbolica_transparencia.html' def get_context_data(self, **kwargs): - context = super(VotacaoSimbolicaTransparenciaDetailView, - self).get_context_data(**kwargs) + context = super().get_context_data(**kwargs) materia_votacao = self.request.GET.get('materia', None) @@ -3015,7 +3214,7 @@ class SessaoListView(ListView): return SessaoPlenaria.objects.all().order_by('-data_inicio') def get_context_data(self, **kwargs): - context = super(SessaoListView, self).get_context_data(**kwargs) + context = super().get_context_data(**kwargs) paginator = context['paginator'] page_obj = context['page_obj'] @@ -3179,8 +3378,7 @@ class PesquisarSessaoPlenariaView(FilterView): logger = logging.getLogger(__name__) def get_filterset_kwargs(self, filterset_class): - super(PesquisarSessaoPlenariaView, - self).get_filterset_kwargs(filterset_class) + super().get_filterset_kwargs(filterset_class) kwargs = {'data': self.request.GET or None} @@ -3196,8 +3394,7 @@ class PesquisarSessaoPlenariaView(FilterView): return kwargs def get_context_data(self, **kwargs): - context = super(PesquisarSessaoPlenariaView, - self).get_context_data(**kwargs) + context = super().get_context_data(**kwargs) context['title'] = _('Pesquisar Sessão Plenária') paginator = context['paginator'] @@ -3209,7 +3406,7 @@ class PesquisarSessaoPlenariaView(FilterView): return context def get(self, request, *args, **kwargs): - super(PesquisarSessaoPlenariaView, self).get(request) + super().get(request) # Se a pesquisa estiver quebrando com a paginação # Olhe esta função abaixo @@ -3247,8 +3444,7 @@ class PesquisarPautaSessaoView(PesquisarSessaoPlenariaView): logger.debug('Pesquisa de PautaSessao.') def get_context_data(self, **kwargs): - context = super(PesquisarPautaSessaoView, - self).get_context_data(**kwargs) + context = super().get_context_data(**kwargs) context['title'] = _('Pesquisar Pauta de Sessão') return context @@ -3269,8 +3465,7 @@ class AdicionarVariasMateriasExpediente(PermissionRequiredForAppCrudMixin, logger = logging.getLogger(__name__) def get_filterset_kwargs(self, filterset_class): - super(AdicionarVariasMateriasExpediente, - self).get_filterset_kwargs(filterset_class) + super().get_filterset_kwargs(filterset_class) kwargs = {'data': self.request.GET or None} @@ -3294,8 +3489,7 @@ class AdicionarVariasMateriasExpediente(PermissionRequiredForAppCrudMixin, return kwargs def get_context_data(self, **kwargs): - context = super(MateriaLegislativaPesquisaView, - self).get_context_data(**kwargs) + context = super().get_context_data(**kwargs) context['title'] = _('Pesquisar Matéria Legislativa') context['root_pk'] = self.kwargs['pk'] @@ -3362,8 +3556,7 @@ class AdicionarVariasMateriasOrdemDia(AdicionarVariasMateriasExpediente): logger = logging.getLogger(__name__) def get_filterset_kwargs(self, filterset_class): - super(AdicionarVariasMateriasExpediente, - self).get_filterset_kwargs(filterset_class) + super().get_filterset_kwargs(filterset_class) kwargs = {'data': self.request.GET or None} @@ -3581,8 +3774,7 @@ class VotacaoEmBlocoExpediente(PermissionRequiredForAppCrudMixin, ListView): retiradapauta=None) def get_context_data(self, **kwargs): - context = super(VotacaoEmBlocoExpediente, - self).get_context_data(**kwargs) + context = super().get_context_data(**kwargs) context['pk'] = self.kwargs['pk'] context['root_pk'] = self.kwargs['pk'] if not verifica_sessao_iniciada(self.request, self.kwargs['pk']): diff --git a/sapl/settings.py b/sapl/settings.py index 1167d33f3..07b2d6904 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.158' +SAPL_VERSION = '3.1.159' if DEBUG: EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' @@ -239,7 +239,7 @@ DEFAULT_FROM_EMAIL = config('DEFAULT_FROM_EMAIL', cast=str, default='') SERVER_EMAIL = config('SERVER_EMAIL', cast=str, default='') EMAIL_RUNNING = None -MAX_DOC_UPLOAD_SIZE = 60 * 1024 * 1024 # 60MB +MAX_DOC_UPLOAD_SIZE = 80 * 1024 * 1024 # 80MB MAX_IMAGE_UPLOAD_SIZE = 2 * 1024 * 1024 # 2MB # Internationalization diff --git a/sapl/static/sapl/css/relatorio.css b/sapl/static/sapl/css/relatorio.css index b51a40c2b..6fd166c99 100644 --- a/sapl/static/sapl/css/relatorio.css +++ b/sapl/static/sapl/css/relatorio.css @@ -1,5 +1,5 @@ @page{ - margin-top: 4.5cm; + margin-top: 5.2cm; size: A4 portrait; } @@ -53,4 +53,41 @@ fieldset { page-break-after: avoid; margin:5px; padding:0px; -} \ No newline at end of file +} + + +table { + max-width: 520px; +} +table.grayTable { + border: 1px solid #6e6e6e; + width: 100%; + text-align: left; + border-collapse: collapse; +} +table.grayTable td, table.grayTable th { + border: 1px solid #000000; +} +table.grayTable tbody td { + font-size: 10px; + max-width: 80px; + overflow-wrap: break-word; + word-wrap: break-word; + text-align: justify; +} +table.grayTable tr:nth-child(even) { + background: #dddddd; +} +table.grayTable thead { + background: #BBBBBB; + border-bottom: 2px solid #000000; +} +table.grayTable thead th { + font-size:10px; + color: rgb(0, 0, 0); + border-left: 1px solid #000000; + +} +table.grayTable thead th:first-child { + border-left: none; +} \ No newline at end of file diff --git a/sapl/templates/base.html b/sapl/templates/base.html index 0df0d6dee..653677a32 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.158 + Release: 3.1.159

diff --git a/sapl/templates/base/RelatorioAtas_filter.html b/sapl/templates/base/RelatorioAtas_filter.html index adc494431..9eab9ae8f 100644 --- a/sapl/templates/base/RelatorioAtas_filter.html +++ b/sapl/templates/base/RelatorioAtas_filter.html @@ -1,6 +1,7 @@ {% extends "crud/list.html" %} {% load i18n %} {% load crispy_forms_tags staticfiles %} +{% load webpack_static from webpack_loader %} {% block base_content %} {% if not filter_url %} @@ -26,7 +27,7 @@ {{sessao}} - + {% endfor %} diff --git a/sapl/templates/base/RelatorioDataFimPrazoTramitacao_filter.html b/sapl/templates/base/RelatorioDataFimPrazoTramitacao_filter.html index 0960a958b..caa585a82 100644 --- a/sapl/templates/base/RelatorioDataFimPrazoTramitacao_filter.html +++ b/sapl/templates/base/RelatorioDataFimPrazoTramitacao_filter.html @@ -14,7 +14,7 @@ PARÂMETROS DE PESQUISA:
 Período: {{ data_tramitacao }}
 Tipo de matéria: {{ tipo }}
-  Status atual: {{ tramitacao__status }}
+  Status de tramitação: {{ tramitacao__status }}
 Local de origem: {{ tramitacao__unidade_tramitacao_local }}
 Local de destino: {{ tramitacao__unidade_tramitacao_destino }}


{% if object_list %} diff --git a/sapl/templates/base/RelatorioPresencaSessao_filter.html b/sapl/templates/base/RelatorioPresencaSessao_filter.html index 71032d36a..c3ad733af 100644 --- a/sapl/templates/base/RelatorioPresencaSessao_filter.html +++ b/sapl/templates/base/RelatorioPresencaSessao_filter.html @@ -20,34 +20,93 @@




PERÍODO: {{periodo}}
- TOTAIS NO PERÍODO - SESSÕES: {{total_sessao}} - ORDENS DO DIA: {{total_ordemdia}} + Legislatura: {{legislatura}}
+ Sessão Legislativa: {{sessao_legislativa}}
+ Tipo Sessão Plenária: {{tipo}}
+ TOTAIS NO PERÍODO - SESSÕES: {{total_sessao}} - ORDENS DO DIA: {{total_ordemdia}}
+ Exibir presença das Ordens do Dia: {% if exibir_ordem %} Sim {% else %} Não {% endif %}
- + + - + {% if exibir_ordem %} {% endif %} - - + {% if exibir_ordem %} + + + {% endif %} {% for p in parlamentares %} - + + - - + {% if exibir_ordem %} + + + {% endif %} {% endfor %}
Nome Parlamentar / PartidoTitularTitular?Ativo? SessãoOrdem do DiaOrdem do Dia
(Qtd) ( % )(Qtd)( % )(Qtd)( % )
{{p.parlamentar}} / {{p.parlamentar|filiacao_intervalo_filter:date_range|default:"Sem Partido"}}{%if p.titular %} Sim {% else %} Não {% endif %}{% if p.titular %} Sim {% else %} Não {% endif %}{% if p.parlamentar.ativo %} Sim {% else %} Não {% endif %} {{p.sessao_count}} {{p.sessao_porc}}{{p.ordemdia_count}}{{p.ordemdia_porc}}{{p.ordemdia_count}}{{p.ordemdia_porc}}
{% endif %} {% endblock base_content %} + +{% block extra_js %} + + + +{% endblock %} diff --git a/sapl/templates/base/anexadas_ciclicas.html b/sapl/templates/base/anexadas_ciclicas.html new file mode 100644 index 000000000..9008d3428 --- /dev/null +++ b/sapl/templates/base/anexadas_ciclicas.html @@ -0,0 +1,35 @@ +{% extends "base.html" %} +{% load common_tags %} +{% block base_content %} +
+

Lista de Matérias Anexadas Cíclicas

+
+ {% if not anexadas_ciclicas %} +

{{ NO_ENTRIES_MSG }}

+ {% else %} + + + + + + + + + {% for data, fim, inicio in anexadas_ciclicas %} + + + + + + {% endfor %} + +
Matéria Fim do CicloMatéria Início do Ciclo
{{ data }} + {{ fim }} + + {{ inicio }} +
+ {% endif %} +
+ {% include 'paginacao.html' %} +
+{% endblock base_content %} \ No newline at end of file diff --git a/sapl/templates/base/anexados_ciclicos.html b/sapl/templates/base/anexados_ciclicos.html new file mode 100644 index 000000000..0e98659d3 --- /dev/null +++ b/sapl/templates/base/anexados_ciclicos.html @@ -0,0 +1,36 @@ +{% extends "base.html" %} +{% load common_tags %} +{% block base_content %} +
+

Lista de Documentos Anexados Cíclicos

+
+ {% if not anexados_ciclicos %} +

{{ NO_ENTRIES_MSG }}

+ {% else %} + + + + + + + + + + {% for data, fim, inicio in anexados_ciclicos %} + + + + + + {% endfor %} + +
Data AnexaçãoDocumento Fim do CicloDocumento Início do Ciclo
{{ data }} + {{ fim }} + + {{ inicio }} +
+ {% endif %} +
+ {% include 'paginacao.html' %} +
+{% endblock base_content %} diff --git a/sapl/templates/base/autores_duplicados.html b/sapl/templates/base/autores_duplicados.html index 361ff7add..0b9234429 100644 --- a/sapl/templates/base/autores_duplicados.html +++ b/sapl/templates/base/autores_duplicados.html @@ -1,30 +1,30 @@ {% extends "base.html" %} {% load common_tags %} {% block base_content %} -
-

Lista de Autores Duplicados

-
- {% if not autores_duplicados %} -

{{ NO_ENTRIES_MSG }}

- {% else %} - - - - - - - - - {% for autor in autores_duplicados %} - - - - - {% endfor %} - -
AutorQuantidade
{{ autor.nome }}{{ autor.count }}
- {% endif %} -
- {% include 'paginacao.html'%} -
+
+

Lista de Autores Duplicados

+
+ {% if not autores_duplicados %} +

{{ NO_ENTRIES_MSG }}

+ {% else %} + + + + + + + + + {% for autor in autores_duplicados %} + + + + + {% endfor %} + +
AutorQuantidade
{{ autor.nome }}{{ autor.count }}
+ {% endif %} +
+ {% include 'paginacao.html' %} +
{% endblock base_content %} \ No newline at end of file diff --git a/sapl/templates/base/bancada_comissao_autor_externo.html b/sapl/templates/base/bancada_comissao_autor_externo.html index 9edaf21f7..15789ae10 100644 --- a/sapl/templates/base/bancada_comissao_autor_externo.html +++ b/sapl/templates/base/bancada_comissao_autor_externo.html @@ -1,36 +1,36 @@ {% extends "base.html" %} {% load common_tags %} {% block base_content %} -
-

Lista de Bancadas e Comissões com Autor Externo

-
- {% if not bancada_comissao_autor_externo %} -

{{ NO_ENTRIES_MSG }}

- {% else %} - - - - - - - - - - {% for autor, objeto, descricao_objeto, link in bancada_comissao_autor_externo %} - - - - - - {% endfor %} - -
Descrição do ObjetoObjetoAutor
{{ descricao_objeto }} - {{ objeto }} - - {{ autor.nome }} -
- {% endif %} -
- {% include 'paginacao.html'%} -
+
+

Lista de Bancadas e Comissões com Autor Externo

+
+ {% if not bancada_comissao_autor_externo %} +

{{ NO_ENTRIES_MSG }}

+ {% else %} + + + + + + + + + + {% for autor, objeto, descricao_objeto, link in bancada_comissao_autor_externo %} + + + + + + {% endfor %} + +
Descrição do ObjetoObjetoAutor
{{ descricao_objeto }} + {{ objeto }} + + {{ autor.nome }} +
+ {% endif %} +
+ {% include 'paginacao.html' %} +
{% endblock base_content %} \ No newline at end of file diff --git a/sapl/templates/base/filiacoes_sem_data_filiacao.html b/sapl/templates/base/filiacoes_sem_data_filiacao.html index 22d24f972..949306def 100644 --- a/sapl/templates/base/filiacoes_sem_data_filiacao.html +++ b/sapl/templates/base/filiacoes_sem_data_filiacao.html @@ -1,32 +1,32 @@ {% extends "base.html" %} {% load common_tags %} {% block base_content %} -
-

Lista de Filiações sem Data Filiação

-
- {% if not filiacoes_sem_data_filiacao %} -

{{ NO_ENTRIES_MSG }}

- {% else %} - - - - - - - - - {% for filiacao in filiacoes_sem_data_filiacao %} - - - - - {% endfor %} - -
Parlamentar FiliadoPartido
- {{ filiacao.parlamentar }} - {{ filiacao.partido }}
- {% endif %} -
- {% include 'paginacao.html'%} -
+
+

Lista de Filiações sem Data Filiação

+
+ {% if not filiacoes_sem_data_filiacao %} +

{{ NO_ENTRIES_MSG }}

+ {% else %} + + + + + + + + + {% for filiacao in filiacoes_sem_data_filiacao %} + + + + + {% endfor %} + +
Parlamentar FiliadoPartido
+ {{ filiacao.parlamentar }} + {{ filiacao.partido }}
+ {% endif %} +
+ {% include 'paginacao.html' %} +
{% endblock base_content %} \ No newline at end of file diff --git a/sapl/templates/base/legislatura_infindavel.html b/sapl/templates/base/legislatura_infindavel.html index a44c544e1..05a708ae9 100644 --- a/sapl/templates/base/legislatura_infindavel.html +++ b/sapl/templates/base/legislatura_infindavel.html @@ -1,32 +1,32 @@ {% extends "base.html" %} {% load common_tags %} {% block base_content %} -
-

Lista de Legislaturas sem Data Fim

-
- {% if not legislatura_infindavel %} -

{{ NO_ENTRIES_MSG }}

- {% else %} - - - - - - - - - - {% for legislatura in legislatura_infindavel %} - - - - - - {% endfor %} - -
Número LegislaturaData EleiçãoData Início
{{ legislatura.numero }}{{ legislatura.data_eleicao }}{{ legislatura.data_inicio }}
- {% endif %} -
- {% include 'paginacao.html'%} -
+
+

Lista de Legislaturas sem Data Fim

+
+ {% if not legislatura_infindavel %} +

{{ NO_ENTRIES_MSG }}

+ {% else %} + + + + + + + + + + {% for legislatura in legislatura_infindavel %} + + + + + + {% endfor %} + +
Número LegislaturaData EleiçãoData Início
{{ legislatura.numero }}{{ legislatura.data_eleicao }}{{ legislatura.data_inicio }}
+ {% endif %} +
+ {% include 'paginacao.html' %} +
{% endblock base_content %} \ No newline at end of file diff --git a/sapl/templates/base/lista_inconsistencias.html b/sapl/templates/base/lista_inconsistencias.html index 121422ade..309495199 100644 --- a/sapl/templates/base/lista_inconsistencias.html +++ b/sapl/templates/base/lista_inconsistencias.html @@ -1,22 +1,22 @@ {% extends "base.html" %} {% load common_tags %} {% block base_content %} -
-

Lista de Inconsistências

-
- - - {% for complemento_link, nome, valor in tabela_inconsistencias %} - - - - - {% endfor %} - -
- {{ nome }} - {{ valor }}
-
- {% include 'paginacao.html'%} +
+

Lista de Inconsistências

+
+ + + {% for complemento_link, nome, valor in tabela_inconsistencias %} + + + + + {% endfor %} + +
+ {{ nome }} + {{ valor }}
+
+ {% include 'paginacao.html' %}
{% endblock base_content %} \ No newline at end of file diff --git a/sapl/templates/base/mandato_sem_data_inicio.html b/sapl/templates/base/mandato_sem_data_inicio.html index 09c151d7a..00837e03f 100644 --- a/sapl/templates/base/mandato_sem_data_inicio.html +++ b/sapl/templates/base/mandato_sem_data_inicio.html @@ -1,32 +1,32 @@ {% extends "base.html" %} {% load common_tags %} {% block base_content %} -
-

Lista de Mandatos sem Data Inicial

-
- {% if not mandato_sem_data_inicio %} -

{{ NO_ENTRIES_MSG }}

- {% else %} - - - - - - - - - {% for mandato in mandato_sem_data_inicio %} - - - - - {% endfor %} - -
Parlamentar do MandatoLegislatura do Mandato
- {{ mandato.parlamentar }} - {{ mandato.legislatura }}
- {% endif %} -
- {% include 'paginacao.html'%} -
+
+

Lista de Mandatos sem Data Inicial

+
+ {% if not mandato_sem_data_inicio %} +

{{ NO_ENTRIES_MSG }}

+ {% else %} + + + + + + + + + {% for mandato in mandato_sem_data_inicio %} + + + + + {% endfor %} + +
Parlamentar do MandatoLegislatura do Mandato
+ {{ mandato.parlamentar }} + {{ mandato.legislatura }}
+ {% endif %} +
+ {% include 'paginacao.html' %} +
{% endblock base_content %} \ No newline at end of file diff --git a/sapl/templates/base/materias_protocolo_inexistente.html b/sapl/templates/base/materias_protocolo_inexistente.html index 879a6d460..059053b06 100644 --- a/sapl/templates/base/materias_protocolo_inexistente.html +++ b/sapl/templates/base/materias_protocolo_inexistente.html @@ -1,34 +1,34 @@ {% extends "base.html" %} {% load common_tags %} {% block base_content %} -
-

Lista de Matérias Legislativas com Protocolo Inexistente

-
- {% if not materias_protocolo_inexistente %} -

{{ NO_ENTRIES_MSG }}

- {% else %} - - - - - - - - - - {% for materia, ano, numero_protocolo in materias_protocolo_inexistente %} - - - - - - {% endfor %} - -
Matéria LegislativaAnoNúmero Protocolo
- {{ materia }} - {{ ano }}{{ numero_protocolo }}
- {% endif %} -
- {% include 'paginacao.html'%} -
+
+

Lista de Matérias Legislativas com Protocolo Inexistente

+
+ {% if not materias_protocolo_inexistente %} +

{{ NO_ENTRIES_MSG }}

+ {% else %} + + + + + + + + + + {% for materia, ano, numero_protocolo in materias_protocolo_inexistente %} + + + + + + {% endfor %} + +
Matéria LegislativaAnoNúmero Protocolo
+ {{ materia }} + {{ ano }}{{ numero_protocolo }}
+ {% endif %} +
+ {% include 'paginacao.html' %} +
{% endblock base_content %} \ No newline at end of file diff --git a/sapl/templates/base/parlamentares_duplicados.html b/sapl/templates/base/parlamentares_duplicados.html index 2e63a04bc..0bb86e2ce 100644 --- a/sapl/templates/base/parlamentares_duplicados.html +++ b/sapl/templates/base/parlamentares_duplicados.html @@ -1,32 +1,32 @@ {% extends "base.html" %} {% load common_tags %} {% block base_content %} -
-

Lista de Parlamentares Duplicados

-
- {% if not parlamentares_duplicados %} -

{{ NO_ENTRIES_MSG }}

- {% else %} - - - - - - - - - {% for parlamentar, quantidade in parlamentares_duplicados %} - - - - - {% endfor %} - -
Nome do ParlamentarQuantidade
- {{ parlamentar }} - {{ quantidade }}
- {% endif %} -
- {% include 'paginacao.html'%} -
+
+

Lista de Parlamentares Duplicados

+
+ {% if not parlamentares_duplicados %} +

{{ NO_ENTRIES_MSG }}

+ {% else %} + + + + + + + + + {% for quantidade, parlamentar in parlamentares_duplicados %} + + + + + {% endfor %} + +
Nome do ParlamentarQuantidade
+ {{ parlamentar }} + {{ quantidade }}
+ {% endif %} +
+ {% include 'paginacao.html' %} +
{% endblock base_content %} \ No newline at end of file diff --git a/sapl/templates/base/parlamentares_filiacoes_intersecao.html b/sapl/templates/base/parlamentares_filiacoes_intersecao.html index b0949e05a..80dedd41a 100644 --- a/sapl/templates/base/parlamentares_filiacoes_intersecao.html +++ b/sapl/templates/base/parlamentares_filiacoes_intersecao.html @@ -1,34 +1,34 @@ {% extends "base.html" %} {% load common_tags %} {% block base_content %} -
-

Lista de Parlamentares com Filiações com Interseção

-
- {% if not parlamentares_filiacoes_intersecao %} -

{{ NO_ENTRIES_MSG }}

- {% else %} - - - - - - - - - - {% for parlamentar, filiacao_a , filiacao_b in parlamentares_filiacoes_intersecao %} - - - - - - {% endfor %} - -
ParlamentarFiliação 1Filiação 2
- {{ parlamentar }} - {{filiacao_a.data|date:"d/m/Y"}} - {{filiacao_a.data_desfiliacao|date:"d/m/Y"}}{{filiacao_b.data|date:"d/m/Y"}} - {{filiacao_b.data_desfiliacao|date:"d/m/Y"}}
- {% endif %} -
- {% include 'paginacao.html'%} -
+
+

Lista de Parlamentares com Filiações com Interseção

+
+ {% if not parlamentares_filiacoes_intersecao %} +

{{ NO_ENTRIES_MSG }}

+ {% else %} + + + + + + + + + + {% for parlamentar, filiacao_a , filiacao_b in parlamentares_filiacoes_intersecao %} + + + + + + {% endfor %} + +
ParlamentarFiliação 1Filiação 2
+ {{ parlamentar }} + {{ filiacao_a.data|date:"d/m/Y" }} - {{ filiacao_a.data_desfiliacao|date:"d/m/Y" }}{{ filiacao_b.data|date:"d/m/Y" }} - {{ filiacao_b.data_desfiliacao|date:"d/m/Y" }}
+ {% endif %} +
+ {% include 'paginacao.html' %} +
{% endblock base_content %} \ No newline at end of file diff --git a/sapl/templates/base/parlamentares_mandatos_intersecao.html b/sapl/templates/base/parlamentares_mandatos_intersecao.html index 2d4c915a4..5ba19017b 100644 --- a/sapl/templates/base/parlamentares_mandatos_intersecao.html +++ b/sapl/templates/base/parlamentares_mandatos_intersecao.html @@ -1,34 +1,34 @@ {% extends "base.html" %} {% load common_tags %} {% block base_content %} -
-

Lista de Parlamentares com Mandatos em Interseção

-
- {% if not parlamentares_mandatos_intersecao %} -

{{ NO_ENTRIES_MSG }}

- {% else %} - - - - - - - - - - {% for parlamentar, mandato_a, mandato_b in parlamentares_mandatos_intersecao %} - - - - - - {% endfor %} - -
ParlamentarMandato 1Mandato 2
- {{ parlamentar }} - {{ mandato_a.legislatura}}
{{mandato_a.data_inicio_mandato|date:"d/m/Y"}} - {{mandato_a.data_fim_mandato|date:"d/m/Y"}}
{{ mandato_b.legislatura }}
{{mandato_b.data_inicio_mandato|date:"d/m/Y"}} - {{mandato_b.data_fim_mandato|date:"d/m/Y"}}
- {% endif %} -
- {% include 'paginacao.html'%} -
+
+

Lista de Parlamentares com Mandatos em Interseção

+
+ {% if not parlamentares_mandatos_intersecao %} +

{{ NO_ENTRIES_MSG }}

+ {% else %} + + + + + + + + + + {% for parlamentar, mandato_a, mandato_b in parlamentares_mandatos_intersecao %} + + + + + + {% endfor %} + +
ParlamentarMandato 1Mandato 2
+ {{ parlamentar }} + {{ mandato_a.legislatura }}
{{ mandato_a.data_inicio_mandato|date:"d/m/Y" }} - {{ mandato_a.data_fim_mandato|date:"d/m/Y" }}
{{ mandato_b.legislatura }}
{{ mandato_b.data_inicio_mandato|date:"d/m/Y" }} - {{ mandato_b.data_fim_mandato|date:"d/m/Y" }}
+ {% endif %} +
+ {% include 'paginacao.html' %} +
{% endblock base_content %} \ No newline at end of file diff --git a/sapl/templates/base/protocolos_com_materias.html b/sapl/templates/base/protocolos_com_materias.html index 1e98bad5c..25a88e49a 100644 --- a/sapl/templates/base/protocolos_com_materias.html +++ b/sapl/templates/base/protocolos_com_materias.html @@ -1,30 +1,30 @@ {% extends "base.html" %} {% load common_tags %} {% block base_content %} -
-

Lista de Protocolos que Excedem o Limite de Matérias Vinculadas

-
- {% if not protocolos_com_materias %} -

{{ NO_ENTRIES_MSG }}

- {% else %} - - - - - - - - - {% for materia, quantidade in protocolos_com_materias %} - - - - - {% endfor %} - -
ProtocoloQuantidade de Matérias Vinculas
{{ materia.numero_protocolo }}/{{ materia.ano }}{{ quantidade }}
- {% endif %} -
- {% include 'paginacao.html'%} -
+
+

Lista de Protocolos que Excedem o Limite de Matérias Vinculadas

+
+ {% if not protocolos_com_materias %} +

{{ NO_ENTRIES_MSG }}

+ {% else %} + + + + + + + + + {% for materia, quantidade in protocolos_com_materias %} + + + + + {% endfor %} + +
ProtocoloQuantidade de Matérias Vinculas
{{ materia.numero_protocolo }}/{{ materia.ano }}{{ quantidade }}
+ {% endif %} +
+ {% include 'paginacao.html' %} +
{% endblock base_content %} \ No newline at end of file diff --git a/sapl/templates/base/protocolos_duplicados.html b/sapl/templates/base/protocolos_duplicados.html index 15056991f..6a45a0045 100644 --- a/sapl/templates/base/protocolos_duplicados.html +++ b/sapl/templates/base/protocolos_duplicados.html @@ -1,32 +1,32 @@ {% extends "base.html" %} {% load common_tags %} {% block base_content %} -
-

Lista de Protocolos Duplicados

-
- {% if not protocolos_duplicados %} -

{{ NO_ENTRIES_MSG }}

- {% else %} - - - - - - - - - {% for protocolo, quantidade in protocolos_duplicados %} - - - - - {% endfor %} - -
ProtocoloQuantidade
- {{ protocolo }} - {{ quantidade }}
- {% endif %} -
- {% include 'paginacao.html'%} -
+
+

Lista de Protocolos Duplicados

+
+ {% if not protocolos_duplicados %} +

{{ NO_ENTRIES_MSG }}

+ {% else %} + + + + + + + + + {% for protocolo, quantidade in protocolos_duplicados %} + + + + + {% endfor %} + +
ProtocoloQuantidade
+ {{ protocolo }} + {{ quantidade }}
+ {% endif %} +
+ {% include 'paginacao.html' %} +
{% endblock base_content %} \ No newline at end of file diff --git a/sapl/templates/base/relatorios_list.html b/sapl/templates/base/relatorios_list.html index 5ac6d4d77..c2fcf4d0d 100644 --- a/sapl/templates/base/relatorios_list.html +++ b/sapl/templates/base/relatorios_list.html @@ -37,8 +37,8 @@ Histórico de tramitações por período e local informados. - Tramitações de Matérias por fim de prazo - Tramitações com fim de prazo no intervalo informado. + Tramitações de Matérias + Tramitações de matéria com status informado no intervalo. Reunião de Comissão diff --git a/sapl/templates/comissoes/reuniao_detail.html b/sapl/templates/comissoes/reuniao_detail.html index c8c32cd98..be4152e44 100644 --- a/sapl/templates/comissoes/reuniao_detail.html +++ b/sapl/templates/comissoes/reuniao_detail.html @@ -1,4 +1,4 @@ -{% extends "crud/detail.html" %} +{% extends "crud/detail_detail.html" %} {% load i18n %} {% block detail_content %} diff --git a/sapl/templates/crud/detail.html b/sapl/templates/crud/detail.html index 0058fc917..2d4f86b91 100644 --- a/sapl/templates/crud/detail.html +++ b/sapl/templates/crud/detail.html @@ -72,13 +72,21 @@

Este navegador não suporta o elemento áudio.

- {% elif column.text|video_url %} + {% elif column.text|is_video_url %}
-
+ {% elif column.text|youtube_url %} + + {% elif column.text|facebook_url %} +
+
{% elif column.text|url %} {% else %} @@ -131,4 +139,7 @@ {% endblock table_content %} {% endblock base_content %} -{% block extra_js %}{% endblock %} +{% block extra_js %} +
+ +{% endblock %} diff --git a/sapl/templates/floppyforms/image_thumbnail.html b/sapl/templates/floppyforms/image_thumbnail.html index 7d7b414a1..79d0f40db 100644 --- a/sapl/templates/floppyforms/image_thumbnail.html +++ b/sapl/templates/floppyforms/image_thumbnail.html @@ -1,5 +1,5 @@ {% load i18n staticfiles thumbnail %} - +{% load webpack_static from webpack_loader %}
-
diff --git a/sapl/templates/materia/despachoinicial_multicreate_form.html b/sapl/templates/materia/despachoinicial_multicreate_form.html new file mode 100644 index 000000000..2d1045d09 --- /dev/null +++ b/sapl/templates/materia/despachoinicial_multicreate_form.html @@ -0,0 +1,7 @@ +{% extends "crud/form.html" %} +{% load i18n %} +{% load crispy_forms_tags %} +{% load common_tags %} + +{% block extra_js %} +{% endblock %} diff --git a/sapl/templates/materia/em_lote/acessorio.html b/sapl/templates/materia/em_lote/acessorio.html index 89fc2fff8..a2afa460d 100644 --- a/sapl/templates/materia/em_lote/acessorio.html +++ b/sapl/templates/materia/em_lote/acessorio.html @@ -22,7 +22,7 @@
- {% for t in tipos_docs %} {% endfor %}
@@ -31,14 +31,14 @@
- +
- +
@@ -47,7 +47,7 @@
- +
@@ -65,7 +65,7 @@
- +
diff --git a/sapl/templates/materia/em_lote/tramitacao.html b/sapl/templates/materia/em_lote/tramitacao.html index 5cc4a0b90..1c68274e6 100644 --- a/sapl/templates/materia/em_lote/tramitacao.html +++ b/sapl/templates/materia/em_lote/tramitacao.html @@ -1,134 +1,28 @@ {% extends "crud/detail.html" %} {% load i18n crispy_forms_tags %} -{% block actions %}{% endblock %} {% block detail_content %} - {% if not show_results %} - {% crispy filter.form %} - {% endif %} - - {% if show_results %} - {% if object_list|length > 0 %} - {% if object_list|length == 1 %} -

{% trans 'Pesquisa concluída com sucesso! Foi encontrada 1 matéria.'%}

- {% else %} -

Foram encontradas {{object_list|length}} matérias.

- {% endif %} -
- {% csrf_token %} -
- 1. Detalhes da tramitação: - -
-
- - -
- -
- - -
- -
- - -
-
- -
-
- - -
- -
- - -
-
- -
-
- - -
- -
- - -
- -
- - -
-
- -
-
- - -
-
-
- -


- -
- 2. Selecione as matérias para tramitação: - -
-
- -
-
- - - - - {% for materia in object_list %} - - - - - {% endfor %} - -
Matéria
- - - {{materia.tipo.sigla}} {{materia.tipo.descricao}} {{materia.numero}}/{{materia.ano}} - -
-
- -
+ {% if not show_results %} + {% crispy filter.form %} {% else %} -

Nenhuma matéria encontrada.

- {% endif %} - {% endif %} + {% if object_list|length > 0 %} + {% if object_list|length == 1 %} +

{% trans 'Pesquisa concluída com sucesso! Foi encontrada 1 matéria.'%}

+ {% else %} +

Foram encontradas {{object_list|length}} matérias.

+ {% endif %} + {% crispy form %} + {% else %} +

Nenhuma matéria encontrada.

+ {% endif %} + + {% endif%} {% endblock detail_content %} + {% block extra_js %} {% endblock %} diff --git a/sapl/templates/navbar.yaml b/sapl/templates/navbar.yaml index 5d50d2122..b5e11a036 100644 --- a/sapl/templates/navbar.yaml +++ b/sapl/templates/navbar.yaml @@ -35,7 +35,7 @@ url: sapl.protocoloadm:pesq_doc_adm - title: {% trans 'Tramitação em Lote' %} url: sapl.protocoloadm:primeira_tramitacao_em_lote_docadm - check_permission: sapl.protocoloadm:add_tramitacaoadministrativo + check_permission: protocoloadm.add_tramitacaoadministrativo - title: {% trans 'Atividade Legislativa' %} children: diff --git a/sapl/templates/norma/normajuridica_detail.html b/sapl/templates/norma/normajuridica_detail.html index e45a7e486..7d8509830 100644 --- a/sapl/templates/norma/normajuridica_detail.html +++ b/sapl/templates/norma/normajuridica_detail.html @@ -87,6 +87,38 @@ +

+ + {% if user.is_superuser %} +
+ {% if object.user %} +
+
+

Usuário

+
+
+ +
+
+
+
+ {% endif %} + {% if object.ip %} +
+
+

IP

+
+
+
{{object.ip}}
+
+
+
+
+ {% endif %} +
+ {% endif %} {% if object.texto_articulado.exists and object.texto_articulado.first.has_view_permission %}
diff --git a/sapl/templates/norma/normajuridica_form.html b/sapl/templates/norma/normajuridica_form.html index 4c29fc98b..91531c4d1 100644 --- a/sapl/templates/norma/normajuridica_form.html +++ b/sapl/templates/norma/normajuridica_form.html @@ -57,6 +57,54 @@ numeroField.val(numero.replace(/^0+/, '')); } }); + + var modal_estilos = 'display: block;' + +'width: 85%; max-width: 600px;' + +'background: #fff; padding: 15px;' + +'border-radius: 5px;' + +'-webkit-box-shadow: 0px 6px 14px -2px rgba(0,0,0,0.75);' + +'-moz-box-shadow: 0px 6px 14px -2px rgba(0,0,0,0.75);' + +'box-shadow: 0px 6px 14px -2px rgba(0,0,0,0.75);' + +'position: fixed;' + +'top: 50%; left: 50%;' + +'transform: translate(-50%,-50%);' + +'z-index: 99999999; text-align: center'; + + var fundo_modal_estilos = 'top: 0; right: 0;' + +'bottom: 0; left: 0; position: fixed;' + +'background-color: rgba(0, 0, 0, 0.6); z-index: 99999999;' + +'display: none;'; + + var meu_modal = '
' + +'
' + +'

Atenção! Ano de apresentação e ano da norma são diferentes.


' + +'' + +'
'; + + function verifica_ano(){ + let ano = $("select#id_ano.select").val(); + let data_apresentacao = $("input#id_data.dateinput").val(); + let ano_apresentacao = data_apresentacao.substr(data_apresentacao.length - 4); + + if(ano && ano_apresentacao && ano_apresentacao != ano){ + $('#fundo_modal').fadeIn(); + } + } + + $(document).ready(function() { + $("body").append(meu_modal); + + $("#fundo_modal, #close_model_btn").click(function(){ $("#fundo_modal").hide(); }); + $("#meu_modal").click(function(e){ e.stopPropagation(); }); + + $("select#id_ano.select.form-control").blur(function(){ + verifica_ano(); + }); + $("input#id_data.dateinput.form-control").blur(function(){ + verifica_ano(); + }); + }); {% endblock %} diff --git a/sapl/templates/painel/index.html b/sapl/templates/painel/index.html index 476e700c8..217a09053 100644 --- a/sapl/templates/painel/index.html +++ b/sapl/templates/painel/index.html @@ -89,17 +89,22 @@ Considerações Finais:
- -
+ +

Resultado

- -

+ +

+

+ +
+

Matéria em Votação

+ +
-
-

Matéria em Votação

- - +
@@ -234,6 +239,7 @@ $("#sessao_plenaria").text(data["sessao_plenaria"]) $("#sessao_plenaria_data").text("Data Início: " + data["sessao_plenaria_data"]) $("#sessao_plenaria_hora_inicio").text("Hora Início: " + data["sessao_plenaria_hora_inicio"]) + $("#sessao_solene_tema").text(data["tema_solene"]) if (data["status_painel"] == false) { $("#message").text("PAINEL ENCONTRA-SE FECHADO"); } @@ -241,6 +247,12 @@ $("#message").text(""); } + if (data["sessao_solene"]){ + $("#resultado_votacao_div").hide(); + $("#obs_materia_div").hide(); + $('#tema_solene_div').show(); + } + if (data["brasao"] != null) $("#logo-painel").attr("src", data["brasao"]); @@ -381,7 +393,11 @@ audioAlertFinish.play(); } - if (data['materia_legislativa_texto']){ + if(data['sessao_finalizada']){ + $("#obs_materia_div").hide(); + $("#resultado_votacao_div").hide(); + } + else if (data['materia_legislativa_texto']){ if (data["status_painel"] == true){ $("#materia_legislativa_texto").text(data["materia_legislativa_texto"]); } @@ -410,15 +426,18 @@ $("#resultado_votacao").text(data["tipo_resultado"]); $("#resultado_votacao").css("color", "#45919D"); var resultado_votacao_upper = $("#resultado_votacao").text().toUpperCase(); - if (resultado_votacao_upper.search("APROV") != -1){ - $("#resultado_votacao").css("color", "green"); - } - if (resultado_votacao_upper.search("REJEIT") != -1){ - $("#resultado_votacao").css("color", "red"); - } + if (resultado_votacao_upper.search("APROV") != -1){ + $("#resultado_votacao").css("color", "green"); + $("#mat_em_votacao").text("Matéria Votada"); + } + if (resultado_votacao_upper.search("REJEIT") != -1){ + $("#resultado_votacao").css("color", "red"); + $("#mat_em_votacao").text("Matéria Votada"); + } } else{ $("#resultado_votacao").text(''); + $("#mat_em_votacao").text("Matéria em Votação"); } }, error: function(err) { diff --git a/sapl/templates/painel/voto_nominal.html b/sapl/templates/painel/voto_nominal.html index aa1a8f9cf..ecd915c25 100644 --- a/sapl/templates/painel/voto_nominal.html +++ b/sapl/templates/painel/voto_nominal.html @@ -90,25 +90,23 @@

{% csrf_token %} -
-
+
- -
-   -
-   -
+
+   +
+   +
-
-
-
-
-
-
-
- +
+
+
+ + + + +
{% else %} @@ -139,11 +137,13 @@ document.getElementById("date").innerHTML = n; $(window).on('beforeunload', function () { - $("input[type=submit], input[type=button]").prop("disabled", "disabled"); - }); - $( "#votosim" ).mouseleave(function(){document.location.reload(true);}); - $( "#votonao" ).mouseleave(function(){document.location.reload(true);}); - $( "#votoabstencao" ).mouseleave(function(){document.location.reload(true);}); + $("input[type=submit], input[type=button]").prop("disabled", "disabled"); + }); + + //TODO: Este código é necessário? + // $( "#votosim" ).mouseleave(function(){document.location.reload(true);}); + // $( "#votonao" ).mouseleave(function(){document.location.reload(true);}); + // $( "#votoabstencao" ).mouseleave(function(){document.location.reload(true);}); $(document).on('keyup', (e) => { var tecla_press = e.keyCode; @@ -160,34 +160,34 @@ case 86: //86 = valor da tecla V document.querySelectorAll("#voltar button")[0].click(); break; - }; }); - $(document).ready( - function(){ - - function checkTime(i) { - if (i<10) {i = "0" + i}; // add zero in front of numbers < 10 - return i; - } - function startTime() { - var today=new Date(); - var h=today.getHours(); - var m=today.getMinutes(); - var s=today.getSeconds(); - m = checkTime(m); - s = checkTime(s); - $("#relogio").text(h+":"+m+":"+s) - var t = setTimeout(function(){ - startTime() - }, 500); - } - startTime(); - - setTimeout(function() { - document.location.reload(true); - }, 30000) - }); + function checkTime(i) { + if (i<10) + i = "0" + i; // add zero in front of numbers < 10 + return i; + } + + function startTime() { + var today=new Date(); + var h=today.getHours(); + var m=today.getMinutes(); + var s=today.getSeconds(); + m = checkTime(m); + s = checkTime(s); + $("#relogio").text(h+":"+m+":"+s) + var t = setTimeout(function(){ + startTime() + }, 500); + } + + $(document).ready(function(){ + startTime(); + + setTimeout(function() { + document.location.reload(true); + }, 30000) + }); diff --git a/sapl/templates/parlamentares/materias.html b/sapl/templates/parlamentares/materias.html index c5e1448c8..bd30fd7b0 100644 --- a/sapl/templates/parlamentares/materias.html +++ b/sapl/templates/parlamentares/materias.html @@ -11,7 +11,7 @@ Matérias ({{nome_parlamentar}}) -

Primeiro Autor

+

Primeiro Autor


{% for autoria in autoria.0 %} @@ -20,7 +20,7 @@ @@ -32,7 +32,7 @@

Total: {{ autoria.1 }}


-

Co-Autor

+

Co-Autor


{{ materias.1 }}  - + {{ materias.2}}
{% for coautoria in coautoria.0 %} @@ -41,7 +41,7 @@ diff --git a/sapl/templates/protocoloadm/protocolo_list.html b/sapl/templates/protocoloadm/protocolo_list.html index 27c63edc8..337745da4 100644 --- a/sapl/templates/protocoloadm/protocolo_list.html +++ b/sapl/templates/protocoloadm/protocolo_list.html @@ -2,7 +2,6 @@ {% load i18n %} {% load tz %} {% load crispy_forms_tags %} -{% load static %} {% load webpack_static from webpack_loader %} {% block detail_content %} diff --git a/sapl/templates/protocoloadm/protocolo_mostrar.html b/sapl/templates/protocoloadm/protocolo_mostrar.html index 8c3f669b7..b5e7328e0 100644 --- a/sapl/templates/protocoloadm/protocolo_mostrar.html +++ b/sapl/templates/protocoloadm/protocolo_mostrar.html @@ -22,7 +22,7 @@ {% endif %} Natureza do Processo: {% if protocolo.tipo_processo == 0 %} Administrativo {% elif protocolo.tipo_processo == 1 %} Legislativo {% endif %}
- Número de Páginas: {{ protocolo.numero_paginas }}
+ Número de Páginas: {{ protocolo.numero_paginas|default_if_none:"Não informado" }}
Observação: {{ protocolo.observacao|default:"Não informado" }}
Anulado: {% if protocolo.anulado %} Sim {% else %} Não {% endif %}

diff --git a/sapl/templates/relatorios/header_ata.html b/sapl/templates/relatorios/header_ata.html index 4cc43efe6..5e7f87356 100644 --- a/sapl/templates/relatorios/header_ata.html +++ b/sapl/templates/relatorios/header_ata.html @@ -8,24 +8,28 @@ - +
- +
    -
  • {{casa.nome}}

  • +
  • {{casa.nome}}

  • Sistema de Apoio ao Processo Legislativo

-

- + {% if info %} +

{{info}}

+ {% else %} +

+ {% endif %} + \ No newline at end of file diff --git a/sapl/templates/relatorios/relatorio_ata.html b/sapl/templates/relatorios/relatorio_ata.html index c559585fb..eb2ea39b0 100644 --- a/sapl/templates/relatorios/relatorio_ata.html +++ b/sapl/templates/relatorios/relatorio_ata.html @@ -33,7 +33,7 @@ } } - + diff --git a/sapl/templates/relatorios/relatorio_doc_administrativos.html b/sapl/templates/relatorios/relatorio_doc_administrativos.html index 75723a976..7d7738a42 100644 --- a/sapl/templates/relatorios/relatorio_doc_administrativos.html +++ b/sapl/templates/relatorios/relatorio_doc_administrativos.html @@ -1,12 +1,11 @@ {% load i18n %} {% load common_tags %} -{% load static %} - +{% load crispy_forms_tags staticfiles %} - + diff --git a/sapl/templates/relatorios/relatorio_sessao_plenaria.html b/sapl/templates/relatorios/relatorio_sessao_plenaria.html new file mode 100644 index 000000000..d88af15dc --- /dev/null +++ b/sapl/templates/relatorios/relatorio_sessao_plenaria.html @@ -0,0 +1,183 @@ +{% load static %} + + + + + + + + + + + +
+

Informações Básicas

+

Tipo da Sessão: {{inf_basicas_dic.nom_sessao}}

+

Abertura: {{inf_basicas_dic.dat_inicio_sessao}} - {{inf_basicas_dic.hr_inicio_sessao}}

+

Encerramento: {{inf_basicas_dic.dat_fim_sessao}} - {{inf_basicas_dic.hr_fim_sessao}}

+ +

Mesa Diretora

+ {% for membro in lst_mesa%} +

{{membro.des_cargo}}: {{membro.nom_parlamentar}}/{{membro.sgl_partido}}

+ {% endfor%} + +

Lista de Presença da Sessão

+ {% for membro in lst_presenca_sessao%} +

{{membro.nom_parlamentar}}/{{membro.sgl_partido}}

+ {% endfor%} + +

Justificativas de Ausência da Sessão

+ +
{{ materias.1 }}  - + {{ materias.2}}
+ + + + + + + + + {% for ausencia in lst_ausencia_sessao%} + + + + + + {% endfor %} + + +
ParlamentarJustificativaAusente em
{{ausencia.parlamentar}}{{ausencia.justificativa}}{{ausencia.tipo}}
+ +

Expedientes

+ {% for expediente in lst_expedientes%} +

{{expediente.nom_expediente}}

+

{{expediente.txt_expediente|safe}}

+ {% endfor%} + + +

Matérias do Expediente

+ + + + + + + + + + + {% for materia in lst_expediente_materia%} + + + + + + {% endfor %} + + +
MatériaEmentaResultado da Votação
+
+
{{materia.num_ordem}} - {{materia.id_materia}}
+
Turno: {{materia.des_turno}}
+
{{materia.num_autores}}: {{materia.nom_autor}}
+
+
{{materia.txt_ementa}}
{{materia.nom_resultado}}
+ +

Oradores do Expediente

+ + {% for orador in lst_oradores_expediente%} + +

{{orador.num_ordem}} - {{orador.nom_parlamentar}}/{{orador.sgl_partido}}

+ + {% endfor %} + +

Lista de Presença da Ordem do Dia

+ + {% for orador in lst_presenca_ordem_dia%} + +

{{orador.nom_parlamentar}}/{{orador.sgl_partido}}

+ + {% endfor %} + +

Matérias da Ordem do Dia

+ + + + + + + + + + + {% for materia in lst_votacao%} + + + + + + {% endfor %} + + +
MatériaEmentaResultado da Votação
+
+
{{materia.num_ordem}} - {{materia.id_materia}}
+
Turno: {{materia.des_turno}}
+
{{materia.num_autores}}: {{materia.nom_autor}}
+
+
{{materia.txt_ementa}}
{{materia.nom_resultado}}
+ +
+

Oradores das Explicações Pessoais

+ {% for orador in lst_oradores%} + +

{{orador.num_ordem}} - {{orador.nom_parlamentar}}/{{orador.sgl_partido}}

+ + {% endfor %} +
+ + +

Ocorrências da Sessão

+ {% for ocorrencia in lst_ocorrencias%} +

{{ocorrencia}}

+ {% endfor %} + + + + + + \ No newline at end of file diff --git a/sapl/templates/rest_framework_docs/base.html b/sapl/templates/rest_framework_docs/base.html index 6614ea4e5..7e12127e7 100644 --- a/sapl/templates/rest_framework_docs/base.html +++ b/sapl/templates/rest_framework_docs/base.html @@ -1,4 +1,4 @@ -{% load static from staticfiles %} +{% load webpack_static from webpack_loader %} @@ -10,7 +10,7 @@ {% block title %}DRF Docs{% endblock %} {% block style %} - + {% endblock %} @@ -76,6 +76,6 @@ - + diff --git a/sapl/templates/sessao/blocos_ata/identificacao_basica.html b/sapl/templates/sessao/blocos_ata/identificacao_basica.html index 84eeb2a11..2beac1818 100644 --- a/sapl/templates/sessao/blocos_ata/identificacao_basica.html +++ b/sapl/templates/sessao/blocos_ata/identificacao_basica.html @@ -2,8 +2,14 @@

Identificação Básica: {% for b in basica %} - {{b}} - {% if not forloop.last %} ; {% endif %} + {{b}} + {% if not forloop.last %} + ; + {% else %} + {% if tema_solene %} + ; {{tema_solene}} + {% endif %} + {% endif %} {% endfor %}

\ No newline at end of file diff --git a/sapl/templates/sessao/blocos_resumo/identificacao_basica.html b/sapl/templates/sessao/blocos_resumo/identificacao_basica.html index 75ba5ba68..3c7859079 100644 --- a/sapl/templates/sessao/blocos_resumo/identificacao_basica.html +++ b/sapl/templates/sessao/blocos_resumo/identificacao_basica.html @@ -5,6 +5,10 @@ {% for b in basica %}
{{b}}
{% endfor %} + {% if tema_solene %} +

+
{{tema_solene}}
+ {% endif %}


diff --git a/sapl/templates/sessao/layouts.yaml b/sapl/templates/sessao/layouts.yaml index f5356a829..caeafc963 100644 --- a/sapl/templates/sessao/layouts.yaml +++ b/sapl/templates/sessao/layouts.yaml @@ -11,6 +11,15 @@ SessaoPlenaria: - upload_pauta upload_ata upload_anexo - url_audio url_video +SessaoSolene: + {% trans 'Dados Básicos' %}: + - legislatura sessao_legislativa tipo:3 numero:1 + - data_inicio:5 hora_inicio:5 iniciada + - data_fim:5 hora_fim:5 finalizada + - tema_solene + - upload_pauta upload_ata upload_anexo + - url_audio url_video + TipoResultadoVotacao: {% trans 'Tipo de Resultado da Votação' %}: - nome diff --git a/sapl/templates/sessao/pauta_sessao_list.html b/sapl/templates/sessao/pauta_sessao_list.html index ce3e5c77b..535efdcc2 100644 --- a/sapl/templates/sessao/pauta_sessao_list.html +++ b/sapl/templates/sessao/pauta_sessao_list.html @@ -1,6 +1,6 @@ {% extends "base.html" %} {% load i18n staticfiles %} - +{% load webpack_static from webpack_loader %} {% block base_content %} {% if not page_obj %} @@ -18,7 +18,7 @@ {{sessao}} - + {% endfor %} diff --git a/sapl/templates/sessao/resumo.html b/sapl/templates/sessao/resumo.html index b8ca19eff..4c86bcc34 100644 --- a/sapl/templates/sessao/resumo.html +++ b/sapl/templates/sessao/resumo.html @@ -20,6 +20,16 @@

+ + {% include 'sessao/blocos_resumo/'|add:primeiro_ordenacao %} {% include 'sessao/blocos_resumo/'|add:segundo_ordenacao %} diff --git a/sapl/templates/sessao/sessaoplenaria_form.html b/sapl/templates/sessao/sessaoplenaria_form.html index aa18e0105..17148fb6d 100644 --- a/sapl/templates/sessao/sessaoplenaria_form.html +++ b/sapl/templates/sessao/sessaoplenaria_form.html @@ -7,63 +7,88 @@ diff --git a/sapl/templates/sessao/subnav-solene.yaml b/sapl/templates/sessao/subnav-solene.yaml new file mode 100644 index 000000000..34f381005 --- /dev/null +++ b/sapl/templates/sessao/subnav-solene.yaml @@ -0,0 +1,34 @@ +{% load i18n common_tags %} + +- title: {% trans 'Abertura' %} + children: + - title: {% trans 'Dados Básicos' %} + url: sessaoplenaria_detail + - title: {% trans 'Mesa' %} + url: mesa + - title: {% trans 'Presença' %} + url: presenca + - title: {% trans 'Explicações Pessoais' %} + url: orador_list + - title: {% trans 'Ocorrências da Sessão' %} + url: ocorrencia_sessao + +- title: {% trans 'Expedientes' %} + children: + - title: {% trans 'Expediente Diversos' %} + url: expediente + - title: {% trans 'Oradores do Expediente' %} + url: oradorexpediente_list + +- title: {% trans 'Painel Eletrônico' %} + url: painel + {% if not 'painel_aberto'|get_config_attr %}check_permission: painel.list_painel{%endif%} + check_permission: painel.list_painel + +- title: {% trans 'Resumo' %} + children: + - title: {% trans 'Resumo' %} + url: resumo + - title: {% trans 'Extrato' %} + url: resumo_ata + check_permission: sessao.add_sessaoplenaria diff --git a/setup.py b/setup.py index bb2a50556..14a2cd564 100644 --- a/setup.py +++ b/setup.py @@ -43,7 +43,7 @@ install_requires = [ ] setup( name='interlegis-sapl', - version='3.1.158', + version='3.1.159', packages=find_packages(), include_package_data=True, license='GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007', diff --git a/solr/sapl_configset/conf/managed-schema b/solr/sapl_configset/conf/managed-schema index 0cba1950a..b6ecffaaf 100644 --- a/solr/sapl_configset/conf/managed-schema +++ b/solr/sapl_configset/conf/managed-schema @@ -120,6 +120,7 @@ + diff --git a/solr/sapl_configset/conf/saplconfigset.zip b/solr/sapl_configset/conf/saplconfigset.zip index bdbe33d75..dc965167c 100644 Binary files a/solr/sapl_configset/conf/saplconfigset.zip and b/solr/sapl_configset/conf/saplconfigset.zip differ diff --git a/solr/sapl_configset/conf/schema.xml b/solr/sapl_configset/conf/schema.xml deleted file mode 100644 index 7bf3fc428..000000000 --- a/solr/sapl_configset/conf/schema.xml +++ /dev/null @@ -1,166 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - id - - - text - - - -