Browse Source

Merge tag '3.1.159' into migracao

migracao
Marcio Mazza 6 years ago
parent
commit
0fbe113b1e
  1. 21
      docker-compose.yml
  2. 15
      release.sh
  3. 1
      requirements/requirements.txt
  4. 33
      sapl/audiencia/forms.py
  5. 59
      sapl/audiencia/tests/test_audiencia.py
  6. 86
      sapl/base/forms.py
  7. 42
      sapl/base/templatetags/common_tags.py
  8. 24
      sapl/base/tests/test_base.py
  9. 629
      sapl/base/tests/test_view_base.py
  10. 23
      sapl/base/urls.py
  11. 203
      sapl/base/views.py
  12. 46
      sapl/comissoes/forms.py
  13. 22
      sapl/comissoes/tests/test_comissoes.py
  14. 112
      sapl/compilacao/tests/test_compilacao.py
  15. 376
      sapl/materia/forms.py
  16. 21
      sapl/materia/migrations/0051_auto_20190703_1414.py
  17. 2
      sapl/materia/models.py
  18. 22
      sapl/materia/urls.py
  19. 333
      sapl/materia/views.py
  20. 38
      sapl/norma/forms.py
  21. 28
      sapl/norma/migrations/0025_auto_20190704_1403.py
  22. 18
      sapl/norma/models.py
  23. 51
      sapl/norma/views.py
  24. 5
      sapl/painel/views.py
  25. 6
      sapl/parlamentares/urls.py
  26. 13
      sapl/parlamentares/views.py
  27. 27
      sapl/protocoloadm/forms.py
  28. 25
      sapl/protocoloadm/migrations/0022_auto_20190715_1101.py
  29. 11
      sapl/protocoloadm/models.py
  30. 30
      sapl/protocoloadm/tests/test_protocoloadm.py
  31. 7
      sapl/protocoloadm/views.py
  32. 3
      sapl/relatorios/templates/pdf_sessao_plenaria_gerar.py
  33. 4
      sapl/relatorios/urls.py
  34. 79
      sapl/relatorios/views.py
  35. 13
      sapl/rules/apps.py
  36. 45
      sapl/sessao/forms.py
  37. 20
      sapl/sessao/migrations/0041_sessaoplenaria_tema_solene.py
  38. 16
      sapl/sessao/migrations/0042_merge_20190612_0925.py
  39. 2
      sapl/sessao/models.py
  40. 8
      sapl/sessao/urls.py
  41. 264
      sapl/sessao/views.py
  42. 4
      sapl/settings.py
  43. 41
      sapl/static/sapl/css/relatorio.css
  44. 2
      sapl/templates/base.html
  45. 3
      sapl/templates/base/RelatorioAtas_filter.html
  46. 2
      sapl/templates/base/RelatorioDataFimPrazoTramitacao_filter.html
  47. 75
      sapl/templates/base/RelatorioPresencaSessao_filter.html
  48. 35
      sapl/templates/base/anexadas_ciclicas.html
  49. 36
      sapl/templates/base/anexados_ciclicos.html
  50. 52
      sapl/templates/base/autores_duplicados.html
  51. 64
      sapl/templates/base/bancada_comissao_autor_externo.html
  52. 56
      sapl/templates/base/filiacoes_sem_data_filiacao.html
  53. 56
      sapl/templates/base/legislatura_infindavel.html
  54. 34
      sapl/templates/base/lista_inconsistencias.html
  55. 56
      sapl/templates/base/mandato_sem_data_inicio.html
  56. 60
      sapl/templates/base/materias_protocolo_inexistente.html
  57. 56
      sapl/templates/base/parlamentares_duplicados.html
  58. 60
      sapl/templates/base/parlamentares_filiacoes_intersecao.html
  59. 60
      sapl/templates/base/parlamentares_mandatos_intersecao.html
  60. 52
      sapl/templates/base/protocolos_com_materias.html
  61. 56
      sapl/templates/base/protocolos_duplicados.html
  62. 4
      sapl/templates/base/relatorios_list.html
  63. 2
      sapl/templates/comissoes/reuniao_detail.html
  64. 17
      sapl/templates/crud/detail.html
  65. 4
      sapl/templates/floppyforms/image_thumbnail.html
  66. 7
      sapl/templates/materia/despachoinicial_multicreate_form.html
  67. 10
      sapl/templates/materia/em_lote/acessorio.html
  68. 140
      sapl/templates/materia/em_lote/tramitacao.html
  69. 114
      sapl/templates/materia/materialegislativa_form.html
  70. 2
      sapl/templates/navbar.yaml
  71. 32
      sapl/templates/norma/normajuridica_detail.html
  72. 48
      sapl/templates/norma/normajuridica_form.html
  73. 49
      sapl/templates/painel/index.html
  74. 94
      sapl/templates/painel/voto_nominal.html
  75. 8
      sapl/templates/parlamentares/materias.html
  76. 1
      sapl/templates/protocoloadm/protocolo_list.html
  77. 2
      sapl/templates/protocoloadm/protocolo_mostrar.html
  78. 14
      sapl/templates/relatorios/header_ata.html
  79. 2
      sapl/templates/relatorios/relatorio_ata.html
  80. 7
      sapl/templates/relatorios/relatorio_doc_administrativos.html
  81. 183
      sapl/templates/relatorios/relatorio_sessao_plenaria.html
  82. 6
      sapl/templates/rest_framework_docs/base.html
  83. 10
      sapl/templates/sessao/blocos_ata/identificacao_basica.html
  84. 4
      sapl/templates/sessao/blocos_resumo/identificacao_basica.html
  85. 9
      sapl/templates/sessao/layouts.yaml
  86. 4
      sapl/templates/sessao/pauta_sessao_list.html
  87. 10
      sapl/templates/sessao/resumo.html
  88. 121
      sapl/templates/sessao/sessaoplenaria_form.html
  89. 34
      sapl/templates/sessao/subnav-solene.yaml
  90. 2
      setup.py
  91. 1
      solr/sapl_configset/conf/managed-schema
  92. BIN
      solr/sapl_configset/conf/saplconfigset.zip
  93. 166
      solr/sapl_configset/conf/schema.xml

21
docker-compose.yml

@ -11,8 +11,7 @@ sapldb:
ports: ports:
- "5432:5432" - "5432:5432"
sapl: sapl:
image: interlegis/sapl:3.1.158 image: interlegis/sapl:3.1.159
# build: .
restart: always restart: always
environment: environment:
ADMIN_PASSWORD: interlegis ADMIN_PASSWORD: interlegis
@ -24,27 +23,11 @@ sapl:
EMAIL_HOST_USER: usuariosmtp EMAIL_HOST_USER: usuariosmtp
EMAIL_SEND_USER: usuariosmtp EMAIL_SEND_USER: usuariosmtp
EMAIL_HOST_PASSWORD: senhasmtp EMAIL_HOST_PASSWORD: senhasmtp
# USE_SOLR: 'True'
# SOLR_COLLECTION: sapl
# SOLR_URL: http://saplsolr:8983
TZ: America/Sao_Paulo TZ: America/Sao_Paulo
volumes: volumes:
- sapl_data:/var/interlegis/sapl/data - sapl_data:/var/interlegis/sapl/data
- sapl_media:/var/interlegis/sapl/media - sapl_media:/var/interlegis/sapl/media
links: links:
- sapldb - sapldb
# - saplsolr
ports: ports:
- "80:80" - "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"

15
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 ## 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 commit -m "Release: $FINAL_VERSION"
git tag $FINAL_VERSION git tag $FINAL_VERSION
echo "sending to github..." echo "Para enviar pro github execute..."
git push origin echo "git push origin 3.1.x"
git push origin $FINAL_VERSION echo "git push origin "$FINAL_VERSION
echo "done." echo "done."
} }
case "$1" in case "$1" in
--latest) --latest)
git fetch
echo $LATEST_VERSION echo $LATEST_VERSION
exit 0 exit 0
;; ;;
--major) --major)
git fetch
set_major_version set_major_version
echo "generating major release: "$FINAL_VERSION echo "generating major release: "$FINAL_VERSION
# git tag $FINAL_VERSION # git tag $FINAL_VERSION
@ -93,6 +95,7 @@ case "$1" in
exit 0 exit 0
;; ;;
--rc) --rc)
git fetch
set_rc_version set_rc_version
echo "generating release candidate: "$FINAL_VERSION echo "generating release candidate: "$FINAL_VERSION
# git tag $FINAL_VERSION # git tag $FINAL_VERSION
@ -100,10 +103,6 @@ case "$1" in
commit_and_push commit_and_push
exit 0 exit 0
;; ;;
--undo)
git tag -d $LATEST_VERSION
exit 0
;;
--top) --top)
git tag | sort --version-sort | tail "-$2" git tag | sort --version-sort | tail "-$2"
exit 0 exit 0

1
requirements/requirements.txt

@ -15,6 +15,7 @@ django-extensions==2.1.4
django-image-cropping==1.2 django-image-cropping==1.2
django-webpack-loader==0.6.0 django-webpack-loader==0.6.0
drf-yasg==1.13.0 drf-yasg==1.13.0
ruamel.yaml>=0.15.34,<0.16.0
easy-thumbnails==2.5 easy-thumbnails==2.5
python-decouple==3.1 python-decouple==3.1
psycopg2-binary==2.7.6.1 psycopg2-binary==2.7.6.1

33
sapl/audiencia/forms.py

@ -1,6 +1,7 @@
import logging import logging
from django import forms from django import forms
from sapl.settings import MAX_DOC_UPLOAD_SIZE
from django.core.exceptions import ObjectDoesNotExist, ValidationError from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.db import transaction from django.db import transaction
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
@ -106,8 +107,6 @@ class AudienciaForm(FileFieldCheckMixin, forms.ModelForm):
else: else:
cleaned_data['numero'] = 1 cleaned_data['numero'] = 1
if self.cleaned_data['hora_inicio'] and self.cleaned_data['hora_fim']: if self.cleaned_data['hora_inicio'] and self.cleaned_data['hora_fim']:
if (self.cleaned_data['hora_fim'] < if (self.cleaned_data['hora_fim'] <
self.cleaned_data['hora_inicio']): 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.') self.logger.error('Hora de fim anterior à hora de início.')
raise ValidationError(msg) 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 return cleaned_data
@ -140,3 +155,17 @@ class AnexoAudienciaPublicaForm(forms.ModelForm):
row1, row2)) row1, row2))
super(AnexoAudienciaPublicaForm, self).__init__( super(AnexoAudienciaPublicaForm, self).__init__(
*args, **kwargs) *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

59
sapl/audiencia/tests/test_audiencia.py

@ -1,11 +1,62 @@
import pytest import pytest
from django.utils.translation import ugettext as _ import datetime
from model_mommy import mommy from model_mommy import mommy
from django.utils.translation import ugettext as _
from sapl.audiencia import forms 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 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) @pytest.mark.django_db(transaction=False)
def test_valida_campos_obrigatorios_audiencia_form(): def test_valida_campos_obrigatorios_audiencia_form():
form = forms.AudienciaForm(data={}) form = forms.AudienciaForm(data={})
@ -35,7 +86,5 @@ def test_audiencia_form_hora_invalida():
'data': '2016-10-01', 'data': '2016-10-01',
'hora_inicio': '10:00', 'hora_inicio': '10:00',
'hora_fim': '9:00', 'hora_fim': '9:00',
}) })
assert not form.is_valid() assert not form.is_valid()

86
sapl/base/forms.py

@ -2,7 +2,6 @@ import logging
import os import os
from crispy_forms.bootstrap import FieldWithButtons, InlineRadios, StrictButton 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 crispy_forms.layout import HTML, Button, Div, Field, Fieldset, Layout, Row
from django import forms from django import forms
from django.conf import settings 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.comissoes.models import Reuniao, Comissao
from sapl.crispy_layout_mixin import (SaplFormLayout, form_actions, to_column, from sapl.crispy_layout_mixin import (SaplFormLayout, form_actions, to_column,
to_row) to_row)
from sapl.crispy_layout_mixin import SaplFormHelper
from sapl.materia.models import ( from sapl.materia.models import (
MateriaLegislativa, UnidadeTramitacao, StatusTramitacao) MateriaLegislativa, UnidadeTramitacao, StatusTramitacao)
from sapl.norma.models import (NormaJuridica, NormaEstatisticas) 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, models_with_gr_for_model, qs_override_django_filter,
choice_anos_com_normas, choice_anos_com_materias, choice_anos_com_normas, choice_anos_com_materias,
FilterOverridesMetaMixin, FileFieldCheckMixin) FilterOverridesMetaMixin, FileFieldCheckMixin)
from .models import AppConfig, CasaLegislativa from .models import AppConfig, CasaLegislativa
@ -65,11 +66,6 @@ def get_roles():
class UsuarioCreateForm(ModelForm): class UsuarioCreateForm(ModelForm):
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
username = forms.CharField(
required=True,
label="Nome de usuário",
max_length=30
)
firstname = forms.CharField( firstname = forms.CharField(
required=True, required=True,
label="Nome", label="Nome",
@ -108,8 +104,11 @@ class UsuarioCreateForm(ModelForm):
class Meta: class Meta:
model = get_user_model() model = get_user_model()
fields = ['username', 'firstname', 'lastname', 'email', fields = [
'password1', 'password2', 'user_active', 'roles'] get_user_model().USERNAME_FIELD, 'firstname', 'lastname',
'password1', 'password2', 'user_active', 'roles'
] + (['email']
if get_user_model().USERNAME_FIELD != 'email' else [])
def clean(self): def clean(self):
super().clean() super().clean()
@ -151,9 +150,9 @@ class UsuarioCreateForm(ModelForm):
class UsuarioFilterSet(django_filters.FilterSet): class UsuarioFilterSet(django_filters.FilterSet):
username = django_filters.CharFilter( username = django_filters.CharFilter(
label=_('Nome de Usuário'), label=_('Nome de Usuário'),
lookup_expr='icontains') lookup_expr='icontains')
class Meta: class Meta:
@ -164,7 +163,7 @@ class UsuarioFilterSet(django_filters.FilterSet):
super(UsuarioFilterSet, self).__init__(*args, **kwargs) super(UsuarioFilterSet, self).__init__(*args, **kwargs)
row0 = to_row([('username', 12)]) row0 = to_row([('username', 12)])
self.form.helper = SaplFormHelper() self.form.helper = SaplFormHelper()
self.form.helper.form_method = 'GET' self.form.helper.form_method = 'GET'
self.form.helper.layout = Layout( self.form.helper.layout = Layout(
@ -190,24 +189,30 @@ class UsuarioEditForm(ModelForm):
class Meta: class Meta:
model = get_user_model() 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): def __init__(self, *args, **kwargs):
super(UsuarioEditForm, self).__init__(*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)]) ('user_active', 6)])
row2 = to_row( row3 = to_row(
[('password1', 6), [('password1', 6),
('password2', 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 = SaplFormHelper()
self.helper.layout = Layout( self.helper.layout = Layout(
row1, row1,
row2, row2,
row3,
'roles', 'roles',
form_actions(label='Salvar Alterações')) form_actions(label='Salvar Alterações'))
@ -867,22 +872,36 @@ class RelatorioPresencaSessaoFilterSet(django_filters.FilterSet):
class Meta(FilterOverridesMetaMixin): class Meta(FilterOverridesMetaMixin):
model = SessaoPlenaria model = SessaoPlenaria
fields = ['data_inicio'] fields = ['data_inicio',
'sessao_legislativa',
'tipo',
'legislatura']
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(RelatorioPresencaSessaoFilterSet, self).__init__( super(RelatorioPresencaSessaoFilterSet, self).__init__(
*args, **kwargs) *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.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)]) 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 = SaplFormHelper()
self.form.helper.form_method = 'GET' self.form.helper.form_method = 'GET'
self.form.helper.layout = Layout( self.form.helper.layout = Layout(
Fieldset(_('Presença dos parlamentares nas sessões plenárias'), Fieldset(_('Presença dos parlamentares nas sessões plenárias'),
row1, form_actions(label='Pesquisar')) row1, row2, row3, form_actions(label='Pesquisar'))
) )
@property @property
@ -900,7 +919,7 @@ class RelatorioHistoricoTramitacaoFilterSet(django_filters.FilterSet):
class Meta(FilterOverridesMetaMixin): class Meta(FilterOverridesMetaMixin):
model = MateriaLegislativa model = MateriaLegislativa
fields = ['tipo', 'tramitacao__status', 'tramitacao__data_tramitacao', 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): def __init__(self, *args, **kwargs):
super(RelatorioHistoricoTramitacaoFilterSet, self).__init__( super(RelatorioHistoricoTramitacaoFilterSet, self).__init__(
@ -908,8 +927,10 @@ class RelatorioHistoricoTramitacaoFilterSet(django_filters.FilterSet):
self.filters['tipo'].label = 'Tipo de Matéria' self.filters['tipo'].label = 'Tipo de Matéria'
self.filters['tramitacao__status'].label = _('Status') self.filters['tramitacao__status'].label = _('Status')
self.filters['tramitacao__unidade_tramitacao_local'].label = _('Unidade Local (Origem)') self.filters['tramitacao__unidade_tramitacao_local'].label = _(
self.filters['tramitacao__unidade_tramitacao_destino'].label = _('Unidade Destino') 'Unidade Local (Origem)')
self.filters['tramitacao__unidade_tramitacao_destino'].label = _(
'Unidade Destino')
row1 = to_row([('tramitacao__data_tramitacao', 12)]) row1 = to_row([('tramitacao__data_tramitacao', 12)])
row2 = to_row([('tramitacao__unidade_tramitacao_local', 6), row2 = to_row([('tramitacao__unidade_tramitacao_local', 6),
@ -927,7 +948,6 @@ class RelatorioHistoricoTramitacaoFilterSet(django_filters.FilterSet):
) )
class RelatorioDataFimPrazoTramitacaoFilterSet(django_filters.FilterSet): class RelatorioDataFimPrazoTramitacaoFilterSet(django_filters.FilterSet):
@property @property
@ -946,7 +966,8 @@ class RelatorioDataFimPrazoTramitacaoFilterSet(django_filters.FilterSet):
*args, **kwargs) *args, **kwargs)
self.filters['tipo'].label = 'Tipo de Matéria' 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__unidade_tramitacao_destino'].label = 'Unidade Destino'
self.filters['tramitacao__status'].label = 'Status de tramitação' 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 = SaplFormHelper()
self.form.helper.form_method = 'GET' self.form.helper.form_method = 'GET'
self.form.helper.layout = Layout( self.form.helper.layout = Layout(
Fieldset(_('Tramitações por fim de prazo'), Fieldset(_('Tramitações'),
row1, row2, row3, row1, row2, row3,
form_actions(label='Pesquisar')) form_actions(label='Pesquisar'))
) )
@ -1409,7 +1430,7 @@ class PartidoForm(FileFieldCheckMixin, ModelForm):
[('sigla', 2), [('sigla', 2),
('nome', 6), ('nome', 6),
('data_criacao', 2), ('data_criacao', 2),
('data_extincao', 2),]) ('data_extincao', 2), ])
row2 = to_row([('observacao', 12)]) row2 = to_row([('observacao', 12)])
row3 = to_row([('logo_partido', 12)]) row3 = to_row([('logo_partido', 12)])
@ -1424,10 +1445,11 @@ class PartidoForm(FileFieldCheckMixin, ModelForm):
if not self.is_valid(): if not self.is_valid():
return cleaned_data return cleaned_data
if cleaned_data['data_criacao'] and cleaned_data['data_extincao']: if cleaned_data['data_criacao'] and cleaned_data['data_extincao']:
if cleaned_data['data_criacao'] > 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 return cleaned_data
@ -1441,9 +1463,9 @@ class RelatorioHistoricoTramitacaoAdmFilterSet(django_filters.FilterSet):
class Meta(FilterOverridesMetaMixin): class Meta(FilterOverridesMetaMixin):
model = DocumentoAdministrativo model = DocumentoAdministrativo
fields = ['tipo', 'tramitacaoadministrativo__status', fields = ['tipo', 'tramitacaoadministrativo__status',
'tramitacaoadministrativo__data_tramitacao', 'tramitacaoadministrativo__data_tramitacao',
'tramitacaoadministrativo__unidade_tramitacao_local', 'tramitacaoadministrativo__unidade_tramitacao_local',
'tramitacaoadministrativo__unidade_tramitacao_destino'] 'tramitacaoadministrativo__unidade_tramitacao_destino']
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -1452,8 +1474,10 @@ class RelatorioHistoricoTramitacaoAdmFilterSet(django_filters.FilterSet):
self.filters['tipo'].label = 'Tipo de Documento' self.filters['tipo'].label = 'Tipo de Documento'
self.filters['tramitacaoadministrativo__status'].label = _('Status') self.filters['tramitacaoadministrativo__status'].label = _('Status')
self.filters['tramitacaoadministrativo__unidade_tramitacao_local'].label = _('Unidade Local (Origem)') self.filters['tramitacaoadministrativo__unidade_tramitacao_local'].label = _(
self.filters['tramitacaoadministrativo__unidade_tramitacao_destino'].label = _('Unidade Destino') 'Unidade Local (Origem)')
self.filters['tramitacaoadministrativo__unidade_tramitacao_destino'].label = _(
'Unidade Destino')
row1 = to_row([('tramitacaoadministrativo__data_tramitacao', 12)]) row1 = to_row([('tramitacaoadministrativo__data_tramitacao', 12)])
row2 = to_row([('tramitacaoadministrativo__unidade_tramitacao_local', 6), row2 = to_row([('tramitacaoadministrativo__unidade_tramitacao_local', 6),

42
sapl/base/templatetags/common_tags.py

@ -223,8 +223,34 @@ def audio_url(value):
@register.filter @register.filter
def video_url(value): def is_video_url(value):
return True if url(value) and value.endswith("mp4") else False 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 @register.filter
@ -272,12 +298,20 @@ def urldetail(obj):
@register.filter @register.filter
def filiacao_data_filter(parlamentar, data_inicio): 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 @register.filter
def filiacao_intervalo_filter(parlamentar, date_range): 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 @register.simple_tag

24
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'

629
sapl/base/tests/test_view_base.py

@ -1,6 +1,635 @@
import pytest import pytest
from model_mommy import mommy
import datetime
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _ 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) @pytest.mark.django_db(transaction=False)

23
sapl/base/urls.py

@ -29,17 +29,13 @@ from .views import (AlterarSenha, AppConfigCrud, CasaLegislativaCrud,
EstatisticasAcessoNormas, EstatisticasAcessoNormas,
RelatoriosListView, RelatoriosListView,
ListarInconsistenciasView, ListarProtocolosDuplicadosView, ListarInconsistenciasView, ListarProtocolosDuplicadosView,
ListarProtocolosComMateriasView, ListarProtocolosComMateriasView, ListarMatProtocoloInexistenteView,
ListarMatProtocoloInexistenteView,
ListarParlamentaresDuplicadosView, ListarParlamentaresDuplicadosView,
ListarFiliacoesSemDataFiliacaoView, ListarFiliacoesSemDataFiliacaoView, ListarMandatoSemDataInicioView,
ListarMandatoSemDataInicioView, ListarParlMandatosIntersecaoView, ListarParlFiliacoesIntersecaoView,
ListarParlMandatosIntersecaoView, ListarAutoresDuplicadosView, ListarBancadaComissaoAutorExternoView,
ListarParlFiliacoesIntersecaoView, ListarLegislaturaInfindavelView, ListarAnexadasCiclicasView,
ListarAutoresDuplicadosView, ListarAnexadosCiclicosView, pesquisa_textual,
ListarBancadaComissaoAutorExternoView,
ListarLegislaturaInfindavelView,
pesquisa_textual,
RelatorioHistoricoTramitacaoAdmView) RelatorioHistoricoTramitacaoAdmView)
@ -180,6 +176,13 @@ urlpatterns = [
url(r'^sistema/inconsistencias/legislatura_infindavel$', url(r'^sistema/inconsistencias/legislatura_infindavel$',
ListarLegislaturaInfindavelView.as_view(), ListarLegislaturaInfindavelView.as_view(),
name='lista_legislatura_infindavel'), 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', url(r'^sistema/pesquisa-textual',
pesquisa_textual, pesquisa_textual,
name='pesquisa_textual'), name='pesquisa_textual'),

203
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.base.models import Autor, TipoAutor
from sapl.comissoes.models import Reuniao, Comissao from sapl.comissoes.models import Reuniao, Comissao
from sapl.crud.base import CrudAux, make_pagination 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) TipoMateriaLegislativa, StatusTramitacao, UnidadeTramitacao)
from sapl.norma.models import (NormaJuridica, NormaEstatisticas) 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, from sapl.protocoloadm.models import (Protocolo, TipoDocumentoAdministrativo,
StatusTramitacaoAdministrativo, StatusTramitacaoAdministrativo,
DocumentoAdministrativo) DocumentoAdministrativo, Anexado)
from sapl.sessao.models import (PresencaOrdemDia, SessaoPlenaria, from sapl.sessao.models import (PresencaOrdemDia, SessaoPlenaria,
SessaoPlenariaPresenca, Bancada) SessaoPlenariaPresenca, Bancada, TipoSessaoPlenaria)
from sapl.utils import (parlamentares_ativos, gerar_hash_arquivo, SEPARADOR_HASH_PROPOSICAO, from sapl.utils import (parlamentares_ativos, gerar_hash_arquivo, SEPARADOR_HASH_PROPOSICAO,
show_results_filter_set, mail_service_configured, show_results_filter_set, mail_service_configured,
intervalos_tem_intersecao, remover_acentos) intervalos_tem_intersecao, remover_acentos)
@ -342,14 +342,61 @@ class RelatorioPresencaSessaoView(FilterView):
# Verifica se os campos foram preenchidos # Verifica se os campos foram preenchidos
if not self.filterset.form.is_valid(): if not self.filterset.form.is_valid():
return context 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: sessao_legislativa_pk = self.request.GET.get('sessao_legislativa')
where = context['object_list'].query.where if sessao_legislativa_pk:
_range = where.children[0].rhs 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' tipo_sessao_plenaria_pk = self.request.GET.get('tipo')
param0 = {'%s' % sufixo: _range} 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 com Mandato no intervalo de tempo (Ativos)
parlamentares_qs = parlamentares_ativos( parlamentares_qs = parlamentares_ativos(
_range[0], _range[1]).order_by('nome_parlamentar') _range[0], _range[1]).order_by('nome_parlamentar')
@ -357,16 +404,12 @@ class RelatorioPresencaSessaoView(FilterView):
'id', flat=True) 'id', flat=True)
# Presenças de cada Parlamentar em Sessões # Presenças de cada Parlamentar em Sessões
presenca_sessao = SessaoPlenariaPresenca.objects.filter( presenca_sessao = SessaoPlenariaPresenca.objects.filter(**param0).values_list(
parlamentar_id__in=parlamentares_id,
sessao_plenaria__data_inicio__range=_range).values_list(
'parlamentar_id').annotate( 'parlamentar_id').annotate(
sessao_count=Count('id')) sessao_count=Count('id'))
# Presenças de cada Ordem do Dia # Presenças de cada Ordem do Dia
presenca_ordem = PresencaOrdemDia.objects.filter( presenca_ordem = PresencaOrdemDia.objects.filter(**param0).values_list(
parlamentar_id__in=parlamentares_id,
sessao_plenaria__data_inicio__range=_range).values_list(
'parlamentar_id').annotate( 'parlamentar_id').annotate(
sessao_count=Count('id')) sessao_count=Count('id'))
@ -433,6 +476,14 @@ class RelatorioPresencaSessaoView(FilterView):
context['periodo'] = ( context['periodo'] = (
self.request.GET['data_inicio_0'] + self.request.GET['data_inicio_0'] +
' - ' + self.request.GET['data_inicio_1']) ' - ' + 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() qr = self.request.GET.copy()
context['filter_url'] = ('&' + qr.urlencode()) if len(qr) > 0 else '' context['filter_url'] = ('&' + qr.urlencode()) if len(qr) > 0 else ''
@ -498,7 +549,7 @@ class RelatorioDataFimPrazoTramitacaoView(FilterView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(RelatorioDataFimPrazoTramitacaoView, context = super(RelatorioDataFimPrazoTramitacaoView,
self).get_context_data(**kwargs) 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(): if not self.filterset.form.is_valid():
return context return context
qr = self.request.GET.copy() qr = self.request.GET.copy()
@ -1016,10 +1067,128 @@ class ListarInconsistenciasView(PermissionRequiredMixin, ListView):
len(legislatura_infindavel()) 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 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(): def legislatura_infindavel():
return Legislatura.objects.filter(data_fim__isnull=True).order_by('-numero') return Legislatura.objects.filter(data_fim__isnull=True).order_by('-numero')

46
sapl/comissoes/forms.py

@ -1,6 +1,7 @@
import logging import logging
from django import forms from django import forms
from sapl.settings import MAX_DOC_UPLOAD_SIZE
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db import transaction 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 ({})." 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'])) .format(self.cleaned_data['hora_fim'], self.cleaned_data['hora_inicio']))
raise ValidationError(msg) 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 return self.cleaned_data
@ -412,6 +430,20 @@ class DocumentoAcessorioCreateForm(FileFieldCheckMixin, forms.ModelForm):
def create_documentoacessorio(self): def create_documentoacessorio(self):
reuniao = Reuniao.objects.get(id=self.initial['parent_pk']) 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): class DocumentoAcessorioEditForm(FileFieldCheckMixin, forms.ModelForm):
@ -424,3 +456,17 @@ class DocumentoAcessorioEditForm(FileFieldCheckMixin, forms.ModelForm):
def __init__(self, user=None, **kwargs): def __init__(self, user=None, **kwargs):
super(DocumentoAcessorioEditForm, self).__init__(**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

22
sapl/comissoes/tests/test_comissoes.py

@ -3,7 +3,8 @@ from django.core.urlresolvers import reverse
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from model_mommy import mommy from model_mommy import mommy
from sapl.comissoes.models import Comissao, 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.parlamentares.models import Filiacao, Parlamentar, Partido
from sapl.comissoes import forms from sapl.comissoes import forms
@ -45,6 +46,20 @@ def make_filiacao():
return Filiacao.objects.first() 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) @pytest.mark.django_db(transaction=False)
def test_incluir_parlamentar_errors(admin_client): def test_incluir_parlamentar_errors(admin_client):
comissao = make_comissao() comissao = make_comissao()
@ -106,7 +121,7 @@ def test_periodo_invalidas():
form = forms.PeriodoForm(data={'data_inicio': '10/11/2017', form = forms.PeriodoForm(data={'data_inicio': '10/11/2017',
'data_fim': '09/11/2017' 'data_fim': '09/11/2017'
}) })
assert not form.is_valid() assert not form.is_valid()
assert form.errors['__all__'] == [_('A Data Final não pode ser menor que ' assert form.errors['__all__'] == [_('A Data Final não pode ser menor que '
'a Data Inicial')] 'a Data Inicial')]
@ -123,7 +138,7 @@ def test_valida_campos_obrigatorios_periodo_form():
assert errors['data_inicio'] == [_('Este campo é obrigatório.')] assert errors['data_inicio'] == [_('Este campo é obrigatório.')]
assert len(errors) == 1 assert len(errors) == 1
@pytest.mark.django_db(transaction=False) @pytest.mark.django_db(transaction=False)
def test_valida_campos_obrigatorios_reuniao_form(): 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 errors['hora_inicio'] == [_('Este campo é obrigatório.')]
assert len(errors) == 6 assert len(errors) == 6

112
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

376
sapl/materia/forms.py

@ -3,7 +3,7 @@ import logging
import os import os
from crispy_forms.bootstrap import Alert, InlineRadios 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) Layout, Row)
from django import forms from django import forms
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
@ -156,25 +156,6 @@ class MateriaSimplificadaForm(FileFieldCheckMixin, ModelForm):
) )
super(MateriaSimplificadaForm, self).__init__(*args, **kwargs) 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): class MateriaLegislativaForm(FileFieldCheckMixin, ModelForm):
@ -256,13 +237,6 @@ class MateriaLegislativaForm(FileFieldCheckMixin, ModelForm):
raise ValidationError( raise ValidationError(
_('Tipo do Protocolo deve ser o mesmo do Tipo Matéria')) _('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'] ano_origem_externa = cleaned_data['ano_origem_externa']
data_origem_externa = cleaned_data['data_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 " "pode ser diferente do ano na data de "
"origem externa")) "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 return cleaned_data
def save(self, commit=False): def save(self, commit=False):
@ -285,7 +265,7 @@ class MateriaLegislativaForm(FileFieldCheckMixin, ModelForm):
materia = super(MateriaLegislativaForm, self).save(commit) materia = super(MateriaLegislativaForm, self).save(commit)
materia.save() materia.save()
if self.cleaned_data['autor']: if self.cleaned_data['autor']:
autoria = Autoria() autoria = Autoria()
autoria.primeiro_autor = primeiro_autor autoria.primeiro_autor = primeiro_autor
@ -364,6 +344,20 @@ class DocumentoAcessorioForm(FileFieldCheckMixin, ModelForm):
class Meta: class Meta:
model = DocumentoAcessorio model = DocumentoAcessorio
fields = ['tipo', 'nome', 'data', 'autor', 'ementa', 'arquivo'] 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): class RelatoriaForm(ModelForm):
@ -561,7 +555,8 @@ class TramitacaoForm(ModelForm):
materia.em_tramitacao = False if tramitacao.status.indicador == "F" else True materia.em_tramitacao = False if tramitacao.status.indicador == "F" else True
materia.save() materia.save()
tramitar_anexadas = sapl.base.models.AppConfig.attr('tramitacao_materia') tramitar_anexadas = sapl.base.models.AppConfig.attr(
'tramitacao_materia')
if tramitar_anexadas: if tramitar_anexadas:
lista_tramitacao = [] lista_tramitacao = []
anexadas_list = lista_anexados(materia) anexadas_list = lista_anexados(materia)
@ -571,35 +566,38 @@ class TramitacaoForm(ModelForm):
ma.em_tramitacao = False if tramitacao.status.indicador == "F" else True ma.em_tramitacao = False if tramitacao.status.indicador == "F" else True
ma.save() ma.save()
lista_tramitacao.append(Tramitacao( lista_tramitacao.append(Tramitacao(
status=tramitacao.status, status=tramitacao.status,
materia=ma, materia=ma,
data_tramitacao=tramitacao.data_tramitacao, data_tramitacao=tramitacao.data_tramitacao,
unidade_tramitacao_local=tramitacao.unidade_tramitacao_local, unidade_tramitacao_local=tramitacao.unidade_tramitacao_local,
data_encaminhamento=tramitacao.data_encaminhamento, data_encaminhamento=tramitacao.data_encaminhamento,
unidade_tramitacao_destino=tramitacao.unidade_tramitacao_destino, unidade_tramitacao_destino=tramitacao.unidade_tramitacao_destino,
urgente=tramitacao.urgente, urgente=tramitacao.urgente,
turno=tramitacao.turno, turno=tramitacao.turno,
texto=tramitacao.texto, texto=tramitacao.texto,
data_fim_prazo=tramitacao.data_fim_prazo, data_fim_prazo=tramitacao.data_fim_prazo,
user=tramitacao.user, user=tramitacao.user,
ip=tramitacao.ip ip=tramitacao.ip
)) ))
Tramitacao.objects.bulk_create(lista_tramitacao) Tramitacao.objects.bulk_create(lista_tramitacao)
return 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 # exceto os campos id, documento_id e timestamp
def compara_tramitacoes_mat(tramitacao1, tramitacao2): def compara_tramitacoes_mat(tramitacao1, tramitacao2):
if not tramitacao1 or not tramitacao2: if not tramitacao1 or not tramitacao2:
return False return False
lst_items = ['id', 'materia_id', 'timestamp'] 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] != '_'))] values = [(k, v) for k, v in tramitacao1.__dict__.items()
other_values = [(k,v) for k,v in tramitacao2.__dict__.items() if (k not in lst_items and k[0] != '_')] 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 return values == other_values
class TramitacaoUpdateForm(TramitacaoForm): class TramitacaoUpdateForm(TramitacaoForm):
unidade_tramitacao_local = forms.ModelChoiceField( unidade_tramitacao_local = forms.ModelChoiceField(
queryset=UnidadeTramitacao.objects.all(), 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.em_tramitacao = False if nova_tram_principal.status.indicador == "F" else True
materia.save() materia.save()
tramitar_anexadas = sapl.base.models.AppConfig.attr('tramitacao_materia') tramitar_anexadas = sapl.base.models.AppConfig.attr(
'tramitacao_materia')
if tramitar_anexadas: if tramitar_anexadas:
anexadas_list = lista_anexados(materia) anexadas_list = lista_anexados(materia)
for ma in anexadas_list: for ma in anexadas_list:
@ -696,6 +695,7 @@ class TramitacaoUpdateForm(TramitacaoForm):
ma.save() ma.save()
return nova_tram_principal return nova_tram_principal
class LegislacaoCitadaForm(ModelForm): class LegislacaoCitadaForm(ModelForm):
tipo = forms.ModelChoiceField( tipo = forms.ModelChoiceField(
@ -869,8 +869,10 @@ class AnexadaForm(ModelForm):
data_desanexacao = cleaned_data['data_desanexacao'] if cleaned_data['data_desanexacao'] else data_anexacao data_desanexacao = cleaned_data['data_desanexacao'] if cleaned_data['data_desanexacao'] else data_anexacao
if data_anexacao > data_desanexacao: if data_anexacao > data_desanexacao:
self.logger.error("Data de anexação posterior à data de desanexação.") self.logger.error(
raise ValidationError(_("Data de anexação posterior à data de desanexação.")) "Data de anexação posterior à data de desanexação.")
raise ValidationError(
_("Data de anexação posterior à data de desanexação."))
try: try:
self.logger.info("Tentando obter objeto MateriaLegislativa (numero={}, ano={}, tipo={})." self.logger.info("Tentando obter objeto MateriaLegislativa (numero={}, ano={}, tipo={})."
@ -899,9 +901,10 @@ class AnexadaForm(ModelForm):
if is_anexada: if is_anexada:
self.logger.error("Matéria já se encontra anexada.") self.logger.error("Matéria já se encontra anexada.")
raise ValidationError(_('Matéria já se encontra anexada')) raise ValidationError(_('Matéria já se encontra anexada'))
ciclico = False 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: while anexadas_anexada and not ciclico:
anexadas = [] anexadas = []
@ -910,15 +913,17 @@ class AnexadaForm(ModelForm):
if materia_principal == anexa.materia_anexada: if materia_principal == anexa.materia_anexada:
ciclico = True ciclico = True
else: else:
for a in Anexada.objects.filter(materia_principal=anexa.materia_anexada): for a in Anexada.objects.filter(materia_principal=anexa.materia_anexada):
anexadas.append(a) anexadas.append(a)
anexadas_anexada = anexadas anexadas_anexada = anexadas
if ciclico: if ciclico:
self.logger.error("A matéria não pode ser anexada por uma de suas anexadas.") self.logger.error(
raise ValidationError(_("A matéria não pode ser anexada por uma de suas anexadas.")) "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 cleaned_data['materia_anexada'] = materia_anexada
@ -1079,9 +1084,6 @@ class MateriaLegislativaFilterSet(django_filters.FilterSet):
HTML(autor_modal), HTML(autor_modal),
row4, row6, row7, row9, row4, row6, row7, row9,
form_actions(label=_('Pesquisar'))) form_actions(label=_('Pesquisar')))
) )
@property @property
@ -1140,9 +1142,50 @@ def filtra_tramitacao_destino_and_status(status, destino):
'materia_id', flat=True) '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): class DespachoInicialForm(ModelForm):
comissao = forms.ModelChoiceField( comissao = forms.ModelChoiceField(
queryset=Comissao.objects.filter(ativa=True)) queryset=Comissao.objects.filter(ativa=True), label=_('Comissão'))
class Meta: class Meta:
model = DespachoInicial model = DespachoInicial
@ -1184,7 +1227,8 @@ class AutoriaForm(ModelForm):
if 'initial' in kwargs and 'materia' in kwargs['initial']: if 'initial' in kwargs and 'materia' in kwargs['initial']:
materia = kwargs['initial']['materia'] 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), row1 = to_row([('tipo_autor', 4),
('autor', 4), ('autor', 4),
@ -1254,8 +1298,9 @@ class AutoriaMultiCreateForm(Form):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
if 'initial' in kwargs and 'autores' in kwargs['initial']: 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)]) row1 = to_row([('tipo_autor', 10), ('primeiro_autor', 2)])
row2 = to_row([('autor', 12), ]) row2 = to_row([('autor', 12), ])
@ -1456,7 +1501,8 @@ class TipoProposicaoForm(ModelForm):
self.fields['content_type'].choices = [ self.fields['content_type'].choices = [
(ct.pk, ct) for k, ct in content_types.items()] (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: if self.instance.pk:
self.fields[ self.fields[
@ -1536,6 +1582,197 @@ class TipoProposicaoSelect(Select):
return option 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 = '''
<br\><br\><br\>
<fieldset>
<legend style="font-size: 24px;">Selecione as matérias para tramitação:</legend>
<table class="table table-striped table-hover">
<div class="controls">
<div class="checkbox">
<label for="id_check_all">
<input type="checkbox" id="id_check_all" onchange="checkAll(this)" /> Marcar/Desmarcar Todos
</label>
</div>
</div>
<thead>
<tr><th>Matéria</th></tr>
</thead>
<tbody>
{% for materia in object_list %}
<tr>
<td>
<input type="checkbox" name="materias" value="{{materia.id}}" {% if check %} checked {% endif %}/>
<a href="{% url 'sapl.materia:materialegislativa_detail' materia.id %}">
{{materia.tipo.sigla}} {{materia.tipo.descricao}} {{materia.numero}}/{{materia.ano}}
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</fieldset>
'''
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): class ProposicaoForm(FileFieldCheckMixin, forms.ModelForm):
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -1584,7 +1821,6 @@ class ProposicaoForm(FileFieldCheckMixin, forms.ModelForm):
'observacao', 'observacao',
'texto_original', 'texto_original',
'materia_de_vinculo', 'materia_de_vinculo',
'tipo_materia', 'tipo_materia',
'numero_materia', 'numero_materia',
'ano_materia', 'ano_materia',
@ -1672,12 +1908,11 @@ class ProposicaoForm(FileFieldCheckMixin, forms.ModelForm):
def clean_texto_original(self): def clean_texto_original(self):
texto_original = self.cleaned_data.get('texto_original', False) texto_original = self.cleaned_data.get('texto_original', False)
if texto_original and texto_original.size > MAX_DOC_UPLOAD_SIZE: if texto_original and texto_original.size > MAX_DOC_UPLOAD_SIZE:
max_size = str(MAX_DOC_UPLOAD_SIZE / (1024 * 1024)) raise ValidationError("O arquivo Texto Original deve ser menor que {0:.1f} mb, o tamanho atual desse arquivo é {1:.1f} mb" \
self.logger.error( .format((MAX_DOC_UPLOAD_SIZE/1024)/1024, (texto_original.size/1024)/1024))
"- Arquivo muito grande. ( > {0}MB )".format(max_size))
raise ValidationError(
"Arquivo muito grande. ( > {0}MB )".format(max_size))
return texto_original return texto_original
def gerar_hash(self, inst, receber_recibo): 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." self.logger.info("MateriaLegislativa vinculada (tipo_id={}, ano={}, numero={}) com sucesso."
.format(tm, am, nm)) .format(tm, am, nm))
cd['materia_de_vinculo'] = materia_de_vinculo cd['materia_de_vinculo'] = materia_de_vinculo
return cd return cd
def save(self, commit=True): def save(self, commit=True):
@ -1766,7 +2002,7 @@ class ProposicaoForm(FileFieldCheckMixin, forms.ModelForm):
ano=timezone.now().year).aggregate(Max('numero_proposicao')) ano=timezone.now().year).aggregate(Max('numero_proposicao'))
numero__max = numero__max['numero_proposicao__max'] numero__max = numero__max['numero_proposicao__max']
inst.numero_proposicao = ( 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) self.gerar_hash(inst, receber_recibo)
@ -1864,7 +2100,7 @@ class ConfirmarProposicaoForm(ProposicaoForm):
attrs={'readonly': 'readonly'})) attrs={'readonly': 'readonly'}))
regime_tramitacao = forms.ModelChoiceField(label="Regime de tramitação", 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( gerar_protocolo = forms.ChoiceField(
required=False, required=False,

21
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'),
),
]

2
sapl/materia/models.py

@ -485,7 +485,7 @@ class AssuntoMateria(models.Model):
@reversion.register() @reversion.register()
class DespachoInicial(models.Model): class DespachoInicial(models.Model):
materia = models.ForeignKey(MateriaLegislativa, on_delete=models.CASCADE) 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: class Meta:
verbose_name = _('Despacho Inicial') verbose_name = _('Despacho Inicial')

22
sapl/materia/urls.py

@ -26,9 +26,11 @@ from sapl.materia.views import (AcompanhamentoConfirmarView,
TramitacaoEmLoteView, UnidadeTramitacaoCrud, TramitacaoEmLoteView, UnidadeTramitacaoCrud,
proposicao_texto, recuperar_materia, proposicao_texto, recuperar_materia,
ExcluirTramitacaoEmLoteView, RetornarProposicao, ExcluirTramitacaoEmLoteView, RetornarProposicao,
MateriaPesquisaSimplesView) MateriaPesquisaSimplesView,
DespachoInicialMultiCreateView)
from sapl.norma.views import NormaPesquisaSimplesView from sapl.norma.views import NormaPesquisaSimplesView
from sapl.protocoloadm.views import (FichaPesquisaAdmView, FichaSelecionaAdmView) from sapl.protocoloadm.views import (
FichaPesquisaAdmView, FichaSelecionaAdmView)
from .apps import AppConfig from .apps import AppConfig
@ -55,13 +57,20 @@ urlpatterns_impressos = [
name='impressos_materia_pesquisa'), name='impressos_materia_pesquisa'),
url(r'^materia/impressos/ficha-pesquisa-adm/$', url(r'^materia/impressos/ficha-pesquisa-adm/$',
FichaPesquisaAdmView.as_view(), FichaPesquisaAdmView.as_view(),
name= 'impressos_ficha_pesquisa_adm'), name='impressos_ficha_pesquisa_adm'),
url(r'^materia/impressos/ficha-seleciona-adm/$', url(r'^materia/impressos/ficha-seleciona-adm/$',
FichaSelecionaAdmView.as_view(), FichaSelecionaAdmView.as_view(),
name= 'impressos_ficha_seleciona_adm'), name='impressos_ficha_seleciona_adm'),
] ]
urlpatterns_materia = [ 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<pk>\d+)/despachoinicial/create',
DespachoInicialMultiCreateView.as_view(),
name='despacho-inicial-multi'),
url(r'^materia/', include(MateriaLegislativaCrud.get_urls() + url(r'^materia/', include(MateriaLegislativaCrud.get_urls() +
AnexadaCrud.get_urls() + AnexadaCrud.get_urls() +
AutoriaCrud.get_urls() + AutoriaCrud.get_urls() +
@ -76,7 +85,8 @@ urlpatterns_materia = [
url(r'^materia/(?P<pk>[0-9]+)/create_simplificado$', url(r'^materia/(?P<pk>[0-9]+)/create_simplificado$',
CriarProtocoloMateriaView.as_view(), CriarProtocoloMateriaView.as_view(),
name='materia_create_simplificado'), 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<pk>[0-9]+)/ta$', url(r'^materia/(?P<pk>[0-9]+)/ta$',
MateriaTaView.as_view(), name='materia_ta'), MateriaTaView.as_view(), name='materia_ta'),
@ -96,6 +106,7 @@ urlpatterns_materia = [
AutoriaMultiCreateView.as_view(), AutoriaMultiCreateView.as_view(),
name='autoria_multicreate'), name='autoria_multicreate'),
url(r'^materia/acessorio-em-lote', DocumentoAcessorioEmLoteView.as_view(), url(r'^materia/acessorio-em-lote', DocumentoAcessorioEmLoteView.as_view(),
name='acessorio_em_lote'), name='acessorio_em_lote'),
url(r'^materia/(?P<pk>\d+)/anexada-em-lote', MateriaAnexadaEmLoteView.as_view(), url(r'^materia/(?P<pk>\d+)/anexada-em-lote', MateriaAnexadaEmLoteView.as_view(),
@ -136,6 +147,7 @@ urlpatterns_proposicao = [
name='proposicao_texto'), name='proposicao_texto'),
url(r'^proposicao/(?P<pk>\d+)/retornar', RetornarProposicao.as_view(), url(r'^proposicao/(?P<pk>\d+)/retornar', RetornarProposicao.as_view(),
name='retornar-proposicao'), name='retornar-proposicao'),
] ]
urlpatterns_sistema = [ urlpatterns_sistema = [

333
sapl/materia/views.py

@ -1,13 +1,11 @@
from datetime import datetime
import itertools
import logging import logging
import os import os
import shutil
import tempfile
import weasyprint
import itertools
from datetime import datetime
from random import choice from random import choice
import shutil
from string import ascii_letters, digits from string import ascii_letters, digits
import tempfile
from crispy_forms.layout import HTML from crispy_forms.layout import HTML
from django.conf import settings 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.views.generic.edit import FormView
from django_filters.views import FilterView from django_filters.views import FilterView
import weasyprint import weasyprint
import weasyprint
import sapl import sapl
from sapl.base.email_utils import do_envia_email_confirmacao from sapl.base.email_utils import do_envia_email_confirmacao
@ -47,11 +46,12 @@ from sapl.materia.forms import (AnexadaForm, AutoriaForm,
ConfirmarProposicaoForm, ConfirmarProposicaoForm,
DevolverProposicaoForm, LegislacaoCitadaForm, DevolverProposicaoForm, LegislacaoCitadaForm,
OrgaoForm, ProposicaoForm, TipoProposicaoForm, OrgaoForm, ProposicaoForm, TipoProposicaoForm,
TramitacaoForm, TramitacaoUpdateForm, MateriaPesquisaSimplesForm) TramitacaoForm, TramitacaoUpdateForm, MateriaPesquisaSimplesForm,
DespachoInicialCreateForm)
from sapl.norma.models import LegislacaoCitada from sapl.norma.models import LegislacaoCitada
from sapl.parlamentares.models import Legislatura from sapl.parlamentares.models import Legislatura
from sapl.protocoloadm.models import Protocolo from sapl.protocoloadm.models import Protocolo
from sapl.settings import MEDIA_ROOT from sapl.settings import MEDIA_ROOT, MAX_DOC_UPLOAD_SIZE
from sapl.utils import (YES_NO_CHOICES, autor_label, autor_modal, SEPARADOR_HASH_PROPOSICAO, from sapl.utils import (YES_NO_CHOICES, autor_label, autor_modal, SEPARADOR_HASH_PROPOSICAO,
gerar_hash_arquivo, get_base_url, get_client_ip, gerar_hash_arquivo, get_base_url, get_client_ip,
get_mime_type_from_file_extension, montar_row_autor, get_mime_type_from_file_extension, montar_row_autor,
@ -69,7 +69,8 @@ from .forms import (AcessorioEmLoteFilterSet, AcompanhamentoMateriaForm,
filtra_tramitacao_destino, filtra_tramitacao_destino,
filtra_tramitacao_destino_and_status, filtra_tramitacao_destino_and_status,
filtra_tramitacao_status, filtra_tramitacao_status,
ExcluirTramitacaoEmLote, compara_tramitacoes_mat) ExcluirTramitacaoEmLote, compara_tramitacoes_mat,
TramitacaoEmLoteForm)
from .models import (AcompanhamentoMateria, Anexada, AssuntoMateria, Autoria, from .models import (AcompanhamentoMateria, Anexada, AssuntoMateria, Autoria,
DespachoInicial, DocumentoAcessorio, MateriaAssunto, DespachoInicial, DocumentoAcessorio, MateriaAssunto,
MateriaLegislativa, Numeracao, Orgao, Origem, Proposicao, MateriaLegislativa, Numeracao, Orgao, Origem, Proposicao,
@ -1140,9 +1141,9 @@ class RelatoriaCrud(MasterDetailCrud):
parlamentar = relatoria.parlamentar parlamentar = relatoria.parlamentar
comissao = relatoria.comissao comissao = relatoria.comissao
composicoes = [p.composicao for p in composicoes = [p.composicao for p in
Participacao.objects.filter( Participacao.objects.filter(
parlamentar=parlamentar, parlamentar=parlamentar,
composicao__comissao=comissao)] composicao__comissao=comissao)]
data_designacao = relatoria.data_designacao_relator data_designacao = relatoria.data_designacao_relator
composicao = '' composicao = ''
for c in composicoes: for c in composicoes:
@ -1203,7 +1204,8 @@ class TramitacaoCrud(MasterDetailCrud):
'-timestamp', '-timestamp',
'-id').first() '-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:
if ultima_tramitacao.unidade_tramitacao_destino: if ultima_tramitacao.unidade_tramitacao_destino:
context['form'].fields[ context['form'].fields[
@ -1257,7 +1259,8 @@ class TramitacaoCrud(MasterDetailCrud):
layout_key = 'TramitacaoUpdate' layout_key = 'TramitacaoUpdate'
def form_valid(self, form): 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() self.object = form.save()
dict_objeto_novo = self.object.__dict__ dict_objeto_novo = self.object.__dict__
@ -1269,7 +1272,8 @@ class TramitacaoCrud(MasterDetailCrud):
'data_encaminhamento', 'data_fim_prazo', 'urgente', 'turno' '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: for atributo in atributos:
if dict_objeto_antigo[atributo] != dict_objeto_novo[atributo]: if dict_objeto_antigo[atributo] != dict_objeto_novo[atributo]:
self.object.user = user self.object.user = user
@ -1312,7 +1316,7 @@ class TramitacaoCrud(MasterDetailCrud):
materia = tramitacao.materia materia = tramitacao.materia
url = reverse('sapl.materia:tramitacao_list', url = reverse('sapl.materia:tramitacao_list',
kwargs={'pk': materia.id}) kwargs={'pk': materia.id})
ultima_tramitacao = materia.tramitacao_set.order_by( ultima_tramitacao = materia.tramitacao_set.order_by(
'-data_tramitacao', '-data_tramitacao',
'-timestamp', '-timestamp',
@ -1331,7 +1335,8 @@ class TramitacaoCrud(MasterDetailCrud):
if materia.tramitacao_set.count() == 0: if materia.tramitacao_set.count() == 0:
materia.em_tramitacao = False materia.em_tramitacao = False
materia.save() materia.save()
tramitar_anexadas = sapl.base.models.AppConfig.attr('tramitacao_materia') tramitar_anexadas = sapl.base.models.AppConfig.attr(
'tramitacao_materia')
if tramitar_anexadas: if tramitar_anexadas:
mat_anexadas = lista_anexados(materia) mat_anexadas = lista_anexados(materia)
for ma in mat_anexadas: for ma in mat_anexadas:
@ -1353,7 +1358,7 @@ class TramitacaoCrud(MasterDetailCrud):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context['user'] = self.request.user context['user'] = self.request.user
return context return context
def montar_helper_documento_acessorio(self): def montar_helper_documento_acessorio(self):
autor_row = montar_row_autor('autor') autor_row = montar_row_autor('autor')
@ -1484,20 +1489,58 @@ class AutoriaMultiCreateView(PermissionRequiredForAppCrudMixin, FormView):
autores_selecionados = form.cleaned_data['autor'] autores_selecionados = form.cleaned_data['autor']
primeiro_autor = form.cleaned_data['primeiro_autor'] primeiro_autor = form.cleaned_data['primeiro_autor']
for autor in autores_selecionados: 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) 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 <small>(%s)</small>' % (
_('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): class DespachoInicialCrud(MasterDetailCrud):
model = DespachoInicial model = DespachoInicial
parent_field = 'materia' parent_field = 'materia'
help_topic = 'despacho_autoria' help_topic = 'despacho_autoria'
public = [RP_LIST, RP_DETAIL] public = [RP_LIST, RP_DETAIL]
class CreateView(MasterDetailCrud.CreateView):
form_class = DespachoInicialForm
class UpdateView(MasterDetailCrud.UpdateView): class UpdateView(MasterDetailCrud.UpdateView):
form_class = DespachoInicialForm form_class = DespachoInicialForm
@ -1620,7 +1663,7 @@ class MateriaLegislativaCrud(Crud):
form_class = MateriaLegislativaForm form_class = MateriaLegislativaForm
def get_initial(self): def get_initial(self):
initial = super(CreateView, self).get_initial() initial = super().get_initial()
initial['user'] = self.request.user initial['user'] = self.request.user
initial['ip'] = get_client_ip(self.request) initial['ip'] = get_client_ip(self.request)
@ -1639,7 +1682,7 @@ class MateriaLegislativaCrud(Crud):
dict_objeto_antigo = MateriaLegislativa.objects.get( dict_objeto_antigo = MateriaLegislativa.objects.get(
pk=self.kwargs['pk'] pk=self.kwargs['pk']
).__dict__ ).__dict__
self.object = form.save() self.object = form.save()
dict_objeto_novo = self.object.__dict__ dict_objeto_novo = self.object.__dict__
@ -1647,7 +1690,7 @@ class MateriaLegislativaCrud(Crud):
'tipo_id', 'ano', 'numero', 'data_apresentacao', 'numero_protocolo', 'tipo_id', 'ano', 'numero', 'data_apresentacao', 'numero_protocolo',
'tipo_apresentacao', 'texto_original', 'apelido', 'dias_prazo', 'polemica', 'tipo_apresentacao', 'texto_original', 'apelido', 'dias_prazo', 'polemica',
'objeto', 'regime_tramitacao_id', 'em_tramitacao', 'data_fim_prazo', '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', 'numero_origem_externa', 'ano_origem_externa', 'local_origem_externa_id',
'data_origem_externa', 'ementa', 'indexacao', 'observacao' 'data_origem_externa', 'ementa', 'indexacao', 'observacao'
] ]
@ -1664,7 +1707,7 @@ class MateriaLegislativaCrud(Crud):
anexadas = lista_anexados(materia) anexadas = lista_anexados(materia)
for anexada in anexadas: 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() anexada.save()
return super().form_valid(form) return super().form_valid(form)
@ -1686,7 +1729,8 @@ class MateriaLegislativaCrud(Crud):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context['user'] = self.request.user 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 return context
class ListView(Crud.ListView, RedirectView): class ListView(Crud.ListView, RedirectView):
@ -2073,20 +2117,29 @@ class DocumentoAcessorioEmLoteView(PermissionRequiredMixin, FilterView):
msg = _('Autor tem que ter menos do que 50 caracteres.') msg = _('Autor tem que ter menos do que 50 caracteres.')
messages.add_message(request, messages.ERROR, msg) messages.add_message(request, messages.ERROR, msg)
return self.get(request, self.kwargs) return self.get(request, self.kwargs)
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) tmp_name = os.path.join(MEDIA_ROOT, request.FILES['arquivo'].name)
with open(tmp_name, 'wb') as destination: with open(tmp_name, 'wb') as destination:
for chunk in request.FILES['arquivo'].chunks(): for chunk in request.FILES['arquivo'].chunks():
destination.write(chunk) destination.write(chunk)
try: try:
doc_data = tz.localize(datetime.strptime( doc_data = tz.localize(datetime.strptime(
request.POST['data'], "%d/%m/%Y")) request.POST['data'], "%d/%m/%Y"))
except Exception as e: except Exception as e:
msg = _('Formato da data incorreto. O formato deve ser da forma dd/mm/aaaa.') msg = _(
messages.add_message(request, messages.ERROR, msg) 'Formato da data incorreto. O formato deve ser da forma dd/mm/aaaa.')
self.logger.error("User={}. {}. Data inserida: {}".format(username, str(msg), request.POST['data'])) messages.add_message(request, messages.ERROR, msg)
os.remove(tmp_name) self.logger.error("User={}. {}. Data inserida: {}".format(
return self.get(request, self.kwargs) username, str(msg), request.POST['data']))
os.remove(tmp_name)
return self.get(request, self.kwargs)
for materia_id in marcadas: for materia_id in marcadas:
doc = DocumentoAcessorio() doc = DocumentoAcessorio()
@ -2100,25 +2153,27 @@ class DocumentoAcessorioEmLoteView(PermissionRequiredMixin, FilterView):
try: try:
doc.clean_fields() doc.clean_fields()
except ValidationError as e: except ValidationError as e:
for m in [ '%s: %s' % (DocumentoAcessorio()._meta.get_field(k).verbose_name, '</br>'.join(v)) for m in ['%s: %s' % (DocumentoAcessorio()._meta.get_field(k).verbose_name, '</br>'.join(v))
for k,v in e.message_dict.items() ]: for k, v in e.message_dict.items()]:
# Insere as mensagens de erro no formato: # Insere as mensagens de erro no formato:
# 'verbose_name do nome do campo': 'mensagem de erro' # 'verbose_name do nome do campo': 'mensagem de erro'
messages.add_message(request, messages.ERROR, m) 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) os.remove(tmp_name)
return self.get(request, self.kwargs) return self.get(request, self.kwargs)
doc.save() doc.save()
diretorio = os.path.join(MEDIA_ROOT, diretorio = os.path.join(MEDIA_ROOT,
'sapl/public/documentoacessorio', 'sapl/public/documentoacessorio',
str(doc_data.year), str(doc_data.year),
str(doc.id)) str(doc.id))
if not os.path.exists(diretorio): if not os.path.exists(diretorio):
os.makedirs(diretorio) os.makedirs(diretorio)
file_path = os.path.join(diretorio, file_path = os.path.join(diretorio,
request.FILES['arquivo'].name) request.FILES['arquivo'].name)
shutil.copy2(tmp_name, file_path) 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() doc.save()
os.remove(tmp_name) os.remove(tmp_name)
@ -2144,17 +2199,17 @@ class MateriaAnexadaEmLoteView(PermissionRequiredMixin, FilterView):
# Verifica se os campos foram preenchidos # Verifica se os campos foram preenchidos
if not self.request.GET.get('tipo', " "): 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) 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', " "): 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) messages.add_message(self.request, messages.ERROR, msg)
return context return context
if not self.request.GET.get('data_apresentacao_0', " ") or not self.request.GET.get('data_apresentacao_1', " "): 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) messages.add_message(self.request, messages.ERROR, msg)
return context return context
@ -2163,8 +2218,10 @@ class MateriaAnexadaEmLoteView(PermissionRequiredMixin, FilterView):
'numero', '-ano') 'numero', '-ano')
principal = MateriaLegislativa.objects.get(pk=self.kwargs['pk']) principal = MateriaLegislativa.objects.get(pk=self.kwargs['pk'])
not_list = [self.kwargs['pk']] + \ not_list = [self.kwargs['pk']] + \
[m for m in principal.materia_principal_set.all().values_list('materia_anexada_id', flat=True)] [m for m in principal.materia_principal_set.all(
context['object_list'] = context['object_list'].exclude(pk__in=not_list) ).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['temp_object_list'] = context['object_list']
context['object_list'] = [] context['object_list'] = []
@ -2172,12 +2229,12 @@ class MateriaAnexadaEmLoteView(PermissionRequiredMixin, FilterView):
materia_anexada = obj materia_anexada = obj
ciclico = False ciclico = False
anexadas_anexada = Anexada.objects.filter( anexadas_anexada = Anexada.objects.filter(
materia_principal = materia_anexada materia_principal=materia_anexada
) )
while anexadas_anexada and not ciclico: while anexadas_anexada and not ciclico:
anexadas = [] anexadas = []
for anexa in anexadas_anexada: for anexa in anexadas_anexada:
if principal == anexa.materia_anexada: if principal == anexa.materia_anexada:
@ -2185,7 +2242,7 @@ class MateriaAnexadaEmLoteView(PermissionRequiredMixin, FilterView):
else: else:
for a in Anexada.objects.filter(materia_principal=anexa.materia_anexada): for a in Anexada.objects.filter(materia_principal=anexa.materia_anexada):
anexadas.append(a) anexadas.append(a)
anexadas_anexada = anexadas anexadas_anexada = anexadas
if not ciclico: if not ciclico:
@ -2216,7 +2273,7 @@ class MateriaAnexadaEmLoteView(PermissionRequiredMixin, FilterView):
if len(marcadas) == 0: if len(marcadas) == 0:
msg = _('Nenhuma máteria foi selecionada.') msg = _('Nenhuma máteria foi selecionada.')
messages.add_message(request, messages.ERROR, msg) messages.add_message(request, messages.ERROR, msg)
if data_anexacao > v_data_desanexacao: if data_anexacao > v_data_desanexacao:
msg = _('Data de anexação posterior à data de desanexação.') msg = _('Data de anexação posterior à data de desanexação.')
messages.add_message(request, messages.ERROR, msg) messages.add_message(request, messages.ERROR, msg)
@ -2241,7 +2298,8 @@ class MateriaAnexadaEmLoteView(PermissionRequiredMixin, FilterView):
msg = _('Matéria(s) anexada(s).') msg = _('Matéria(s) anexada(s).')
messages.add_message(request, messages.SUCCESS, msg) 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) return HttpResponseRedirect(success_url)
@ -2254,39 +2312,34 @@ class PrimeiraTramitacaoEmLoteView(PermissionRequiredMixin, FilterView):
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(PrimeiraTramitacaoEmLoteView, context = super(PrimeiraTramitacaoEmLoteView,
self).get_context_data(**kwargs) self).get_context_data(**kwargs)
context['subnav_template_name'] = 'materia/em_lote/subnav_em_lote.yaml' context['subnav_template_name'] = 'materia/em_lote/subnav_em_lote.yaml'
context['primeira_tramitacao'] = self.primeira_tramitacao
# Verifica se os campos foram preenchidos # Verifica se os campos foram preenchidos
if not self.filterset.form.is_valid(): if not self.filterset.form.is_valid():
return context return context
context['title'] = _('Primeira Tramitação em Lote') context['object_list'] = context['object_list'].order_by(
'ano', 'numero')
qr = self.request.GET.copy() 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 self.primeira_tramitacao:
if (type(self.__dict__['filterset']).__name__ == context['title'] = _('Primeira Tramitação em Lote')
'PrimeiraTramitacaoEmLoteFilterSet'): # Pega somente documentos que não possuem tramitação
context['object_list'] = context['object_list'].filter( context['object_list'] = [obj for obj in context['object_list']
tramitacao__isnull=True) if obj.tramitacao_set.all().count() == 0]
else: else:
context['title'] = _('Tramitação em Lote') context['title'] = _('Tramitação em Lote')
context['unidade_local'] = [UnidadeTramitacao.objects.get( context['form'].fields['unidade_tramitacao_local'].initial = UnidadeTramitacao.objects.get(
id=qr['tramitacao__unidade_tramitacao_destino'])] id=qr['tramitacao__unidade_tramitacao_destino'])
context['object_list'] = context['object_list'].order_by(
'ano', 'numero')
context['filter_url'] = ('&' + qr.urlencode()) if len(qr) > 0 else '' context['filter_url'] = ('&' + qr.urlencode()) if len(qr) > 0 else ''
@ -2295,127 +2348,43 @@ class PrimeiraTramitacaoEmLoteView(PermissionRequiredMixin, FilterView):
return context return context
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
user = request.user
ip = get_client_ip(request)
marcadas = request.POST.getlist('materia_id') materias_ids = request.POST.getlist('materias')
if not materias_ids:
tz = timezone.get_current_timezone() msg = _("Escolha alguma matéria para ser tramitada.")
username = request.user.username
if len(marcadas) == 0:
msg = _('Nenhuma máteria foi selecionada.')
messages.add_message(request, messages.ERROR, msg) messages.add_message(request, messages.ERROR, msg)
return self.get(request, self.kwargs) return self.get(request, self.kwargs)
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']: form = TramitacaoEmLoteForm(request.POST,
data_encaminhamento = None initial= {'materias': materias_ids,
else: 'user': user, 'ip':ip})
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)
if request.POST['data_fim_prazo'] == '': if form.is_valid():
data_fim_prazo = None form.save()
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)
# issue https://github.com/interlegis/sapl/issues/1123 msg = _('Tramitação completa.')
# TODO: usar Form self.logger.info('user=' + user.username + '. Tramitação completa.')
urgente = request.POST['urgente'] == 'True' messages.add_message(request, messages.SUCCESS, msg)
flag_error = False return self.get_success_url()
materias_principais = [m for m in MateriaLegislativa.objects.filter(id__in=marcadas)] return self.form_invalid(form)
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)
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 def form_invalid(self, form, *args, **kwargs):
ip = get_client_ip(request) for key, erros in form.errors.items():
t = Tramitacao( if not key=='__all__':
materia=materia, [messages.add_message(self.request, messages.ERROR, form.fields[key].label + ": " + e) for e in erros]
data_tramitacao=data_tramitacao, else:
data_encaminhamento=data_encaminhamento, [messages.add_message(self.request, messages.ERROR, e) for e in erros]
data_fim_prazo=data_fim_prazo, return self.get(self.request, kwargs, {'form':form})
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)
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): class TramitacaoEmLoteView(PrimeiraTramitacaoEmLoteView):
filterset_class = TramitacaoEmLoteFilterSet filterset_class = TramitacaoEmLoteFilterSet
@ -2428,7 +2397,7 @@ class TramitacaoEmLoteView(PrimeiraTramitacaoEmLoteView):
qr = self.request.GET.copy() qr = self.request.GET.copy()
context['primeira_tramitacao'] = False context['primeira_tramitacao'] = self.primeira_tramitacao
if ('tramitacao__status' in qr and if ('tramitacao__status' in qr and
'tramitacao__unidade_tramitacao_destino' in qr and 'tramitacao__unidade_tramitacao_destino' in qr and

38
sapl/norma/forms.py

@ -132,8 +132,12 @@ class NormaJuridicaForm(FileFieldCheckMixin, ModelForm):
'indexacao', 'indexacao',
'observacao', 'observacao',
'texto_integral', 'texto_integral',
'assuntos'] 'assuntos',
widgets = {'assuntos': widgets.CheckboxSelectMultiple} 'user',
'ip']
widgets = {'assuntos': widgets.CheckboxSelectMultiple,
'user': forms.HiddenInput(),
'ip': forms.HiddenInput()}
def clean(self): def clean(self):
@ -189,27 +193,17 @@ class NormaJuridicaForm(FileFieldCheckMixin, ModelForm):
else: else:
cleaned_data['materia'] = None 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 return cleaned_data
def clean_texto_integral(self): def clean_texto_integral(self):
super(NormaJuridicaForm, self).clean() super(NormaJuridicaForm, self).clean()
texto_integral = self.cleaned_data.get('texto_integral', False) texto_integral = self.cleaned_data.get('texto_integral', False)
if texto_integral and texto_integral.size > MAX_DOC_UPLOAD_SIZE: if texto_integral and texto_integral.size > MAX_DOC_UPLOAD_SIZE:
max_size = str(MAX_DOC_UPLOAD_SIZE / (1024 * 1024)) raise ValidationError("O arquivo Texto Integral deve ser menor que {0:.1f} mb, o tamanho atual desse arquivo é {1:.1f} mb" \
tam_fornecido = str(texto_integral.size / (1024 * 1024)) .format((MAX_DOC_UPLOAD_SIZE/1024)/1024, (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))
return texto_integral return texto_integral
def save(self, commit=False): def save(self, commit=False):
@ -283,16 +277,16 @@ class AnexoNormaJuridicaForm(FileFieldCheckMixin, ModelForm):
def clean(self): def clean(self):
cleaned_data = super(AnexoNormaJuridicaForm, self).clean() cleaned_data = super(AnexoNormaJuridicaForm, self).clean()
if not self.is_valid(): if not self.is_valid():
return cleaned_data return cleaned_data
anexo_arquivo = self.cleaned_data.get('anexo_arquivo', False) anexo_arquivo = self.cleaned_data.get('anexo_arquivo', False)
if anexo_arquivo and anexo_arquivo.size > MAX_DOC_UPLOAD_SIZE: if anexo_arquivo and anexo_arquivo.size > MAX_DOC_UPLOAD_SIZE:
max_size = str(MAX_DOC_UPLOAD_SIZE / (1024 * 1024)) raise ValidationError("O Arquivo Anexo deve ser menor que {0:.1f} mb, o tamanho atual desse arquivo é {1:.1f} mb" \
tam_fornecido = str(anexo_arquivo.size / (1024 * 1024)) .format((MAX_DOC_UPLOAD_SIZE/1024)/1024, (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))
return cleaned_data return cleaned_data
def save(self, commit=False): def save(self, commit=False):

28
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'),
),
]

18
sapl/norma/models.py

@ -10,7 +10,9 @@ from sapl.base.models import Autor
from sapl.compilacao.models import TextoArticulado from sapl.compilacao.models import TextoArticulado
from sapl.materia.models import MateriaLegislativa from sapl.materia.models import MateriaLegislativa
from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES, 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() @reversion.register()
@ -138,6 +140,20 @@ class NormaJuridica(models.Model):
through_fields=('norma', 'autor'), through_fields=('norma', 'autor'),
symmetrical=False) 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: class Meta:
verbose_name = _('Norma Jurídica') verbose_name = _('Norma Jurídica')
verbose_name_plural = _('Normas Jurídicas') verbose_name_plural = _('Normas Jurídicas')

51
sapl/norma/views.py

@ -20,7 +20,7 @@ from sapl.base.models import AppConfig
from sapl.compilacao.views import IntegracaoTaView from sapl.compilacao.views import IntegracaoTaView
from sapl.crud.base import (RP_DETAIL, RP_LIST, Crud, CrudAux, from sapl.crud.base import (RP_DETAIL, RP_LIST, Crud, CrudAux,
MasterDetailCrud, make_pagination) 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, from .forms import (AnexoNormaJuridicaForm, NormaFilterSet, NormaJuridicaForm,
NormaPesquisaSimplesForm, NormaRelacionadaForm, AutoriaNormaForm) NormaPesquisaSimplesForm, NormaRelacionadaForm, AutoriaNormaForm)
@ -217,20 +217,24 @@ class NormaCrud(Crud):
return self.search_url return self.search_url
def get_initial(self): 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: try:
self.logger.debug( self.logger.debug(
'user=' + username + '. Tentando obter objeto de modelo da esfera da federação.') 'user=' + username + '. Tentando obter objeto de modelo da esfera da federação.')
esfera = sapl.base.models.AppConfig.objects.last( esfera = sapl.base.models.AppConfig.objects.last(
).esfera_federacao ).esfera_federacao
self.initial['esfera_federacao'] = esfera initial['esfera_federacao'] = esfera
except: except:
self.logger.error( self.logger.error(
'user=' + username + '. Erro ao obter objeto de modelo da esfera da federação.') 'user=' + username + '. Erro ao obter objeto de modelo da esfera da federação.')
pass pass
self.initial['complemento'] = False initial['complemento'] = False
return self.initial return initial
layout_key = 'NormaJuridicaCreate' layout_key = 'NormaJuridicaCreate'
@ -249,7 +253,7 @@ class NormaCrud(Crud):
layout_key = 'NormaJuridicaCreate' layout_key = 'NormaJuridicaCreate'
def get_initial(self): def get_initial(self):
initial = super(UpdateView, self).get_initial() initial = super().get_initial()
norma = NormaJuridica.objects.get(id=self.kwargs['pk']) norma = NormaJuridica.objects.get(id=self.kwargs['pk'])
if norma.materia: if norma.materia:
initial['tipo_materia'] = norma.materia.tipo initial['tipo_materia'] = norma.materia.tipo
@ -258,6 +262,41 @@ class NormaCrud(Crud):
initial['esfera_federacao'] = norma.esfera_federacao initial['esfera_federacao'] = norma.esfera_federacao
return initial 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): def recuperar_norma(request):
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

5
sapl/painel/views.py

@ -523,11 +523,14 @@ def get_dados_painel(request, pk):
if casa and app_config and (bool(casa.logotipo)): if casa and app_config and (bool(casa.logotipo)):
brasao = casa.logotipo.url \ brasao = casa.logotipo.url \
if app_config.mostrar_brasao_painel else None if app_config.mostrar_brasao_painel else None
response = { response = {
'sessao_plenaria': str(sessao), 'sessao_plenaria': str(sessao),
'sessao_plenaria_data': sessao.data_inicio.strftime('%d/%m/%Y'), 'sessao_plenaria_data': sessao.data_inicio.strftime('%d/%m/%Y'),
'sessao_plenaria_hora_inicio': sessao.hora_inicio, '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_aparte': get_cronometro_status(request, 'aparte'),
'cronometro_discurso': get_cronometro_status(request, 'discurso'), 'cronometro_discurso': get_cronometro_status(request, 'discurso'),
'cronometro_ordem': get_cronometro_status(request, 'ordem'), 'cronometro_ordem': get_cronometro_status(request, 'ordem'),

6
sapl/parlamentares/urls.py

@ -19,7 +19,8 @@ from sapl.parlamentares.views import (CargoMesaCrud, ColigacaoCrud,
parlamentares_frente_selected, parlamentares_frente_selected,
remove_parlamentar_composicao, remove_parlamentar_composicao,
parlamentares_filiados, BlocoCrud, parlamentares_filiados, BlocoCrud,
PesquisarParlamentarView, VincularParlamentarView) PesquisarParlamentarView, VincularParlamentarView,
get_sessoes_legislatura)
from .apps import AppConfig from .apps import AppConfig
@ -90,5 +91,8 @@ urlpatterns = [
url(r'^mesa-diretora/remove-parlamentar-composicao/$', url(r'^mesa-diretora/remove-parlamentar-composicao/$',
remove_parlamentar_composicao, name='remove_parlamentar_composicao'), remove_parlamentar_composicao, name='remove_parlamentar_composicao'),
url(r'^parlamentar/get-sessoes-legislatura/$',
get_sessoes_legislatura, name='get_sessoes_legislatura'),
] ]

13
sapl/parlamentares/views.py

@ -1176,4 +1176,15 @@ class BlocoCrud(CrudAux):
form_class = BlocoForm form_class = BlocoForm
def get_success_url(self): def get_success_url(self):
return reverse('sapl.parlamentares:bloco_list') 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)

27
sapl/protocoloadm/forms.py

@ -5,6 +5,7 @@ from crispy_forms.bootstrap import InlineRadios, Alert, FormActions
from sapl.crispy_layout_mixin import SaplFormHelper from sapl.crispy_layout_mixin import SaplFormHelper
from crispy_forms.layout import HTML, Button, Column, Fieldset, Layout, Div, Submit from crispy_forms.layout import HTML, Button, Column, Fieldset, Layout, Div, Submit
from django import forms from django import forms
from sapl.settings import MAX_DOC_UPLOAD_SIZE
from django.core.exceptions import (MultipleObjectsReturned, from django.core.exceptions import (MultipleObjectsReturned,
ObjectDoesNotExist, ValidationError) ObjectDoesNotExist, ValidationError)
from django.db import models, transaction from django.db import models, transaction
@ -657,6 +658,20 @@ class DocumentoAcessorioAdministrativoForm(FileFieldCheckMixin, ModelForm):
'data': forms.DateInput(format='%d/%m/%Y') '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): class TramitacaoAdmForm(ModelForm):
@ -1142,6 +1157,12 @@ class DocumentoAdministrativoForm(FileFieldCheckMixin, ModelForm):
' documento vinculado' ' documento vinculado'
% (numero_protocolo, ano_protocolo))) % (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 return self.cleaned_data
def save(self, commit=True): def save(self, commit=True):
@ -1587,12 +1608,6 @@ class TramitacaoEmLoteAdmForm(ModelForm):
'maior que a data de tramitação!') 'maior que a data de tramitação!')
raise ValidationError(msg) 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 return cleaned_data
@transaction.atomic @transaction.atomic

25
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'),
),
]

11
sapl/protocoloadm/models.py

@ -57,7 +57,6 @@ class Protocolo(models.Model):
null=False, null=False,
choices=RANGE_ANOS, choices=RANGE_ANOS,
verbose_name=_('Ano do Protocolo')) verbose_name=_('Ano do Protocolo'))
data = models.DateField(null=True, blank=True, data = models.DateField(null=True, blank=True,
verbose_name=_('Data do Protocolo'), verbose_name=_('Data do Protocolo'),
help_text=_('Informado manualmente')) help_text=_('Informado manualmente'))
@ -66,12 +65,12 @@ class Protocolo(models.Model):
help_text=_('Informado manualmente')) help_text=_('Informado manualmente'))
timestamp_data_hora_manual = models.DateTimeField(default=timezone.now) timestamp_data_hora_manual = models.DateTimeField(default=timezone.now)
user_data_hora_manual = models.CharField( user_data_hora_manual = models.CharField(
max_length=20, blank=True, max_length=256, blank=True,
verbose_name=_('IP'), verbose_name=_('IP'),
help_text=_('Usuário que está realizando Protocolo e informando ' help_text=_('Usuário que está realizando Protocolo e informando '
'data e hora manualmente.')) 'data e hora manualmente.'))
ip_data_hora_manual = models.CharField( ip_data_hora_manual = models.CharField(
max_length=15, blank=True, max_length=256, blank=True,
verbose_name=_('IP'), verbose_name=_('IP'),
help_text=_('Endereço IP da estação de trabalho ' help_text=_('Endereço IP da estação de trabalho '
'do usuário que está realizando Protocolo e informando ' 'do usuário que está realizando Protocolo e informando '
@ -346,12 +345,12 @@ class TramitacaoAdministrativo(models.Model):
class Anexado(models.Model): class Anexado(models.Model):
documento_principal = models.ForeignKey( documento_principal = models.ForeignKey(
DocumentoAdministrativo, related_name='documento_principal_set', DocumentoAdministrativo, related_name='documento_principal_set',
on_delete = models.CASCADE, on_delete=models.CASCADE,
verbose_name=_('Documento Principal') verbose_name=_('Documento Principal')
) )
documento_anexado = models.ForeignKey( documento_anexado = models.ForeignKey(
DocumentoAdministrativo, related_name='documento_anexado_set', DocumentoAdministrativo, related_name='documento_anexado_set',
on_delete = models.CASCADE, on_delete=models.CASCADE,
verbose_name=_('Documento Anexado') verbose_name=_('Documento Anexado')
) )
data_anexacao = models.DateField(verbose_name=_('Data Anexação')) 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_tipo': self.documento_anexado.tipo,
'documento_anexado_numero': self.documento_anexado.numero, 'documento_anexado_numero': self.documento_anexado.numero,
'documento_anexado_ano': self.documento_anexado.ano 'documento_anexado_ano': self.documento_anexado.ano
} }
@reversion.register() @reversion.register()

30
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!"] ["A data fim de prazo deve ser maior que a data de tramitação!"]
assert not form.is_valid() 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 = TramitacaoEmLoteAdmForm(initial={'documentos': documentos}, data={})
form.data = {'data_tramitacao': '2019-05-14', form.data = {'data_tramitacao': '2019-05-14',
'data_encaminhamento' : '2019-05-15', '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 'Status: Este campo é obrigatório.' in msgs
assert 'Unidade Destino: 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 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, response = admin_client.post(url_lote,
{'documentos': documentos, {'documentos': documentos,

7
sapl/protocoloadm/views.py

@ -11,6 +11,7 @@ from django.contrib.auth.mixins import PermissionRequiredMixin
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.db import transaction
from django.db.models import Max, Q from django.db.models import Max, Q
from django.http import Http404, HttpResponse, JsonResponse from django.http import Http404, HttpResponse, JsonResponse
from django.http.response import HttpResponseRedirect from django.http.response import HttpResponseRedirect
@ -539,6 +540,7 @@ class ProtocoloDocumentoView(PermissionRequiredMixin,
initial['hora'] = timezone.localtime(timezone.now()) initial['hora'] = timezone.localtime(timezone.now())
return initial return initial
@transaction.atomic
def form_valid(self, form): def form_valid(self, form):
protocolo = form.save(commit=False) protocolo = form.save(commit=False)
username = self.request.user.username username = self.request.user.username
@ -726,6 +728,7 @@ class ProtocoloMateriaView(PermissionRequiredMixin, CreateView):
initial['hora'] = timezone.localtime(timezone.now()) initial['hora'] = timezone.localtime(timezone.now())
return initial return initial
@transaction.atomic
def form_valid(self, form): def form_valid(self, form):
protocolo = form.save(commit=False) protocolo = form.save(commit=False)
username = self.request.user.username username = self.request.user.username
@ -983,7 +986,7 @@ class DocumentoAnexadoEmLoteView(PermissionRequiredMixin, FilterView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super( context = super(
DocumentoAnexadoEmLoteView,self DocumentoAnexadoEmLoteView, self
).get_context_data(**kwargs) ).get_context_data(**kwargs)
context['root_pk'] = self.kwargs['pk'] context['root_pk'] = self.kwargs['pk']
@ -1545,7 +1548,7 @@ class TramitacaoEmLoteAdmView(PrimeiraTramitacaoEmLoteAdmView):
qr = self.request.GET.copy() qr = self.request.GET.copy()
context['primeira_tramitacao'] = False context['primeira_tramitacao'] = self.primeira_tramitacao
if ('tramitacao__status' in qr and if ('tramitacao__status' in qr and
'tramitacao__unidade_tramitacao_destino' in qr and 'tramitacao__unidade_tramitacao_destino' in qr and

3
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: if dat_fim_sessao or hr_fim_sessao:
tmp += '\t\t<para style="P2" spaceAfter="5"><b>Encerramento: </b> ' + \ tmp += '\t\t<para style="P2" spaceAfter="5"><b>Encerramento: </b> ' + \
dat_fim_sessao + ' <b>- </b> ' + hr_fim_sessao + '</para>\n' dat_fim_sessao + ' <b>- </b> ' + hr_fim_sessao + '</para>\n'
if inf_basicas_dic.get('tema_solene'):
tmp += '\t\t<para style="P2" spaceAfter="5"><b>Tema da Sessão Solene: </b> ' + \
inf_basicas_dic['tema_solene'] + '</para>\n'
return tmp return tmp

4
sapl/relatorios/urls.py

@ -6,7 +6,7 @@ from .views import (relatorio_capa_processo,
relatorio_etiqueta_protocolo, relatorio_materia, relatorio_etiqueta_protocolo, relatorio_materia,
relatorio_ordem_dia, relatorio_pauta_sessao, relatorio_ordem_dia, relatorio_pauta_sessao,
relatorio_protocolo, relatorio_sessao_plenaria, relatorio_protocolo, relatorio_sessao_plenaria,
resumo_ata_pdf) resumo_ata_pdf, relatorio_sessao_plenaria_pdf)
app_name = AppConfig.name app_name = AppConfig.name
@ -31,4 +31,6 @@ urlpatterns = [
relatorio_pauta_sessao, name='relatorio_pauta_sessao'), relatorio_pauta_sessao, name='relatorio_pauta_sessao'),
url(r'^relatorios/(?P<pk>\d+)/resumo_ata$', url(r'^relatorios/(?P<pk>\d+)/resumo_ata$',
resumo_ata_pdf, name='resumo_ata_pdf'), resumo_ata_pdf, name='resumo_ata_pdf'),
url(r'^relatorios/(?P<pk>\d+)/sessao-plenaria-pdf$',
relatorio_sessao_plenaria_pdf, name='relatorio_sessao_plenaria_pdf'),
] ]

79
sapl/relatorios/views.py

@ -519,6 +519,9 @@ def get_sessao_plenaria(sessao, casa):
inf_basicas_dic["dat_fim_sessao"] = '' inf_basicas_dic["dat_fim_sessao"] = ''
inf_basicas_dic["hr_fim_sessao"] = sessao.hora_fim inf_basicas_dic["hr_fim_sessao"] = sessao.hora_fim
inf_basicas_dic["nom_camara"] = casa.nome inf_basicas_dic["nom_camara"] = casa.nome
if sessao.tipo.nome == 'Solene':
inf_basicas_dic["tema_solene"] = sessao.tema_solene
# Conteudo multimidia # Conteudo multimidia
cont_mult_dic = {} cont_mult_dic = {}
@ -1350,3 +1353,79 @@ def relatorio_doc_administrativos(request, context):
return response 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

13
sapl/rules/apps.py

@ -84,18 +84,7 @@ def create_proxy_permissions(
ctypes.add(ctype) ctypes.add(ctype)
# FIXME: Retirar try except quando sapl passar a usar django 1.11 _all_perms_of_klass = _get_all_permissions(klass._meta)
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)
for perm in _all_perms_of_klass: for perm in _all_perms_of_klass:
searched_perms.append((ctype, perm)) searched_perms.append((ctype, perm))

45
sapl/sessao/forms.py

@ -11,6 +11,7 @@ from django.forms.widgets import CheckboxSelectMultiple
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
import django_filters import django_filters
from sapl.settings import MAX_DOC_UPLOAD_SIZE
from sapl.base.models import Autor, TipoAutor from sapl.base.models import Autor, TipoAutor
from sapl.crispy_layout_mixin import SaplFormHelper from sapl.crispy_layout_mixin import SaplFormHelper
from sapl.crispy_layout_mixin import form_actions, to_row, SaplFormLayout 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 " "entre a data de início e fim tanto da "
"Legislatura quanto da Sessão Legislativa.") "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 return self.cleaned_data
@ -656,6 +675,12 @@ class OradorForm(ModelForm):
"Já existe orador nesta posição de ordem de pronunciamento" "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 return self.cleaned_data
class Meta: class Meta:
@ -691,6 +716,12 @@ class OradorExpedienteForm(ModelForm):
raise ValidationError(_( raise ValidationError(_(
'Já existe orador nesta posição da ordem de pronunciamento')) '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 return self.cleaned_data
class Meta: class Meta:
@ -728,6 +759,12 @@ class OradorOrdemDiaForm(ModelForm):
"Já existe orador nesta posição de ordem de pronunciamento" "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 return self.cleaned_data
class Meta: class Meta:
@ -966,9 +1003,15 @@ class JustificativaAusenciaForm(ModelForm):
sessao_plenaria = self.instance.sessao_plenaria 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: if not sessao_plenaria.finalizada or sessao_plenaria.finalizada is None:
raise ValidationError( 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: else:
return self.cleaned_data return self.cleaned_data

20
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'),
),
]

16
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 = [
]

2
sapl/sessao/models.py

@ -208,6 +208,8 @@ class SessaoPlenaria(models.Model):
interativa = models.NullBooleanField(blank=True, interativa = models.NullBooleanField(blank=True,
choices=YES_NO_CHOICES, choices=YES_NO_CHOICES,
verbose_name=_('Sessão interativa')) verbose_name=_('Sessão interativa'))
tema_solene = models.TextField(
blank=True, max_length=500, verbose_name=_('Tema da Sessão Solene'))
class Meta: class Meta:
verbose_name = _('Sessão Plenária') verbose_name = _('Sessão Plenária')

8
sapl/sessao/urls.py

@ -33,7 +33,8 @@ from sapl.sessao.views import (AdicionarVariasMateriasExpediente,
renumerar_materias_expediente, renumerar_materias_expediente,
sessao_legislativa_legislatura_ajax, sessao_legislativa_legislatura_ajax,
VotacaoEmBlocoOrdemDia, VotacaoEmBlocoExpediente, VotacaoEmBlocoOrdemDia, VotacaoEmBlocoExpediente,
VotacaoEmBlocoSimbolicaView, VotacaoEmBlocoNominalView) VotacaoEmBlocoSimbolicaView, VotacaoEmBlocoNominalView,
recuperar_nome_tipo_sessao)
from .apps import AppConfig from .apps import AppConfig
@ -68,6 +69,9 @@ urlpatterns = [
recuperar_numero_sessao_view, recuperar_numero_sessao_view,
name='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/', url(r'^sessao/sessao-legislativa-legislatura-ajax/',
sessao_legislativa_legislatura_ajax, sessao_legislativa_legislatura_ajax,
name='sessao_legislativa_legislatura_ajax_view'), name='sessao_legislativa_legislatura_ajax_view'),
@ -173,7 +177,7 @@ urlpatterns = [
name='votacaonominalexpdetail'), name='votacaonominalexpdetail'),
url(r'^sessao/(?P<pk>\d+)/matexp/votsimb/(?P<oid>\d+)/(?P<mid>\d+)$', url(r'^sessao/(?P<pk>\d+)/matexp/votsimb/(?P<oid>\d+)/(?P<mid>\d+)$',
VotacaoExpedienteView.as_view(), name='votacaosimbolicaexp'), VotacaoExpedienteView.as_view(), name='votacaosimbolicaexp'),
url(r'^sessao/(?P<pk>\d+)/matexp/votsec/view/(?P<oid>\d+)/(?P<mid>\d+)$', url(r'^sessao/(?P<pk>\d+)/matexp/votsimb/view/(?P<oid>\d+)/(?P<mid>\d+)$',
VotacaoExpedienteEditView.as_view(), name='votacaosimbolicaexpedit'), VotacaoExpedienteEditView.as_view(), name='votacaosimbolicaexpedit'),
url(r'^sessao/(?P<pk>\d+)/matexp/votsec/(?P<oid>\d+)/(?P<mid>\d+)$', url(r'^sessao/(?P<pk>\d+)/matexp/votsec/(?P<oid>\d+)/(?P<mid>\d+)$',
VotacaoExpedienteView.as_view(), name='votacaosecretaexp'), VotacaoExpedienteView.as_view(), name='votacaosecretaexp'),

264
sapl/sessao/views.py

@ -515,7 +515,7 @@ class MateriaOrdemDiaCrud(MasterDetailCrud):
form_class = OrdemDiaForm form_class = OrdemDiaForm
def get_initial(self): def get_initial(self):
initial = super(UpdateView, self).get_initial() initial = super().get_initial()
initial['tipo_materia'] = self.object.materia.tipo.id initial['tipo_materia'] = self.object.materia.tipo.id
initial['numero_materia'] = self.object.materia.numero initial['numero_materia'] = self.object.materia.numero
initial['ano_materia'] = self.object.materia.ano initial['ano_materia'] = self.object.materia.ano
@ -576,7 +576,7 @@ class ExpedienteMateriaCrud(MasterDetailCrud):
form_class = ExpedienteMateriaForm form_class = ExpedienteMateriaForm
def get_initial(self): def get_initial(self):
initial = super(CreateView, self).get_initial() initial = super().get_initial()
initial['data_ordem'] = SessaoPlenaria.objects.get( initial['data_ordem'] = SessaoPlenaria.objects.get(
pk=self.kwargs['pk']).data_inicio.strftime('%d/%m/%Y') pk=self.kwargs['pk']).data_inicio.strftime('%d/%m/%Y')
max_numero_ordem = ExpedienteMateria.objects.filter( max_numero_ordem = ExpedienteMateria.objects.filter(
@ -594,7 +594,7 @@ class ExpedienteMateriaCrud(MasterDetailCrud):
form_class = ExpedienteMateriaForm form_class = ExpedienteMateriaForm
def get_initial(self): def get_initial(self):
initial = super(UpdateView, self).get_initial() initial = super().get_initial()
initial['tipo_materia'] = self.object.materia.tipo.id initial['tipo_materia'] = self.object.materia.tipo.id
initial['numero_materia'] = self.object.materia.numero initial['numero_materia'] = self.object.materia.numero
initial['ano_materia'] = self.object.materia.ano initial['ano_materia'] = self.object.materia.ano
@ -614,6 +614,15 @@ class OradorCrud(MasterDetailCrud):
class ListView(MasterDetailCrud.ListView): class ListView(MasterDetailCrud.ListView):
ordering = ['numero_ordem', 'parlamentar'] 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): class CreateView(MasterDetailCrud.CreateView):
form_class = OradorForm form_class = OradorForm
@ -625,18 +634,87 @@ class OradorCrud(MasterDetailCrud):
return reverse('sapl.sessao:orador_list', return reverse('sapl.sessao:orador_list',
kwargs={'pk': self.kwargs['pk']}) kwargs={'pk': self.kwargs['pk']})
class UpdateView(MasterDetailCrud.UpdateView): class UpdateView(MasterDetailCrud.UpdateView):
form_class = OradorForm form_class = OradorForm
def get_initial(self): 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({'id_sessao': self.object.sessao_plenaria.id})
initial.update({'numero': self.object.numero_ordem}) initial.update({'numero': self.object.numero_ordem})
return initial 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): class OradorExpedienteCrud(OradorCrud):
model = OradorExpediente model = OradorExpediente
@ -647,6 +725,17 @@ class OradorExpedienteCrud(OradorCrud):
def get_initial(self): def get_initial(self):
return {'id_sessao': self.kwargs['pk']} 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): def get_success_url(self):
return reverse('sapl.sessao:oradorexpediente_list', return reverse('sapl.sessao:oradorexpediente_list',
kwargs={'pk': self.kwargs['pk']}) kwargs={'pk': self.kwargs['pk']})
@ -659,6 +748,40 @@ class OradorExpedienteCrud(OradorCrud):
'numero': self.object.numero_ordem} '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): class OradorOrdemDiaCrud(OradorCrud):
model = OradorOrdemDia model = OradorOrdemDia
@ -676,7 +799,7 @@ class OradorOrdemDiaCrud(OradorCrud):
form_class = OradorOrdemDiaForm form_class = OradorOrdemDiaForm
def get_initial(self): 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({'id_sessao': self.object.sessao_plenaria.id})
initial.update({'numero': self.object.numero_ordem}) initial.update({'numero': self.object.numero_ordem})
@ -735,6 +858,16 @@ def sessao_legislativa_legislatura_ajax(request):
return JsonResponse({'sessao_legislativa': lista_sessoes}) 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): class SessaoCrud(Crud):
model = SessaoPlenaria model = SessaoPlenaria
help_topic = 'sessao_legislativa' help_topic = 'sessao_legislativa'
@ -764,6 +897,18 @@ class SessaoCrud(Crud):
form_class = SessaoPlenariaForm 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): def get_initial(self):
return {'sessao_legislativa': self.object.sessao_legislativa} return {'sessao_legislativa': self.object.sessao_legislativa}
@ -772,6 +917,10 @@ class SessaoCrud(Crud):
form_class = SessaoPlenariaForm form_class = SessaoPlenariaForm
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@property
def layout_key(self):
return 'SessaoSolene'
@property @property
def cancel_url(self): def cancel_url(self):
return self.search_url return self.search_url
@ -804,6 +953,26 @@ class SessaoCrud(Crud):
namespace = self.model._meta.app_config.name namespace = self.model._meta.app_config.name
return reverse('%s:%s' % (namespace, 'sessaoplenaria_list')) 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, class SessaoPermissionMixin(PermissionRequiredForAppCrudMixin,
FormMixin, FormMixin,
@ -837,6 +1006,10 @@ class PresencaView(FormMixin, PresencaMixin, DetailView):
context = FormMixin.get_context_data(self, **kwargs) context = FormMixin.get_context_data(self, **kwargs)
context['title'] = '%s <small>(%s)</small>' % ( context['title'] = '%s <small>(%s)</small>' % (
_('Presença'), self.object) _('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 return context
@method_decorator(permission_required( @method_decorator(permission_required(
@ -921,17 +1094,23 @@ class PainelView(PermissionRequiredForAppCrudMixin, TemplateView):
cronometro_ordem = cronometro_ordem.seconds cronometro_ordem = cronometro_ordem.seconds
cronometro_consideracoes = cronometro_consideracoes.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 = TemplateView.get_context_data(self, **kwargs)
context.update({ context.update({
'head_title': str(_('Painel Plenário')), 'head_title': str(_('Painel Plenário')),
'sessao_id': kwargs['pk'], 'sessao_id': sessao_pk,
'root_pk': kwargs['pk'], 'root_pk': sessao_pk,
'sessaoplenaria': SessaoPlenaria.objects.get(pk=kwargs['pk']), 'sessaoplenaria': sessao,
'cronometro_discurso': cronometro_discurso, 'cronometro_discurso': cronometro_discurso,
'cronometro_aparte': cronometro_aparte, 'cronometro_aparte': cronometro_aparte,
'cronometro_ordem': cronometro_ordem, 'cronometro_ordem': cronometro_ordem,
'cronometro_consideracoes': cronometro_consideracoes}) 'cronometro_consideracoes': cronometro_consideracoes})
tipo_sessao = sessao.tipo
if tipo_sessao.nome == "Solene":
context.update({'subnav_template_name': 'sessao/subnav-solene.yaml'})
return context return context
@ -1154,8 +1333,12 @@ class MesaView(FormMixin, DetailView):
context = FormMixin.get_context_data(self, **kwargs) context = FormMixin.get_context_data(self, **kwargs)
context['title'] = '%s <small>(%s)</small>' % ( context['title'] = '%s <small>(%s)</small>' % (
_('Mesa Diretora'), self.object) _('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 return context
def get_success_url(self): def get_success_url(self):
pk = self.kwargs['pk'] pk = self.kwargs['pk']
return reverse('sapl.sessao:mesa', kwargs={'pk': 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 '' abertura = data_inicio.strftime('%d/%m/%Y') if data_inicio else ''
data_fim = sessao_plenaria.data_fim data_fim = sessao_plenaria.data_fim
encerramento = data_fim.strftime('%d/%m/%Y') + ' -' if data_fim else '' 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}, _('Tipo de Sessão: %(tipo)s') % {'tipo': sessao_plenaria.tipo},
_('Abertura: %(abertura)s - %(hora_inicio)s') % { _('Abertura: %(abertura)s - %(hora_inicio)s') % {
'abertura': abertura, 'hora_inicio': sessao_plenaria.hora_inicio}, 'abertura': abertura, 'hora_inicio': sessao_plenaria.hora_inicio},
_('Encerramento: %(encerramento)s %(hora_fim)s') % { _('Encerramento: %(encerramento)s %(hora_fim)s') % {
'encerramento': encerramento, 'hora_fim': sessao_plenaria.hora_fim} 'encerramento': encerramento, 'hora_fim': sessao_plenaria.hora_fim},
], ],
'sessaoplenaria': sessao_plenaria}) '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): def get_conteudo_multimidia(sessao_plenaria):
@ -1531,10 +1718,11 @@ def get_assinaturas(sessao_plenaria):
context.update( context.update(
{'texto_assinatura': 'Assinatura da Mesa Diretora da Sessão'}) {'texto_assinatura': 'Assinatura da Mesa Diretora da Sessão'})
context.update({'assinatura_mesa': mesa_dia}) 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( context.update(
{'texto_assinatura': 'Assinatura do Presidente da Sessão'}) {'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 return context
@ -1824,6 +2012,11 @@ class ResumoView(DetailView):
'decimo_terceiro_ordenacao': 'oradores_explicacoes.html', 'decimo_terceiro_ordenacao': 'oradores_explicacoes.html',
'decimo_quarto_ordenacao': 'ocorrencias_da_sessao.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 return context
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
@ -1848,6 +2041,10 @@ class ExpedienteView(FormMixin, DetailView):
context = FormMixin.get_context_data(self, **kwargs) context = FormMixin.get_context_data(self, **kwargs)
context['title'] = '%s <small>(%s)</small>' % ( context['title'] = '%s <small>(%s)</small>' % (
_('Expediente Diversos'), self.object) _('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 return context
@method_decorator(permission_required('sessao.add_expedientesessao')) @method_decorator(permission_required('sessao.add_expedientesessao'))
@ -1937,6 +2134,10 @@ class OcorrenciaSessaoView(FormMixin, DetailView):
context = FormMixin.get_context_data(self, **kwargs) context = FormMixin.get_context_data(self, **kwargs)
context['title'] = 'Ocorrências da Sessão <small>(%s)</small>' % ( context['title'] = 'Ocorrências da Sessão <small>(%s)</small>' % (
self.object) 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 return context
def delete(self): def delete(self):
@ -2662,8 +2863,7 @@ class VotacaoNominalTransparenciaDetailView(TemplateView):
template_name = 'sessao/votacao/nominal_transparencia.html' template_name = 'sessao/votacao/nominal_transparencia.html'
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(VotacaoNominalTransparenciaDetailView, context = super().get_context_data(**kwargs)
self).get_context_data(**kwargs)
materia_votacao = self.request.GET.get('materia', None) materia_votacao = self.request.GET.get('materia', None)
@ -2747,8 +2947,7 @@ class VotacaoSimbolicaTransparenciaDetailView(TemplateView):
template_name = 'sessao/votacao/simbolica_transparencia.html' template_name = 'sessao/votacao/simbolica_transparencia.html'
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(VotacaoSimbolicaTransparenciaDetailView, context = super().get_context_data(**kwargs)
self).get_context_data(**kwargs)
materia_votacao = self.request.GET.get('materia', None) materia_votacao = self.request.GET.get('materia', None)
@ -3015,7 +3214,7 @@ class SessaoListView(ListView):
return SessaoPlenaria.objects.all().order_by('-data_inicio') return SessaoPlenaria.objects.all().order_by('-data_inicio')
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(SessaoListView, self).get_context_data(**kwargs) context = super().get_context_data(**kwargs)
paginator = context['paginator'] paginator = context['paginator']
page_obj = context['page_obj'] page_obj = context['page_obj']
@ -3179,8 +3378,7 @@ class PesquisarSessaoPlenariaView(FilterView):
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def get_filterset_kwargs(self, filterset_class): def get_filterset_kwargs(self, filterset_class):
super(PesquisarSessaoPlenariaView, super().get_filterset_kwargs(filterset_class)
self).get_filterset_kwargs(filterset_class)
kwargs = {'data': self.request.GET or None} kwargs = {'data': self.request.GET or None}
@ -3196,8 +3394,7 @@ class PesquisarSessaoPlenariaView(FilterView):
return kwargs return kwargs
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(PesquisarSessaoPlenariaView, context = super().get_context_data(**kwargs)
self).get_context_data(**kwargs)
context['title'] = _('Pesquisar Sessão Plenária') context['title'] = _('Pesquisar Sessão Plenária')
paginator = context['paginator'] paginator = context['paginator']
@ -3209,7 +3406,7 @@ class PesquisarSessaoPlenariaView(FilterView):
return context return context
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
super(PesquisarSessaoPlenariaView, self).get(request) super().get(request)
# Se a pesquisa estiver quebrando com a paginação # Se a pesquisa estiver quebrando com a paginação
# Olhe esta função abaixo # Olhe esta função abaixo
@ -3247,8 +3444,7 @@ class PesquisarPautaSessaoView(PesquisarSessaoPlenariaView):
logger.debug('Pesquisa de PautaSessao.') logger.debug('Pesquisa de PautaSessao.')
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(PesquisarPautaSessaoView, context = super().get_context_data(**kwargs)
self).get_context_data(**kwargs)
context['title'] = _('Pesquisar Pauta de Sessão') context['title'] = _('Pesquisar Pauta de Sessão')
return context return context
@ -3269,8 +3465,7 @@ class AdicionarVariasMateriasExpediente(PermissionRequiredForAppCrudMixin,
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def get_filterset_kwargs(self, filterset_class): def get_filterset_kwargs(self, filterset_class):
super(AdicionarVariasMateriasExpediente, super().get_filterset_kwargs(filterset_class)
self).get_filterset_kwargs(filterset_class)
kwargs = {'data': self.request.GET or None} kwargs = {'data': self.request.GET or None}
@ -3294,8 +3489,7 @@ class AdicionarVariasMateriasExpediente(PermissionRequiredForAppCrudMixin,
return kwargs return kwargs
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(MateriaLegislativaPesquisaView, context = super().get_context_data(**kwargs)
self).get_context_data(**kwargs)
context['title'] = _('Pesquisar Matéria Legislativa') context['title'] = _('Pesquisar Matéria Legislativa')
context['root_pk'] = self.kwargs['pk'] context['root_pk'] = self.kwargs['pk']
@ -3362,8 +3556,7 @@ class AdicionarVariasMateriasOrdemDia(AdicionarVariasMateriasExpediente):
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def get_filterset_kwargs(self, filterset_class): def get_filterset_kwargs(self, filterset_class):
super(AdicionarVariasMateriasExpediente, super().get_filterset_kwargs(filterset_class)
self).get_filterset_kwargs(filterset_class)
kwargs = {'data': self.request.GET or None} kwargs = {'data': self.request.GET or None}
@ -3581,8 +3774,7 @@ class VotacaoEmBlocoExpediente(PermissionRequiredForAppCrudMixin, ListView):
retiradapauta=None) retiradapauta=None)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(VotacaoEmBlocoExpediente, context = super().get_context_data(**kwargs)
self).get_context_data(**kwargs)
context['pk'] = self.kwargs['pk'] context['pk'] = self.kwargs['pk']
context['root_pk'] = self.kwargs['pk'] context['root_pk'] = self.kwargs['pk']
if not verifica_sessao_iniciada(self.request, self.kwargs['pk']): if not verifica_sessao_iniciada(self.request, self.kwargs['pk']):

4
sapl/settings.py

@ -41,7 +41,7 @@ ALLOWED_HOSTS = ['*']
LOGIN_REDIRECT_URL = '/' LOGIN_REDIRECT_URL = '/'
LOGIN_URL = '/login/?next=' LOGIN_URL = '/login/?next='
SAPL_VERSION = '3.1.158' SAPL_VERSION = '3.1.159'
if DEBUG: if DEBUG:
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' 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='') SERVER_EMAIL = config('SERVER_EMAIL', cast=str, default='')
EMAIL_RUNNING = None 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 MAX_IMAGE_UPLOAD_SIZE = 2 * 1024 * 1024 # 2MB
# Internationalization # Internationalization

41
sapl/static/sapl/css/relatorio.css

@ -1,5 +1,5 @@
@page{ @page{
margin-top: 4.5cm; margin-top: 5.2cm;
size: A4 portrait; size: A4 portrait;
} }
@ -53,4 +53,41 @@ fieldset {
page-break-after: avoid; page-break-after: avoid;
margin:5px; margin:5px;
padding:0px; padding:0px;
} }
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;
}

2
sapl/templates/base.html

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

3
sapl/templates/base/RelatorioAtas_filter.html

@ -1,6 +1,7 @@
{% extends "crud/list.html" %} {% extends "crud/list.html" %}
{% load i18n %} {% load i18n %}
{% load crispy_forms_tags staticfiles %} {% load crispy_forms_tags staticfiles %}
{% load webpack_static from webpack_loader %}
{% block base_content %} {% block base_content %}
{% if not filter_url %} {% if not filter_url %}
@ -26,7 +27,7 @@
<tr> <tr>
<td>{{sessao}}</td> <td>{{sessao}}</td>
<td><a href="{{ sessao.upload_ata.url }}"> <td><a href="{{ sessao.upload_ata.url }}">
<img src="{% static 'img/file.png' %}"> <img src="{% webpack_static 'img/file.png' %}">
</a></td> </a></td>
</tr> </tr>
{% endfor %} {% endfor %}

2
sapl/templates/base/RelatorioDataFimPrazoTramitacao_filter.html

@ -14,7 +14,7 @@
<b>PARÂMETROS DE PESQUISA:<br /></b> <b>PARÂMETROS DE PESQUISA:<br /></b>
&emsp;Período: {{ data_tramitacao }} <br /> &emsp;Período: {{ data_tramitacao }} <br />
&emsp;Tipo de matéria: {{ tipo }}<br /> &emsp;Tipo de matéria: {{ tipo }}<br />
&emsp;Status atual: {{ tramitacao__status }}<br /> &emsp;Status de tramitação: {{ tramitacao__status }}<br />
&emsp;Local de origem: {{ tramitacao__unidade_tramitacao_local }}<br /> &emsp;Local de origem: {{ tramitacao__unidade_tramitacao_local }}<br />
&emsp;Local de destino: {{ tramitacao__unidade_tramitacao_destino }}<br /><br /><br /> &emsp;Local de destino: {{ tramitacao__unidade_tramitacao_destino }}<br /><br /><br />
{% if object_list %} {% if object_list %}

75
sapl/templates/base/RelatorioPresencaSessao_filter.html

@ -20,34 +20,93 @@
</div> </div>
<br /><br /><br /><br /> <br /><br /><br /><br />
<b>PERÍODO: {{periodo}}</b><br /> <b>PERÍODO: {{periodo}}</b><br />
<b>TOTAIS NO PERÍODO - SESSÕES: {{total_sessao}} - ORDENS DO DIA: {{total_ordemdia}}</b> <b>Legislatura: {{legislatura}}</b><br />
<b>Sessão Legislativa: {{sessao_legislativa}}</b><br />
<b>Tipo Sessão Plenária: {{tipo}}<br /> </b>
<b>TOTAIS NO PERÍODO - SESSÕES: {{total_sessao}} - ORDENS DO DIA: {{total_ordemdia}}</b><br />
<b>Exibir presença das Ordens do Dia: {% if exibir_ordem %} Sim {% else %} Não {% endif %}</b><br />
<table class="table table-bordered table-hover presenca_table"> <table class="table table-bordered table-hover presenca_table">
<thead class="thead-default" align="center"> <thead class="thead-default" align="center">
<tr class="active"> <tr class="active">
<th rowspan="2">Nome Parlamentar / Partido</th> <th rowspan="2">Nome Parlamentar / Partido</th>
<th rowspan="2">Titular</th> <th rowspan="2">Titular?</th>
<th rowspan="2">Ativo?</th>
<th colspan="2">Sessão</th> <th colspan="2">Sessão</th>
<th colspan="2">Ordem do Dia</th> {% if exibir_ordem %} <th colspan="2">Ordem do Dia</th> {% endif %}
</tr> </tr>
<tr class="active"> <tr class="active">
<th>(Qtd)</th> <th>(Qtd)</th>
<th>( % )</th> <th>( % )</th>
<th>(Qtd)</th> {% if exibir_ordem %}
<th>( % )</th> <th>(Qtd)</th>
<th>( % )</th>
{% endif %}
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for p in parlamentares %} {% for p in parlamentares %}
<tr> <tr>
<td><b>{{p.parlamentar}}</b> / {{p.parlamentar|filiacao_intervalo_filter:date_range|default:"Sem Partido"}}</td> <td><b>{{p.parlamentar}}</b> / {{p.parlamentar|filiacao_intervalo_filter:date_range|default:"Sem Partido"}}</td>
<td>{%if p.titular %} Sim {% else %} Não {% endif %}</td> <td>{% if p.titular %} Sim {% else %} Não {% endif %}</td>
<td>{% if p.parlamentar.ativo %} Sim {% else %} Não {% endif %}</td>
<td>{{p.sessao_count}}</td> <td>{{p.sessao_count}}</td>
<td>{{p.sessao_porc}}</td> <td>{{p.sessao_porc}}</td>
<td>{{p.ordemdia_count}}</td> {% if exibir_ordem %}
<td>{{p.ordemdia_porc}}</td> <td>{{p.ordemdia_count}}</td>
<td>{{p.ordemdia_porc}}</td>
{% endif %}
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
{% endif %} {% endif %}
{% endblock base_content %} {% endblock base_content %}
{% block extra_js %}
<script type="text/javascript">
$(document).ready(function(){
var original_options = [];
$.each($("#id_sessao_legislativa").children(), function(idx, obj) {
original_options.push($(obj));
});
$('#id_legislatura').change(function(event) {
let legislatura = $("#id_legislatura").val();
var json_data = {
legislatura : legislatura,
}
if(legislatura){
$.getJSON("{% url 'sapl.parlamentares:get_sessoes_legislatura' %}", json_data, function(data){
if (data) {
$("#id_sessao_legislativa").children().remove();
if (data.sessoes_legislativas.length > 1) {
sel = $("#id_sessao_legislativa").append("<option>---------</option>");
sel.children().last().attr("value", "");
}
$.each(data.sessoes_legislativas, function(idx, obj) {
$("#id_sessao_legislativa")
.append($("<option></option>")
.attr("value", obj[0])
.text(obj[1]));
});
}
});
}else{
$("#id_sessao_legislativa").children().remove();
$.each(original_options, function(idx, obj) {
$("#id_sessao_legislativa").append(obj);
});
$("#id_sessao_legislativa").children().first().attr('selected', true);
}
})
});
</script>
{% endblock %}

35
sapl/templates/base/anexadas_ciclicas.html

@ -0,0 +1,35 @@
{% extends "base.html" %}
{% load common_tags %}
{% block base_content %}
<fieldset>
<h1>Lista de Matérias Anexadas Cíclicas</h1>
<br/>
{% if not anexadas_ciclicas %}
<p>{{ NO_ENTRIES_MSG }}</p>
{% else %}
<table class="table table-striped table-hover">
<thead>
<tr>
<th>Matéria Fim do Ciclo</th>
<th>Matéria Início do Ciclo</th>
</tr>
</thead>
<tbody>
{% for data, fim, inicio in anexadas_ciclicas %}
<tr>
<td>{{ data }}</td>
<td>
<a href="{% url 'sapl.materia:materialegislativa_detail' fim.pk %}">{{ fim }}</a>
</td>
<td>
<a href="{% url 'sapl.materia:materialegislativa_detail' inicio.pk %}">{{ inicio }}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</fieldset>
{% include 'paginacao.html' %}
<br/>
{% endblock base_content %}

36
sapl/templates/base/anexados_ciclicos.html

@ -0,0 +1,36 @@
{% extends "base.html" %}
{% load common_tags %}
{% block base_content %}
<fieldset>
<h1>Lista de Documentos Anexados Cíclicos</h1>
<br/>
{% if not anexados_ciclicos %}
<p>{{ NO_ENTRIES_MSG }}</p>
{% else %}
<table class="table table-striped table-hover">
<thead>
<tr>
<th>Data Anexação</th>
<th>Documento Fim do Ciclo</th>
<th>Documento Início do Ciclo</th>
</tr>
</thead>
<tbody>
{% for data, fim, inicio in anexados_ciclicos %}
<tr>
<td>{{ data }}</td>
<td>
<a href="{% url 'sapl.protocoloadm:documentoadministrativo_detail' fim.pk %}">{{ fim }}</a>
</td>
<td>
<a href="{% url 'sapl.protocoloadm:documentoadministrativo_detail' inicio.pk %}">{{ inicio }}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</fieldset>
{% include 'paginacao.html' %}
<br/>
{% endblock base_content %}

52
sapl/templates/base/autores_duplicados.html

@ -1,30 +1,30 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load common_tags %} {% load common_tags %}
{% block base_content %} {% block base_content %}
<fieldset> <fieldset>
<h1>Lista de Autores Duplicados</h1> <h1>Lista de Autores Duplicados</h1>
<br/> <br/>
{% if not autores_duplicados %} {% if not autores_duplicados %}
<p>{{ NO_ENTRIES_MSG }}</p> <p>{{ NO_ENTRIES_MSG }}</p>
{% else %} {% else %}
<table class="table table-striped table-hover"> <table class="table table-striped table-hover">
<thead> <thead>
<tr> <tr>
<th>Autor</th> <th>Autor</th>
<th>Quantidade</th> <th>Quantidade</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for autor in autores_duplicados %} {% for autor in autores_duplicados %}
<tr> <tr>
<td>{{ autor.nome }}</td> <td>{{ autor.nome }}</td>
<td>{{ autor.count }}</td> <td>{{ autor.count }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
{% endif %} {% endif %}
</fieldset> </fieldset>
{% include 'paginacao.html'%} {% include 'paginacao.html' %}
<br/> <br/>
{% endblock base_content %} {% endblock base_content %}

64
sapl/templates/base/bancada_comissao_autor_externo.html

@ -1,36 +1,36 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load common_tags %} {% load common_tags %}
{% block base_content %} {% block base_content %}
<fieldset> <fieldset>
<h1>Lista de Bancadas e Comissões com Autor Externo</h1> <h1>Lista de Bancadas e Comissões com Autor Externo</h1>
<br/> <br/>
{% if not bancada_comissao_autor_externo %} {% if not bancada_comissao_autor_externo %}
<p>{{ NO_ENTRIES_MSG }}</p> <p>{{ NO_ENTRIES_MSG }}</p>
{% else %} {% else %}
<table class="table table-striped table-hover"> <table class="table table-striped table-hover">
<thead> <thead>
<tr> <tr>
<th>Descrição do Objeto</th> <th>Descrição do Objeto</th>
<th>Objeto</th> <th>Objeto</th>
<th>Autor</th> <th>Autor</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for autor, objeto, descricao_objeto, link in bancada_comissao_autor_externo %} {% for autor, objeto, descricao_objeto, link in bancada_comissao_autor_externo %}
<tr> <tr>
<td>{{ descricao_objeto }}</td> <td>{{ descricao_objeto }}</td>
<td> <td>
<a href="{% url 'sapl_index' %}{{ link }}/{{ objeto.pk }}">{{ objeto }}</a> <a href="{% url 'sapl_index' %}{{ link }}/{{ objeto.pk }}">{{ objeto }}</a>
</td> </td>
<td> <td>
<a href="{% url 'sapl_index' %}sistema/autor/{{ autor.pk }}">{{ autor.nome }}</a> <a href="{% url 'sapl.base:autor_detail' autor.pk %}">{{ autor.nome }}</a>
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
{% endif %} {% endif %}
</fieldset> </fieldset>
{% include 'paginacao.html'%} {% include 'paginacao.html' %}
<br/> <br/>
{% endblock base_content %} {% endblock base_content %}

56
sapl/templates/base/filiacoes_sem_data_filiacao.html

@ -1,32 +1,32 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load common_tags %} {% load common_tags %}
{% block base_content %} {% block base_content %}
<fieldset> <fieldset>
<h1>Lista de Filiações sem Data Filiação</h1> <h1>Lista de Filiações sem Data Filiação</h1>
<br/> <br/>
{% if not filiacoes_sem_data_filiacao %} {% if not filiacoes_sem_data_filiacao %}
<p>{{ NO_ENTRIES_MSG }}</p> <p>{{ NO_ENTRIES_MSG }}</p>
{% else %} {% else %}
<table class="table table-striped table-hover"> <table class="table table-striped table-hover">
<thead> <thead>
<tr> <tr>
<th>Parlamentar Filiado</th> <th>Parlamentar Filiado</th>
<th>Partido</th> <th>Partido</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for filiacao in filiacoes_sem_data_filiacao %} {% for filiacao in filiacoes_sem_data_filiacao %}
<tr> <tr>
<td> <td>
<a href="{% url 'sapl_index' %}parlamentar/filiacao/{{ filiacao.pk }}">{{ filiacao.parlamentar }}</a> <a href="{% url 'sapl.parlamentares:filiacao_detail' filiacao.pk %}">{{ filiacao.parlamentar }}</a>
</td> </td>
<td>{{ filiacao.partido }}</td> <td>{{ filiacao.partido }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
{% endif %} {% endif %}
</fieldset> </fieldset>
{% include 'paginacao.html'%} {% include 'paginacao.html' %}
<br/> <br/>
{% endblock base_content %} {% endblock base_content %}

56
sapl/templates/base/legislatura_infindavel.html

@ -1,32 +1,32 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load common_tags %} {% load common_tags %}
{% block base_content %} {% block base_content %}
<fieldset> <fieldset>
<h1>Lista de Legislaturas sem Data Fim</h1> <h1>Lista de Legislaturas sem Data Fim</h1>
<br/> <br/>
{% if not legislatura_infindavel %} {% if not legislatura_infindavel %}
<p>{{ NO_ENTRIES_MSG }}</p> <p>{{ NO_ENTRIES_MSG }}</p>
{% else %} {% else %}
<table class="table table-striped table-hover"> <table class="table table-striped table-hover">
<thead> <thead>
<tr> <tr>
<th>Número Legislatura</th> <th>Número Legislatura</th>
<th>Data Eleição</th> <th>Data Eleição</th>
<th>Data Início</th> <th>Data Início</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for legislatura in legislatura_infindavel %} {% for legislatura in legislatura_infindavel %}
<tr> <tr>
<td>{{ legislatura.numero }}</td> <td>{{ legislatura.numero }}</td>
<td>{{ legislatura.data_eleicao }}</td> <td>{{ legislatura.data_eleicao }}</td>
<td>{{ legislatura.data_inicio }}</td> <td>{{ legislatura.data_inicio }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
{% endif %} {% endif %}
</fieldset> </fieldset>
{% include 'paginacao.html'%} {% include 'paginacao.html' %}
<br/> <br/>
{% endblock base_content %} {% endblock base_content %}

34
sapl/templates/base/lista_inconsistencias.html

@ -1,22 +1,22 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load common_tags %} {% load common_tags %}
{% block base_content %} {% block base_content %}
<fieldset> <fieldset>
<h1>Lista de Inconsistências</h1> <h1>Lista de Inconsistências</h1>
<br/> <br/>
<table class="table table-striped table-hover"> <table class="table table-striped table-hover">
<tbody> <tbody>
{% for complemento_link, nome, valor in tabela_inconsistencias %} {% for complemento_link, nome, valor in tabela_inconsistencias %}
<tr> <tr>
<td> <td>
<a href="{% url 'sapl.base:lista_inconsistencias' %}{{ complemento_link }}">{{ nome }}</a> <a href="{% url 'sapl.base:lista_inconsistencias' %}{{ complemento_link }}">{{ nome }}</a>
</td> </td>
<td>{{ valor }}</td> <td>{{ valor }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
</fieldset> </fieldset>
{% include 'paginacao.html'%} {% include 'paginacao.html' %}
<br/> <br/>
{% endblock base_content %} {% endblock base_content %}

56
sapl/templates/base/mandato_sem_data_inicio.html

@ -1,32 +1,32 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load common_tags %} {% load common_tags %}
{% block base_content %} {% block base_content %}
<fieldset> <fieldset>
<h1>Lista de Mandatos sem Data Inicial</h1> <h1>Lista de Mandatos sem Data Inicial</h1>
<br/> <br/>
{% if not mandato_sem_data_inicio %} {% if not mandato_sem_data_inicio %}
<p>{{ NO_ENTRIES_MSG }}</p> <p>{{ NO_ENTRIES_MSG }}</p>
{% else %} {% else %}
<table class="table table-striped table-hover"> <table class="table table-striped table-hover">
<thead> <thead>
<tr> <tr>
<th>Parlamentar do Mandato</th> <th>Parlamentar do Mandato</th>
<th>Legislatura do Mandato</th> <th>Legislatura do Mandato</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for mandato in mandato_sem_data_inicio %} {% for mandato in mandato_sem_data_inicio %}
<tr> <tr>
<td> <td>
<a href="{% url 'sapl_index' %}parlamentar/mandato/{{ mandato.pk }}">{{ mandato.parlamentar }}</a> <a href="{% url 'sapl.parlamentares:mandato_detail' mandato.pk %}">{{ mandato.parlamentar }}</a>
</td> </td>
<td>{{ mandato.legislatura }}</td> <td>{{ mandato.legislatura }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
{% endif %} {% endif %}
</fieldset> </fieldset>
{% include 'paginacao.html'%} {% include 'paginacao.html' %}
<br/> <br/>
{% endblock base_content %} {% endblock base_content %}

60
sapl/templates/base/materias_protocolo_inexistente.html

@ -1,34 +1,34 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load common_tags %} {% load common_tags %}
{% block base_content %} {% block base_content %}
<fieldset> <fieldset>
<h1>Lista de Matérias Legislativas com Protocolo Inexistente</h1> <h1>Lista de Matérias Legislativas com Protocolo Inexistente</h1>
<br/> <br/>
{% if not materias_protocolo_inexistente %} {% if not materias_protocolo_inexistente %}
<p>{{ NO_ENTRIES_MSG }}</p> <p>{{ NO_ENTRIES_MSG }}</p>
{% else %} {% else %}
<table class="table table-striped table-hover"> <table class="table table-striped table-hover">
<thead> <thead>
<tr> <tr>
<th>Matéria Legislativa</th> <th>Matéria Legislativa</th>
<th>Ano</th> <th>Ano</th>
<th>Número Protocolo</th> <th>Número Protocolo</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for materia, ano, numero_protocolo in materias_protocolo_inexistente %} {% for materia, ano, numero_protocolo in materias_protocolo_inexistente %}
<tr> <tr>
<td> <td>
<a href="{% url 'sapl_index' %}materia/{{ materia.pk }}">{{ materia }}</a> <a href="{% url 'sapl.materia:materialegislativa_detail' materia.pk %}">{{ materia }}</a>
</td> </td>
<td>{{ ano }}</td> <td>{{ ano }}</td>
<td>{{ numero_protocolo }}</td> <td>{{ numero_protocolo }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
{% endif %} {% endif %}
</fieldset> </fieldset>
{% include 'paginacao.html'%} {% include 'paginacao.html' %}
<br/> <br/>
{% endblock base_content %} {% endblock base_content %}

56
sapl/templates/base/parlamentares_duplicados.html

@ -1,32 +1,32 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load common_tags %} {% load common_tags %}
{% block base_content %} {% block base_content %}
<fieldset> <fieldset>
<h1>Lista de Parlamentares Duplicados</h1> <h1>Lista de Parlamentares Duplicados</h1>
<br/> <br/>
{% if not parlamentares_duplicados %} {% if not parlamentares_duplicados %}
<p>{{ NO_ENTRIES_MSG }}</p> <p>{{ NO_ENTRIES_MSG }}</p>
{% else %} {% else %}
<table class="table table-striped table-hover"> <table class="table table-striped table-hover">
<thead> <thead>
<tr> <tr>
<th>Nome do Parlamentar</th> <th>Nome do Parlamentar</th>
<th>Quantidade</th> <th>Quantidade</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for parlamentar, quantidade in parlamentares_duplicados %} {% for quantidade, parlamentar in parlamentares_duplicados %}
<tr> <tr>
<td> <td>
<a href="{% url 'sapl.parlamentares:pesquisar_parlamentar' %}?nome_parlamentar={{parlamentar}}">{{ parlamentar }}</a> <a href="{% url 'sapl.parlamentares:pesquisar_parlamentar' %}?nome_parlamentar={{ parlamentar }}">{{ parlamentar }}</a>
</td> </td>
<td>{{ quantidade }}</td> <td>{{ quantidade }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
{% endif %} {% endif %}
</fieldset> </fieldset>
{% include 'paginacao.html'%} {% include 'paginacao.html' %}
<br/> <br/>
{% endblock base_content %} {% endblock base_content %}

60
sapl/templates/base/parlamentares_filiacoes_intersecao.html

@ -1,34 +1,34 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load common_tags %} {% load common_tags %}
{% block base_content %} {% block base_content %}
<fieldset> <fieldset>
<h1>Lista de Parlamentares com Filiações com Interseção</h1> <h1>Lista de Parlamentares com Filiações com Interseção</h1>
<br/> <br/>
{% if not parlamentares_filiacoes_intersecao %} {% if not parlamentares_filiacoes_intersecao %}
<p>{{ NO_ENTRIES_MSG }}</p> <p>{{ NO_ENTRIES_MSG }}</p>
{% else %} {% else %}
<table class="table table-striped table-hover"> <table class="table table-striped table-hover">
<thead> <thead>
<tr> <tr>
<th>Parlamentar</th> <th>Parlamentar</th>
<th>Filiação 1</th> <th>Filiação 1</th>
<th>Filiação 2</th> <th>Filiação 2</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for parlamentar, filiacao_a , filiacao_b in parlamentares_filiacoes_intersecao %} {% for parlamentar, filiacao_a , filiacao_b in parlamentares_filiacoes_intersecao %}
<tr> <tr>
<td> <td>
<a href="{% url 'sapl_index' %}parlamentar/{{ parlamentar.pk }}/filiacao">{{ parlamentar }}</a> <a href="{% url 'sapl.parlamentares:filiacao_list' parlamentar.pk %}">{{ parlamentar }}</a>
</td> </td>
<td>{{filiacao_a.data|date:"d/m/Y"}} - {{filiacao_a.data_desfiliacao|date:"d/m/Y"}}</td> <td>{{ filiacao_a.data|date:"d/m/Y" }} - {{ filiacao_a.data_desfiliacao|date:"d/m/Y" }}</td>
<td>{{filiacao_b.data|date:"d/m/Y"}} - {{filiacao_b.data_desfiliacao|date:"d/m/Y"}}</td> <td>{{ filiacao_b.data|date:"d/m/Y" }} - {{ filiacao_b.data_desfiliacao|date:"d/m/Y" }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
{% endif %} {% endif %}
</fieldset> </fieldset>
{% include 'paginacao.html'%} {% include 'paginacao.html' %}
<br/> <br/>
{% endblock base_content %} {% endblock base_content %}

60
sapl/templates/base/parlamentares_mandatos_intersecao.html

@ -1,34 +1,34 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load common_tags %} {% load common_tags %}
{% block base_content %} {% block base_content %}
<fieldset> <fieldset>
<h1>Lista de Parlamentares com Mandatos em Interseção</h1> <h1>Lista de Parlamentares com Mandatos em Interseção</h1>
<br/> <br/>
{% if not parlamentares_mandatos_intersecao %} {% if not parlamentares_mandatos_intersecao %}
<p>{{ NO_ENTRIES_MSG }}</p> <p>{{ NO_ENTRIES_MSG }}</p>
{% else %} {% else %}
<table class="table table-striped table-hover"> <table class="table table-striped table-hover">
<thead> <thead>
<tr> <tr>
<th>Parlamentar</th> <th>Parlamentar</th>
<th>Mandato 1</th> <th>Mandato 1</th>
<th>Mandato 2</th> <th>Mandato 2</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for parlamentar, mandato_a, mandato_b in parlamentares_mandatos_intersecao %} {% for parlamentar, mandato_a, mandato_b in parlamentares_mandatos_intersecao %}
<tr> <tr>
<td> <td>
<a href="{% url 'sapl_index' %}parlamentar/{{ parlamentar.pk }}/mandato">{{ parlamentar }}</a> <a href="{% url 'sapl.parlamentares:mandato_list' parlamentar.pk %}">{{ parlamentar }}</a>
</td> </td>
<td>{{ mandato_a.legislatura}}</br>{{mandato_a.data_inicio_mandato|date:"d/m/Y"}} - {{mandato_a.data_fim_mandato|date:"d/m/Y"}}</td> <td>{{ mandato_a.legislatura }}</br>{{ mandato_a.data_inicio_mandato|date:"d/m/Y" }} - {{ mandato_a.data_fim_mandato|date:"d/m/Y" }}</td>
<td>{{ mandato_b.legislatura }}</br>{{mandato_b.data_inicio_mandato|date:"d/m/Y"}} - {{mandato_b.data_fim_mandato|date:"d/m/Y"}}</td> <td>{{ mandato_b.legislatura }}</br>{{ mandato_b.data_inicio_mandato|date:"d/m/Y" }} - {{ mandato_b.data_fim_mandato|date:"d/m/Y" }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
{% endif %} {% endif %}
</fieldset> </fieldset>
{% include 'paginacao.html'%} {% include 'paginacao.html' %}
<br/> <br/>
{% endblock base_content %} {% endblock base_content %}

52
sapl/templates/base/protocolos_com_materias.html

@ -1,30 +1,30 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load common_tags %} {% load common_tags %}
{% block base_content %} {% block base_content %}
<fieldset> <fieldset>
<h1>Lista de Protocolos que Excedem o Limite de Matérias Vinculadas</h1> <h1>Lista de Protocolos que Excedem o Limite de Matérias Vinculadas</h1>
<br/> <br/>
{% if not protocolos_com_materias %} {% if not protocolos_com_materias %}
<p>{{ NO_ENTRIES_MSG }}</p> <p>{{ NO_ENTRIES_MSG }}</p>
{% else %} {% else %}
<table class="table table-striped table-hover"> <table class="table table-striped table-hover">
<thead> <thead>
<tr> <tr>
<th>Protocolo</th> <th>Protocolo</th>
<th>Quantidade de Matérias Vinculas</th> <th>Quantidade de Matérias Vinculas</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for materia, quantidade in protocolos_com_materias %} {% for materia, quantidade in protocolos_com_materias %}
<tr> <tr>
<td>{{ materia.numero_protocolo }}/{{ materia.ano }}</td> <td>{{ materia.numero_protocolo }}/{{ materia.ano }}</td>
<td>{{ quantidade }}</td> <td>{{ quantidade }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
{% endif %} {% endif %}
</fieldset> </fieldset>
{% include 'paginacao.html'%} {% include 'paginacao.html' %}
<br/> <br/>
{% endblock base_content %} {% endblock base_content %}

56
sapl/templates/base/protocolos_duplicados.html

@ -1,32 +1,32 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load common_tags %} {% load common_tags %}
{% block base_content %} {% block base_content %}
<fieldset> <fieldset>
<h1>Lista de Protocolos Duplicados</h1> <h1>Lista de Protocolos Duplicados</h1>
<br/> <br/>
{% if not protocolos_duplicados %} {% if not protocolos_duplicados %}
<p>{{ NO_ENTRIES_MSG }}</p> <p>{{ NO_ENTRIES_MSG }}</p>
{% else %} {% else %}
<table class="table table-striped table-hover"> <table class="table table-striped table-hover">
<thead> <thead>
<tr> <tr>
<th>Protocolo</th> <th>Protocolo</th>
<th>Quantidade</th> <th>Quantidade</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for protocolo, quantidade in protocolos_duplicados %} {% for protocolo, quantidade in protocolos_duplicados %}
<tr> <tr>
<td> <td>
<a href="{% url 'sapl.protocoloadm:protocolo' %}?numero={{protocolo.numero}}&ano={{protocolo.ano}}">{{ protocolo }}</a> <a href="{% url 'sapl.protocoloadm:protocolo' %}?numero={{ protocolo.numero }}&ano={{ protocolo.ano }}">{{ protocolo }}</a>
</td> </td>
<td>{{ quantidade }}</td> <td>{{ quantidade }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
{% endif %} {% endif %}
</fieldset> </fieldset>
{% include 'paginacao.html'%} {% include 'paginacao.html' %}
<br/> <br/>
{% endblock base_content %} {% endblock base_content %}

4
sapl/templates/base/relatorios_list.html

@ -37,8 +37,8 @@
<td> Histórico de tramitações por período e local informados. </td> <td> Histórico de tramitações por período e local informados. </td>
</tr> </tr>
<tr> <tr>
<td><a href="{% url 'sapl.base:data_fim_prazo_tramitacoes' %}">Tramitações de Matérias por fim de prazo</a></td> <td><a href="{% url 'sapl.base:data_fim_prazo_tramitacoes' %}">Tramitações de Matérias</a></td>
<td> Tramitações com fim de prazo no intervalo informado. </td> <td> Tramitações de matéria com status informado no intervalo. </td>
</tr> </tr>
<tr> <tr>
<td><a href="{% url 'sapl.base:reuniao' %}">Reunião de Comissão</a></td> <td><a href="{% url 'sapl.base:reuniao' %}">Reunião de Comissão</a></td>

2
sapl/templates/comissoes/reuniao_detail.html

@ -1,4 +1,4 @@
{% extends "crud/detail.html" %} {% extends "crud/detail_detail.html" %}
{% load i18n %} {% load i18n %}
{% block detail_content %} {% block detail_content %}

17
sapl/templates/crud/detail.html

@ -72,13 +72,21 @@
<p>Este navegador não suporta o elemento áudio.</p> <p>Este navegador não suporta o elemento áudio.</p>
</audio> </audio>
</div> </div>
{% elif column.text|video_url %} {% elif column.text|is_video_url %}
<div class="form-control-static"> <div class="form-control-static">
<video width="320" height="120" controls> <video width="420" height="230" controls>
<source src="{{ column.text|safe }}" type="video/{{ column.text|file_extension }}"> <source src="{{ column.text|safe }}" type="video/{{ column.text|file_extension }}">
<p>Este navegador não suporta o elemento vídeo.</p> <p>Este navegador não suporta o elemento vídeo.</p>
</video> </video>
</div> </div>
{% elif column.text|youtube_url %}
<iframe id="ytplayer" type="text/html" width="420" height="230"
src="http://www.youtube.com/embed/{{ column.text|youtube_id }}"
frameborder="0"></iframe>
{% elif column.text|facebook_url %}
<div class="fb-video" data-href="{{ column.text|safe }}"
data-width="420" data-show-text="false">
</div>
{% elif column.text|url %} {% elif column.text|url %}
<div class="form-control-static"><a href="{{ column.text|safe }}"> {{ column.text|safe|default:"" }} </a></div> <div class="form-control-static"><a href="{{ column.text|safe }}"> {{ column.text|safe|default:"" }} </a></div>
{% else %} {% else %}
@ -131,4 +139,7 @@
{% endblock table_content %} {% endblock table_content %}
{% endblock base_content %} {% endblock base_content %}
{% block extra_js %}{% endblock %} {% block extra_js %}
<div id="fb-root"></div>
<script async defer crossorigin="anonymous" src="https://connect.facebook.net/en_US/sdk.js#xfbml=1&version=v3.3"></script>
{% endblock %}

4
sapl/templates/floppyforms/image_thumbnail.html

@ -1,5 +1,5 @@
{% load i18n staticfiles thumbnail %} {% load i18n staticfiles thumbnail %}
{% load webpack_static from webpack_loader %}
<div id="div_{{ field.auto_id }}" <div id="div_{{ field.auto_id }}"
class="control-group class="control-group
{% if form_show_errors %} {% if form_show_errors %}
@ -43,7 +43,7 @@
{% else %} {% else %}
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<img src="{% static 'img/perfil.png' %}"" <img src="{% webpack_static 'img/perfil.png' %}"
class="img-thumbnail"/> class="img-thumbnail"/>
</div> </div>
</div> </div>

7
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 %}

10
sapl/templates/materia/em_lote/acessorio.html

@ -22,7 +22,7 @@
<div class="col-md-4"> <div class="col-md-4">
<div class="form-group"> <div class="form-group">
<label>Tipo*</label> <label>Tipo*</label>
<select name="tipo" class="form-control" required="True"> <select name="tipo" class="form-control" required>
{% for t in tipos_docs %} <option>{{t}}</option> {% endfor %} {% for t in tipos_docs %} <option>{{t}}</option> {% endfor %}
</select> </select>
</div> </div>
@ -31,14 +31,14 @@
<div class="col-md-4"> <div class="col-md-4">
<div class="form-group"> <div class="form-group">
<label>Nome*</label> <label>Nome*</label>
<input type="text" name="nome" class="form-control" required="True"> <input type="text" name="nome" class="form-control" required>
</div> </div>
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<div class="form-group"> <div class="form-group">
<label>Data*</label> <label>Data*</label>
<input type="text" name="data" class="form-control dateinput" required="True"> <input type="text" name="data" class="form-control dateinput" required>
</div> </div>
</div> </div>
</div> </div>
@ -47,7 +47,7 @@
<div class="col-md-12"> <div class="col-md-12">
<div class="form-group"> <div class="form-group">
<label>Autor:</label> <label>Autor:</label>
<input type="text" name="autor" class="form-control" required="False"> <input type="text" name="autor" class="form-control">
</div> </div>
</div> </div>
</div> </div>
@ -65,7 +65,7 @@
<div class="col-md-12"> <div class="col-md-12">
<div class="form-group"> <div class="form-group">
<label>Texto Integral*</label> <label>Texto Integral*</label>
<input type="file" name="arquivo" required="True"> <input type="file" name="arquivo" required>
</div> </div>
</div> </div>
</div> </div>

140
sapl/templates/materia/em_lote/tramitacao.html

@ -1,134 +1,28 @@
{% extends "crud/detail.html" %} {% extends "crud/detail.html" %}
{% load i18n crispy_forms_tags %} {% load i18n crispy_forms_tags %}
{% block actions %}{% endblock %}
{% block detail_content %} {% block detail_content %}
{% if not show_results %} {% if not show_results %}
{% crispy filter.form %} {% crispy filter.form %}
{% endif %}
{% if show_results %}
{% if object_list|length > 0 %}
{% if object_list|length == 1 %}
<h3 style="text-align: right;">{% trans 'Pesquisa concluída com sucesso! Foi encontrada 1 matéria.'%}</h3>
{% else %}
<h3 style="text-align: right;">Foram encontradas {{object_list|length}} matérias.</h3>
{% endif %}
<form method="POST">
{% csrf_token %}
<fieldset>
<legend>1. Detalhes da tramitação:</legend>
<div class="row">
<div class="col-md-4">
<label>Data da Tramitação*</label>
<input type="text" name="data_tramitacao" class="form-control dateinput" required="True">
</div>
<div class="col-md-4">
<label>Data Encaminhamento</label>
<input type="text" name="data_encaminhamento" class="form-control dateinput">
</div>
<div class="col-md-4">
<label>Data Fim do Prazo</label>
<input type="text" name="data_fim_prazo" class="form-control dateinput">
</div>
</div>
<div class="row">
<div class="col-md-6">
<label>Unidade Local*</label>
<select id="id_unidade_tramitacao_local" name="unidade_tramitacao_local" class="form-control" required="True">
{% if unidade_local|length > 1 %}<option></option>{% endif %}
{% for u in unidade_local %} <option value="{{u.id}}">{{u}}</option> {% endfor %}
</select>
</div>
<div class="col-md-6">
<label>Unidade Destino*</label>
<select name="unidade_tramitacao_destino" class="form-control" required="True">
<option></option>
{% for u in unidade_destino %} <option value="{{u.id}}">{{u}}</option> {% endfor %}
</select>
</div>
</div>
<div class="row">
<div class="col-md-4">
<label>Status*</label>
<select name="status" class="form-control" required="True">
<option></option>
{% for s in status_tramitacao %} <option value="{{s.id}}">{{s}}</option> {% endfor %}
</select>
</div>
<div class="col-md-4">
<label>Urgente?*</label>
<select name="urgente" class="form-control" required="True">
<option></option>
{% for u in urgente_tramitacao %} <option value="{{u|first}}">{{u|last}}</option> {% endfor %}
</select>
</div>
<div class="col-md-4">
<label>Turno</label>
<select name="turno" class="form-control">
<option></option>
{% for t in turnos_tramitacao %} <option value="{{t|first}}">{{t|last}}</option> {% endfor %}
</select>
</div>
</div>
<div class="row">
<div class="col-md-12">
<label>Texto da Ação*</label>
<textarea name="texto" class="textarea form-control" cols="40" rows="10" required="True"></textarea>
</div>
</div>
</fieldset>
<br /><br /><br />
<fieldset>
<legend>2. Selecione as matérias para tramitação:</legend>
<table class="table table-striped table-hover">
<div class="controls">
<div class="checkbox">
<label for="id_check_all">
<input type="checkbox" id="id_check_all" onchange="checkAll(this)" /> Marcar/Desmarcar Todos
</label>
</div>
</div>
<thead>
<tr><th>Matéria</th></tr>
</thead>
<tbody>
{% for materia in object_list %}
<tr>
<td>
<input type="checkbox" name="materia_id" value="{{materia.id}}" {% if check %} checked {% endif %}/>
<a href="{% url 'sapl.materia:materialegislativa_detail' materia.id %}">
{{materia.tipo.sigla}} {{materia.tipo.descricao}} {{materia.numero}}/{{materia.ano}}
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</fieldset>
<input type="submit" value="Salvar" class="btn btn-primary"S>
</form>
{% else %} {% else %}
<tr><td><h3 style="text-align: right;">Nenhuma matéria encontrada.</h3></td></tr> {% if object_list|length > 0 %}
{% endif %} {% if object_list|length == 1 %}
{% endif %} <h3 style="text-align: right;">{% trans 'Pesquisa concluída com sucesso! Foi encontrada 1 matéria.'%}</h3>
{% else %}
<h3 style="text-align: right;">Foram encontradas {{object_list|length}} matérias.</h3>
{% endif %}
{% crispy form %}
{% else %}
<tr><td><h3 style="text-align: right;">Nenhuma matéria encontrada.</h3></td></tr>
{% endif %}
{% endif%}
{% endblock detail_content %} {% endblock detail_content %}
{% block extra_js %} {% block extra_js %}
<script language="JavaScript"> <script language="JavaScript">
function checkAll(elem) { function checkAll(elem) {
let checkboxes = document.getElementsByName('materia_id'); let checkboxes = document.getElementsByName('materias');
for (let i = 0; i < checkboxes.length; i++) { for (let i = 0; i < checkboxes.length; i++) {
if (checkboxes[i].type == 'checkbox') if (checkboxes[i].type == 'checkbox')
checkboxes[i].checked = elem.checked; checkboxes[i].checked = elem.checked;

114
sapl/templates/materia/materialegislativa_form.html

@ -24,42 +24,88 @@
$("#id_tipo, #id_ano").change(recuperar_numero_ano); $("#id_tipo, #id_ano").change(recuperar_numero_ano);
function compare(a, b) { function compare(a, b) {
if (a.text < b.text) if (a.text < b.text)
return -1; return -1;
if (a.text > b.text) if (a.text > b.text)
return 1; return 1;
return 0; return 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 = '<div id="fundo_modal" style="'+fundo_modal_estilos+'">'
+'<div id="meu_modal" style="'+modal_estilos+'">'
+'<h2>Atenção! Ano de apresentação e ano da matéria são diferentes.</h2><br />'
+'<button id="close_model_btn" type="button" class="btn btn-warning" data-dismiss="modal">'
+'Compreendo e quero continuar</button>'
+'</div></div>';
function verifica_ano(){
let ano = $("select#id_ano.select").val();
let data_apresentacao = $("input#id_data_apresentacao.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() { $(document).ready(function() {
$("#id_tipo_autor").change(function() { $("#id_tipo_autor").change(function() {
var tipo_selecionado = $("#id_tipo_autor").val(); var tipo_selecionado = $("#id_tipo_autor").val();
var autor_selecionado = $("#id_autor").val(); var autor_selecionado = $("#id_autor").val();
$("#id_autor option").remove() $("#id_autor option").remove()
if (tipo_selecionado !== undefined && tipo_selecionado !== null) { if (tipo_selecionado !== undefined && tipo_selecionado !== null) {
var json_data = { var json_data = {
tipo : tipo_selecionado, tipo : tipo_selecionado,
data_relativa : $("#id_data_apresentacao").val() data_relativa : $("#id_data_apresentacao").val()
} }
$.getJSON("/api/autor/possiveis", json_data, function(data){ $.getJSON("/api/autor/possiveis", json_data, function(data){
if (data) { if (data) {
var results = data.sort(compare); var results = data.sort(compare);
if (results.length > 1) { if (results.length > 1) {
$("#id_autor").append("<option>-----</option>"); $("#id_autor").append("<option>-----</option>");
}
$.each(results, function(idx, obj) {
$("#id_autor")
.append($("<option></option>")
.attr("value", obj.value)
.text(obj.text));
});
$("#id_autor").val(autor_selecionado);
}
});
} }
}); $.each(results, function(idx, obj) {
$("#id_tipo_autor").trigger('change'); $("#id_autor")
}); .append($("<option></option>")
.attr("value", obj.value)
.text(obj.text));
});
$("#id_autor").val(autor_selecionado);
}
});
}
});
$("#id_tipo_autor").trigger('change');
$("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").change(function(){
verifica_ano();
});
$("input#id_data_apresentacao.dateinput.form-control").change(function(){
verifica_ano();
});
});
</script> </script>
{% endblock %} {% endblock %}

2
sapl/templates/navbar.yaml

@ -35,7 +35,7 @@
url: sapl.protocoloadm:pesq_doc_adm url: sapl.protocoloadm:pesq_doc_adm
- title: {% trans 'Tramitação em Lote' %} - title: {% trans 'Tramitação em Lote' %}
url: sapl.protocoloadm:primeira_tramitacao_em_lote_docadm url: sapl.protocoloadm:primeira_tramitacao_em_lote_docadm
check_permission: sapl.protocoloadm:add_tramitacaoadministrativo check_permission: protocoloadm.add_tramitacaoadministrativo
- title: {% trans 'Atividade Legislativa' %} - title: {% trans 'Atividade Legislativa' %}
children: children:

32
sapl/templates/norma/normajuridica_detail.html

@ -87,6 +87,38 @@
</div> </div>
</div> </div>
<br><br>
{% if user.is_superuser %}
<div class="row">
{% if object.user %}
<div class="col-sm-6">
<div id="div_id_user" class="form-group">
<p class="control-label">Usuário</p>
<div class="controls">
<div class="form-control-static">
<div class="dont-break-out">
<a href="{% url 'sapl.base:user_edit' object.user.pk %}">{{object.user}}</a>
</div>
</div>
</div>
</div>
</div>
{% endif %}
{% if object.ip %}
<div class="col-sm-6">
<div id="div_ip_user" class="form-group">
<p class="control-label">IP</p>
<div class="controls">
<div class="form-control-static">
<div class="dont-break-out">{{object.ip}}</div>
</div>
</div>
</div>
</div>
{% endif %}
</div>
{% endif %}
{% if object.texto_articulado.exists and object.texto_articulado.first.has_view_permission %} {% if object.texto_articulado.exists and object.texto_articulado.first.has_view_permission %}
<hr /> <hr />
<div class="row"> <div class="row">

48
sapl/templates/norma/normajuridica_form.html

@ -57,6 +57,54 @@
numeroField.val(numero.replace(/^0+/, '')); 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 = '<div id="fundo_modal" style="'+fundo_modal_estilos+'">'
+'<div id="meu_modal" style="'+modal_estilos+'">'
+'<h2>Atenção! Ano de apresentação e ano da norma são diferentes.</h2><br />'
+'<button id="close_model_btn" type="button" class="btn btn-warning" data-dismiss="modal">'
+'Compreendo e quero continuar</button>'
+'</div></div>';
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();
});
});
</script> </script>
{% endblock %} {% endblock %}

49
sapl/templates/painel/index.html

@ -89,17 +89,22 @@
Considerações Finais: <span id="cronometro_consideracoes"></span> Considerações Finais: <span id="cronometro_consideracoes"></span>
</div> </div>
</div> </div>
<div class="col-md-6 text-center painel"> <div class="col-md-6 text-center painel" id="resultado_votacao_div">
<h2 class="text-subtitle">Resultado</h2> <h2 class="text-subtitle">Resultado</h2>
<span id="votacao" class="text-value"></span> <span id="votacao" class="text-value"></span>
<h2><span id="resultado_votacao" lass="text-title"></span> <h2><span id="resultado_votacao" lass="text-title"></span>
</div>
<div class="col-md-6 text-center painel" id="obs_materia_div">
<h2 class="text-subtitle" id="mat_em_votacao">Matéria em Votação</h2>
<span id="materia_legislativa_texto" class="text-value"></span>
<span id="observacao_materia" class="text-value"></span>
</div> </div>
<div class="col-md-6 text-center painel"> <div class="col-md-6 text-center painel" id="tema_solene_div" style="display: none">
<h2 class="text-subtitle">Matéria em Votação</h2> <h2 class="text-subtitle">Tema da Sessão Solene</h2>
<span id="materia_legislativa_texto" class="text-value"></span> <span id="sessao_solene_tema" class="text-value"></span>
<span id="observacao_materia" class="text-value"></span>
</div> </div>
</div> </div>
</div> </div>
@ -234,6 +239,7 @@
$("#sessao_plenaria").text(data["sessao_plenaria"]) $("#sessao_plenaria").text(data["sessao_plenaria"])
$("#sessao_plenaria_data").text("Data Início: " + data["sessao_plenaria_data"]) $("#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_plenaria_hora_inicio").text("Hora Início: " + data["sessao_plenaria_hora_inicio"])
$("#sessao_solene_tema").text(data["tema_solene"])
if (data["status_painel"] == false) { if (data["status_painel"] == false) {
$("#message").text("PAINEL ENCONTRA-SE FECHADO"); $("#message").text("PAINEL ENCONTRA-SE FECHADO");
} }
@ -241,6 +247,12 @@
$("#message").text(""); $("#message").text("");
} }
if (data["sessao_solene"]){
$("#resultado_votacao_div").hide();
$("#obs_materia_div").hide();
$('#tema_solene_div').show();
}
if (data["brasao"] != null) if (data["brasao"] != null)
$("#logo-painel").attr("src", data["brasao"]); $("#logo-painel").attr("src", data["brasao"]);
@ -381,7 +393,11 @@
audioAlertFinish.play(); 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){ if (data["status_painel"] == true){
$("#materia_legislativa_texto").text(data["materia_legislativa_texto"]); $("#materia_legislativa_texto").text(data["materia_legislativa_texto"]);
} }
@ -410,15 +426,18 @@
$("#resultado_votacao").text(data["tipo_resultado"]); $("#resultado_votacao").text(data["tipo_resultado"]);
$("#resultado_votacao").css("color", "#45919D"); $("#resultado_votacao").css("color", "#45919D");
var resultado_votacao_upper = $("#resultado_votacao").text().toUpperCase(); var resultado_votacao_upper = $("#resultado_votacao").text().toUpperCase();
if (resultado_votacao_upper.search("APROV") != -1){ if (resultado_votacao_upper.search("APROV") != -1){
$("#resultado_votacao").css("color", "green"); $("#resultado_votacao").css("color", "green");
} $("#mat_em_votacao").text("Matéria Votada");
if (resultado_votacao_upper.search("REJEIT") != -1){ }
$("#resultado_votacao").css("color", "red"); if (resultado_votacao_upper.search("REJEIT") != -1){
} $("#resultado_votacao").css("color", "red");
$("#mat_em_votacao").text("Matéria Votada");
}
} }
else{ else{
$("#resultado_votacao").text(''); $("#resultado_votacao").text('');
$("#mat_em_votacao").text("Matéria em Votação");
} }
}, },
error: function(err) { error: function(err) {

94
sapl/templates/painel/voto_nominal.html

@ -90,25 +90,23 @@
<br /><br /> <br /><br />
<form method='POST'> <form method='POST'>
{% csrf_token %} {% csrf_token %}
<div class="row container-detail clearfix" style="text-align:center"> <div class="row" align="center">
<div class="row">
<div class="col-md-12" id="votos"> <div class="col-md-12" id="votos">
<div id="votosim"><input type="submit" class="btn btn-lg btn-success" id="voto" name="voto" type="submit" value="Sim" /></div>
<div id="votosim"><input type="submit" class="btn btn-lg btn-success" id="voto" name="voto" type="submit" value="Sim" /></div> &nbsp;
&nbsp; <div id="votonao"><input type="submit" class="btn btn-lg btn-danger" id="voto" name="voto" type="submit" value="Não"/></div>
<div id="votonao"><input type="submit" class="btn btn-lg btn-danger" id="voto" name="voto" type="submit" value="Não"/></div> &nbsp;
&nbsp; <div id="votoabstencao"><input type="submit" class="btn btn-lg btn-secondary" id="voto" name="voto" type="submit" value="Abstenção"/></div>
<div id="votoabstencao"><input type="submit" class="btn btn-lg btn-secondary" id="voto" name="voto" type="submit" value="Abstenção"/></div>
</div> </div>
</br>
</br>
</br>
</br>
</br>
<div id='voltar'><center><button type="button" class="btn btn-lg btn-secondary" onclick="javascript:window.close()">Voltar</button></center></div>
</div> </div>
</div> </br>
</br>
<center><table style="width:30%">
<tr>
<td style="text-align:center"><button type="button" class="btn btn-lg btn-primary" onclick="javascript:window.location.reload(true)">Atualizar</button></td>
<td style="text-align:center"><button type="button" class="btn btn-lg btn-secondary" onclick="javascript:window.close()">Sair</button></td>
</tr>
</table></center>
</form> </form>
</div> </div>
{% else %} {% else %}
@ -139,11 +137,13 @@
document.getElementById("date").innerHTML = n; document.getElementById("date").innerHTML = n;
$(window).on('beforeunload', function () { $(window).on('beforeunload', function () {
$("input[type=submit], input[type=button]").prop("disabled", "disabled"); $("input[type=submit], input[type=button]").prop("disabled", "disabled");
}); });
$( "#votosim" ).mouseleave(function(){document.location.reload(true);});
$( "#votonao" ).mouseleave(function(){document.location.reload(true);}); //TODO: Este código é necessário?
$( "#votoabstencao" ).mouseleave(function(){document.location.reload(true);}); // $( "#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) => { $(document).on('keyup', (e) => {
var tecla_press = e.keyCode; var tecla_press = e.keyCode;
@ -160,34 +160,34 @@
case 86: //86 = valor da tecla V case 86: //86 = valor da tecla V
document.querySelectorAll("#voltar button")[0].click(); document.querySelectorAll("#voltar button")[0].click();
break; break;
}; };
}); });
$(document).ready( function checkTime(i) {
function(){ if (i<10)
i = "0" + i; // add zero in front of numbers < 10
function checkTime(i) { return i;
if (i<10) {i = "0" + i}; // add zero in front of numbers < 10 }
return i;
} function startTime() {
function startTime() { var today=new Date();
var today=new Date(); var h=today.getHours();
var h=today.getHours(); var m=today.getMinutes();
var m=today.getMinutes(); var s=today.getSeconds();
var s=today.getSeconds(); m = checkTime(m);
m = checkTime(m); s = checkTime(s);
s = checkTime(s); $("#relogio").text(h+":"+m+":"+s)
$("#relogio").text(h+":"+m+":"+s) var t = setTimeout(function(){
var t = setTimeout(function(){ startTime()
startTime() }, 500);
}, 500); }
}
startTime(); $(document).ready(function(){
startTime();
setTimeout(function() {
document.location.reload(true); setTimeout(function() {
}, 30000) document.location.reload(true);
}); }, 30000)
});
</script> </script>

8
sapl/templates/parlamentares/materias.html

@ -11,7 +11,7 @@
Matérias <small>({{nome_parlamentar}})</small> Matérias <small>({{nome_parlamentar}})</small>
</h1> </h1>
</div> </div>
<center><h2>Primeiro Autor</h2></center> <h2 style="text-align: center">Primeiro Autor</h2>
<br/> <br/>
<table class="table table-striped table-bordered"> <table class="table table-striped table-bordered">
{% for autoria in autoria.0 %} {% for autoria in autoria.0 %}
@ -20,7 +20,7 @@
<tr> <tr>
<td>{{ materias.1 }}&nbsp;</td> <td>{{ materias.1 }}&nbsp;</td>
<td> <td>
<a href="{% url 'sapl.materia:pesquisar_materia'%}?tipo={{materias.0}}&ano={{autoria.0}}&autoria__autor={{autor_pk}}&autoria__primeiro_autor=1"> <a href="{% url 'sapl.materia:pesquisar_materia'%}?tipo={{materias.0}}&ano={{autoria.0}}&autoria__autor={{autor_pk}}&autoria__primeiro_autor=True">
{{ materias.2}} {{ materias.2}}
</a> </a>
</td> </td>
@ -32,7 +32,7 @@
<h2>Total: {{ autoria.1 }}</h2><br/> <h2>Total: {{ autoria.1 }}</h2><br/>
<center><h2>Co-Autor</h2></center> <h2 style="text-align: center">Co-Autor</h2>
<br/> <br/>
<table class="table table-striped table-bordered"> <table class="table table-striped table-bordered">
{% for coautoria in coautoria.0 %} {% for coautoria in coautoria.0 %}
@ -41,7 +41,7 @@
<tr> <tr>
<td>{{ materias.1 }}&nbsp;</td> <td>{{ materias.1 }}&nbsp;</td>
<td> <td>
<a href="{% url 'sapl.materia:pesquisar_materia'%}?tipo={{materias.0}}&ano={{coautoria.0}}&autoria__autor={{autor_pk}}&autoria__primeiro_autor=0"> <a href="{% url 'sapl.materia:pesquisar_materia'%}?tipo={{materias.0}}&ano={{coautoria.0}}&autoria__autor={{autor_pk}}&autoria__primeiro_autor=False">
{{ materias.2}} {{ materias.2}}
</a> </a>
</td> </td>

1
sapl/templates/protocoloadm/protocolo_list.html

@ -2,7 +2,6 @@
{% load i18n %} {% load i18n %}
{% load tz %} {% load tz %}
{% load crispy_forms_tags %} {% load crispy_forms_tags %}
{% load static %}
{% load webpack_static from webpack_loader %} {% load webpack_static from webpack_loader %}
{% block detail_content %} {% block detail_content %}

2
sapl/templates/protocoloadm/protocolo_mostrar.html

@ -22,7 +22,7 @@
{% endif %} <!-- TODO: convert if-else to custom tag --> {% endif %} <!-- TODO: convert if-else to custom tag -->
<strong>Natureza do Processo: </strong>{% if protocolo.tipo_processo == 0 %} Administrativo {% elif protocolo.tipo_processo == 1 %} Legislativo {% endif %}</br> <strong>Natureza do Processo: </strong>{% if protocolo.tipo_processo == 0 %} Administrativo {% elif protocolo.tipo_processo == 1 %} Legislativo {% endif %}</br>
<strong>Número de Páginas: </strong> {{ protocolo.numero_paginas }} </br> <strong>Número de Páginas: </strong> {{ protocolo.numero_paginas|default_if_none:"Não informado" }}</br>
<strong>Observação: </strong>{{ protocolo.observacao|default:"Não informado" }}</br> <strong>Observação: </strong>{{ protocolo.observacao|default:"Não informado" }}</br>
<strong>Anulado: {% if protocolo.anulado %} <font color="red"> Sim {% else %} <font color="green"> Não {% endif %} </font></strong> <strong>Anulado: {% if protocolo.anulado %} <font color="red"> Sim {% else %} <font color="green"> Não {% endif %} </font></strong>
<br /><br /> <br /><br />

14
sapl/templates/relatorios/header_ata.html

@ -8,24 +8,28 @@
<html lang="pt-br"> <html lang="pt-br">
<head> <head>
<link rel="stylesheet" href="{% static 'sapl/css/header-relatorio.css'%}"> <link rel="stylesheet" href="{% static 'sapl/css/header-relatorio.css'%}">
</head> </head>
<body> <body>
<section id="informations"> <section id="informations">
<dl> <dl>
<dt class="image-header"> <dt class="image-header">
<img src="{% if logotipo %}{{ MEDIA_URL }}{{ logotipo }}{% else %}{% webpack_static 'img/logo.png' %}{% endif %}"> <img style="max-height:2cm;max-width:2cm" src="{% if logotipo %}{{ MEDIA_URL }}{{ logotipo }}{% else %}{% webpack_static 'img/logo.png' %}{% endif %}">
</dt> </dt>
<dd class="title"> <dd class="title">
<ul> <ul>
<li style="margin-top:10px"><h2>{{casa.nome}}</h2></li> <li style="margin-top:10px;max-height:2cm;"><h2>{{casa.nome}}</h2></li>
<li><h3>Sistema de Apoio ao Processo Legislativo</h3></li> <li><h3>Sistema de Apoio ao Processo Legislativo</h3></li>
</ul> </ul>
</dd> </dd>
</dl> </dl>
</section> </section>
<p></p> {% if info %}
</body> <p><b>{{info}}</b></p>
{% else %}
<p></p>
{% endif %}
</body>
</html> </html>

2
sapl/templates/relatorios/relatorio_ata.html

@ -33,7 +33,7 @@
} }
} }
</style> </style>
<link rel="stylesheet" href="{% static '/sapl/css/relatorio.css'%}"> <link rel="stylesheet" href="{% static 'sapl/css/relatorio.css'%}">
</head> </head>
<body> <body>

7
sapl/templates/relatorios/relatorio_doc_administrativos.html

@ -1,12 +1,11 @@
{% load i18n %} {% load i18n %}
{% load common_tags %} {% load common_tags %}
{% load static %} {% load crispy_forms_tags staticfiles %}
<head> <head>
<style> <style>
@page{ @page{
margin-top: 4.5cm; margin-top: 5cm;
size: A4 portrait; size: A4 portrait;
@bottom-right { @bottom-right {
@ -39,7 +38,7 @@
} }
} }
</style> </style>
<link rel="stylesheet" href="{% static '/sapl/css/relatorio.css'%}"> <link rel="stylesheet" href="{% static '/sapl/css/relatorio.css'%}">
</head> </head>
<body> <body>

183
sapl/templates/relatorios/relatorio_sessao_plenaria.html

@ -0,0 +1,183 @@
{% load static %}
<!DOCTYPE html>
<meta charset="utf-8">
</meta>
<html lang="pt-br">
<head>
<style>
@page{
margin-top: 5cm;
size: A4 portrait;
@bottom-right {
content: "Página" counter(page);
height: 3cm;
font-size: 8pt;
}
@bottom-center {
border-top: 1px solid black;
font-size: 8pt;
height: 1cm;
content: "{{rodape|safe}}";
font-style:italic;
}
@bottom-left {
content: "{{data}}";
height: 3cm;
font-size: 8pt;
}
@top-center {
content: string(title);
}
header {
width: 0;
height: 0;
visibility: hidden;
string-set: title content();
}
}
</style>
<link rel="stylesheet" href="{% static '/sapl/css/relatorio.css'%}">
</head>
<body>
<div style="margin-bottom: 3cm">
<h2 class="gray-title">Informações Básicas</h2>
<p><b>Tipo da Sessão:</b> {{inf_basicas_dic.nom_sessao}}</p>
<p><b>Abertura:</b> {{inf_basicas_dic.dat_inicio_sessao}} - {{inf_basicas_dic.hr_inicio_sessao}}</p>
<p><b>Encerramento:</b> {{inf_basicas_dic.dat_fim_sessao}} - {{inf_basicas_dic.hr_fim_sessao}}</p>
<h2 class="gray-title">Mesa Diretora</h2>
{% for membro in lst_mesa%}
<p><b>{{membro.des_cargo}}:</b> {{membro.nom_parlamentar}}/{{membro.sgl_partido}}</p>
{% endfor%}
<h2 class="gray-title">Lista de Presença da Sessão</h2>
{% for membro in lst_presenca_sessao%}
<p>{{membro.nom_parlamentar}}/{{membro.sgl_partido}}</p>
{% endfor%}
<h2 class="gray-title">Justificativas de Ausência da Sessão</h2>
<table class="grayTable">
<thead>
<tr>
<th>Parlamentar</th>
<th>Justificativa</th>
<th>Ausente em</th>
</tr>
</thead>
<tbody>
{% for ausencia in lst_ausencia_sessao%}
<tr>
<td>{{ausencia.parlamentar}}</td>
<td>{{ausencia.justificativa}}</td>
<td>{{ausencia.tipo}}</td>
</tr>
{% endfor %}
</tbody>
</table>
<h2 class="gray-title">Expedientes</h2>
{% for expediente in lst_expedientes%}
<h3>{{expediente.nom_expediente}}</h3>
<p style="margin-bottom: 1cm">{{expediente.txt_expediente|safe}}</p>
{% endfor%}
<h2 class="gray-title">Matérias do Expediente</h2>
<table class="grayTable">
<thead>
<tr>
<th>Matéria</th>
<th>Ementa</th>
<th>Resultado da Votação</th>
</tr>
</thead>
<tbody>
{% for materia in lst_expediente_materia%}
<tr>
<td style="width:300px">
<dl>
<dt><b>{{materia.num_ordem}} -</b> {{materia.id_materia}}</dt>
<dt><b>Turno:</b> {{materia.des_turno}}</dt>
<dt><b>{{materia.num_autores}}: </b>{{materia.nom_autor}}</dt>
</dl>
</td>
<td><div style="margin:10px">{{materia.txt_ementa}}</div></td>
<td style="width:10px"><b>{{materia.nom_resultado}}</b></td>
</tr>
{% endfor %}
</tbody>
</table>
<h2 class="gray-title">Oradores do Expediente</h2>
{% for orador in lst_oradores_expediente%}
<tr>
<p> <b>{{orador.num_ordem}}</b> - {{orador.nom_parlamentar}}/{{orador.sgl_partido}}</p>
</tr>
{% endfor %}
<h2 class="gray-title">Lista de Presença da Ordem do Dia</h2>
{% for orador in lst_presenca_ordem_dia%}
<tr>
<p>{{orador.nom_parlamentar}}/{{orador.sgl_partido}}</p>
</tr>
{% endfor %}
<h2 class="gray-title">Matérias da Ordem do Dia</h2>
<table class="grayTable" style="height: 145px;" width="443">
<thead>
<tr>
<th>Matéria</th>
<th>Ementa</th>
<th>Resultado da Votação</th>
</tr>
</thead>
<tbody>
{% for materia in lst_votacao%}
<tr>
<td style="width:300px">
<dl>
<dt><b>{{materia.num_ordem}} -</b> {{materia.id_materia}}</dt>
<dt><b>Turno:</b> {{materia.des_turno}}</dt>
<dt><b>{{materia.num_autores}}: </b>{{materia.nom_autor}}</dt>
</dl>
</td>
<td><div style="margin:10px">{{materia.txt_ementa}}</div></td>
<td style="width:30px"><b>{{materia.nom_resultado}}</b></td>
</tr>
{% endfor %}
</tbody>
</table>
<div>
<h2 class="gray-title">Oradores das Explicações Pessoais</h2>
{% for orador in lst_oradores%}
<tr>
<p style="page-break-after: avoid;">{{orador.num_ordem}} - {{orador.nom_parlamentar}}/{{orador.sgl_partido}}</p>
</tr>
{% endfor %}
</div>
<h2 class="gray-title">Ocorrências da Sessão</h2>
{% for ocorrencia in lst_ocorrencias%}
<p>{{ocorrencia}}</p>
{% endfor %}
</div>
</body>
</html>

6
sapl/templates/rest_framework_docs/base.html

@ -1,4 +1,4 @@
{% load static from staticfiles %} {% load webpack_static from webpack_loader %}
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
@ -10,7 +10,7 @@
<title>{% block title %}DRF Docs{% endblock %}</title> <title>{% block title %}DRF Docs{% endblock %}</title>
{% block style %} {% block style %}
<link rel="stylesheet" href="{% static "rest_framework_docs/css/style.css" %}"> <link rel="stylesheet" href="{% webpack_static "rest_framework_docs/css/style.css" %}">
{% endblock %} {% endblock %}
</head> </head>
@ -76,6 +76,6 @@
</div> </div>
<!-- Dist.js - Inlcuded Live API, jQuery, Bootstrap --> <!-- Dist.js - Inlcuded Live API, jQuery, Bootstrap -->
<script type="text/javascript" src="{% static "rest_framework_docs/js/dist.min.js" %}"></script> <script type="text/javascript" src="{% webpack_static "rest_framework_docs/js/dist.min.js" %}"></script>
</body> </body>
</html> </html>

10
sapl/templates/sessao/blocos_ata/identificacao_basica.html

@ -2,8 +2,14 @@
<p align="justify"> <p align="justify">
<strong>Identificação Básica: </strong> <strong>Identificação Básica: </strong>
{% for b in basica %} {% for b in basica %}
{{b}} {{b}}
{% if not forloop.last %} ; {% endif %} {% if not forloop.last %}
;
{% else %}
{% if tema_solene %}
; {{tema_solene}}
{% endif %}
{% endif %}
{% endfor %} {% endfor %}
</p> </p>
</fieldset> </fieldset>

4
sapl/templates/sessao/blocos_resumo/identificacao_basica.html

@ -5,6 +5,10 @@
{% for b in basica %} {% for b in basica %}
<div class="col-md-4">{{b}}</div> <div class="col-md-4">{{b}}</div>
{% endfor %} {% endfor %}
{% if tema_solene %}
<br><br>
<div class="col-md-12">{{tema_solene}}</div>
{% endif %}
</div> </div>
</fieldset> </fieldset>
<br /><br /><br /> <br /><br /><br />

9
sapl/templates/sessao/layouts.yaml

@ -11,6 +11,15 @@ SessaoPlenaria:
- upload_pauta upload_ata upload_anexo - upload_pauta upload_ata upload_anexo
- url_audio url_video - 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: TipoResultadoVotacao:
{% trans 'Tipo de Resultado da Votação' %}: {% trans 'Tipo de Resultado da Votação' %}:
- nome - nome

4
sapl/templates/sessao/pauta_sessao_list.html

@ -1,6 +1,6 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load i18n staticfiles %} {% load i18n staticfiles %}
{% load webpack_static from webpack_loader %}
{% block base_content %} {% block base_content %}
{% if not page_obj %} {% if not page_obj %}
@ -18,7 +18,7 @@
<tr> <tr>
<td>{{sessao}}</td> <td>{{sessao}}</td>
<td><a href="{% url 'sapl.relatorios:relatorio_sessao_plenaria' sessao.id %}"> <td><a href="{% url 'sapl.relatorios:relatorio_sessao_plenaria' sessao.id %}">
<img src="{% static 'img/file.png' %}"> <img src="{% webpack_static 'img/file.png' %}">
</a></td> </a></td>
</tr> </tr>
{% endfor %} {% endfor %}

10
sapl/templates/sessao/resumo.html

@ -20,6 +20,16 @@
</p> </p>
</div> </div>
<div>
<p align="right">
<strong>
<a href="{% url 'sapl.relatorios:relatorio_sessao_plenaria_pdf' sessaoplenaria.pk %}">
Impressão PDF (Novo)
</a>
</strong>
</p>
</div>
{% include 'sessao/blocos_resumo/'|add:primeiro_ordenacao %} {% include 'sessao/blocos_resumo/'|add:primeiro_ordenacao %}
{% include 'sessao/blocos_resumo/'|add:segundo_ordenacao %} {% include 'sessao/blocos_resumo/'|add:segundo_ordenacao %}

121
sapl/templates/sessao/sessaoplenaria_form.html

@ -7,63 +7,88 @@
<script language="Javascript"> <script language="Javascript">
{% if not object %} function recuperar_numero_sessao() {
// faz recuperação do próximo número apenas em caso de inclusão de sessão plenária var tipo = $("#id_tipo").val();
// Seleciona o numero de acordo com o tipo var sessao = $("#id_sessao_legislativa").val();
function recuperar_numero_sessao() { var legislatura = $("#id_legislatura").val();
var tipo = $("#id_tipo").val() var data_ini = $("#id_data_inicio").val();
var sessao = $("#id_sessao_legislativa").val()
var legislatura = $("#id_legislatura").val() if (tipo) {
var data_ini = $("#id_data_inicio").val() $.get("{% url 'sapl.sessao:recuperar_numero_sessao_view' %}",
{
if (tipo) { tipo: tipo,
$.get("{% url 'sapl.sessao:recuperar_numero_sessao_view' %}", sessao_legislativa: sessao,
{ data_inicio: data_ini
tipo: tipo, },
sessao_legislativa: sessao, function(data, status) {
data_inicio: data_ini $("#id_numero").val(data.numero);
}, }
function(data, status) { );
$("#id_numero").val(data.numero);
}
);
}
else{
$("#id_numero").val('');
}
} }
$("#id_tipo").click(recuperar_numero_sessao); }
$("#id_sessao_legislativa").click(recuperar_numero_sessao);
$("#id_legislatura").click(recuperar_numero_sessao); function recuperar_tipo_sessao(){
{% endif %} var tipo = $("#id_tipo").val();
var sessao = $("#id_sessao_legislativa").val();
$("#div_id_tema_solene").hide();
if (tipo) {
$.get("{% url 'sapl.sessao:recuperar_nome_tipo_sessao' %}",
{
tipo: tipo,
sessao_legislativa:sessao
},
function(data, status) {
if(data.nome_tipo == "Solene"){
$("#div_id_tema_solene").show();
}
}
);
}
}
// Filtra as choices de sessao legislativa pela legislatura function altera_legislatura(){
$(function() { var id_legislatura = $("#id_legislatura").val();
var id_sessao_leg = $("#id_sessao_legislativa").val();
$("#id_sessao_legislativa option").remove();
if (id_legislatura) {
$.get("{% url 'sapl.sessao:sessao_legislativa_legislatura_ajax_view' %}", {legislatura: id_legislatura}, function(data) {
lista_sessoes = data['sessao_legislativa'];
function altera_legislatura(){ $('#id_sessao_legislativa').append('<option value="">' + '---------' + '</option>');
var id_legislatura = $("#id_legislatura").val(); for (i = 0; i < lista_sessoes.length; i++) {
var id_sessao_leg = $("#id_sessao_legislativa").val(); $('#id_sessao_legislativa').append('<option value="' + lista_sessoes[i][0] + '">' + lista_sessoes[i][1] + '</option>');
}
$("#id_sessao_legislativa").val(id_sessao_leg);
});
}
else{
$("#id_sessao_legislativa option").remove(); $("#id_sessao_legislativa option").remove();
if (id_legislatura) {
$.get("{% url 'sapl.sessao:sessao_legislativa_legislatura_ajax_view' %}", {legislatura: id_legislatura}, function(data) {
lista_sessoes = data['sessao_legislativa'];
$('#id_sessao_legislativa').append('<option value="">' + '---------' + '</option>');
for (i = 0; i < lista_sessoes.length; i++) {
$('#id_sessao_legislativa').append('<option value="' + lista_sessoes[i][0] + '">' + lista_sessoes[i][1] + '</option>');
}
$("#id_sessao_legislativa").val(id_sessao_leg);
});
}
else{
$("#id_sessao_legislativa option").remove();
}
} }
}
$("#id_legislatura").ready(altera_legislatura);
$(document).ready(function(){
{% if not object %}
// faz recuperação do próximo número apenas em caso de inclusão de sessão plenária
// Seleciona o numero de acordo com o tipo
$("#id_tipo").click(recuperar_numero_sessao);
$("#id_sessao_legislativa").click(recuperar_numero_sessao);
$("#id_legislatura").click(recuperar_numero_sessao);
{% endif %}
// Filtra as choices de sessao legislativa pela legislatura
$("#id_legislatura").ready(altera_legislatura);
$("#id_legislatura").change(altera_legislatura); $("#id_legislatura").change(altera_legislatura);
// Referente a Sessão Solene
$("#id_tipo").ready(recuperar_tipo_sessao);
$("#id_tipo").change(recuperar_tipo_sessao);
}); });
</script> </script>

34
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

2
setup.py

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

1
solr/sapl_configset/conf/managed-schema

@ -120,6 +120,7 @@
<field name="django_ct" type="string" indexed="true" stored="true" multiValued="false"/> <field name="django_ct" type="string" indexed="true" stored="true" multiValued="false"/>
<field name="django_id" type="string" indexed="true" stored="true" multiValued="false"/> <field name="django_id" type="string" indexed="true" stored="true" multiValued="false"/>
<field name="text" type="text_pt" indexed="true" stored="true" multiValued="false" /> <field name="text" type="text_pt" indexed="true" stored="true" multiValued="false" />
<field name="last_update" type="pdate" indexed="true" stored="true" default="NOW" />
<!-- This can be enabled, in case the client does not know what fields may be searched. It isn't enabled by default <!-- This can be enabled, in case the client does not know what fields may be searched. It isn't enabled by default
because it's very expensive to index everything twice. --> because it's very expensive to index everything twice. -->

BIN
solr/sapl_configset/conf/saplconfigset.zip

Binary file not shown.

166
solr/sapl_configset/conf/schema.xml

@ -1,166 +0,0 @@
<?xml version="1.0" ?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<schema name="default" version="1.6">
<types>
<fieldtype name="string" class="solr.StrField" sortMissingLast="true" omitNorms="true"/>
<fieldType name="boolean" class="solr.BoolField" sortMissingLast="true" omitNorms="true"/>
<fieldType name="booleans" class="solr.BoolField" sortMissingLast="true" multiValued="true"/>
<fieldtype name="binary" class="solr.BinaryField"/>
<!-- Numeric field types that manipulate the value into
a string value that isn't human-readable in its internal form,
but with a lexicographic ordering the same as the numeric ordering,
so that range queries work correctly. -->
<fieldType name="pint" class="solr.IntPointField" docValues="true" />
<fieldType name="pfloat" class="solr.FloatPointField" docValues="true" />
<fieldType name="plong" class="solr.LongPointField" docValues="true" />
<fieldType name="pdouble" class="solr.DoublePointField" docValues="true"/>
<fieldType name="pdate" class="solr.DatePointField" docValues="true" />
<!-- A Trie based date field ifor faster date range queries and date faceting. -->
<fieldType name="pints" class="solr.IntPointField" docValues="true" multiValued="true"/>
<fieldType name="pfloats" class="solr.FloatPointField" docValues="true" multiValued="true"/>
<fieldType name="plongs" class="solr.LongPointField" docValues="true" multiValued="true"/>
<fieldType name="pdoubles" class="solr.DoublePointField" docValues="true" multiValued="true"/>
<fieldType name="pdates" class="solr.DatePointField" docValues="true" multiValued="true"/>
<fieldType name="point" class="solr.PointType" dimension="2" subFieldSuffix="_d"/>
<fieldType name="location" class="solr.LatLonType" subFieldSuffix="_coordinate"/>
<fieldtype name="geohash" class="solr.GeoHashField"/>
<fieldType name="text_general" class="solr.TextField" positionIncrementGap="100">
<analyzer type="index">
<tokenizer class="solr.StandardTokenizerFactory"/>
<filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" />
<!-- in this example, we will only use synonyms at query time
<filter class="solr.SynonymFilterFactory" synonyms="index_synonyms.txt" ignoreCase="true" expand="false"/>
-->
<filter class="solr.LowerCaseFilterFactory"/>
</analyzer>
<analyzer type="query">
<tokenizer class="solr.StandardTokenizerFactory"/>
<filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" />
<filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
<filter class="solr.LowerCaseFilterFactory"/>
</analyzer>
</fieldType>
<!-- Portuguese -->
<dynamicField name="*_txt_pt" type="text_pt" indexed="true" stored="true"/>
<fieldType name="text_pt" class="solr.TextField" positionIncrementGap="100">
<analyzer>
<tokenizer class="solr.StandardTokenizerFactory"/>
<filter class="solr.LowerCaseFilterFactory"/>
<filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/stopwords_pt.txt" format="snowball" />
<filter class="solr.PortugueseLightStemFilterFactory"/>
<!-- less aggressive: <filter class="solr.PortugueseMinimalStemFilterFactory"/> -->
<!-- more aggressive: <filter class="solr.SnowballPorterFilterFactory" language="Portuguese"/> -->
<!-- most aggressive: <filter class="solr.PortugueseStemFilterFactory"/> -->
</analyzer>
</fieldType>
<fieldType name="text_en" class="solr.TextField" positionIncrementGap="100">
<analyzer type="index">
<tokenizer class="solr.StandardTokenizerFactory"/>
<filter class="solr.StopFilterFactory"
ignoreCase="true"
words="lang/stopwords_en.txt"
/>
<filter class="solr.LowerCaseFilterFactory"/>
<filter class="solr.EnglishPossessiveFilterFactory"/>
<filter class="solr.KeywordMarkerFilterFactory" protected="protwords.txt"/>
<!-- Optionally you may want to use this less aggressive stemmer instead of PorterStemFilterFactory:
<filter class="solr.EnglishMinimalStemFilterFactory"/>
-->
<filter class="solr.PorterStemFilterFactory"/>
</analyzer>
<analyzer type="query">
<tokenizer class="solr.StandardTokenizerFactory"/>
<filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
<filter class="solr.StopFilterFactory"
ignoreCase="true"
words="lang/stopwords_en.txt"
/>
<filter class="solr.LowerCaseFilterFactory"/>
<filter class="solr.EnglishPossessiveFilterFactory"/>
<filter class="solr.KeywordMarkerFilterFactory" protected="protwords.txt"/>
<!-- Optionally you may want to use this less aggressive stemmer instead of PorterStemFilterFactory:
<filter class="solr.EnglishMinimalStemFilterFactory"/>
-->
<filter class="solr.PorterStemFilterFactory"/>
</analyzer>
</fieldType>
<fieldType name="text_ws" class="solr.TextField" positionIncrementGap="100">
<analyzer>
<tokenizer class="solr.WhitespaceTokenizerFactory"/>
</analyzer>
</fieldType>
<fieldType name="ngram" class="solr.TextField" >
<analyzer type="index">
<tokenizer class="solr.KeywordTokenizerFactory"/>
<filter class="solr.LowerCaseFilterFactory"/>
<filter class="solr.NGramFilterFactory" minGramSize="3" maxGramSize="15" />
</analyzer>
<analyzer type="query">
<tokenizer class="solr.KeywordTokenizerFactory"/>
<filter class="solr.LowerCaseFilterFactory"/>
</analyzer>
</fieldType>
<fieldType name="edge_ngram" class="solr.TextField" positionIncrementGap="1">
<analyzer type="index">
<tokenizer class="solr.WhitespaceTokenizerFactory" />
<filter class="solr.LowerCaseFilterFactory" />
<filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="0" catenateNumbers="0" catenateAll="0" splitOnCaseChange="1"/>
<filter class="solr.EdgeNGramFilterFactory" minGramSize="2" maxGramSize="15" />
</analyzer>
<analyzer type="query">
<tokenizer class="solr.WhitespaceTokenizerFactory" />
<filter class="solr.LowerCaseFilterFactory" />
<filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="0" catenateNumbers="0" catenateAll="0" splitOnCaseChange="1"/>
</analyzer>
</fieldType>
</types>
<fields>
<!-- general -->
<field name="id" type="string" indexed="true" stored="true" multiValued="false" required="true"/>
<field name="django_ct" type="string" indexed="true" stored="true" multiValued="false"/>
<field name="django_id" type="string" indexed="true" stored="true" multiValued="false"/>
<field name="_version_" type="plong" indexed="true" stored ="true"/>
<field name="text" type="text_pt" indexed="true" stored="true" multiValued="false" />
<field name="last_update" type="pdate" indexed="true" stored="true" default="NOW" />
</fields>
<!-- field to use to determine and enforce document uniqueness. -->
<uniqueKey>id</uniqueKey>
<!-- field for the QueryParser to use when an explicit fieldname is absent -->
<df>text</df>
<!-- SolrQueryParser configuration: defaultOperator="AND|OR" -->
<solrQueryParser q.op="AND"/>
</schema>
Loading…
Cancel
Save