Browse Source

Merge tag '3.1.161-RC3' into migracao

migracao
Marcio Mazza 5 years ago
parent
commit
74ea4e0285
  1. 2
      .gitignore
  2. 8
      Dockerfile.dev
  3. 2
      busy-wait.sh
  4. 2
      check_solr.sh
  5. 40
      docker-compose-dev.yml
  6. 99
      docker-compose.yml
  7. 2
      docker-env.sh
  8. 40
      docs/howtogit.rst
  9. 17
      docs/token-auth.rst
  10. 2
      gunicorn_start.sh
  11. 2
      release.sh
  12. 14
      requirements/requirements.txt
  13. 2
      requirements/test-requirements.txt
  14. 27
      sapl/api/migrations/0001_initial.py
  15. 0
      sapl/api/migrations/__init__.py
  16. 5
      sapl/api/pagination.py
  17. 2
      sapl/api/serializers.py
  18. 5
      sapl/api/urls.py
  19. 39
      sapl/api/views.py
  20. 72
      sapl/audiencia/forms.py
  21. 28
      sapl/audiencia/migrations/0015_auto_20200518_1158.py
  22. 13
      sapl/audiencia/models.py
  23. 14
      sapl/audiencia/tests/test_audiencia.py
  24. 362
      sapl/base/forms.py
  25. 4
      sapl/base/tests/test_base.py
  26. 150
      sapl/base/tests/test_view_base.py
  27. 7
      sapl/base/urls.py
  28. 194
      sapl/base/views.py
  29. 143
      sapl/comissoes/forms.py
  30. 26
      sapl/comissoes/migrations/0024_auto_20200602_0915.py
  31. 21
      sapl/comissoes/migrations/0025_auto_20200605_1051.py
  32. 6
      sapl/comissoes/models.py
  33. 20
      sapl/comissoes/tests/test_comissoes.py
  34. 7
      sapl/comissoes/views.py
  35. 9
      sapl/compilacao/admin.py
  36. 22
      sapl/compilacao/tests/test_compilacao.py
  37. 4
      sapl/compilacao/tests/test_tipo_texto_articulado_form.py
  38. 10
      sapl/crud/base.py
  39. 12
      sapl/crud/tests/test_base.py
  40. 2
      sapl/legacy/run_legacy_tests.sh
  41. 2
      sapl/legacy/scripts/migra_dbs.sh
  42. 2
      sapl/legacy/scripts/migra_um_db.sh
  43. 2
      sapl/legacy/scripts/recria_dbs_postgres.sh
  44. 2
      sapl/legacy/scripts/recria_um_db_postgres.sh
  45. 2
      sapl/legacy/scripts/shell_para_migracao.sh
  46. 45
      sapl/materia/forms.py
  47. 23
      sapl/materia/migrations/0065_auto_20200313_1137.py
  48. 20
      sapl/materia/migrations/0066_auto_20200313_1441.py
  49. 30
      sapl/materia/migrations/0067_auto_20200416_1538.py
  50. 10
      sapl/materia/models.py
  51. 106
      sapl/materia/tests/test_materia.py
  52. 12
      sapl/materia/tests/test_materia_form.py
  53. 6
      sapl/materia/urls.py
  54. 351
      sapl/materia/views.py
  55. 2
      sapl/norma/forms.py
  56. 20
      sapl/norma/migrations/0032_auto_20200221_1533.py
  57. 20
      sapl/norma/migrations/0033_auto_20200416_1538.py
  58. 4
      sapl/norma/models.py
  59. 20
      sapl/norma/tests/test_norma.py
  60. 2
      sapl/norma/views.py
  61. 3
      sapl/painel/views.py
  62. 20
      sapl/parlamentares/migrations/0031_auto_20200407_1406.py
  63. 2
      sapl/parlamentares/models.py
  64. 8
      sapl/parlamentares/tests/test_mandato.py
  65. 38
      sapl/parlamentares/tests/test_parlamentares.py
  66. 4
      sapl/parlamentares/urls.py
  67. 19
      sapl/parlamentares/views.py
  68. 48
      sapl/protocoloadm/forms.py
  69. 20
      sapl/protocoloadm/migrations/0031_documentoadministrativo_caractere_identificador.py
  70. 25
      sapl/protocoloadm/migrations/0032_auto_20200416_1538.py
  71. 8
      sapl/protocoloadm/models.py
  72. 105
      sapl/protocoloadm/tests/test_protocoloadm.py
  73. 7
      sapl/protocoloadm/urls.py
  74. 66
      sapl/protocoloadm/views.py
  75. 621
      sapl/relatorios/views.py
  76. 99
      sapl/sessao/forms.py
  77. 30
      sapl/sessao/migrations/0051_auto_20200416_1538.py
  78. 6
      sapl/sessao/models.py
  79. 80
      sapl/sessao/tests/test_sessao.py
  80. 30
      sapl/sessao/tests/test_sessao_view.py
  81. 17
      sapl/sessao/urls.py
  82. 132
      sapl/sessao/views.py
  83. 21
      sapl/settings.py
  84. 15
      sapl/static/sapl/css/ancora.css
  85. 3
      sapl/static/sapl/css/relatorio.css
  86. 126
      sapl/static/sapl/frontend/css/chunk-vendors.42151acc.css
  87. BIN
      sapl/static/sapl/frontend/css/chunk-vendors.42151acc.css.gz
  88. 126
      sapl/static/sapl/frontend/css/chunk-vendors.aa0d128d.css
  89. BIN
      sapl/static/sapl/frontend/css/chunk-vendors.aa0d128d.css.gz
  90. 1
      sapl/static/sapl/frontend/css/global.278b5d61.css
  91. BIN
      sapl/static/sapl/frontend/css/global.278b5d61.css.gz
  92. 1
      sapl/static/sapl/frontend/css/global.3b8f6afb.css
  93. BIN
      sapl/static/sapl/frontend/css/global.3b8f6afb.css.gz
  94. BIN
      sapl/static/sapl/frontend/fonts/fa-brands-400.06147b6c.ttf.gz
  95. BIN
      sapl/static/sapl/frontend/fonts/fa-brands-400.13685372.ttf
  96. BIN
      sapl/static/sapl/frontend/fonts/fa-brands-400.13685372.ttf.gz
  97. BIN
      sapl/static/sapl/frontend/fonts/fa-brands-400.5063b105.eot.gz
  98. BIN
      sapl/static/sapl/frontend/fonts/fa-brands-400.a06da7f0.woff2
  99. BIN
      sapl/static/sapl/frontend/fonts/fa-brands-400.c1868c95.eot
  100. BIN
      sapl/static/sapl/frontend/fonts/fa-brands-400.c1868c95.eot.gz

2
.gitignore

@ -105,3 +105,5 @@ solr-*/
# ignora tudo dentro de media, mas cria a pasta no checkout
media/*
!media/.gitkeep
restauracoes/*

8
Dockerfile.dev

@ -0,0 +1,8 @@
FROM python:3.7
ENV PYTHONUNBUFFERED 1
WORKDIR /sapl-dev
COPY requirements ./requirements/
RUN apt update && \
apt -y install graphviz-dev && \
pip install -r ./requirements/dev-requirements.txt
EXPOSE 8000

2
busy-wait.sh

@ -1,4 +1,4 @@
#!/bin/sh
#!/usr/bin/env bash
while true; do
COUNT_PG=`psql $1 -c '\l \q' | grep sapl | wc -l`

2
check_solr.sh

@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
# Pass the base SOLR URL as parameter, i.e., bash check_solr http://localhost:8983

40
docker-compose-dev.yml

@ -0,0 +1,40 @@
version: '3.7'
services:
sapldb-dev:
container_name: sapldb-dev
image: postgres:10.5-alpine
environment:
POSTGRES_PASSWORD: sapl
POSTGRES_USER: sapl
POSTGRES_DB: sapl
ports:
- "5433:5432"
networks:
- sapl-net-dev
sapl-dev:
container_name: sapl-dev
image: sapl:dev
build:
context: .
dockerfile: Dockerfile.dev
command: python3 manage.py runserver 0:8000
volumes:
- .:/sapl-dev
ports:
- "8000:8000"
environment:
SECRET_KEY: '$dkhxm-$zvxdox$g2-&w^1i!_z1juq0xwox6e3#gy6w_88!3t^'
DEBUG: 'True'
DATABASE_URL: postgresql://sapl:sapl@sapldb-dev:5432/sapl
TZ: America/Sao_Paulo
depends_on:
- sapldb-dev
networks:
- sapl-net-dev
networks:
sapl-net-dev:
name: sapl-net-dev
driver: bridge

99
docker-compose.yml

@ -1,34 +1,65 @@
sapldb:
image: postgres:10.5-alpine
restart: always
environment:
POSTGRES_PASSWORD: sapl
POSTGRES_USER: sapl
POSTGRES_DB: sapl
PGDATA : /var/lib/postgresql/data/
volumes:
- sapldb_data:/var/lib/postgresql/data/
ports:
- "5433:5432"
sapl:
image: interlegis/sapl:3.1.160-RC12
#build: .
restart: always
environment:
ADMIN_PASSWORD: interlegis
ADMIN_EMAIL: email@dominio.net
DEBUG: 'False'
EMAIL_PORT: 587
EMAIL_USE_TLS: 'False'
EMAIL_HOST: smtp.dominio.net
EMAIL_HOST_USER: usuariosmtp
EMAIL_SEND_USER: usuariosmtp
EMAIL_HOST_PASSWORD: senhasmtp
TZ: America/Sao_Paulo
volumes:
- sapl_data:/var/interlegis/sapl/data
- sapl_media:/var/interlegis/sapl/media
links:
- sapldb
ports:
- "80:80"
version: "3.7"
services:
sapldb:
image: postgres:10.5-alpine
restart: always
environment:
POSTGRES_PASSWORD: sapl
POSTGRES_USER: sapl
POSTGRES_DB: sapl
PGDATA : /var/lib/postgresql/data/
volumes:
- sapldb_data:/var/lib/postgresql/data/
ports:
- "5433:5432"
networks:
- sapl-net
saplsolr:
image: solr:8.3
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"
networks:
- sapl-net
sapl:
image: interlegis/sapl:3.1.161-RC3
#build: .
restart: always
environment:
ADMIN_PASSWORD: interlegis
ADMIN_EMAIL: email@dominio.net
DEBUG: 'False'
EMAIL_PORT: 587
EMAIL_USE_TLS: 'False'
EMAIL_HOST: smtp.dominio.net
EMAIL_HOST_USER: usuariosmtp
EMAIL_SEND_USER: usuariosmtp
EMAIL_HOST_PASSWORD: senhasmtp
USE_SOLR: 'True'
SOLR_COLLECTION: sapl
SOLR_URL: http://saplsolr:8983
TZ: America/Sao_Paulo
volumes:
- sapl_data:/var/interlegis/sapl/data
- sapl_media:/var/interlegis/sapl/media
depends_on:
- sapldb
- saplsolr
ports:
- "80:80"
networks:
- sapl-net
networks:
sapl-net:
name: sapl-net
driver: bridge
volumes:
sapldb_data:
sapl_data:
sapl_media:
solr_data:
solr_configsets:

2
docker-env.sh

@ -1,4 +1,4 @@
#/bin/bash
#!/usr/bin/env bash
KEY=`python gen-key.py`
echo $KEY

40
docs/howtogit.rst

@ -1,15 +1,17 @@
De forma muito simples e em linhas gerais o básico sobre GIT
====
De forma muito simples e em linhas gerais o básico sobre Git
====
Glosário
---------
Git - Sistema de controle de versão de aquivos
GitHub - É um serviço web que oferece diversas funcionalidades extras aplicadas ao git
GitHub - É um serviço web que oferece diversas funcionalidades extras aplicadas ao Git
Branch - Significa ramificar seu projeto, criar um snapshot.
Branch - Significa ramificar seu projeto, criar um snapshot
Merge - Significa incorporar seu branch no master
Merge - Significa incorporar seu branch ao master
Pode ser útil
@ -23,58 +25,52 @@ Exibir informações:
git status
Ver repositorio
Ver repositório:
git remote -v
Para definir repositorio
Definir repositório:
git remote set-url origin https://github.com/interlegis/sapl.git
Para criar um branch
Criar um branch:
git checkout -b nome_branch
git add arquivos
Para remover um branch
Remover um branch:
git branch -d nome-branch
Para comitar
Commitar:
git commit -m "Comentário"
Para enviar o branch
Enviar o branch:
git push origin nome_branch
Na base local descartar alguma alteração feita nos arquivos:
Na base local, descartar alguma alteração feita nos arquivos:
git checkout -- <arquivo>
Ao invés dissoremover todas as alterações e commits locais, recuperar o histórico mais recente do servidor e apontar para seu branch master local
Ao invés disso, remover todas as alterações e commits locais, recuperar o histórico mais recente do servidor e apontar para seu branch master local:
git fetch origin
git reset --hard origin/master
Atualizar para alguma brach especifica (ex:785-atualizar-migracao):
Atualizar para algum branch específico (ex:785-atualizar-migracao):
git checkout 785-atualizar-migracao
Voltar para a branch master
Voltar para a branch master:
git checkout master
Verificar 5 ultimos comits:
Verificar os últimos 5 commits:
git log --oneline -n 5
Referência:

17
docs/token-auth.rst

@ -0,0 +1,17 @@
1. Realizar o migrate
./manage.py migrate
2. Criar um API Token para usuário e anotar a API Key gerada.
python3 manage.py drf_create_token admin
3. Testar endpoint
curl http://localhost:8000/api/version -H 'Authorization: Token <API Key>'
4. Exemplo de POST
curl -d '{"nome_completo”:”Gozer The Gozerian“, "nome_parlamentar": “Gozer”, "sexo":"M"}' -X POST http://localhost:8000/api/parlamentares/parlamentar/ -H 'Authorization: Token <API Key>' -H 'Content-Type: application/json'
Note: If you use TokenAuthentication in production you must ensure that your API is only available over https.
References: https://www.django-rest-framework.org/api-guide/authentication/#tokenauthentication

2
gunicorn_start.sh

@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
# As seen in http://tutos.readthedocs.org/en/latest/source/ndg.html

2
release.sh

@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
##
## Versioning info: [major].[minor].[patch][-RC[num]], example: 3.1.159, 3.1.159-RC1

14
requirements/requirements.txt

@ -1,4 +1,4 @@
django>=1.11.27,<2.0
django>=1.11.29,<2.0
django-haystack==2.8.1
django-filter==2.0.0
djangorestframework==3.9.1
@ -24,13 +24,13 @@ pytz==2019.3
rtyaml==0.0.5
python-magic==0.4.15
unipath==1.1
WeasyPrint==50
Pillow==6.2.0
WeasyPrint==51
Pillow==6.2.2
gunicorn==19.9.0
more-itertools==8.2.0
pysolr==3.6.0
PyPDF4==1.27.0
pyoai==2.5.0
git+git://github.com/interlegis/trml2pdf.git
git+git://github.com/interlegis/django-admin-bootstrapped
git+https://github.com/interlegis/trml2pdf
git+https://github.com/interlegis/django-admin-bootstrapped

2
requirements/test-requirements.txt

@ -3,7 +3,7 @@ coverage==4.1
django-webtest==1.7.8
flake8==2.6.2
isort==4.2.5
model-mommy==1.2.6
model-bakery==1.1.0
pep8==1.7.0
pytest==2.9.2
pytest-cov==2.3.0

27
sapl/api/migrations/0001_initial.py

@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2020-04-27 17:40
from __future__ import unicode_literals
from django.db import migrations
from django.conf import settings
from django.contrib.auth import get_user_model
from rest_framework.authtoken.models import Token
def adiciona_token_de_usuarios(apps, schema_editor):
for user in get_user_model().objects.all():
Token.objects.get_or_create(user=user)
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('authtoken', '0002_auto_20160226_1747')
]
operations = [
migrations.RunPython(adiciona_token_de_usuarios)
]

0
sapl/api/migrations/__init__.py

5
sapl/api/pagination.py

@ -8,6 +8,11 @@ class StandardPagination(pagination.PageNumberPagination):
page_size_query_param = 'page_size'
max_page_size = 50
def paginate_queryset(self, queryset, request, view=None):
if request.query_params.get('get_all', False) == 'true':
return None
return super().paginate_queryset(queryset, request, view=view)
def get_paginated_response(self, data):
try:
previous_page_number = self.page.previous_page_number()

2
sapl/api/serializers.py

@ -139,7 +139,7 @@ class ParlamentarResumeSerializer(serializers.ModelSerializer):
# Caso não exista filiação com essas condições
except ObjectDoesNotExist:
self.logger.error("user=" + username + ". Parlamentar com (data<={} e data_desfiliacao>={}) "
self.logger.warning("user=" + username + ". Parlamentar com (data<={} e data_desfiliacao>={}) "
"ou (data<={} e data_desfiliacao=Null)) não possui filiação."
.format(legislatura.data_fim, legislatura.data_fim, legislatura.data_fim))
filiacao = 'Não possui filiação'

5
sapl/api/urls.py

@ -6,7 +6,7 @@ from rest_framework.routers import DefaultRouter
from sapl.api.deprecated import MateriaLegislativaViewSet, SessaoPlenariaViewSet,\
AutoresProvaveisListView, AutoresPossiveisListView, AutorListView,\
ModelChoiceView
from sapl.api.views import SaplApiViewSetConstrutor
from sapl.api.views import SaplApiViewSetConstrutor, AppVersionView, recria_token
from .apps import AppConfig
@ -70,7 +70,8 @@ urlpatterns = [
url(r'^api/', include(deprecated_urlpatterns_api)),
url(r'^api/', include(urlpatterns_api_doc)),
url(r'^api/', include(urlpatterns_router)),
url(r'^api/version', AppVersionView.as_view()),
url(r'^api/recriar-token/(?P<pk>\d*)$', recria_token, name="recria_token"),
# implementar caminho para autenticação
# https://www.django-rest-framework.org/tutorial/4-authentication-and-permissions/

39
sapl/api/views.py

@ -3,8 +3,11 @@ import logging
from django import apps
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.core.urlresolvers import reverse_lazy
from django.db.models import Q
from django.db.models.fields.files import FileField
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.utils.decorators import classonlymethod
from django.utils.text import capfirst
from django.utils.translation import ugettext_lazy as _
@ -16,10 +19,14 @@ from django_filters.utils import resolve_field
from django.utils import timezone
from django.core.exceptions import ObjectDoesNotExist
from rest_framework import serializers as rest_serializers
from rest_framework.decorators import action
from rest_framework.authtoken.models import Token
from rest_framework.decorators import action, api_view, permission_classes
from rest_framework.fields import SerializerMethodField
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet
from rest_framework.permissions import IsAuthenticated, IsAdminUser
from rest_framework.views import APIView
from sapl.api.forms import SaplFilterSetMixin
from sapl.api.permissions import SaplModelPermissions
@ -36,6 +43,21 @@ from sapl.utils import models_with_gr_for_model, choice_anos_com_sessaoplenaria
from sapl.parlamentares.models import Mandato, Parlamentar, Legislatura
@receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_auth_token(sender, instance=None, created=False, **kwargs):
if created:
Token.objects.create(user=instance)
@api_view(['POST'])
@permission_classes([IsAdminUser])
def recria_token(request, pk):
Token.objects.get(user_id=pk).delete()
token = Token.objects.create(user_id=pk)
return Response({"message": "Token recriado com sucesso!", "token": token.key})
class BusinessRulesNotImplementedMixin:
def create(self, request, *args, **kwargs):
raise Exception(_("POST Create não implementado"))
@ -587,3 +609,18 @@ class _NormaJuridicaViewset:
def destaques(self, request, *args, **kwargs):
self.queryset = self.get_queryset().filter(norma_de_destaque=True)
return self.list(request, *args, **kwargs)
class AppVersionView(APIView):
permission_classes = (IsAuthenticated,)
def get(self, request):
content = {
'name': 'SAPL',
'description': 'Sistema de Apoio ao Processo Legislativo',
'version': settings.SAPL_VERSION,
'user': request.user.username,
'is_authenticated': request.user.is_authenticated(),
}
return Response(content)

72
sapl/audiencia/forms.py

@ -10,44 +10,56 @@ from crispy_forms.layout import Button, Column, Fieldset, HTML, Layout
from sapl.audiencia.models import AudienciaPublica, TipoAudienciaPublica, AnexoAudienciaPublica
from sapl.crispy_layout_mixin import form_actions, SaplFormHelper, SaplFormLayout, to_row
from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa
from sapl.parlamentares.models import Parlamentar
from sapl.utils import timezone, FileFieldCheckMixin, validar_arquivo
class AudienciaForm(FileFieldCheckMixin, forms.ModelForm):
logger = logging.getLogger(__name__)
data_atual = timezone.now()
tipo = forms.ModelChoiceField(required=True,
label='Tipo de Audiência Pública',
queryset=TipoAudienciaPublica.objects.all().order_by('nome'))
tipo = forms.ModelChoiceField(
required=True,
label=_('Tipo de Audiência Pública'),
queryset=TipoAudienciaPublica.objects.all().order_by('nome'))
tipo_materia = forms.ModelChoiceField(
label=_('Tipo Matéria'),
required=False,
queryset=TipoMateriaLegislativa.objects.all(),
empty_label='Selecione',
)
empty_label=_('Selecione'))
numero_materia = forms.CharField(
label='Número Matéria', required=False)
label=_('Número Matéria'),
required=False)
ano_materia = forms.CharField(
label='Ano Matéria',
label=_('Ano Matéria'),
required=False)
materia = forms.ModelChoiceField(required=False,
widget=forms.HiddenInput(),
queryset=MateriaLegislativa.objects.all())
materia = forms.ModelChoiceField(
required=False,
widget=forms.HiddenInput(),
queryset=MateriaLegislativa.objects.all())
parlamentar_autor = forms.ModelChoiceField(
label=_("Parlamentar Autor"),
required=False,
queryset=Parlamentar.objects.all())
requerimento = forms.ModelChoiceField(
label=_("Requerimento"),
required=False,
queryset=MateriaLegislativa.objects.select_related("tipo").filter(tipo__descricao="Requerimento"))
class Meta:
model = AudienciaPublica
fields = ['tipo', 'numero', 'nome',
'tema', 'data', 'hora_inicio', 'hora_fim',
'observacao', 'audiencia_cancelada', 'url_audio',
'observacao', 'audiencia_cancelada', 'parlamentar_autor', 'requerimento', 'url_audio',
'url_video', 'upload_pauta', 'upload_ata',
'upload_anexo', 'tipo_materia', 'numero_materia',
'ano_materia', 'materia']
def __init__(self, **kwargs):
super(AudienciaForm, self).__init__(**kwargs)
@ -62,9 +74,7 @@ class AudienciaForm(FileFieldCheckMixin, forms.ModelForm):
for t in tipos:
t.save()
def clean(self):
cleaned_data = super(AudienciaForm, self).clean()
if not self.is_valid():
return cleaned_data
@ -72,6 +82,8 @@ class AudienciaForm(FileFieldCheckMixin, forms.ModelForm):
materia = cleaned_data['numero_materia']
ano_materia = cleaned_data['ano_materia']
tipo_materia = cleaned_data['tipo_materia']
parlamentar_autor = cleaned_data["parlamentar_autor"]
requerimento = cleaned_data["requerimento"]
if materia and ano_materia and tipo_materia:
try:
@ -83,8 +95,10 @@ class AudienciaForm(FileFieldCheckMixin, forms.ModelForm):
except ObjectDoesNotExist:
msg = _('A matéria %s%s/%s não existe no cadastro'
' de matérias legislativas.' % (tipo_materia, materia, ano_materia))
self.logger.error('A MateriaLegislativa %s%s/%s não existe no cadastro'
' de matérias legislativas.' % (tipo_materia, materia, ano_materia))
self.logger.warn(
'A MateriaLegislativa %s%s/%s não existe no cadastro'
' de matérias legislativas.' % (tipo_materia, materia, ano_materia)
)
raise ValidationError(msg)
else:
self.logger.info("MateriaLegislativa %s%s/%s obtida com sucesso." % (tipo_materia, materia, ano_materia))
@ -94,8 +108,10 @@ class AudienciaForm(FileFieldCheckMixin, forms.ModelForm):
campos = [materia, tipo_materia, ano_materia]
if campos.count(None) + campos.count('') < len(campos):
msg = _('Preencha todos os campos relacionados à Matéria Legislativa')
self.logger.error('Algum campo relacionado à MatériaLegislativa %s%s/%s \
não foi preenchido.' % (tipo_materia, materia, ano_materia))
self.logger.warn(
'Algum campo relacionado à MatériaLegislativa %s%s/%s \
não foi preenchido.' % (tipo_materia, materia, ano_materia)
)
raise ValidationError(msg)
if not cleaned_data['numero']:
@ -107,12 +123,16 @@ class AudienciaForm(FileFieldCheckMixin, forms.ModelForm):
cleaned_data['numero'] = 1
if self.cleaned_data['hora_inicio'] and self.cleaned_data['hora_fim']:
if (self.cleaned_data['hora_fim'] <
self.cleaned_data['hora_inicio']):
msg = _('A hora de fim ({}) não pode ser anterior a hora '
'de início({})'.format(self.cleaned_data['hora_fim'], self.cleaned_data['hora_inicio']))
self.logger.error('Hora de fim anterior à hora de início.')
raise ValidationError(msg)
if self.cleaned_data['hora_fim'] < self.cleaned_data['hora_inicio']:
msg = _('A hora de fim ({}) não pode ser anterior a hora de início({})'
.format(self.cleaned_data['hora_fim'], self.cleaned_data['hora_inicio']))
self.logger.warn(
'Hora de fim anterior à hora de início.'
)
raise ValidationError(msg)
if parlamentar_autor.autor.first() not in requerimento.autores.all():
raise ValidationError("Parlamentar Autor selecionado não faz parte da autoria do Requerimento selecionado.")
upload_pauta = self.cleaned_data.get('upload_pauta', False)
upload_ata = self.cleaned_data.get('upload_ata', False)
@ -120,10 +140,10 @@ class AudienciaForm(FileFieldCheckMixin, forms.ModelForm):
if upload_pauta:
validar_arquivo(upload_pauta, "Pauta da Audiência Pública")
if upload_ata:
validar_arquivo(upload_ata, "Ata da Audiência Pública")
if upload_anexo:
validar_arquivo(upload_anexo, "Anexo da Audiência Pública")

28
sapl/audiencia/migrations/0015_auto_20200518_1158.py

@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2020-05-18 14:58
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('parlamentares', '0031_auto_20200407_1406'),
('materia', '0067_auto_20200416_1538'),
('audiencia', '0014_auto_20191023_1538'),
]
operations = [
migrations.AddField(
model_name='audienciapublica',
name='parlamentar_autor',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='parlamentares.Parlamentar', verbose_name='Parlamentar Autor'),
),
migrations.AddField(
model_name='audienciapublica',
name='requerimento',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='requerimento', to='materia.MateriaLegislativa', verbose_name='Requerimento da Audiência Pública'),
),
]

13
sapl/audiencia/models.py

@ -80,6 +80,19 @@ class AudienciaPublica(models.Model):
default=False,
choices=YES_NO_CHOICES,
verbose_name=_('Audiência Cancelada?'))
parlamentar_autor = models.ForeignKey(
Parlamentar,
on_delete=models.PROTECT,
null=True,
blank=True,
verbose_name=_('Parlamentar Autor'))
requerimento = models.ForeignKey(
MateriaLegislativa,
null=True,
blank=True,
on_delete=models.PROTECT,
verbose_name=_('Requerimento da Audiência Pública'),
related_name=_('requerimento'))
url_audio = models.URLField(
max_length=150, blank=True,
verbose_name=_('URL Arquivo Áudio (Formatos MP3 / AAC)'))

14
sapl/audiencia/tests/test_audiencia.py

@ -1,6 +1,6 @@
import pytest
import datetime
from model_mommy import mommy
from model_bakery import baker
from django.utils.translation import ugettext as _
from sapl.audiencia import forms
@ -11,7 +11,7 @@ from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa
@pytest.mark.django_db(transaction=False)
def test_tipo_audiencia_publica_model():
mommy.make(TipoAudienciaPublica,
baker.make(TipoAudienciaPublica,
nome='Teste_Nome_Tipo_Audiencia_Publica',
tipo='A')
@ -22,7 +22,7 @@ def test_tipo_audiencia_publica_model():
@pytest.mark.django_db(transaction=False)
def test_audiencia_publica_model():
mommy.make(AudienciaPublica,
baker.make(AudienciaPublica,
numero=1,
nome='Teste_Nome_Audiencia_Publica',
tema='Teste_Tema_Audiencia_Publica',
@ -43,14 +43,14 @@ def test_audiencia_publica_model():
@pytest.mark.django_db(transaction=False)
def test_anexo_audiencia_publica_model():
audiencia = mommy.make(AudienciaPublica,
audiencia = baker.make(AudienciaPublica,
numero=2,
nome='Nome_Audiencia_Publica',
tema='Tema_Audiencia_Publica',
data='2017-04-22',
hora_inicio='17:04')
mommy.make(AnexoAudienciaPublica,
baker.make(AnexoAudienciaPublica,
audiencia=audiencia)
anexo_audiencia_publica = AnexoAudienciaPublica.objects.first()
@ -76,9 +76,9 @@ def test_valida_campos_obrigatorios_audiencia_form():
@pytest.mark.django_db(transaction=False)
def test_audiencia_form_hora_invalida():
tipo_materia = mommy.make(TipoMateriaLegislativa)
tipo_materia = baker.make(TipoMateriaLegislativa)
tipo = mommy.make(TipoAudienciaPublica)
tipo = baker.make(TipoAudienciaPublica)
form = forms.AudienciaForm(data={'nome': 'Nome da Audiencia',
'tema': 'Tema da Audiencia',

362
sapl/base/forms.py

@ -117,8 +117,7 @@ class UsuarioCreateForm(ModelForm):
data = self.cleaned_data
if data['password1'] != data['password2']:
self.logger.error('Erro de validação. Senhas informadas ({}, {}) são diferentes.'.format(
data['password1'], data['password2']))
self.logger.warn('Erro de validação. Senhas informadas são diferentes.')
raise ValidationError('Senhas informadas são diferentes')
return data
@ -177,54 +176,82 @@ class UsuarioEditForm(ModelForm):
# ROLES = [(g.id, g.name) for g in Group.objects.all().order_by('name')]
ROLES = []
token = forms.CharField(
required=False,
label="Token",
max_length=40,
widget=forms.TextInput(attrs={'readonly': 'readonly'}))
first_name = forms.CharField(
required=False,
label="Nome",
max_length=30)
last_name = forms.CharField(
required=False,
label="Sobrenome",
max_length=30)
password1 = forms.CharField(
required=False, widget=forms.PasswordInput, label='Senha')
required=False,
widget=forms.PasswordInput,
label='Senha')
password2 = forms.CharField(
required=False, widget=forms.PasswordInput, label='Confirmar senha')
user_active = forms.ChoiceField(choices=YES_NO_CHOICES, required=True,
label="Usuário ativo?", initial='True')
required=False, widget=forms.PasswordInput,
label='Confirmar senha')
user_active = forms.ChoiceField(
choices=YES_NO_CHOICES,
required=True,
label="Usuário ativo?",
initial='True')
roles = forms.MultipleChoiceField(
required=True, widget=forms.CheckboxSelectMultiple(), choices=get_roles)
required=True,
widget=forms.CheckboxSelectMultiple(),
choices=get_roles)
class Meta:
model = get_user_model()
fields = [
get_user_model().USERNAME_FIELD, 'password1',
'password2', 'user_active', 'roles'
] + (['email']
if get_user_model().USERNAME_FIELD != 'email' else [])
get_user_model().USERNAME_FIELD,
"token",
"first_name",
"last_name",
'password1',
'password2',
'user_active',
'roles']
if get_user_model().USERNAME_FIELD != 'email':
fields.extend(['email'])
def __init__(self, *args, **kwargs):
super(UsuarioEditForm, self).__init__(*args, **kwargs)
row1 = to_row([('username', 12)])
row2 = to_row([('email', 6),
('user_active', 6)])
row3 = to_row(
[('password1', 6),
('password2', 6)])
row4 = to_row([(form_actions(label='Salvar Alterações'), 6)])
rows = to_row((
('first_name', 6),
('last_name', 6),
('email', 6),
('user_active', 6),
('password1', 6),
('password2', 6),
('roles', 12)))
self.helper = SaplFormHelper()
self.helper.layout = Layout(
row1,
row2,
row3,
'roles',
form_actions(label='Salvar Alterações'))
'username',
FieldWithButtons('token', StrictButton('Renovar', id="renovar-token", css_class="btn-outline-primary")),
rows,
form_actions(
more=[
HTML("<a href='{% url 'sapl.base:user_detail' object.pk %}' "
"class='btn btn-dark'>Cancelar</a>")],
label='Salvar Alterações'))
def clean(self):
super(UsuarioEditForm, self).clean()
super().clean()
if not self.is_valid():
return self.cleaned_data
data = self.cleaned_data
if data['password1'] and data['password1'] != data['password2']:
self.logger.error('Erro de validação. Senhas informadas ({}, {}) são diferentes.'.format(
data['password1'], data['password2']))
self.logger.warn("Erro de validação. Senhas informadas são diferentes.")
raise ValidationError('Senhas informadas são diferentes')
return data
@ -287,30 +314,37 @@ class SessaoLegislativaForm(FileFieldCheckMixin, ModelForm):
ult = 0
if numero <= ult and flag_edit:
self.logger.error('O número da SessaoLegislativa ({}) é menor ou igual '
'que o de Sessões Legislativas passadas ({})'.format(numero, ult))
self.logger.warn(
'O número da SessaoLegislativa ({}) é menor ou igual '
'que o de Sessões Legislativas passadas ({})'.format(numero, ult)
)
raise ValidationError('O número da Sessão Legislativa não pode ser menor ou igual '
'que o de Sessões Legislativas passadas')
if data_inicio < data_inicio_leg or \
data_inicio > data_fim_leg:
self.logger.error('A data de início ({}) da SessaoLegislativa está compreendida '
'fora da data início ({}) e fim ({}) da Legislatura '
'selecionada'.format(data_inicio, data_inicio_leg, data_fim_leg))
self.logger.warn(
'A data de início ({}) da SessaoLegislativa está compreendida '
'fora da data início ({}) e fim ({}) da Legislatura '
'selecionada'.format(data_inicio, data_inicio_leg, data_fim_leg)
)
raise ValidationError('A data de início da Sessão Legislativa deve estar compreendida '
'entre a data início e fim da Legislatura selecionada')
if data_fim > data_fim_leg or \
data_fim < data_inicio_leg:
self.logger.error('A data de fim ({}) da SessaoLegislativa está compreendida '
'fora da data início ({}) e fim ({}) da Legislatura '
'selecionada.'.format(data_fim, data_inicio_leg, data_fim_leg))
self.logger.warn(
'A data de fim ({}) da SessaoLegislativa está compreendida '
'fora da data início ({}) e fim ({}) da Legislatura '
'selecionada.'.format(data_fim, data_inicio_leg, data_fim_leg)
)
raise ValidationError('A data de fim da Sessão Legislativa deve estar compreendida '
'entre a data início e fim da Legislatura selecionada')
if data_inicio > data_fim:
self.logger.error(
'Data início ({}) superior à data fim ({}).'.format(data_inicio, data_fim))
self.logger.warn(
'Data início ({}) superior à data fim ({}).'.format(data_inicio, data_fim)
)
raise ValidationError(
'Data início não pode ser superior à data fim')
@ -319,8 +353,10 @@ class SessaoLegislativaForm(FileFieldCheckMixin, ModelForm):
if data_inicio_intervalo and data_fim_intervalo and \
data_inicio_intervalo > data_fim_intervalo:
self.logger.error('Data início de intervalo ({}) superior à '
'data fim de intervalo ({}).'.format(data_inicio_intervalo, data_fim_intervalo))
self.logger.warn(
'Data início de intervalo ({}) superior à '
'data fim de intervalo ({}).'.format(data_inicio_intervalo, data_fim_intervalo)
)
raise ValidationError('Data início de intervalo não pode ser '
'superior à data fim de intervalo')
@ -329,10 +365,13 @@ class SessaoLegislativaForm(FileFieldCheckMixin, ModelForm):
data_inicio_intervalo < data_inicio_leg or \
data_inicio_intervalo > data_fim or \
data_inicio_intervalo > data_fim_leg:
self.logger.error('A data de início do intervalo ({}) não está compreendida entre '
'as datas de início ({}) e fim ({}) tanto da Legislatura quanto da '
'própria Sessão Legislativa ({} e {}).'
.format(data_inicio_intervalo, data_inicio_leg, data_fim_leg, data_inicio, data_fim))
self.logger.warn(
'A data de início do intervalo ({}) não está compreendida entre '
'as datas de início ({}) e fim ({}) tanto da Legislatura quanto da '
'própria Sessão Legislativa ({} e {}).'.format(
data_inicio_intervalo, data_inicio_leg, data_fim_leg, data_inicio, data_fim
)
)
raise ValidationError('A data de início do intervalo deve estar compreendida entre '
'as datas de início e fim tanto da Legislatura quanto da '
'própria Sessão Legislativa')
@ -341,10 +380,13 @@ class SessaoLegislativaForm(FileFieldCheckMixin, ModelForm):
data_fim_intervalo > data_fim_leg or \
data_fim_intervalo < data_inicio or \
data_fim_intervalo < data_inicio_leg:
self.logger.error('A data de fim do intervalo ({}) não está compreendida entre '
'as datas de início ({}) e fim ({}) tanto da Legislatura quanto da '
'própria Sessão Legislativa ({} e {}).'
.format(data_fim_intervalo, data_inicio_leg, data_fim_leg, data_inicio, data_fim))
self.logger.warn(
'A data de fim do intervalo ({}) não está compreendida entre '
'as datas de início ({}) e fim ({}) tanto da Legislatura quanto da '
'própria Sessão Legislativa ({} e {}).'.format(
data_fim_intervalo, data_inicio_leg, data_fim_leg, data_inicio, data_fim
)
)
raise ValidationError('A data de fim do intervalo deve estar compreendida entre '
'as datas de início e fim tanto da Legislatura quanto da '
'própria Sessão Legislativa')
@ -538,8 +580,9 @@ class AutorForm(ModelForm):
def valida_igualdade(self, texto1, texto2, msg):
if texto1 != texto2:
self.logger.error(
'Textos diferentes. ("{}" e "{}")'.format(texto1, texto2))
self.logger.warn(
'Textos diferentes. ("{}" e "{}")'.format(texto1, texto2)
)
raise ValidationError(msg)
return True
@ -553,8 +596,10 @@ class AutorForm(ModelForm):
cd = self.cleaned_data
if 'action_user' not in cd or not cd['action_user']:
self.logger.error('Não Informado se o Autor terá usuário '
'vinculado para acesso ao Sistema.')
self.logger.warn(
'Não Informado se o Autor terá usuário '
'vinculado para acesso ao Sistema.'
)
raise ValidationError(_('Informe se o Autor terá usuário '
'vinculado para acesso ao Sistema.'))
@ -564,10 +609,13 @@ class AutorForm(ModelForm):
self.instance.user,
get_user_model().USERNAME_FIELD) != cd['username']:
if 'status_user' not in cd or not cd['status_user']:
self.logger.error('Foi trocado ou removido o usuário deste Autor ({}), '
'mas não foi informado como se deve proceder '
'com o usuário que está sendo desvinculado? ({})'
.format(cd['username'], get_user_model().USERNAME_FIELD))
self.logger.warn(
'Foi trocado ou removido o usuário deste Autor ({}), '
'mas não foi informado como se deve proceder '
'com o usuário que está sendo desvinculado? ({})'.format(
cd['username'], get_user_model().USERNAME_FIELD
)
)
raise ValidationError(
_('Foi trocado ou removido o usuário deste Autor, '
'mas não foi informado como se deve proceder '
@ -584,8 +632,9 @@ class AutorForm(ModelForm):
if cd['action_user'] == 'A':
param_username = {get_user_model().USERNAME_FIELD: cd['username']}
if not User.objects.filter(**param_username).exists():
self.logger.error(
'Não existe usuário com username "%s". ' % cd['username'])
self.logger.warn(
'Não existe usuário com username "%s". ' % cd['username']
)
raise ValidationError(
_('Não existe usuário com username "%s". '
'Para utilizar esse username você deve selecionar '
@ -594,16 +643,19 @@ class AutorForm(ModelForm):
if cd['action_user'] != 'N':
if 'username' not in cd or not cd['username']:
self.logger.error('Username não informado.')
self.logger.warn('Username não informado.')
raise ValidationError(_('O username deve ser informado.'))
param_username = {
'user__' + get_user_model().USERNAME_FIELD: cd['username']}
if qs_autor.filter(**param_username).exists():
self.logger.error(
'Já existe um Autor para este usuário ({}).'.format(cd['username']))
raise ValidationError(
_('Já existe um usuário vinculado a esse autor'))
autor_vinculado = qs_autor.filter(**param_username)
if autor_vinculado.exists():
nome = autor_vinculado[0].nome
error_msg = 'Já existe um autor para este ' \
'usuário ({}): {}'.format(cd['username'], nome)
self.logger.warn(error_msg)
raise ValidationError(_(error_msg))
"""
'if' não é necessário por ser campo obrigatório e o framework
@ -611,33 +663,34 @@ class AutorForm(ModelForm):
ainda assim para renderizar um message.danger no topo do form.
"""
if 'tipo' not in cd or not cd['tipo']:
self.logger.error('Tipo do Autor não selecionado.')
self.logger.warn('Tipo do Autor não selecionado.')
raise ValidationError(
_('O Tipo do Autor deve ser selecionado.'))
tipo = cd['tipo']
if 'nome' in cd and \
Autor.objects.filter(nome=cd['nome']).exists():
raise ValidationError("Autor '%s' já existente!" % cd['nome'])
if not tipo.content_type:
if 'nome' not in cd or not cd['nome']:
self.logger.error('Nome do Autor não informado.')
self.logger.warn('Nome do Autor não informado.')
raise ValidationError(
_('O Nome do Autor deve ser informado.'))
elif qs_autor.filter(nome=cd['nome']).exists():
raise ValidationError("Autor '%s' já existente!" % cd['nome'])
else:
if 'autor_related' not in cd or not cd['autor_related']:
self.logger.error('Registro de %s não escolhido para ser '
'vinculado ao cadastro de Autor' % tipo.descricao)
self.logger.warn(
'Registro de %s não escolhido para ser '
'vinculado ao cadastro de Autor' % tipo.descricao
)
raise ValidationError(
_('Um registro de %s deve ser escolhido para ser '
'vinculado ao cadastro de Autor') % tipo.descricao)
if not tipo.content_type.model_class().objects.filter(
pk=cd['autor_related']).exists():
self.logger.error('O Registro definido (%s-%s) não está na base '
'de %s.' % (cd['autor_related'], cd['q'], tipo.descricao))
self.logger.warn(
'O Registro definido (%s-%s) não está na base '
'de %s.' % (cd['autor_related'], cd['q'], tipo.descricao)
)
raise ValidationError(
_('O Registro definido (%s-%s) não está na base de %s.'
) % (cd['autor_related'], cd['q'], tipo.descricao))
@ -647,8 +700,10 @@ class AutorForm(ModelForm):
content_type_id=cd['tipo'].content_type_id)
if qs_autor_selected.exists():
autor = qs_autor_selected.first()
self.logger.error('Já existe um autor Cadastrado para '
'%s' % autor.autor_related)
self.logger.warn(
'Já existe um autor Cadastrado para '
'%s' % autor.autor_related
)
raise ValidationError(
_('Já existe um autor Cadastrado para %s'
) % autor.autor_related)
@ -712,6 +767,26 @@ class AutorForm(ModelForm):
return autor
class AutorFilterSet(django_filters.FilterSet):
nome = django_filters.CharFilter(label=_('Nome do Autor'), lookup_expr='icontains')
class Meta:
model = Autor
fields = ['nome']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
row0 = to_row([('nome', 12)])
self.form.helper = SaplFormHelper()
self.form.helper.form_method = 'GET'
self.form.helper.layout = Layout(
Fieldset(_('Pesquisa de Autor'),
row0,
form_actions(label='Pesquisar')))
class AutorFormForAdmin(AutorForm):
status_user = forms.ChoiceField(
label=_('Bloqueio do Usuário Existente'),
@ -746,7 +821,7 @@ class RelatorioDocumentosAcessoriosFilterSet(django_filters.FilterSet):
fields = ['tipo', 'materia__tipo', 'data']
def __init__(self, *args, **kwargs):
super(
RelatorioDocumentosAcessoriosFilterSet, self
).__init__(*args, **kwargs)
@ -754,12 +829,12 @@ class RelatorioDocumentosAcessoriosFilterSet(django_filters.FilterSet):
self.filters['tipo'].label = 'Tipo de Documento'
self.filters['materia__tipo'].label = 'Tipo de Matéria do Documento'
self.filters['data'].label = 'Período (Data Inicial - Data Final)'
self.form.fields['tipo'].required = True
row0 = to_row([('tipo', 6),
('materia__tipo', 6)])
row1 = to_row([('data', 12)])
buttons = FormActions(
@ -773,16 +848,15 @@ class RelatorioDocumentosAcessoriosFilterSet(django_filters.FilterSet):
],
Submit('pesquisar', _('Pesquisar'), css_class='float-right',
onclick='return true;'),
css_class='form-group row justify-content-between'
,
css_class='form-group row justify-content-between',
)
self.form.helper = SaplFormHelper()
self.form.helper.form_method = 'GET'
self.form.helper.layout = Layout(
Fieldset(_('Pesquisa'),
row0, row1,
buttons)
row0, row1,
buttons)
)
@ -818,8 +892,7 @@ class RelatorioAtasFilterSet(django_filters.FilterSet):
],
Submit('pesquisar', _('Pesquisar'), css_class='float-right',
onclick='return true;'),
css_class='form-group row justify-content-between'
,
css_class='form-group row justify-content-between',
)
self.form.helper = SaplFormHelper()
@ -829,6 +902,7 @@ class RelatorioAtasFilterSet(django_filters.FilterSet):
row1, buttons, )
)
def ultimo_ano_com_norma():
anos_normas = choice_anos_com_normas()
@ -868,8 +942,7 @@ class RelatorioNormasMesFilterSet(django_filters.FilterSet):
],
Submit('pesquisar', _('Pesquisar'), css_class='float-right',
onclick='return true;'),
css_class='form-group row justify-content-between'
,
css_class='form-group row justify-content-between',
)
self.form.helper = SaplFormHelper()
@ -912,8 +985,7 @@ class EstatisticasAcessoNormasForm(Form):
],
Submit('pesquisar', _('Pesquisar'), css_class='float-right',
onclick='return true;'),
css_class='form-group row justify-content-between'
,
css_class='form-group row justify-content-between',
)
self.helper = SaplFormHelper()
@ -965,8 +1037,7 @@ class RelatorioNormasVigenciaFilterSet(django_filters.FilterSet):
],
Submit('pesquisar', _('Pesquisar'), css_class='float-right',
onclick='return true;'),
css_class='form-group row justify-content-between'
,
css_class='form-group row justify-content-between',
)
self.form.helper = SaplFormHelper()
@ -995,15 +1066,16 @@ class RelatorioPresencaSessaoFilterSet(django_filters.FilterSet):
super(RelatorioPresencaSessaoFilterSet, self).__init__(
*args, **kwargs)
self.form.fields['exibir_ordem_dia'] = forms.BooleanField(required=False,
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.form.initial['exibir_ordem_dia'] = True
self.filters['data_inicio'].label = 'Período (Inicial - Final)'
self.form.fields['legislatura'].required = True
tipo_sessao_ordinaria = self.filters['tipo'].queryset.filter(nome='Ordinária')
tipo_sessao_ordinaria = self.filters['tipo'].queryset.filter(
nome='Ordinária')
if tipo_sessao_ordinaria:
self.form.initial['tipo'] = tipo_sessao_ordinaria.first()
@ -1024,8 +1096,7 @@ class RelatorioPresencaSessaoFilterSet(django_filters.FilterSet):
],
Submit('pesquisar', _('Pesquisar'), css_class='float-right',
onclick='return true;'),
css_class='form-group row justify-content-between'
,
css_class='form-group row justify-content-between',
)
self.form.helper = SaplFormHelper()
@ -1078,7 +1149,7 @@ class RelatorioHistoricoTramitacaoFilterSet(django_filters.FilterSet):
'Pesquisar Autor',
css_class='btn btn-primary btn-sm'), 2),
(Button('limpar',
'limpar Autor',
'Limpar Autor',
css_class='btn btn-primary btn-sm'), 2)
])
@ -1093,8 +1164,7 @@ class RelatorioHistoricoTramitacaoFilterSet(django_filters.FilterSet):
],
Submit('pesquisar', _('Pesquisar'), css_class='float-right',
onclick='return true;'),
css_class='form-group row justify-content-between'
,
css_class='form-group row justify-content-between',
)
self.form.helper = SaplFormHelper()
@ -1149,8 +1219,7 @@ class RelatorioDataFimPrazoTramitacaoFilterSet(django_filters.FilterSet):
],
Submit('pesquisar', _('Pesquisar'), css_class='float-right',
onclick='return true;'),
css_class='form-group row justify-content-between'
,
css_class='form-group row justify-content-between',
)
self.form.helper = SaplFormHelper()
@ -1195,8 +1264,7 @@ class RelatorioReuniaoFilterSet(django_filters.FilterSet):
],
Submit('pesquisar', _('Pesquisar'), css_class='float-right',
onclick='return true;'),
css_class='form-group row justify-content-between'
,
css_class='form-group row justify-content-between',
)
self.form.helper = SaplFormHelper()
@ -1240,8 +1308,7 @@ class RelatorioAudienciaFilterSet(django_filters.FilterSet):
],
Submit('pesquisar', _('Pesquisar'), css_class='float-right',
onclick='return true;'),
css_class='form-group row justify-content-between'
,
css_class='form-group row justify-content-between',
)
self.form.helper = SaplFormHelper()
@ -1256,8 +1323,8 @@ class RelatorioAudienciaFilterSet(django_filters.FilterSet):
class RelatorioMateriasTramitacaoFilterSet(django_filters.FilterSet):
materia__ano = django_filters.ChoiceFilter(required=True,
label='Ano da Matéria',
choices=choice_anos_com_materias)
label='Ano da Matéria',
choices=choice_anos_com_materias)
tramitacao__unidade_tramitacao_destino = django_filters.ModelChoiceFilter(
queryset=UnidadeTramitacao.objects.all(),
@ -1267,6 +1334,11 @@ class RelatorioMateriasTramitacaoFilterSet(django_filters.FilterSet):
queryset=StatusTramitacao.objects.all(),
label=_('Status Atual'))
materia__autores = django_filters.ModelChoiceFilter(
label='Autor da Matéria',
queryset=Autor.objects.all())
@property
def qs(self):
parent = super(RelatorioMateriasTramitacaoFilterSet, self).qs
@ -1278,7 +1350,7 @@ class RelatorioMateriasTramitacaoFilterSet(django_filters.FilterSet):
model = MateriaEmTramitacao
fields = ['materia__ano', 'materia__tipo',
'tramitacao__unidade_tramitacao_destino',
'tramitacao__status']
'tramitacao__status','materia__autores']
def __init__(self, *args, **kwargs):
super(RelatorioMateriasTramitacaoFilterSet, self).__init__(
@ -1290,6 +1362,7 @@ class RelatorioMateriasTramitacaoFilterSet(django_filters.FilterSet):
row2 = to_row([('materia__tipo', 12)])
row3 = to_row([('tramitacao__unidade_tramitacao_destino', 12)])
row4 = to_row([('tramitacao__status', 12)])
row5 = to_row([('materia__autores', 12)])
buttons = FormActions(
*[
@ -1302,15 +1375,14 @@ class RelatorioMateriasTramitacaoFilterSet(django_filters.FilterSet):
],
Submit('pesquisar', _('Pesquisar'), css_class='float-right',
onclick='return true;'),
css_class='form-group row justify-content-between'
,
css_class='form-group row justify-content-between',
)
self.form.helper = SaplFormHelper()
self.form.helper.form_method = 'GET'
self.form.helper.layout = Layout(
Fieldset(_('Pesquisa de Matéria em Tramitação'),
row1, row2, row3, row4,
row1, row2, row3, row4,row5,
buttons,)
)
@ -1343,8 +1415,7 @@ class RelatorioMateriasPorAnoAutorTipoFilterSet(django_filters.FilterSet):
],
Submit('pesquisar', _('Pesquisar'), css_class='float-right',
onclick='return true;'),
css_class='form-group row justify-content-between'
,
css_class='form-group row justify-content-between',
)
self.form.helper = SaplFormHelper()
@ -1356,7 +1427,6 @@ class RelatorioMateriasPorAnoAutorTipoFilterSet(django_filters.FilterSet):
)
class RelatorioMateriasPorAutorFilterSet(django_filters.FilterSet):
autoria__autor = django_filters.CharFilter(widget=forms.HiddenInput())
@ -1364,8 +1434,7 @@ class RelatorioMateriasPorAutorFilterSet(django_filters.FilterSet):
@property
def qs(self):
parent = super().qs
return parent.distinct().filter(autoria__primeiro_autor=True)\
.order_by('autoria__autor', '-autoria__primeiro_autor', 'tipo', '-ano', '-numero')
return parent.distinct().order_by('-ano', '-numero', 'tipo', 'autoria__autor', '-autoria__primeiro_autor')
class Meta(FilterOverridesMetaMixin):
model = MateriaLegislativa
@ -1386,7 +1455,7 @@ class RelatorioMateriasPorAutorFilterSet(django_filters.FilterSet):
'Pesquisar Autor',
css_class='btn btn-primary btn-sm'), 2),
(Button('limpar',
'limpar Autor',
'Limpar Autor',
css_class='btn btn-primary btn-sm'), 10)])
buttons = FormActions(
@ -1400,8 +1469,7 @@ class RelatorioMateriasPorAutorFilterSet(django_filters.FilterSet):
],
Submit('pesquisar', _('Pesquisar'), css_class='float-right',
onclick='return true;'),
css_class='form-group row justify-content-between'
,
css_class='form-group row justify-content-between',
)
self.form.helper = SaplFormHelper()
@ -1439,7 +1507,9 @@ class CasaLegislativaForm(FileFieldCheckMixin, ModelForm):
'uf': forms.Select(attrs={'class': 'selector'}),
'cep': forms.TextInput(attrs={'class': 'cep'}),
'telefone': forms.TextInput(attrs={'class': 'telefone'}),
'fax': forms.TextInput(attrs={'class': 'telefone'}),
# O campo fax foi ocultado porque não é utilizado.
'fax': forms.HiddenInput(),
# 'fax': forms.TextInput(attrs={'class': 'telefone'}),
'logotipo': ImageThumbnailFileInput,
'informacao_geral': forms.Textarea(
attrs={'id': 'texto-rico'})
@ -1516,16 +1586,19 @@ class ConfiguracoesAppForm(ModelForm):
if not self.is_valid():
return cleaned_data
mostrar_brasao_painel = self.cleaned_data.get('mostrar_brasao_painel', False)
mostrar_brasao_painel = self.cleaned_data.get(
'mostrar_brasao_painel', False)
casa = CasaLegislativa.objects.first()
if not casa:
self.logger.error('Não há casa legislativa relacionada.')
self.logger.warn('Não há casa legislativa relacionada.')
raise ValidationError("Não há casa legislativa relacionada.")
if not casa.logotipo and mostrar_brasao_painel:
self.logger.error('Não há logitipo configurado para esta '
'CasaLegislativa ({}).'.format(casa))
self.logger.warn(
'Não há logitipo configurado para esta '
'CasaLegislativa ({}).'.format(casa)
)
raise ValidationError("Não há logitipo configurado para esta "
"Casa legislativa.")
@ -1559,8 +1632,9 @@ class RecuperarSenhaForm(PasswordResetForm):
if not email_existente:
msg = 'Não existe nenhum usuário cadastrado com este e-mail.'
self.logger.error('Não existe nenhum usuário cadastrado com este e-mail ({}).'
.format(self.data['email']))
self.logger.warn(
'Não existe nenhum usuário cadastrado com este e-mail ({}).'.format(self.data['email'])
)
raise ValidationError(msg)
return self.cleaned_data
@ -1627,8 +1701,7 @@ class AlterarSenhaForm(Form):
new_password2 = data['new_password2']
if new_password1 != new_password2:
self.logger.error("'Nova Senha' ({}) diferente de 'Confirmar Senha' ({})".format(
new_password1, new_password2))
self.logger.warn("'Nova Senha' diferente de 'Confirmar Senha'")
raise ValidationError(
"'Nova Senha' diferente de 'Confirmar Senha'")
@ -1637,8 +1710,9 @@ class AlterarSenhaForm(Form):
# TODO: senha atual igual a senha anterior, etc
if len(new_password1) < 6:
self.logger.error(
'A senha informada ({}) não tem o mínimo de 6 caracteres.'.format(new_password1))
self.logger.warn(
'A senha informada não tem o mínimo de 6 caracteres.'
)
raise ValidationError(
"A senha informada deve ter no mínimo 6 caracteres")
@ -1647,20 +1721,24 @@ class AlterarSenhaForm(Form):
user = User.objects.get(username=username)
if user.is_anonymous():
self.logger.error(
'Não é possível alterar senha de usuário anônimo ({}).'.format(username))
self.logger.warn(
'Não é possível alterar senha de usuário anônimo ({}).'.format(username)
)
raise ValidationError(
"Não é possível alterar senha de usuário anônimo")
if not user.check_password(old_password):
self.logger.error('Senha atual informada ({}) não confere '
'com a senha armazenada.'.format(old_password))
self.logger.warn(
'Senha atual informada não confere '
'com a senha armazenada.'
)
raise ValidationError("Senha atual informada não confere "
"com a senha armazenada")
if user.check_password(new_password1):
self.logger.error(
'Nova senha ({}) igual à senha anterior.'.format(new_password1))
self.logger.warn(
'Nova senha igual à senha anterior.'
)
raise ValidationError(
"Nova senha não pode ser igual à senha anterior")
@ -1754,8 +1832,7 @@ class RelatorioHistoricoTramitacaoAdmFilterSet(django_filters.FilterSet):
],
Submit('pesquisar', _('Pesquisar'), css_class='float-right',
onclick='return true;'),
css_class='form-group row justify-content-between'
,
css_class='form-group row justify-content-between',
)
self.form.helper = SaplFormHelper()
@ -1809,8 +1886,7 @@ class RelatorioNormasPorAutorFilterSet(django_filters.FilterSet):
],
Submit('pesquisar', _('Pesquisar'), css_class='float-right',
onclick='return true;'),
css_class='form-group row justify-content-between'
,
css_class='form-group row justify-content-between',
)
self.form.helper = SaplFormHelper()

4
sapl/base/tests/test_base.py

@ -1,12 +1,12 @@
import pytest
from model_mommy import mommy
from model_bakery import baker
from sapl.base.models import CasaLegislativa
@pytest.mark.django_db(transaction=False)
def test_casa_legislativa_model():
mommy.make(CasaLegislativa,
baker.make(CasaLegislativa,
nome='Teste_Nome_Casa_Legislativa',
sigla='TSCL',
endereco='Teste_Endereço_Casa_Legislativa',

150
sapl/base/tests/test_view_base.py

@ -1,9 +1,9 @@
import pytest
from model_mommy import mommy
from model_bakery import baker
import datetime
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
from model_mommy import mommy
from model_bakery import baker
from sapl.base.models import Autor, TipoAutor
from sapl.comissoes.models import Comissao, TipoComissao
@ -26,26 +26,26 @@ from sapl.base.views import (protocolos_duplicados, protocolos_com_materias,
@pytest.mark.django_db(transaction=False)
def test_lista_protocolos_com_materias():
mommy.make(
baker.make(
Protocolo,
numero=15,
ano=2035
)
mommy.make(
baker.make(
Protocolo,
numero=33,
ano=2035
)
tipo_materia = mommy.make(
tipo_materia = baker.make(
TipoMateriaLegislativa,
descricao="Tipo_Materia_Teste"
)
regime_tramitacao = mommy.make(
regime_tramitacao = baker.make(
RegimeTramitacao,
descricao="Regime_Tramitacao_Teste"
)
mommy.make(
baker.make(
MateriaLegislativa,
numero=16,
ano=2035,
@ -54,7 +54,7 @@ def test_lista_protocolos_com_materias():
tipo=tipo_materia,
numero_protocolo=15
)
mommy.make(
baker.make(
MateriaLegislativa,
numero=17,
ano=2035,
@ -74,21 +74,21 @@ def test_lista_protocolos_com_materias():
@pytest.mark.django_db(transaction=False)
def test_lista_materias_protocolo_inexistente():
protocolo_a = mommy.make(
protocolo_a = baker.make(
Protocolo,
numero=15,
ano=2031
)
tipo_materia = mommy.make(
tipo_materia = baker.make(
TipoMateriaLegislativa,
descricao="Tipo_Materia_Teste"
)
regime_tramitacao = mommy.make(
regime_tramitacao = baker.make(
RegimeTramitacao,
descricao="Regime_Tramitacao_Teste"
)
mommy.make(
baker.make(
MateriaLegislativa,
numero=16,
ano=2031,
@ -97,7 +97,7 @@ def test_lista_materias_protocolo_inexistente():
tipo=tipo_materia,
numero_protocolo=15
)
materia = mommy.make(
materia = baker.make(
MateriaLegislativa,
numero=17,
ano=2031,
@ -115,13 +115,13 @@ def test_lista_materias_protocolo_inexistente():
@pytest.mark.django_db(transaction=False)
def test_lista_mandatos_sem_data_inicio():
parlamentar = mommy.make(
parlamentar = baker.make(
Parlamentar,
nome_completo="Nome_Completo_Parlamentar_Teste",
nome_parlamentar="Nome_Parlamentar_Teste",
sexo='M'
)
legislatura = mommy.make(
legislatura = baker.make(
Legislatura,
numero=1,
data_inicio='2015-05-02',
@ -129,12 +129,12 @@ def test_lista_mandatos_sem_data_inicio():
data_eleicao='2015-02-05'
)
mandato_a = mommy.make(
mandato_a = baker.make(
Mandato,
parlamentar=parlamentar,
legislatura=legislatura
)
mommy.make(
baker.make(
Mandato,
parlamentar=parlamentar,
legislatura=legislatura,
@ -149,19 +149,19 @@ def test_lista_mandatos_sem_data_inicio():
@pytest.mark.django_db(transaction=False)
def test_lista_parlamentares_duplicados():
mommy.make(
baker.make(
Parlamentar,
nome_completo="Nome_Completo_Parlamentar_Teste",
nome_parlamentar="Nome_Parlamentar_Teste",
sexo='M'
)
mommy.make(
baker.make(
Parlamentar,
nome_completo="Nome_Completo_Parlamentar_Teste",
nome_parlamentar="Nome_Parlamentar_Teste",
sexo='M'
)
mommy.make(
baker.make(
Parlamentar,
nome_completo="Nome_Completo_Parlamentar_Teste-1",
nome_parlamentar="Nome_Parlamentar_Teste-1",
@ -180,47 +180,47 @@ def test_lista_parlamentares_duplicados():
@pytest.mark.django_db(transaction=False)
def test_lista_parlamentares_mandatos_intersecao():
legislatura = mommy.make(
legislatura = baker.make(
Legislatura,
numero=1,
data_inicio='2017-07-04',
data_fim='2170-05-01',
data_eleicao='2017-04-07'
)
parlamentar_a = mommy.make(
parlamentar_a = baker.make(
Parlamentar,
nome_completo="Nome_Completo_Parlamentar_Teste",
nome_parlamentar="Nome_Parlamentar_Teste",
sexo='M'
)
parlamentar_b = mommy.make(
parlamentar_b = baker.make(
Parlamentar,
nome_completo="Nome_Completo_Parlamentar_Teste-1",
nome_parlamentar="Nome_Parlamentar_Teste-1",
sexo='M'
)
mandato_a = mommy.make(
mandato_a = baker.make(
Mandato,
parlamentar=parlamentar_a,
legislatura=legislatura,
data_inicio_mandato='2017-07-08',
data_fim_mandato='2018-01-07'
)
mandato_b = mommy.make(
mandato_b = baker.make(
Mandato,
parlamentar=parlamentar_a,
legislatura=legislatura,
data_inicio_mandato='2017-07-09'
)
mommy.make(
baker.make(
Mandato,
parlamentar=parlamentar_b,
legislatura=legislatura,
data_inicio_mandato='2017-11-17',
data_fim_mandato='2018-08-02'
)
mommy.make(
baker.make(
Mandato,
parlamentar=parlamentar_b,
legislatura=legislatura,
@ -235,46 +235,46 @@ def test_lista_parlamentares_mandatos_intersecao():
@pytest.mark.django_db(transaction=False)
def test_lista_parlamentares_filiacoes_intersecao():
partido = mommy.make(
partido = baker.make(
Partido,
sigla="ST",
nome="Nome_Partido_Teste"
)
parlamentar_a = mommy.make(
parlamentar_a = baker.make(
Parlamentar,
nome_completo="Nome_Completo_Parlamentar_Teste",
nome_parlamentar="Nome_Parlamentar_Teste",
sexo='M'
)
parlamentar_b = mommy.make(
parlamentar_b = baker.make(
Parlamentar,
nome_completo="Nome_Completo_Parlamentar_Teste-1",
nome_parlamentar="Nome_Parlamentar_Teste-1",
sexo='M'
)
filiacao_a = mommy.make(
filiacao_a = baker.make(
Filiacao,
parlamentar=parlamentar_a,
partido=partido,
data='2018-02-02',
data_desfiliacao='2019-08-01'
)
filiacao_b = mommy.make(
filiacao_b = baker.make(
Filiacao,
parlamentar=parlamentar_a,
partido=partido,
data='2018-02-23',
data_desfiliacao='2020-02-04'
)
mommy.make(
baker.make(
Filiacao,
parlamentar=parlamentar_b,
partido=partido,
data='2018-02-07',
data_desfiliacao='2018-02-27'
)
mommy.make(
baker.make(
Filiacao,
parlamentar=parlamentar_b,
partido=partido,
@ -289,22 +289,22 @@ def test_lista_parlamentares_filiacoes_intersecao():
@pytest.mark.django_db(transaction=False)
def test_lista_autores_duplicados():
tipo_autor = mommy.make(
tipo_autor = baker.make(
TipoAutor,
descricao="Tipo_Autor_Teste"
)
mommy.make(
baker.make(
Autor,
tipo=tipo_autor,
nome="Nome_Autor_Teste"
)
mommy.make(
baker.make(
Autor,
tipo=tipo_autor,
nome="Nome_Autor_Teste"
)
mommy.make(
baker.make(
Autor,
tipo=tipo_autor,
nome="Nome_Autor_Teste-1"
@ -319,16 +319,16 @@ def test_lista_autores_duplicados():
@pytest.mark.django_db(transaction=False)
def test_lista_bancada_comissao_autor_externo():
tipo_autor = mommy.make(
tipo_autor = baker.make(
TipoAutor,
descricao="Tipo_Autor_Teste"
)
tipo_autor_externo = mommy.make(
tipo_autor_externo = baker.make(
TipoAutor,
descricao="Externo"
)
legislatura = mommy.make(
legislatura = baker.make(
Legislatura,
numero=1,
data_inicio='2012-01-03',
@ -336,7 +336,7 @@ def test_lista_bancada_comissao_autor_externo():
data_eleicao='2011-10-04'
)
bancada_a = mommy.make(
bancada_a = baker.make(
Bancada,
legislatura=legislatura,
nome="Bancada_Teste",
@ -347,7 +347,7 @@ def test_lista_bancada_comissao_autor_externo():
tipo=tipo_autor
)
bancada_b = mommy.make(
bancada_b = baker.make(
Bancada,
legislatura=legislatura,
nome="Bancada_Teste-1",
@ -358,14 +358,14 @@ def test_lista_bancada_comissao_autor_externo():
tipo=tipo_autor_externo
)
tipo_comissao = mommy.make(
tipo_comissao = baker.make(
TipoComissao,
nome="Tipo_Comissao_Teste",
natureza='T',
sigla="TCT"
)
comissao_a = mommy.make(
comissao_a = baker.make(
Comissao,
nome="Comissao_Teste",
sigla="CT",
@ -376,7 +376,7 @@ def test_lista_bancada_comissao_autor_externo():
tipo=tipo_autor
)
comissao_b = mommy.make(
comissao_b = baker.make(
Comissao,
nome="Comissao_Teste-1",
sigla="CT1",
@ -399,48 +399,48 @@ def test_lista_bancada_comissao_autor_externo():
@pytest.mark.django_db(transaction=False)
def test_lista_anexados_ciclicas():
## DocumentoAdministrativo
tipo_documento = mommy.make(
tipo_documento = baker.make(
TipoDocumentoAdministrativo,
sigla="TT",
descricao="Tipo_Teste"
)
documento_a = mommy.make(
documento_a = baker.make(
DocumentoAdministrativo,
tipo=tipo_documento,
numero=26,
ano=2019,
data='2019-05-15',
)
documento_b = mommy.make(
documento_b = baker.make(
DocumentoAdministrativo,
tipo=tipo_documento,
numero=27,
ano=2019,
data='2019-05-16',
)
documento_c = mommy.make(
documento_c = baker.make(
DocumentoAdministrativo,
tipo=tipo_documento,
numero=28,
ano=2019,
data='2019-05-17',
)
documento_a1 = mommy.make(
documento_a1 = baker.make(
DocumentoAdministrativo,
tipo=tipo_documento,
numero=29,
ano=2019,
data='2019-05-18',
)
documento_b1 = mommy.make(
documento_b1 = baker.make(
DocumentoAdministrativo,
tipo=tipo_documento,
numero=30,
ano=2019,
data='2019-05-19',
)
documento_c1 = mommy.make(
documento_c1 = baker.make(
DocumentoAdministrativo,
tipo=tipo_documento,
numero=31,
@ -448,43 +448,43 @@ def test_lista_anexados_ciclicas():
data='2019-05-20',
)
mommy.make(
baker.make(
Anexado,
documento_principal=documento_a,
documento_anexado=documento_b,
data_anexacao='2019-05-21'
)
mommy.make(
baker.make(
Anexado,
documento_principal=documento_a,
documento_anexado=documento_c,
data_anexacao='2019-05-22'
)
mommy.make(
baker.make(
Anexado,
documento_principal=documento_b,
documento_anexado=documento_c,
data_anexacao='2019-05-23'
)
mommy.make(
baker.make(
Anexado,
documento_principal=documento_a1,
documento_anexado=documento_b1,
data_anexacao='2019-05-24'
)
mommy.make(
baker.make(
Anexado,
documento_principal=documento_a1,
documento_anexado=documento_c1,
data_anexacao='2019-05-25'
)
mommy.make(
baker.make(
Anexado,
documento_principal=documento_b1,
documento_anexado=documento_c1,
data_anexacao='2019-05-26'
)
mommy.make(
baker.make(
Anexado,
documento_principal=documento_c1,
documento_anexado=documento_b1,
@ -494,16 +494,16 @@ def test_lista_anexados_ciclicas():
lista_documento_ciclicos = anexados_ciclicos(False)
## Matéria
tipo_materia = mommy.make(
tipo_materia = baker.make(
TipoMateriaLegislativa,
descricao="Tipo_Teste"
)
regime_tramitacao = mommy.make(
regime_tramitacao = baker.make(
RegimeTramitacao,
descricao="Regime_Teste"
)
materia_a = mommy.make(
materia_a = baker.make(
MateriaLegislativa,
numero=20,
ano=2018,
@ -511,7 +511,7 @@ def test_lista_anexados_ciclicas():
regime_tramitacao=regime_tramitacao,
tipo=tipo_materia
)
materia_b = mommy.make(
materia_b = baker.make(
MateriaLegislativa,
numero=21,
ano=2019,
@ -519,7 +519,7 @@ def test_lista_anexados_ciclicas():
regime_tramitacao=regime_tramitacao,
tipo=tipo_materia
)
materia_c = mommy.make(
materia_c = baker.make(
MateriaLegislativa,
numero=22,
ano=2019,
@ -527,7 +527,7 @@ def test_lista_anexados_ciclicas():
regime_tramitacao=regime_tramitacao,
tipo=tipo_materia
)
materia_a1 = mommy.make(
materia_a1 = baker.make(
MateriaLegislativa,
numero=23,
ano=2018,
@ -535,7 +535,7 @@ def test_lista_anexados_ciclicas():
regime_tramitacao=regime_tramitacao,
tipo=tipo_materia
)
materia_b1 = mommy.make(
materia_b1 = baker.make(
MateriaLegislativa,
numero=24,
ano=2019,
@ -543,7 +543,7 @@ def test_lista_anexados_ciclicas():
regime_tramitacao=regime_tramitacao,
tipo=tipo_materia
)
materia_c1 = mommy.make(
materia_c1 = baker.make(
MateriaLegislativa,
numero=25,
ano=2019,
@ -552,43 +552,43 @@ def test_lista_anexados_ciclicas():
tipo=tipo_materia
)
mommy.make(
baker.make(
Anexada,
materia_principal=materia_a,
materia_anexada=materia_b,
data_anexacao='2019-05-11'
)
mommy.make(
baker.make(
Anexada,
materia_principal=materia_a,
materia_anexada=materia_c,
data_anexacao='2019-05-12'
)
mommy.make(
baker.make(
Anexada,
materia_principal=materia_b,
materia_anexada=materia_c,
data_anexacao='2019-05-13'
)
mommy.make(
baker.make(
Anexada,
materia_principal=materia_a1,
materia_anexada=materia_b1,
data_anexacao='2019-05-11'
)
mommy.make(
baker.make(
Anexada,
materia_principal=materia_a1,
materia_anexada=materia_c1,
data_anexacao='2019-05-12'
)
mommy.make(
baker.make(
Anexada,
materia_principal=materia_b1,
materia_anexada=materia_c1,
data_anexacao='2019-05-13'
)
mommy.make(
baker.make(
Anexada,
materia_principal=materia_c1,
materia_anexada=materia_b1,

7
sapl/base/urls.py

@ -8,7 +8,8 @@ from django.contrib.auth.views import (password_reset, password_reset_complete,
password_reset_done)
from django.views.generic.base import RedirectView, TemplateView
from sapl.base.views import AutorCrud, ConfirmarEmailView, TipoAutorCrud, get_estatistica
from sapl.base.views import AutorCrud, ConfirmarEmailView, TipoAutorCrud, get_estatistica, DetailUsuarioView, \
PesquisarAutorView
from sapl.settings import EMAIL_SEND_USER, MEDIA_URL
from .apps import AppConfig
@ -45,6 +46,7 @@ app_name = AppConfig.name
admin_user = [
url(r'^sistema/usuario/$', PesquisarUsuarioView.as_view(), name='usuario'),
url(r'^sistema/usuario/create$', CreateUsuarioView.as_view(), name='user_create'),
url(r'^sistema/usuario/(?P<pk>\d+)$', DetailUsuarioView.as_view(), name='user_detail'),
url(r'^sistema/usuario/(?P<pk>\d+)/edit$', EditUsuarioView.as_view(), name='user_edit'),
url(r'^sistema/usuario/(?P<pk>\d+)/delete$', DeleteUsuarioView.as_view(), name='user_delete')
]
@ -72,7 +74,7 @@ recuperar_senha = [
{'template_name': 'base/recupera_senha_email_enviado.html'},
name='recuperar_senha_finalizado'),
url(r'^recuperar-senha/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>.+)/$',
url(r'^recuperar-senha/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>.+)$',
password_reset_confirm,
{'post_reset_redirect': 'sapl.base:recuperar_senha_completo',
'template_name': 'base/nova_senha_form.html',
@ -89,6 +91,7 @@ recuperar_senha = [
urlpatterns = [
url(r'^sistema/autor/tipo/', include(TipoAutorCrud.get_urls())),
url(r'^sistema/autor/', include(AutorCrud.get_urls())),
url(r'^sistema/autor/pesquisar-autor/', PesquisarAutorView.as_view(), name='pesquisar_autor'),
url(r'^sistema/ajuda/(?P<topic>\w+)$',
HelpTopicView.as_view(), name='help_topic'),

194
sapl/base/views.py

@ -4,6 +4,7 @@ import datetime
import logging
import os
from collections import OrderedDict
from django.contrib import messages
from django.contrib.auth import get_user_model
from django.contrib.auth.mixins import PermissionRequiredMixin
@ -23,8 +24,7 @@ from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_decode, urlsafe_base64_encode
from django.utils.translation import string_concat
from django.utils.translation import ugettext_lazy as _
from django.views.generic import (CreateView, DeleteView, FormView, ListView,
UpdateView)
from django.views.generic import (CreateView, DetailView, DeleteView, FormView, ListView, UpdateView)
from django.views.generic.base import RedirectView, TemplateView
from django_filters.views import FilterView
from haystack.views import SearchView
@ -41,7 +41,7 @@ from sapl.relatorios.views import (relatorio_materia_em_tramitacao, relatorio_ma
from sapl import settings
from sapl.audiencia.models import AudienciaPublica, TipoAudienciaPublica
from sapl.base.models import Autor, TipoAutor
from sapl.base.forms import AutorForm, AutorFormForAdmin, TipoAutorForm
from sapl.base.forms import AutorForm, AutorFormForAdmin, TipoAutorForm, AutorFilterSet
from sapl.comissoes.models import Comissao, Reuniao
from sapl.crud.base import CrudAux, make_pagination
from sapl.materia.models import (Anexada, Autoria, DocumentoAcessorio,
@ -56,9 +56,9 @@ from sapl.protocoloadm.models import (Anexado, DocumentoAdministrativo, Protocol
TipoDocumentoAdministrativo)
from sapl.sessao.models import (Bancada, PresencaOrdemDia, SessaoPlenaria,
SessaoPlenariaPresenca, TipoSessaoPlenaria)
from sapl.utils import (gerar_hash_arquivo, intervalos_tem_intersecao,
from sapl.utils import (gerar_hash_arquivo, intervalos_tem_intersecao,
mail_service_configured, parlamentares_ativos,
SEPARADOR_HASH_PROPOSICAO, show_results_filter_set)
SEPARADOR_HASH_PROPOSICAO, show_results_filter_set, num_materias_por_tipo)
from .forms import (AlterarSenhaForm, CasaLegislativaForm,
ConfiguracoesAppForm, RelatorioAtasFilterSet,
@ -78,6 +78,8 @@ from .forms import (AlterarSenhaForm, CasaLegislativaForm,
RelatorioNormasPorAutorFilterSet)
from .models import AppConfig, CasaLegislativa
from rest_framework.authtoken.models import Token
def get_casalegislativa():
return CasaLegislativa.objects.first()
@ -293,6 +295,58 @@ class AutorCrud(CrudAux):
return url_reverse
class PesquisarAutorView(FilterView):
model = Autor
filterset_class = AutorFilterSet
paginate_by = 10
def get_filterset_kwargs(self, filterset_class):
super().get_filterset_kwargs(filterset_class)
kwargs = {'data': self.request.GET or None}
qs = self.get_queryset().order_by('nome').distinct()
kwargs.update({
'queryset': qs,
})
return kwargs
def get_context_data(self, **kwargs):
context = super().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 Autor encontrado!'
context['title'] = _('Autores')
return context
def get(self, request, *args, **kwargs):
super().get(request)
data = self.filterset.data
url = ''
if data:
url = "&" + str(self.request.META['QUERY_STRING'])
if url.startswith("&page"):
ponto_comeco = url.find('nome=') - 1
url = url[ponto_comeco:]
context = self.get_context_data(filter=self.filterset,
object_list=self.object_list,
filter_url=url,
numero_res=len(self.object_list))
context['show_results'] = show_results_filter_set(self.request.GET.copy())
return self.render_to_response(context)
class RelatoriosListView(TemplateView):
template_name='base/relatorios_list.html'
@ -747,6 +801,7 @@ class RelatorioMateriasTramitacaoView(RelatorioMixin, FilterView):
tipo_materia = data['data']['materia__tipo']
unidade_tramitacao_destino = data['data']['tramitacao__unidade_tramitacao_destino']
status_tramitacao = data['data']['tramitacao__status']
autor = data['data']['materia__autores']
kwargs = {}
if ano_materia:
@ -757,17 +812,13 @@ class RelatorioMateriasTramitacaoView(RelatorioMixin, FilterView):
kwargs['tramitacao__unidade_tramitacao_destino'] = unidade_tramitacao_destino
if status_tramitacao:
kwargs['tramitacao__status'] = status_tramitacao
if autor:
kwargs['materia__autores'] = autor
qs = qs.filter(**kwargs)
data['queryset'] = qs
qtdes = { tipo:0 for tipo in TipoMateriaLegislativa.objects.all() }
for i in qs:
qtdes[i.materia.tipo] += 1
# remove as entradas de valor igual a zero
qtdes = {k:v for k,v in qtdes.items() if v > 0}
self.total_resultados_tipos = qtdes
self.total_resultados_tipos = num_materias_por_tipo(qs, "materia__tipo")
return data
@ -819,6 +870,14 @@ class RelatorioMateriasTramitacaoView(RelatorioMixin, FilterView):
)
else:
context['tramitacao__unidade_tramitacao_destino'] = ''
if self.request.GET['materia__autores']:
autor = self.request.GET['materia__autores']
context['materia__autor'] = (
str(Autor.objects.get(id=autor))
)
else:
context['materia__autor'] = ''
context['filter_url'] = ('&' + qr.urlencode()) if len(qr) > 0 else ''
context['show_results'] = show_results_filter_set(qr)
@ -894,13 +953,8 @@ class RelatorioMateriasPorAnoAutorTipoView(RelatorioMixin, FilterView):
context['title'] = _('Matérias por Ano, Autor e Tipo')
if not self.filterset.form.is_valid():
return context
qtdes = {}
for tipo in TipoMateriaLegislativa.objects.all():
qs = context['object_list']
qtde = len(qs.filter(tipo_id=tipo.id))
if qtde > 0:
qtdes[tipo] = qtde
context['qtdes'] = qtdes
qs = context['object_list']
context['qtdes'] = num_materias_por_tipo(qs)
qr = self.request.GET.copy()
context['filter_url'] = ('&' + qr.urlencode()) if len(qr) > 0 else ''
@ -936,13 +990,9 @@ class RelatorioMateriasPorAutorView(RelatorioMixin, FilterView):
if not self.filterset.form.is_valid():
return context
qtdes = {}
for tipo in TipoMateriaLegislativa.objects.all():
qs = context['object_list']
qtde = len(qs.filter(tipo_id=tipo.id))
if qtde > 0:
qtdes[tipo] = qtde
context['qtdes'] = qtdes
qs = context['object_list']
context['materias_resultado'] = list(OrderedDict.fromkeys(qs))
context['qtdes'] = num_materias_por_tipo(qs)
qr = self.request.GET.copy()
context['filter_url'] = ('&' + qr.urlencode()) if len(qr) > 0 else ''
@ -960,8 +1010,8 @@ class RelatorioMateriasPorAutorView(RelatorioMixin, FilterView):
else:
context['autor'] = ''
context['periodo'] = (
self.request.GET['data_apresentacao_0'] +
' - ' + self.request.GET['data_apresentacao_1'])
self.request.GET['data_apresentacao_0'] +
' - ' + self.request.GET['data_apresentacao_1'])
return context
@ -1763,7 +1813,7 @@ class ListarProtocolosDuplicadosView(PermissionRequiredMixin, ListView):
class PesquisarUsuarioView(PermissionRequiredMixin, FilterView):
model = User
model = get_user_model()
filterset_class = UsuarioFilterSet
permission_required = ('base.list_appconfig',)
paginate_by = 10
@ -1782,18 +1832,16 @@ class PesquisarUsuarioView(PermissionRequiredMixin, FilterView):
return kwargs
def get_context_data(self, **kwargs):
context = super(PesquisarUsuarioView,
self).get_context_data(**kwargs)
context = super(PesquisarUsuarioView, 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 usuário encontrado!'
context['title'] = _('Usuários')
context.update({
"page_range": make_pagination(page_obj.number, paginator.num_pages),
"NO_ENTRIES_MSG": "Nenhum usuário encontrado!",
"title": _("Usuários")
})
return context
@ -1820,6 +1868,28 @@ class PesquisarUsuarioView(PermissionRequiredMixin, FilterView):
return self.render_to_response(context)
class DetailUsuarioView(PermissionRequiredMixin, DetailView):
model = get_user_model()
template_name = "base/usuario_detail.html"
permission_required = ('base.detail_appconfig',)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
user = get_user_model().objects.get(id=self.kwargs['pk'])
context.update({
"user": user,
"token": Token.objects.filter(user=user)[0],
"roles": [
{
"checked": "checked" if g in user.groups.all() else "unchecked",
"group": g.name
} for g in Group.objects.all().order_by("name")]
})
return context
class CreateUsuarioView(PermissionRequiredMixin, CreateView):
model = get_user_model()
form_class = UsuarioCreateForm
@ -1827,21 +1897,21 @@ class CreateUsuarioView(PermissionRequiredMixin, CreateView):
fail_message = 'Usuário não criado!'
permission_required = ('base.add_appconfig',)
def get_success_url(self):
return reverse('sapl.base:usuario')
def get_success_url(self, pk):
return reverse('sapl.base:user_detail', kwargs={"pk": pk})
def form_valid(self, form):
data = form.cleaned_data
new_user = get_user_model().objects.create(
username=data['username'],
email=data['email']
email=data['email'],
first_name=data['firstname'],
last_name=data['lastname'],
is_superuser=False,
is_staff=False
)
new_user.first_name = data['firstname']
new_user.last_name = data['lastname']
new_user.set_password(data['password1'])
new_user.is_superuser = False
new_user.is_staff = False
new_user.save()
groups = Group.objects.filter(id__in=data['roles'])
@ -1849,7 +1919,7 @@ class CreateUsuarioView(PermissionRequiredMixin, CreateView):
g.user_set.add(new_user)
messages.success(self.request, self.success_message)
return HttpResponseRedirect(self.get_success_url())
return HttpResponseRedirect(self.get_success_url(new_user.pk))
def form_invalid(self, form):
messages.error(self.request, self.fail_message)
@ -1890,19 +1960,26 @@ class DeleteUsuarioView(PermissionRequiredMixin, DeleteView):
class EditUsuarioView(PermissionRequiredMixin, UpdateView):
model = get_user_model()
form_class = UsuarioEditForm
template_name = "base/usuario_edit.html"
success_message = 'Usuário editado com sucesso!'
permission_required = ('base.change_appconfig',)
def get_success_url(self):
return reverse('sapl.base:usuario')
return reverse('sapl.base:user_detail', kwargs={"pk": self.kwargs['pk']})
def get_initial(self):
initial = super(EditUsuarioView, self).get_initial()
initial = super().get_initial()
user = get_user_model().objects.get(id=self.kwargs['pk'])
roles = [str(g.id) for g in user.groups.all()]
initial['roles'] = roles
initial['user_active'] = user.is_active
initial.update({
"token": Token.objects.filter(user=user)[0],
"first_name": user.first_name,
"last_name": user.last_name,
"roles": roles,
"user_active": user.is_active
})
return initial
@ -1911,8 +1988,11 @@ class EditUsuarioView(PermissionRequiredMixin, UpdateView):
user = form.save(commit=False)
data = form.cleaned_data
# new_user.first_name = data['firstname']
# new_user.last_name = data['lastname']
if 'first_name' in data and data['first_name'] != user.first_name:
user.first_name = data['first_name']
if 'last_name' in data and data['last_name'] != user.last_name:
user.last_name = data['last_name']
if data['password1']:
user.set_password(data['password1'])
@ -1992,9 +2072,15 @@ class AppConfigCrud(CrudAux):
recibo_prop_atual = AppConfig.objects.last().receber_recibo_proposicao
recibo_prop_novo = self.request.POST['receber_recibo_proposicao']
if recibo_prop_novo == 'False' and recibo_prop_atual:
props = Proposicao.objects.filter(hash_code='')
props = Proposicao.objects.filter(hash_code='').exclude(data_envio__isnull=True)
for prop in props:
self.gerar_hash(prop)
try:
self.gerar_hash(prop)
except ValidationError as e:
form.add_error('receber_recibo_proposicao',e)
msg = _("Não foi possível mudar a configuração porque a Proposição {} não possui texto original vinculado!".format(prop))
messages.error(self.request, msg)
return super().form_invalid(form)
return super().form_valid(form)
def gerar_hash(self, inst):

143
sapl/comissoes/forms.py

@ -10,6 +10,7 @@ from django.db import transaction
from django.db.models import Q
from django.forms import ModelForm
from django.utils.translation import ugettext_lazy as _
from django.utils import timezone
from sapl.base.models import Autor, TipoAutor
from sapl.comissoes.models import (Comissao, Composicao,
@ -39,44 +40,41 @@ class ComposicaoForm(forms.ModelForm):
self.fields['comissao'].widget.attrs['disabled'] = 'disabled'
def clean(self):
cleaned_data = super(ComposicaoForm, self).clean()
data = super().clean()
data['comissao'] = self.initial['comissao']
comissao_pk = self.initial['comissao'].id
if not self.is_valid():
return cleaned_data
return data
periodo = data['periodo']
periodo = cleaned_data['periodo']
comissao_pk = self.initial['comissao'].id
cleaned_data['comissao'] = self.initial['comissao']
if periodo.data_fim:
intersecao_periodo = Composicao.objects.filter(
Q(periodo__data_inicio__lte=periodo.data_fim,
periodo__data_fim__gte=periodo.data_fim) |
Q(periodo__data_inicio__gte=periodo.data_inicio,
periodo__data_fim__lte=periodo.data_inicio),
Q(periodo__data_inicio__lte=periodo.data_fim, periodo__data_fim__gte=periodo.data_fim) |
Q(periodo__data_inicio__gte=periodo.data_inicio, periodo__data_fim__lte=periodo.data_inicio),
comissao_id=comissao_pk)
else:
intersecao_periodo = Composicao.objects.filter(
Q(periodo__data_inicio__gte=periodo.data_inicio,
periodo__data_fim__lte=periodo.data_inicio),
Q(periodo__data_inicio__gte=periodo.data_inicio, periodo__data_fim__lte=periodo.data_inicio),
comissao_id=comissao_pk)
if intersecao_periodo:
if periodo.data_fim:
self.logger.error('O período informado ({} a {})'
'choca com períodos já '
'cadastrados para esta comissão'
.format(periodo.data_inicio, periodo.data_fim))
self.logger.warn(
'O período informado ({} a {}) choca com períodos já cadastrados para esta comissão'.format(
periodo.data_inicio, periodo.data_fim
)
)
else:
self.logger.error('O período informado ({} - )'
'choca com períodos já '
'cadastrados para esta comissão'
.format(periodo.data_inicio))
raise ValidationError('O período informado '
'choca com períodos já '
'cadastrados para esta comissão')
self.logger.warn(
'O período informado ({} - ) choca com períodos já cadastrados para esta comissão'.format(
periodo.data_inicio
)
)
raise ValidationError('O período informado choca com períodos já cadastrados para esta comissão')
return cleaned_data
return data
class PeriodoForm(forms.ModelForm):
@ -97,8 +95,10 @@ class PeriodoForm(forms.ModelForm):
data_fim = cleaned_data['data_fim']
if data_fim and data_fim < data_inicio:
self.logger.error('A Data Final ({}) é menor que '
'a Data Inicial({}).'.format(data_fim, data_inicio))
self.logger.warn(
'A Data Final ({}) é menor que '
'a Data Inicial({}).'.format(data_fim, data_inicio)
)
raise ValidationError('A Data Final não pode ser menor que '
'a Data Inicial')
@ -111,9 +111,11 @@ class PeriodoForm(forms.ModelForm):
)
if not legislatura:
self.logger.error('O período informado ({} a {})'
'não está contido em uma única '
'legislatura existente'.format(data_inicio, data_fim))
self.logger.warn(
'O período informado ({} a {})'
'não está contido em uma única '
'legislatura existente'.format(data_inicio, data_fim)
)
raise ValidationError('O período informado '
'deve estar contido em uma única '
'legislatura existente')
@ -151,7 +153,6 @@ class ParticipacaoCreateForm(forms.ModelForm):
values_list('parlamentar',
flat=True
).distinct()
qs = Parlamentar.objects.filter(id__in=parlamentares).distinct().\
exclude(id__in=id_part)
eligible = self.verifica()
@ -174,8 +175,10 @@ class ParticipacaoCreateForm(forms.ModelForm):
if data_desligamento and \
data_designacao > data_desligamento:
self.logger.error('Data de designação ({}) superior '
'à data de desligamento ({})'.format(data_designacao, data_desligamento))
self.logger.warn(
'Data de designação ({}) superior '
'à data de desligamento ({})'.format(data_designacao, data_desligamento)
)
raise ValidationError(_('Data de designação não pode ser superior '
'à data de desligamento'))
@ -185,15 +188,18 @@ class ParticipacaoCreateForm(forms.ModelForm):
if cleaned_data['cargo'].nome in cargos_unicos:
msg = _('Este cargo é único para esta Comissão.')
self.logger.error('Este cargo ({}) é único para esta Comissão.'.format(
cleaned_data['cargo'].nome))
self.logger.warn(
'Este cargo ({}) é único para esta Comissão.'.format(
cleaned_data['cargo'].nome
)
)
raise ValidationError(msg)
return cleaned_data
def create_participacao(self):
composicao = Composicao.objects.get(id=self.initial['parent_pk'])
data_inicio_comissao = composicao.periodo.data_inicio
data_fim_comissao = composicao.periodo.data_fim
data_fim_comissao = composicao.periodo.data_fim if composicao.periodo.data_fim else timezone.now()
q1 = Q(data_fim_mandato__isnull=False,
data_fim_mandato__gte=data_inicio_comissao)
q2 = Q(data_inicio_mandato__gte=data_inicio_comissao) \
@ -262,8 +268,10 @@ class ParticipacaoEditForm(forms.ModelForm):
if data_desligamento and \
data_designacao > data_desligamento:
self.logger.error('Data de designação ({}) superior '
'à data de desligamento ({})'.format(data_designacao, data_desligamento))
self.logger.warn(
'Data de designação ({}) superior '
'à data de desligamento ({})'.format(data_designacao, data_desligamento)
)
raise ValidationError(_('Data de designação não pode ser superior '
'à data de desligamento'))
@ -275,8 +283,11 @@ class ParticipacaoEditForm(forms.ModelForm):
if cleaned_data['cargo'].nome in cargos_unicos:
msg = _('Este cargo é único para esta Comissão.')
self.logger.error('Este cargo ({}) é único para esta Comissão (id={}).'
.format(cleaned_data['cargo'].nome, composicao_id))
self.logger.warn(
'Este cargo ({}) é único para esta Comissão (id={}).'.format(
cleaned_data['cargo'].nome, composicao_id
)
)
raise ValidationError(msg)
return cleaned_data
@ -310,51 +321,70 @@ class ComissaoForm(forms.ModelForm):
if len(self.cleaned_data['nome']) > 100:
msg = _('Nome da Comissão informado ({}) tem mais de 50 caracteres.'.format(
self.cleaned_data['nome']))
self.logger.error(
'Nome da Comissão deve ter no máximo 50 caracteres.')
self.logger.warn(
'Nome da Comissão deve ter no máximo 50 caracteres.'
)
raise ValidationError(msg)
if (self.cleaned_data['data_extincao'] and
self.cleaned_data['data_extincao'] <
self.cleaned_data['data_criacao']):
msg = _('Data de extinção não pode ser menor que a de criação')
self.logger.error('Data de extinção ({}) não pode ser menor que a de criação ({}).'
.format(self.cleaned_data['data_extincao'], self.cleaned_data['data_criacao']))
self.logger.warn(
'Data de extinção ({}) não pode ser menor que a de criação ({}).'.format(
self.cleaned_data['data_extincao'], self.cleaned_data['data_criacao']
)
)
raise ValidationError(msg)
if (self.cleaned_data['data_final_prevista_temp'] and
self.cleaned_data['data_final_prevista_temp'] <
self.cleaned_data['data_criacao']):
msg = _('Data Prevista para Término não pode ser menor que a de criação')
self.logger.error('Data Prevista para Término ({}) não pode ser menor que a de criação ({}).'
.format(self.cleaned_data['data_final_prevista_temp'], self.cleaned_data['data_criacao']))
self.logger.warn(
'Data Prevista para Término ({}) não pode ser menor que a de criação ({}).'.format(
self.cleaned_data['data_final_prevista_temp'], self.cleaned_data['data_criacao']
)
)
raise ValidationError(msg)
if (self.cleaned_data['data_prorrogada_temp'] and
self.cleaned_data['data_prorrogada_temp'] <
self.cleaned_data['data_criacao']):
msg = _('Data Novo Prazo não pode ser menor que a de criação')
self.logger.error('Data Novo Prazo ({}) não pode ser menor que a de criação ({}).'
.format(self.cleaned_data['data_prorrogada_temp'], self.cleaned_data['data_criacao']))
self.logger.warn(
'Data Novo Prazo ({}) não pode ser menor que a de criação ({}).'.format(
self.cleaned_data['data_prorrogada_temp'], self.cleaned_data['data_criacao']
)
)
raise ValidationError(msg)
if (self.cleaned_data['data_instalacao_temp'] and
self.cleaned_data['data_instalacao_temp'] <
self.cleaned_data['data_criacao']):
msg = _('Data de Instalação não pode ser menor que a de criação')
self.logger.error('Data de Instalação ({}) não pode ser menor que a de criação ({}).'
.format(self.cleaned_data['data_instalacao_temp'], self.cleaned_data['data_criacao']))
self.logger.warn(
'Data de Instalação ({}) não pode ser menor que a de criação ({}).'.format(
self.cleaned_data['data_instalacao_temp'], self.cleaned_data['data_criacao']
)
)
raise ValidationError(msg)
if (self.cleaned_data['data_final_prevista_temp'] and self.cleaned_data['data_instalacao_temp'] and
self.cleaned_data['data_final_prevista_temp'] <
self.cleaned_data['data_instalacao_temp']):
msg = _(
'Data Prevista para Término não pode ser menor que a de Instalação.')
self.logger.error('Data Prevista para Término ({}) não pode ser menor que a de Instalação ({}).'
.format(self.cleaned_data['data_final_prevista_temp'], self.cleaned_data['data_instalacao_temp']))
self.logger.warn(
'Data Prevista para Término ({}) não pode ser menor que a de Instalação ({}).'.format(
self.cleaned_data['data_final_prevista_temp'], self.cleaned_data['data_instalacao_temp']
)
)
raise ValidationError(msg)
if (self.cleaned_data['data_prorrogada_temp'] and self.cleaned_data['data_instalacao_temp'] and
self.cleaned_data['data_prorrogada_temp'] <
self.cleaned_data['data_instalacao_temp']):
msg = _('Data Novo Prazo não pode ser menor que a de Instalação.')
self.logger.error('Data Novo Prazo ({}) não pode ser menor que a de Instalação ({}).'
.format(self.cleaned_data['data_prorrogada_temp'], self.cleaned_data['data_instalacao_temp']))
self.logger.warn(
'Data Novo Prazo ({}) não pode ser menor que a de Instalação ({}).'.format(
self.cleaned_data['data_prorrogada_temp'], self.cleaned_data['data_instalacao_temp']
)
)
raise ValidationError(msg)
return self.cleaned_data
@ -400,8 +430,11 @@ class ReuniaoForm(ModelForm):
self.cleaned_data['hora_inicio']):
msg = _(
'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']))
self.logger.warn(
"A hora de término da reunião ({}) não pode ser menor que a de início ({}).".format(
self.cleaned_data['hora_fim'], self.cleaned_data['hora_inicio']
)
)
raise ValidationError(msg)
upload_pauta = self.cleaned_data.get('upload_pauta', False)

26
sapl/comissoes/migrations/0024_auto_20200602_0915.py

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2020-06-02 12:15
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('comissoes', '0023_auto_20191211_1752'),
]
operations = [
migrations.AlterField(
model_name='composicao',
name='comissao',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='comissoes.Comissao', verbose_name='Comissão'),
),
migrations.AlterField(
model_name='participacao',
name='composicao',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='participacao_set', to='comissoes.Composicao', verbose_name='Composição'),
),
]

21
sapl/comissoes/migrations/0025_auto_20200605_1051.py

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2020-06-05 13:51
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('comissoes', '0024_auto_20200602_0915'),
]
operations = [
migrations.AlterField(
model_name='reuniao',
name='comissao',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='comissoes.Comissao', verbose_name='Comissão'),
),
]

6
sapl/comissoes/models.py

@ -141,7 +141,7 @@ class CargoComissao(models.Model):
@reversion.register()
class Composicao(models.Model): # IGNORE
comissao = models.ForeignKey(Comissao,
on_delete=models.PROTECT,
on_delete=models.CASCADE,
verbose_name=_('Comissão'))
periodo = models.ForeignKey(Periodo,
on_delete=models.PROTECT,
@ -160,7 +160,7 @@ class Composicao(models.Model): # IGNORE
class Participacao(models.Model): # ComposicaoComissao
composicao = models.ForeignKey(Composicao,
related_name='participacao_set',
on_delete=models.PROTECT,
on_delete=models.CASCADE,
verbose_name=_('Composição'))
parlamentar = models.ForeignKey(Parlamentar,
on_delete=models.PROTECT,
@ -216,7 +216,7 @@ class Reuniao(models.Model):
verbose_name=_('Periodo da Composicão da Comissão'))
comissao = models.ForeignKey(
Comissao,
on_delete=models.PROTECT,
on_delete=models.CASCADE,
verbose_name=_('Comissão'))
numero = models.PositiveIntegerField(verbose_name=_('Número'))
nome = models.CharField(

20
sapl/comissoes/tests/test_comissoes.py

@ -1,7 +1,7 @@
import pytest
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext as _
from model_mommy import mommy
from model_bakery import baker
from sapl.comissoes.models import Comissao, Composicao, Periodo
from sapl.comissoes.models import TipoComissao, Reuniao
@ -10,18 +10,18 @@ from sapl.comissoes import forms
def make_composicao(comissao):
periodo = mommy.make(Periodo,
periodo = baker.make(Periodo,
data_inicio='2016-01-01',
data_fim='2016-12-31')
mommy.make(Composicao,
baker.make(Composicao,
periodo=periodo,
comissao=comissao)
return Composicao.objects.first()
def make_comissao():
tipo = mommy.make(TipoComissao)
mommy.make(Comissao,
tipo = baker.make(TipoComissao)
baker.make(Comissao,
tipo=tipo,
nome='Comissão Teste',
sigla='CT',
@ -30,15 +30,15 @@ def make_comissao():
def make_filiacao():
partido = mommy.make(Partido,
partido = baker.make(Partido,
nome='Partido Meu',
sigla='PM')
parlamentar = mommy.make(Parlamentar,
parlamentar = baker.make(Parlamentar,
nome_parlamentar='Eduardo',
nome_completo='Eduardo',
sexo='M',
ativo=True)
mommy.make(Filiacao,
baker.make(Filiacao,
data='2016-03-22',
parlamentar=parlamentar,
partido=partido)
@ -48,7 +48,7 @@ def make_filiacao():
@pytest.mark.django_db(transaction=False)
def test_tipo_comissao_model():
mommy.make(TipoComissao,
baker.make(TipoComissao,
nome='Teste_Nome_Tipo_Comissao',
natureza='T',
sigla='TSTC')
@ -80,7 +80,7 @@ def test_incluir_parlamentar_errors(admin_client):
@pytest.mark.django_db(transaction=False)
def test_incluir_comissao_submit(admin_client):
tipo = mommy.make(TipoComissao,
tipo = baker.make(TipoComissao,
sigla='T',
nome='Teste')

7
sapl/comissoes/views.py

@ -150,6 +150,13 @@ class ComposicaoCrud(MasterDetailCrud):
).order_by('-titular', 'cargo__id_ordenacao', 'id')
return context
class DeleteView(MasterDetailCrud.DeleteView):
def delete(self, *args, **kwargs):
composicao = self.get_object()
composicao.delete()
return HttpResponseRedirect(
reverse('sapl.comissoes:composicao_list', kwargs={'pk': composicao.comissao.pk}))
class ComissaoCrud(Crud):
model = Comissao

9
sapl/compilacao/admin.py

@ -1,3 +1,12 @@
from django.contrib import admin
from sapl.compilacao.models import TipoDispositivo
from sapl.utils import register_all_models_in_admin
register_all_models_in_admin(__name__)
admin.site.unregister(TipoDispositivo)
@admin.register(TipoDispositivo)
class TipoDispositivoAdmin(admin.ModelAdmin):
readonly_fields = ("rotulo_prefixo_texto", "rotulo_sufixo_texto",)
list_display = [f.name for f in TipoDispositivo._meta.fields if f.name != 'id']

22
sapl/compilacao/tests/test_compilacao.py

@ -1,5 +1,5 @@
import pytest
from model_mommy import mommy
from model_bakery import baker
from sapl.compilacao.models import PerfilEstruturalTextoArticulado
from sapl.compilacao.models import TipoTextoArticulado
@ -10,7 +10,7 @@ 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(
perfil_estrutural_texto_articulado = baker.make(
PerfilEstruturalTextoArticulado,
nome='Teste_Nome_Perfil',
sigla='TSPETA')
@ -21,7 +21,7 @@ def test_perfil_estrutural_texto_articulado_model():
@pytest.mark.django_db(transaction=False)
def test_tipo_texto_articulado_model():
tipo_texto_articulado = mommy.make(
tipo_texto_articulado = baker.make(
TipoTextoArticulado,
sigla='TTP',
descricao='T_Desc_Tipo_Texto_Articulado'
@ -33,7 +33,7 @@ def test_tipo_texto_articulado_model():
@pytest.mark.django_db(transaction=False)
def test_texto_articulado_model():
texto_articulado = mommy.make(
texto_articulado = baker.make(
TextoArticulado,
ementa='Teste_Ementa_Texto_Articulado',
numero='12345678',
@ -47,7 +47,7 @@ def test_texto_articulado_model():
@pytest.mark.django_db(transaction=False)
def test_tipo_nota_model():
tipo_nota = mommy.make(
tipo_nota = baker.make(
TipoNota,
sigla='TTN',
nome='Teste_Nome_Tipo_Nota'
@ -59,7 +59,7 @@ def test_tipo_nota_model():
@pytest.mark.django_db(transaction=False)
def test_tipo_vide_model():
tipo_vide = mommy.make(
tipo_vide = baker.make(
TipoVide,
sigla='TTV',
nome='Teste_Nome_Tipo_Vide'
@ -71,7 +71,7 @@ def test_tipo_vide_model():
@pytest.mark.django_db(transaction=False)
def test_tipo_dispositivo_model():
tipo_dispositivo = mommy.make(
tipo_dispositivo = baker.make(
TipoDispositivo,
nome='Teste_Nome_Tipo_Dispositivo',
rotulo_ordinal=0
@ -83,24 +83,24 @@ def test_tipo_dispositivo_model():
@pytest.mark.django_db(transaction=False)
def test_tipo_dispositivo_relationship_model():
tipo_dispositivo_pai = mommy.make(
tipo_dispositivo_pai = baker.make(
TipoDispositivo,
nome='Tipo_Dispositivo_Pai',
rotulo_ordinal=0
)
t_dispositivo_filho = mommy.make(
t_dispositivo_filho = baker.make(
TipoDispositivo,
nome='Tipo_Dispositivo_Filho',
rotulo_ordinal=0
)
p_e_texto_articulado = mommy.make(
p_e_texto_articulado = baker.make(
PerfilEstruturalTextoArticulado,
nome='Teste_Nome_Perfil',
sigla='TSPETA')
tipo_dispositivo_relationship = mommy.make(
tipo_dispositivo_relationship = baker.make(
TipoDispositivoRelationship,
pai=tipo_dispositivo_pai,
filho_permitido=t_dispositivo_filho,

4
sapl/compilacao/tests/test_tipo_texto_articulado_form.py

@ -1,6 +1,6 @@
import pytest
from django.utils.translation import ugettext as _
from model_mommy import mommy
from model_bakery import baker
from sapl.compilacao import forms
from sapl.compilacao.models import PerfilEstruturalTextoArticulado, TipoNota
@ -41,7 +41,7 @@ def test_valida_campos_obrigatorios_nota_form():
@pytest.mark.django_db(transaction=False)
def test_nota_form_invalido():
tipo = mommy.make(TipoNota)
tipo = baker.make(TipoNota)
form = forms.NotaForm(data={'titulo': 'titulo',
'texto': 'teste',

10
sapl/crud/base.py

@ -577,8 +577,9 @@ class CrudListView(PermissionRequiredContainerCrudMixin, ListView):
fm = model._meta.get_field(fo)
except Exception as e:
username = self.request.user.username
self.logger.error(
"user=" + username + ". " + str(e))
self.logger.info(
"user=" + username + ". " + str(e)
)
pass
if fm and hasattr(fm, 'related_model')\
@ -607,8 +608,9 @@ class CrudListView(PermissionRequiredContainerCrudMixin, ListView):
# print(ordering)
except Exception as e:
logger.error(string_concat(_(
'ERRO: construção da tupla de ordenação.'), str(e)))
logger.warn(
string_concat(_('ERRO: construção da tupla de ordenação.'), str(e))
)
# print(queryset.query)
if not self.request.user.is_authenticated():

12
sapl/crud/tests/test_base.py

@ -1,6 +1,6 @@
import pytest
from django.core.urlresolvers import reverse
from model_mommy import mommy
from model_bakery import baker
from sapl.crud.base import (CrispyLayoutFormMixin, CrudListView, from_to,
get_field_display, make_pagination)
@ -55,7 +55,7 @@ def test_make_pagination(index, num_pages, result):
def test_get_field_display():
stub = mommy.prepare(Country, is_cold=True)
stub = baker.prepare(Country, is_cold=True)
assert get_field_display(stub, 'name')[1] == stub.name
assert get_field_display(stub, 'continent')[1] == str(stub.continent)
# must return choice display, not the value
@ -88,7 +88,7 @@ def test_layout_fieldnames(_layout, result):
def test_layout_detail_fieldsets():
stub = mommy.make(Country,
stub = baker.make(Country,
name='Brazil',
continent__name='South America',
is_cold=False)
@ -191,7 +191,7 @@ def test_flux_list_paginate_detail(
population, is_cold = i, i % 2 == 0
entries_labels.append([
name, continent, str(population), 'Yes' if is_cold else 'No'])
mommy.make(Country,
baker.make(Country,
name=name,
continent__name=continent,
population=population,
@ -252,7 +252,7 @@ def test_flux_list_paginate_detail(
def test_flux_list_create_detail(app, cancel, make_invalid_submit):
# to have a couple an option for continent field
stub_continent = mommy.make(Continent)
stub_continent = baker.make(Continent)
res = app.get('/country/')
@ -304,7 +304,7 @@ def test_flux_list_create_detail(app, cancel, make_invalid_submit):
def get_detail_page(app):
stub = mommy.make(Country, name='Country Stub')
stub = baker.make(Country, name='Country Stub')
res = app.get('/country/%s' % stub.id)
# on detail page
assert_on_detail_page(res, stub.name)

2
sapl/legacy/run_legacy_tests.sh

@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
# All tests under this directory are excluded in default pytest.ini
# To run them use this script in this directory

2
sapl/legacy/scripts/migra_dbs.sh

@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
# rodar esse script na raiz do projeto

2
sapl/legacy/scripts/migra_um_db.sh

@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
# rodar esse script na raiz do projeto
if [ $# -eq 1 ]; then

2
sapl/legacy/scripts/recria_dbs_postgres.sh

@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
# (Re)cria todos os bancos postgres para migração
# cria um banco postgres (de mesmo nome) para cada banco mysql cujo nome começa com "sapl_"

2
sapl/legacy/scripts/recria_um_db_postgres.sh

@ -1,3 +1,5 @@
#!/usr/bin/env bash
# (Re)cria um db postgres
# uso: recria_um_db_postgres <NOME DO BANCO>

2
sapl/legacy/scripts/shell_para_migracao.sh

@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
# Inicia um shell_plus com as configurações de migração usando um banco específico
# Uso: ./shell_para_migracao.sh <NOME DO BANCO>

45
sapl/materia/forms.py

@ -119,7 +119,7 @@ class ReceberProposicaoForm(Form):
self.helper.layout = Layout(
Fieldset(
_('Incorporar Proposição'), row1,
form_actions(label='Buscar Proposição')
form_actions(label='Recuperar Proposição')
)
)
super(ReceberProposicaoForm, self).__init__(*args, **kwargs)
@ -211,10 +211,8 @@ class MateriaLegislativaForm(FileFieldCheckMixin, ModelForm):
if protocolo:
if not Protocolo.objects.filter(numero=protocolo, ano=ano).exists():
self.logger.error("Protocolo %s/%s não"
" existe" % (protocolo, ano))
raise ValidationError(_('Protocolo %s/%s não'
' existe' % (protocolo, ano)))
self.logger.warning("Protocolo %s/%s não existe" % (protocolo, ano))
raise ValidationError(_('Protocolo %s/%s não existe' % (protocolo, ano)))
if protocolo_antigo != protocolo:
exist_materia = MateriaLegislativa.objects.filter(
@ -517,11 +515,9 @@ class TramitacaoForm(ModelForm):
raise ValidationError(msg)
if cleaned_data['data_tramitacao'] > timezone.now().date():
self.logger.error('A data de tramitação informada ({}) não é ' +
'menor ou igual a data de hoje!'.format(cleaned_data['data_tramitacao']))
msg = _(
'A data de tramitação deve ser ' +
'menor ou igual a data de hoje!')
self.logger.warning('A data de tramitação informada ({}) não é menor ou igual a data de hoje!'
.format(cleaned_data['data_tramitacao']))
msg = _('A data de tramitação deve ser menor ou igual a data de hoje!')
raise ValidationError(msg)
if (ultima_tramitacao and
@ -535,10 +531,8 @@ class TramitacaoForm(ModelForm):
if data_enc_form:
if data_enc_form < data_tram_form:
msg = _('A data de encaminhamento deve ser ' +
'maior que a data de tramitação!')
self.logger.error("A data de encaminhamento ({}) deve ser "
"maior que a data de tramitação! ({})"
msg = _('A data de encaminhamento deve ser maior que a data de tramitação!')
self.logger.warning("A data de encaminhamento ({}) deve ser maior que a data de tramitação! ({})"
.format(data_enc_form, data_tram_form))
raise ValidationError(msg)
@ -904,8 +898,7 @@ class AnexadaForm(ModelForm):
except ObjectDoesNotExist:
msg = _('A {} {}/{} não existe no cadastro de matérias legislativas.'
.format(cleaned_data['tipo'], cleaned_data['numero'], cleaned_data['ano']))
self.logger.error("A matéria a ser anexada não existe no cadastro"
" de matérias legislativas.")
self.logger.warning("A matéria a ser anexada não existe no cadastro de matérias legislativas.")
raise ValidationError(msg)
materia_principal = self.instance.materia_principal
@ -1073,7 +1066,7 @@ class MateriaLegislativaFilterSet(django_filters.FilterSet):
'Pesquisar Autor',
css_class='btn btn-primary btn-sm'), 2),
(Button('limpar',
'limpar Autor',
'Limpar Autor',
css_class='btn btn-primary btn-sm'), 2),
('autoria__primeiro_autor', 2),
('autoria__autor__tipo', 3),
@ -1742,11 +1735,9 @@ class TramitacaoEmLoteForm(ModelForm):
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!')
self.logger.warning('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:
@ -2553,13 +2544,9 @@ class ConfirmarProposicaoForm(ProposicaoForm):
legislatura = Legislatura.objects.filter(
data_inicio__year__lte=timezone.now().year,
data_fim__year__gte=timezone.now().year).first()
data_inicio = legislatura.data_inicio
data_fim = legislatura.data_fim
nm = MateriaLegislativa.objects.filter(
data_apresentacao__gte=data_inicio,
data_apresentacao__lte=data_fim,
tipo=tipo).aggregate(Max('numero'))
ano_inicio = legislatura.data_inicio.year
ano_fim = legislatura.data_fim.year
nm = Protocolo.objects.filter(ano__gte=ano_inicio, ano__lte=ano_fim).aggregate(Max('numero'))
else:
# numeracao == 'U' ou não informada
nm = Protocolo.objects.all().aggregate(Max('numero'))

23
sapl/materia/migrations/0065_auto_20200313_1137.py

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.27 on 2020-03-13 14:37
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('materia', '0064_auto_20200114_1121'),
]
operations = [
migrations.AlterModelOptions(
name='assuntomateria',
options={'ordering': ('assunto', 'dispositivo'), 'verbose_name': 'Assunto de Matéria', 'verbose_name_plural': 'Assuntos de Matéria'},
),
migrations.AlterModelOptions(
name='materiaassunto',
options={'ordering': ('assunto__assunto', '-materia'), 'verbose_name': 'Relação Matéria - Assunto', 'verbose_name_plural': 'Relações Matéria - Assunto'},
),
]

20
sapl/materia/migrations/0066_auto_20200313_1441.py

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2020-03-13 17:41
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('materia', '0065_auto_20200313_1137'),
]
operations = [
migrations.AlterField(
model_name='proposicao',
name='descricao',
field=models.TextField(verbose_name='Ementa'),
),
]

30
sapl/materia/migrations/0067_auto_20200416_1538.py

@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2020-04-16 18:38
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('materia', '0066_auto_20200313_1441'),
]
operations = [
migrations.AlterField(
model_name='materialegislativa',
name='ip',
field=models.CharField(blank=True, default='', max_length=60, verbose_name='IP'),
),
migrations.AlterField(
model_name='proposicao',
name='ip',
field=models.CharField(blank=True, default='', max_length=60, verbose_name='IP'),
),
migrations.AlterField(
model_name='tramitacao',
name='ip',
field=models.CharField(blank=True, default='', max_length=60, verbose_name='IP'),
),
]

10
sapl/materia/models.py

@ -291,7 +291,7 @@ class MateriaLegislativa(models.Model):
)
ip = models.CharField(
verbose_name=_('IP'),
max_length=30,
max_length=60,
blank=True,
default=''
)
@ -487,6 +487,7 @@ class AssuntoMateria(models.Model):
class Meta:
verbose_name = _('Assunto de Matéria')
verbose_name_plural = _('Assuntos de Matéria')
ordering = ('assunto', 'dispositivo')
def __str__(self):
return self.assunto
@ -618,6 +619,7 @@ class MateriaAssunto(models.Model):
class Meta:
verbose_name = _('Relação Matéria - Assunto')
verbose_name_plural = _('Relações Matéria - Assunto')
ordering = ('assunto__assunto', '-materia')
def __str__(self):
return _('%(materia)s - %(assunto)s') % {
@ -769,7 +771,7 @@ class Proposicao(models.Model):
data_devolucao = models.DateTimeField(
blank=True, null=True, verbose_name=_('Data de Devolução'))
descricao = models.TextField(verbose_name=_('Descrição'))
descricao = models.TextField(verbose_name=_('Ementa'))
justificativa_devolucao = models.CharField(
max_length=200,
blank=True,
@ -859,7 +861,7 @@ class Proposicao(models.Model):
)
ip = models.CharField(
verbose_name=_('IP'),
max_length=30,
max_length=60,
blank=True,
default=''
)
@ -1085,7 +1087,7 @@ class Tramitacao(models.Model):
null=True,
blank=True)
ip = models.CharField(verbose_name=_('IP'),
max_length=30,
max_length=60,
blank=True,
default='')
ultima_edicao = models.DateTimeField(

106
sapl/materia/tests/test_materia.py

@ -4,7 +4,7 @@ from django.contrib.contenttypes.models import ContentType
from django.core.files.uploadedfile import SimpleUploadedFile
from django.core.urlresolvers import reverse
from django.db.models import Max
from model_mommy import mommy
from model_bakery import baker
import pytest
from sapl.base.models import Autor, TipoAutor, AppConfig
@ -25,15 +25,15 @@ from sapl.utils import models_with_gr_for_model, lista_anexados
@pytest.mark.django_db(transaction=False)
def test_lista_materias_anexadas():
tipo_materia = mommy.make(
tipo_materia = baker.make(
TipoMateriaLegislativa,
descricao="Tipo_Teste"
)
regime_tramitacao = mommy.make(
regime_tramitacao = baker.make(
RegimeTramitacao,
descricao="Regime_Teste"
)
materia_principal = mommy.make(
materia_principal = baker.make(
MateriaLegislativa,
numero=20,
ano=2018,
@ -41,7 +41,7 @@ def test_lista_materias_anexadas():
regime_tramitacao=regime_tramitacao,
tipo=tipo_materia
)
materia_anexada = mommy.make(
materia_anexada = baker.make(
MateriaLegislativa,
numero=21,
ano=2019,
@ -49,7 +49,7 @@ def test_lista_materias_anexadas():
regime_tramitacao=regime_tramitacao,
tipo=tipo_materia
)
materia_anexada_anexada = mommy.make(
materia_anexada_anexada = baker.make(
MateriaLegislativa,
numero=22,
ano=2020,
@ -58,13 +58,13 @@ def test_lista_materias_anexadas():
tipo=tipo_materia
)
mommy.make(
baker.make(
Anexada,
materia_principal=materia_principal,
materia_anexada=materia_anexada,
data_anexacao="2019-05-11"
)
mommy.make(
baker.make(
Anexada,
materia_principal=materia_anexada,
materia_anexada=materia_anexada_anexada,
@ -80,15 +80,15 @@ def test_lista_materias_anexadas():
@pytest.mark.django_db(transaction=False)
def test_lista_materias_anexadas_ciclo():
tipo_materia = mommy.make(
tipo_materia = baker.make(
TipoMateriaLegislativa,
descricao="Tipo_Teste"
)
regime_tramitacao = mommy.make(
regime_tramitacao = baker.make(
RegimeTramitacao,
descricao="Regime_Teste"
)
materia_principal = mommy.make(
materia_principal = baker.make(
MateriaLegislativa,
numero=20,
ano=2018,
@ -96,7 +96,7 @@ def test_lista_materias_anexadas_ciclo():
regime_tramitacao=regime_tramitacao,
tipo=tipo_materia
)
materia_anexada = mommy.make(
materia_anexada = baker.make(
MateriaLegislativa,
numero=21,
ano=2019,
@ -105,13 +105,13 @@ def test_lista_materias_anexadas_ciclo():
tipo=tipo_materia
)
mommy.make(
baker.make(
Anexada,
materia_principal=materia_principal,
materia_anexada=materia_anexada,
data_anexacao="2019-05-11"
)
mommy.make(
baker.make(
Anexada,
materia_principal=materia_anexada,
materia_anexada=materia_principal,
@ -126,8 +126,8 @@ def test_lista_materias_anexadas_ciclo():
@pytest.mark.django_db(transaction=False)
def make_unidade_tramitacao(descricao):
# Cria uma comissão para ser a unidade de tramitação
tipo_comissao = mommy.make(TipoComissao)
comissao = mommy.make(Comissao,
tipo_comissao = baker.make(TipoComissao)
comissao = baker.make(Comissao,
tipo=tipo_comissao,
nome=descricao,
sigla='T',
@ -138,7 +138,7 @@ def make_unidade_tramitacao(descricao):
assert comissao.nome == descricao
# Cria a unidade
unidade = mommy.make(UnidadeTramitacao, comissao=comissao)
unidade = baker.make(UnidadeTramitacao, comissao=comissao)
assert unidade.comissao == comissao
return unidade
@ -147,10 +147,10 @@ def make_unidade_tramitacao(descricao):
@pytest.mark.django_db(transaction=False)
def make_norma():
# Cria um novo tipo de Norma
tipo = mommy.make(TipoNormaJuridica,
tipo = baker.make(TipoNormaJuridica,
sigla='T1',
descricao='Teste_Tipo_Norma')
mommy.make(NormaJuridica,
baker.make(NormaJuridica,
tipo=tipo,
numero=1,
ano=2016,
@ -169,13 +169,13 @@ def make_norma():
@pytest.mark.django_db(transaction=False)
def make_materia_principal():
regime_tramitacao = mommy.make(RegimeTramitacao, descricao='Teste_Regime')
regime_tramitacao = baker.make(RegimeTramitacao, descricao='Teste_Regime')
# Cria a matéria principal
tipo = mommy.make(TipoMateriaLegislativa,
tipo = baker.make(TipoMateriaLegislativa,
sigla='T1',
descricao='Teste_MateriaLegislativa')
mommy.make(MateriaLegislativa,
baker.make(MateriaLegislativa,
tipo=tipo,
numero='165',
ano='2002',
@ -195,11 +195,11 @@ def test_materia_anexada_submit(admin_client):
materia_principal = make_materia_principal()
# Cria a matéria que será anexada
tipo_anexada = mommy.make(TipoMateriaLegislativa,
tipo_anexada = baker.make(TipoMateriaLegislativa,
sigla='T2',
descricao='Teste_2')
regime_tramitacao = mommy.make(RegimeTramitacao, descricao='Teste_Regime')
mommy.make(MateriaLegislativa,
regime_tramitacao = baker.make(RegimeTramitacao, descricao='Teste_Regime')
baker.make(MateriaLegislativa,
tipo=tipo_anexada,
numero='32',
ano='2004',
@ -230,10 +230,10 @@ def test_materia_anexada_submit(admin_client):
def test_autoria_submit(admin_client):
materia_principal = make_materia_principal()
# Cria um tipo de Autor
tipo_autor = mommy.make(TipoAutor, descricao='Teste Tipo_Autor')
tipo_autor = baker.make(TipoAutor, descricao='Teste Tipo_Autor')
# Cria um Autor
autor = mommy.make(
autor = baker.make(
Autor,
tipo=tipo_autor,
nome='Autor Teste')
@ -261,8 +261,8 @@ def test_despacho_inicial_submit(admin_client):
materia_principal = make_materia_principal()
# Cria uma comissão
tipo_comissao = mommy.make(TipoComissao)
comissao = mommy.make(Comissao,
tipo_comissao = baker.make(TipoComissao)
comissao = baker.make(Comissao,
tipo=tipo_comissao,
nome='Teste',
ativa=True,
@ -312,16 +312,16 @@ def test_documento_acessorio_submit(admin_client):
materia_principal = make_materia_principal()
# Cria um tipo de Autor
tipo_autor = mommy.make(TipoAutor, descricao='Teste Tipo_Autor')
tipo_autor = baker.make(TipoAutor, descricao='Teste Tipo_Autor')
# Cria um Autor
autor = mommy.make(
autor = baker.make(
Autor,
tipo=tipo_autor,
nome='Autor Teste')
# Cria um tipo de documento
tipo = mommy.make(TipoDocumento,
tipo = baker.make(TipoDocumento,
descricao='Teste')
# Testa POST
@ -373,7 +373,7 @@ def test_legislacao_citada_submit(admin_client):
def test_tramitacao_submit(admin_client):
materia_principal = make_materia_principal()
# Cria status para tramitação
status_tramitacao = mommy.make(StatusTramitacao,
status_tramitacao = baker.make(StatusTramitacao,
indicador='F',
sigla='ST',
descricao='Status_Teste')
@ -541,10 +541,10 @@ def test_form_errors_relatoria(admin_client):
@pytest.mark.django_db(transaction=False)
def test_proposicao_submit(admin_client):
tipo_autor = mommy.make(TipoAutor, descricao='Teste Tipo_Autor')
tipo_autor = baker.make(TipoAutor, descricao='Teste Tipo_Autor')
user = get_user_model().objects.filter(is_active=True)[0]
autor = mommy.make(
autor = baker.make(
Autor,
user=user,
tipo=tipo_autor,
@ -557,11 +557,11 @@ def test_proposicao_submit(admin_client):
*models_with_gr_for_model(TipoProposicao))
for pk, mct in enumerate(mcts):
tipo_conteudo_related = mommy.make(mct, pk=pk + 1)
tipo_conteudo_related = baker.make(mct, pk=pk + 1)
response = admin_client.post(
reverse('sapl.materia:proposicao_create'),
{'tipo': mommy.make(
{'tipo': baker.make(
TipoProposicao, pk=3,
tipo_conteudo_related=tipo_conteudo_related).pk,
'descricao': 'Teste proposição',
@ -586,11 +586,11 @@ def test_proposicao_submit(admin_client):
@pytest.mark.django_db(transaction=False)
def test_form_errors_proposicao(admin_client):
tipo_autor = mommy.make(TipoAutor, descricao='Teste Tipo_Autor')
tipo_autor = baker.make(TipoAutor, descricao='Teste Tipo_Autor')
user = get_user_model().objects.filter(is_active=True)[0]
autor = mommy.make(
autor = baker.make(
Autor,
user=user,
tipo=tipo_autor,
@ -616,13 +616,13 @@ def test_form_errors_proposicao(admin_client):
def test_numeracao_materia_legislativa_por_legislatura(admin_client):
# Criar Legislaturas
legislatura1 = mommy.make(Legislatura,
legislatura1 = baker.make(Legislatura,
data_inicio='2014-01-01',
data_fim='2018-12-31',
numero=20,
data_eleicao='2013-10-15'
)
legislatura2 = mommy.make(Legislatura,
legislatura2 = baker.make(Legislatura,
data_inicio='2009-01-01',
data_fim='2013-12-31',
numero=21,
@ -630,9 +630,9 @@ def test_numeracao_materia_legislativa_por_legislatura(admin_client):
)
# Cria uma materia na legislatura1
tipo_materia = mommy.make(TipoMateriaLegislativa,
tipo_materia = baker.make(TipoMateriaLegislativa,
id=1, sequencia_numeracao='L')
materia = mommy.make(MateriaLegislativa,
materia = baker.make(MateriaLegislativa,
tipo=tipo_materia,
ano=2017,
numero=1,
@ -660,9 +660,9 @@ def test_numeracao_materia_legislativa_por_legislatura(admin_client):
def test_numeracao_materia_legislativa_por_ano(admin_client):
# Cria uma materia
tipo_materia = mommy.make(TipoMateriaLegislativa,
tipo_materia = baker.make(TipoMateriaLegislativa,
id=1, sequencia_numeracao='A')
materia = mommy.make(MateriaLegislativa,
materia = baker.make(MateriaLegislativa,
tipo=tipo_materia,
ano=2017,
numero=1
@ -687,38 +687,38 @@ def test_numeracao_materia_legislativa_por_ano(admin_client):
@pytest.mark.django_db(transaction=False)
def test_tramitacoes_materias_anexadas(admin_client):
config = mommy.make(AppConfig, tramitacao_materia=True)
config = baker.make(AppConfig, tramitacao_materia=True)
tipo_materia = mommy.make(
tipo_materia = baker.make(
TipoMateriaLegislativa,
descricao="Tipo_Teste"
)
materia_principal = mommy.make(
materia_principal = baker.make(
MateriaLegislativa,
ano=2018,
data_apresentacao="2018-01-04",
tipo=tipo_materia
)
materia_anexada = mommy.make(
materia_anexada = baker.make(
MateriaLegislativa,
ano=2019,
data_apresentacao="2019-05-04",
tipo=tipo_materia
)
materia_anexada_anexada = mommy.make(
materia_anexada_anexada = baker.make(
MateriaLegislativa,
ano=2020,
data_apresentacao="2020-01-05",
tipo=tipo_materia
)
mommy.make(
baker.make(
Anexada,
materia_principal=materia_principal,
materia_anexada=materia_anexada,
data_anexacao="2019-05-11"
)
mommy.make(
baker.make(
Anexada,
materia_principal=materia_anexada,
materia_anexada=materia_anexada_anexada,
@ -730,7 +730,7 @@ def test_tramitacoes_materias_anexadas(admin_client):
unidade_tramitacao_destino_1 = make_unidade_tramitacao(descricao="Teste 2")
unidade_tramitacao_destino_2 = make_unidade_tramitacao(descricao="Teste 3")
status = mommy.make(
status = baker.make(
StatusTramitacao,
indicador='R')

12
sapl/materia/tests/test_materia_form.py

@ -1,6 +1,6 @@
import pytest
from django.utils.translation import ugettext as _
from model_mommy import mommy
from model_bakery import baker
from sapl.comissoes.models import Comissao, TipoComissao
from sapl.materia import forms
@ -24,7 +24,7 @@ def test_valida_campos_obrigatorios_ficha_pesquisa_form():
@pytest.mark.django_db(transaction=False)
def test_ficha_pesquisa_form_datas_invalidas():
tipo = mommy.make(TipoMateriaLegislativa)
tipo = baker.make(TipoMateriaLegislativa)
form = forms.FichaPesquisaForm(data={'tipo_materia': str(tipo.pk),
'data_inicial': '10/11/2017',
@ -37,7 +37,7 @@ def test_ficha_pesquisa_form_datas_invalidas():
@pytest.mark.django_db(transaction=False)
def test_ficha_pesquisa_form_invalido():
tipo = mommy.make(TipoMateriaLegislativa)
tipo = baker.make(TipoMateriaLegislativa)
form = forms.FichaPesquisaForm(data={'tipo_materia': str(tipo.pk),
'data_inicial': '10/11/2017',
@ -62,7 +62,7 @@ def test_valida_campos_obrigatorios_ficha_seleciona_form():
@pytest.mark.django_db(transaction=False)
def test_ficha_seleciona_form_valido():
materia = mommy.make(MateriaLegislativa)
materia = baker.make(MateriaLegislativa)
form = forms.FichaSelecionaForm(data={'materia': str(materia.pk)})
@ -178,8 +178,8 @@ def test_valida_campos_obrigatorios_devolver_proposicao_form():
@pytest.mark.django_db(transaction=False)
def test_valida_campos_obrigatorios_relatoria_form():
tipo_comissao = mommy.make(TipoComissao)
comissao = mommy.make(Comissao,
tipo_comissao = baker.make(TipoComissao)
comissao = baker.make(Comissao,
tipo=tipo_comissao,
nome='Comissao Teste',
sigla='T',

6
sapl/materia/urls.py

@ -27,7 +27,7 @@ from sapl.materia.views import (AcompanhamentoConfirmarView,
proposicao_texto, recuperar_materia,
ExcluirTramitacaoEmLoteView, RetornarProposicao,
MateriaPesquisaSimplesView,
DespachoInicialMultiCreateView)
DespachoInicialMultiCreateView, get_zip_docacessorios, get_pdf_docacessorios)
from sapl.norma.views import NormaPesquisaSimplesView
from sapl.protocoloadm.views import (
FichaPesquisaAdmView, FichaSelecionaAdmView)
@ -118,6 +118,10 @@ urlpatterns_materia = [
name='tramitacao_em_lote'),
url(r'^materia/excluir-tramitacao-em-lote', ExcluirTramitacaoEmLoteView.as_view(),
name='excluir_tramitacao_em_lote'),
url(r'^materia/docacessorio/zip/(?P<pk>\d+)$', get_zip_docacessorios,
name='compress_docacessorios'),
url(r'^materia/docacessorio/pdf/(?P<pk>\d+)$', get_pdf_docacessorios,
name='merge_docacessorios')
]

351
sapl/materia/views.py

@ -6,11 +6,15 @@ import sapl
import shutil
import tempfile
import weasyprint
import time
from crispy_forms.layout import HTML
from datetime import datetime
from random import choice
from string import ascii_letters, digits
from datetime import datetime
from PyPDF4 import PdfFileReader, PdfFileMerger
import zipfile
from django.conf import settings
from django.contrib import messages
@ -52,7 +56,7 @@ from sapl.settings import MAX_DOC_UPLOAD_SIZE, MEDIA_ROOT
from sapl.utils import (autor_label, autor_modal, gerar_hash_arquivo, get_base_url,
get_client_ip, get_mime_type_from_file_extension, lista_anexados,
mail_service_configured, montar_row_autor, SEPARADOR_HASH_PROPOSICAO,
show_results_filter_set, YES_NO_CHOICES)
show_results_filter_set, YES_NO_CHOICES,get_tempfile_dir)
from .forms import (AcessorioEmLoteFilterSet, AcompanhamentoMateriaForm,
AnexadaEmLoteFilterSet, AdicionarVariasAutoriasFilterSet,
@ -732,8 +736,14 @@ class ProposicaoCrud(Crud):
container_field = 'autor__user'
class BaseMixin(Crud.BaseMixin):
list_field_names = ['data_envio', 'data_recebimento', 'descricao',
'tipo', 'conteudo_gerado_related', 'cancelado', ]
list_field_names = [
'data_envio',
'data_recebimento',
'descricao',
'tipo',
'conteudo_gerado_related',
'cancelado'
]
class BaseLocalMixin:
form_class = ProposicaoForm
@ -747,22 +757,23 @@ class ProposicaoCrud(Crud):
def get(self, request, *args, **kwargs):
if not self._action_is_valid(request, *args, **kwargs):
return redirect(reverse('sapl.materia:proposicao_detail',
kwargs={'pk': kwargs['pk']}))
return redirect(reverse('sapl.materia:proposicao_detail', kwargs={'pk': kwargs['pk']}))
return super().get(self, request, *args, **kwargs)
def post(self, request, *args, **kwargs):
if not self._action_is_valid(request, *args, **kwargs):
return redirect(reverse('sapl.materia:proposicao_detail',
kwargs={'pk': kwargs['pk']}))
return redirect(reverse('sapl.materia:proposicao_detail', kwargs={'pk': kwargs['pk']}))
return super().post(self, request, *args, **kwargs)
class DetailView(Crud.DetailView):
layout_key = 'Proposicao'
permission_required = (RP_DETAIL, 'materia.detail_proposicao_enviada',
'materia.detail_proposicao_devolvida',
'materia.detail_proposicao_incorporada')
permission_required = (
RP_DETAIL,
'materia.detail_proposicao_enviada',
'materia.detail_proposicao_devolvida',
'materia.detail_proposicao_incorporada'
)
logger = logging.getLogger(__name__)
@ -781,7 +792,6 @@ class ProposicaoCrud(Crud):
return context
def get(self, request, *args, **kwargs):
action = request.GET.get('action', '')
user = request.user
username = user.username
@ -798,10 +808,8 @@ class ProposicaoCrud(Crud):
msg_error = _('Proposição já foi enviada e recebida.')
elif p.data_envio:
msg_error = _('Proposição já foi enviada.')
elif not p.texto_original and\
not p.texto_articulado.exists():
msg_error = _('Proposição não possui nenhum tipo de '
'Texto associado.')
elif not p.texto_original and not p.texto_articulado.exists():
msg_error = _('Proposição não possui nenhum tipo de Texto associado.')
else:
if p.texto_articulado.exists():
ta = p.texto_articulado.first()
@ -809,8 +817,7 @@ class ProposicaoCrud(Crud):
ta.editing_locked = True
ta.save()
receber_recibo = BaseAppConfig.attr(
'receber_recibo_proposicao')
receber_recibo = BaseAppConfig.attr('receber_recibo_proposicao')
if not receber_recibo:
ta = p.texto_articulado.first()
@ -820,45 +827,39 @@ class ProposicaoCrud(Crud):
p.data_envio = timezone.now()
p.save()
messages.success(request, _(
'Proposição enviada com sucesso.'))
messages.success(request, _('Proposição enviada com sucesso.'))
try:
self.logger.debug("user=" + username + ". Tentando obter número do objeto MateriaLegislativa com "
"atributos tipo={} e ano={}."
.format(p.tipo.tipo_conteudo_related, p.ano))
self.logger.debug("User={}. Tentando obter número do objeto MateriaLegislativa "
"com atributos tipo={} e ano={}."
.format(username, p.tipo.tipo_conteudo_related, p.ano))
if p.numero_materia_futuro:
numero = p.numero_materia_futuro
else:
numero = MateriaLegislativa.objects.filter(tipo=p.tipo.tipo_conteudo_related,
ano=p.ano).last().numero + 1
messages.success(request, _(
'%s : nº %s de %s <br>Atenção! Este número é apenas um provável '
'número que pode não corresponder com a realidade'
% (p.tipo, numero, p.ano)))
messages.success(request, _("{}: nº {} de {} <br>Atenção! Este número é apenas um provável "
"número que pode não corresponder com a realidade"
.format(p.tipo, numero, p.ano)))
except ValueError as e:
self.logger.error(
"user=" + username + "." + str(e))
self.logger.warning("User=" + username + ". " + str(e))
pass
except AttributeError as e:
self.logger.error(
"user=" + username + "." + str(e))
self.logger.warning("User=" + username + ". " + str(e))
pass
except TypeError as e:
self.logger.error(
"user=" + username + "." + str(e))
self.logger.warning("User=" + username + ". " + str(e))
pass
elif action == 'return':
if not p.data_envio:
self.logger.error(
"user=" + username + ". Proposição (numero={}) ainda não foi enviada.".format(p.numero_proposicao))
self.logger.warning("User={}. Proposição (numero={}) ainda não foi enviada."
.format(username, p.numero_proposicao))
msg_error = _('Proposição ainda não foi enviada.')
elif p.data_recebimento:
self.logger.error("user=" + username + ". Proposição (numero={}) já foi recebida, não é "
"possível retorná-la.".format(p.numero_proposicao))
msg_error = _('Proposição já foi recebida, não é '
'possível retorná-la.')
self.logger.warning("User={}. Proposição (numero={}) já foi recebida, "
"não é possível retorná-la.".format(username, p.numero_proposicao))
msg_error = _('Proposição já foi recebida, não é possível retorná-la.')
else:
p.data_envio = None
p.save()
@ -867,27 +868,24 @@ class ProposicaoCrud(Crud):
ta.privacidade = STATUS_TA_PRIVATE
ta.editing_locked = False
ta.save()
self.logger.info(
"user=" + username + ". Proposição (numero={}) Retornada com sucesso.".format(p.numero_proposicao))
messages.success(request, _(
'Proposição Retornada com sucesso.'))
self.logger.info("User={}. Proposição (numero={}) Retornada com sucesso."
.format(username, p.numero_proposicao))
messages.success(request, _('Proposição Retornada com sucesso.'))
if msg_error:
messages.error(request, msg_error)
# retornar redirecionando para limpar a variavel action
return redirect(reverse('sapl.materia:proposicao_detail',
kwargs={'pk': kwargs['pk']}))
return redirect(reverse('sapl.materia:proposicao_detail', kwargs={'pk': kwargs['pk']}))
def dispatch(self, request, *args, **kwargs):
username = request.user.username
try:
self.logger.debug(
"user=" + username + ". Tentando obter objeto Proposicao com pk={}".format(kwargs['pk']))
self.logger.debug("User={}. Tentando obter objeto Proposicao com pk={}".format(username, kwargs['pk']))
p = Proposicao.objects.get(id=kwargs['pk'])
except Exception as e:
self.logger.error(
"user=" + username + ". Erro ao obter proposicao com pk={}. Retornando 404. ".format(kwargs['pk']) + str(e))
self.logger.warning("User={}. Erro ao obter proposicao com pk={}. Retornando 404. {}"
.format(username, kwargs['pk'], str(e)))
raise Http404()
if not self.has_permission():
@ -897,47 +895,38 @@ class ProposicaoCrud(Crud):
if not p.data_envio and not p.data_devolucao:
raise Http404()
if p.data_devolucao and not request.user.has_perm(
'materia.detail_proposicao_devolvida'):
if p.data_devolucao and not request.user.has_perm('materia.detail_proposicao_devolvida'):
raise Http404()
if p.data_envio and not p.data_recebimento\
and not request.user.has_perm(
'materia.detail_proposicao_enviada'):
if p.data_envio and not p.data_recebimento \
and not request.user.has_perm('materia.detail_proposicao_enviada'):
raise Http404()
if p.data_envio and p.data_recebimento\
and not request.user.has_perm(
'materia.detail_proposicao_incorporada'):
if p.data_envio and p.data_recebimento \
and not request.user.has_perm('materia.detail_proposicao_incorporada'):
raise Http404()
return super(PermissionRequiredMixin, self).dispatch(
request, *args, **kwargs)
return super(PermissionRequiredMixin, self).dispatch(request, *args, **kwargs)
class DeleteView(BaseLocalMixin, Crud.DeleteView):
logger = logging.getLogger(__name__)
def _action_is_valid(self, request, *args, **kwargs):
proposicao = Proposicao.objects.filter(
id=kwargs['pk']).values_list(
'data_envio', 'data_recebimento')
proposicao = Proposicao.objects.filter(id=kwargs['pk']).values_list('data_envio', 'data_recebimento')
username = request.user.username
if proposicao:
if proposicao[0][0] and proposicao[0][1]:
self.logger.error("user=" + username + ". Proposição (id={}) já foi enviada e recebida."
"Não pode mais ser excluida.".format(kwargs['pk']))
msg = _('Proposição já foi enviada e recebida.'
'Não pode mais ser excluida.')
self.logger.warning("User={}. Proposição (id={}) já foi enviada e recebida."
"Não pode mais ser excluida.".format(username, kwargs['pk']))
msg = _('Proposição já foi enviada e recebida. Não pode mais ser excluida.')
elif proposicao[0][0] and not proposicao[0][1]:
self.logger.error("user=" + username + ". Proposição (id={}) já foi enviada mas ainda não recebida "
"pelo protocolo. Use a opção Recuperar Proposição "
"para depois excluí-la.".format(kwargs['pk']))
msg = _('Proposição já foi enviada mas ainda não recebida '
'pelo protocolo. Use a opção Recuperar Proposição '
'para depois excluí-la.')
self.logger.warning("""\
User={}. Proposição (id={}) foi enviada, mas ainda não recebida pelo protocolo. \
Use a opção Recuperar Proposição para depois excluí-la.""".format(username, kwargs['pk']))
msg = _("Proposição já foi enviada mas ainda não recebida pelo protocolo. "
"Use a opção Recuperar Proposição para depois excluí-la.")
if proposicao[0][0] or proposicao[0][1]:
messages.error(request, msg)
@ -945,20 +934,18 @@ class ProposicaoCrud(Crud):
return True
class UpdateView(BaseLocalMixin, Crud.UpdateView):
logger = logging.getLogger(__name__)
form_class = ProposicaoForm
def form_valid(self, form):
tz = timezone.get_current_timezone()
objeto_antigo = Proposicao.objects.get(
pk=self.kwargs['pk']
)
objeto_antigo = Proposicao.objects.get(pk=self.kwargs['pk'])
dict_objeto_antigo = objeto_antigo.__dict__
tipo_texto = self.request.POST.get('tipo_texto', '')
if tipo_texto=='D' and objeto_antigo.texto_articulado.exists() or tipo_texto=='T' and not objeto_antigo.texto_articulado.exists():
if tipo_texto == 'D' and objeto_antigo.texto_articulado.exists()\
or tipo_texto == 'T' and not objeto_antigo.texto_articulado.exists():
self.object.user = self.request.user
self.object.ip = get_client_ip(self.request)
self.object.ultima_edicao = tz.localize(datetime.now())
@ -968,7 +955,10 @@ class ProposicaoCrud(Crud):
dict_objeto_novo = self.object.__dict__
atributos = [
'tipo_id', 'descricao', 'observacao', 'texto_original',
'tipo_id',
'descricao',
'observacao',
'texto_original',
'materia_de_vinculo_id'
]
@ -983,27 +973,22 @@ class ProposicaoCrud(Crud):
return super().form_valid(form)
def _action_is_valid(self, request, *args, **kwargs):
proposicao = Proposicao.objects.filter(
id=kwargs['pk']).values_list(
'data_envio', 'data_recebimento')
proposicao = Proposicao.objects.filter(id=kwargs['pk']).values_list('data_envio', 'data_recebimento')
username = request.user.username
if proposicao:
msg = ''
if proposicao[0][0] and proposicao[0][1]:
self.logger.error('user=' + username + '. Proposição (id={}) já foi enviada e recebida.'
'Não pode mais ser editada'.format(kwargs['pk']))
msg = _('Proposição já foi enviada e recebida.'
'Não pode mais ser editada')
self.logger.warning('User={}. Proposição (id={}) já foi enviada e recebida. '
'Não pode mais ser editada'.format(username, kwargs['pk']))
msg = _('Proposição já foi enviada e recebida. Não pode mais ser editada')
elif proposicao[0][0] and not proposicao[0][1]:
self.logger.error('user=' + username + '. Proposição (id={}) já foi enviada mas ainda não recebida '
'pelo protocolo. Use a opção Recuperar Proposição '
'para voltar para edição.'.format(kwargs['pk']))
msg = _('Proposição já foi enviada mas ainda não recebida '
'pelo protocolo. Use a opção Recuperar Proposição '
'para voltar para edição.')
self.logger.warning("""\
User={}. Proposição (id={}) foi enviada, mas ainda não recebida pelo protocolo. \
Use a opção Recuperar Proposição para voltar para edição.""".format(username, kwargs['pk']))
msg = _("Proposição já foi enviada, mas ainda não recebida pelo protocolo. "
"Use a opção Recuperar Proposição para voltar para edição.")
if proposicao[0][0] or proposicao[0][1]:
messages.error(request, msg)
@ -1011,22 +996,17 @@ class ProposicaoCrud(Crud):
return True
def get_success_url(self):
tipo_texto = self.request.POST.get('tipo_texto', '')
username = self.request.user.username
if tipo_texto == 'T':
messages.info(self.request,
_('Sempre que uma Proposição é inclusa ou '
'alterada e a opção "Texto Articulado " for '
'marcada, você será redirecionado para a '
'edição do Texto Eletrônico.'))
self.logger.debug('user=' + username + '. Sempre que uma Proposição é inclusa ou '
'alterada e a opção "Texto Articulado " for '
'marcada, você será redirecionado para a '
'edição do Texto Eletrônico.')
return reverse('sapl.materia:proposicao_ta',
kwargs={'pk': self.object.pk})
messages.info(self.request, _("""\
Sempre que uma Proposição é inclusa ou alterada e a opção "Texto Articulado " for marcada, \
você será redirecionado para a edição do Texto Eletrônico."""))
self.logger.debug("""\
User={}. Sempre que uma Proposição é inclusa ou alterada e a opção "Texto Articulado" for marcada, \
você será redirecionado para a edição do Texto Eletrônico.""".format(username))
return reverse('sapl.materia:proposicao_ta', kwargs={'pk': self.object.pk})
else:
return Crud.UpdateView.get_success_url(self)
@ -1057,43 +1037,38 @@ class ProposicaoCrud(Crud):
username = self.request.user.username
if tipo_texto == 'T':
messages.info(self.request,
_('Sempre que uma Proposição é inclusa ou '
'alterada e a opção "Texto Articulado " for '
'marcada, você será redirecionado para o '
'Texto Eletrônico. Use a opção "Editar Texto" '
'para construir seu texto.'))
self.logger.debug('user=' + username + '. Sempre que uma Proposição é inclusa ou '
'alterada e a opção "Texto Articulado " for '
'marcada, você será redirecionado para o '
'Texto Eletrônico. Use a opção "Editar Texto" '
'para construir seu texto.')
return reverse('sapl.materia:proposicao_ta',
kwargs={'pk': self.object.pk})
messages.info(self.request, _("""\
Sempre que uma Proposição é inclusa ou alterada e a opção "Texto Articulado" for marcada, \
você será redirecionado para o Texto Eletrônico. \
Use a opção "Editar Texto" para construir seu texto."""))
self.logger.debug("""\
User={}. Sempre que uma Proposição é inclusa ou alterada e a opção "Texto Articulado" for marcada, \
você será redirecionado para o Texto Eletrônico. \
Use a opção "Editar Texto" para construir seu texto.""".format(username))
return reverse('sapl.materia:proposicao_ta', kwargs={'pk': self.object.pk})
else:
return Crud.CreateView.get_success_url(self)
class ListView(Crud.ListView):
ordering = ['-data_envio', 'descricao']
ordering = [
'-data_envio',
'descricao'
]
def get_rows(self, object_list):
for obj in object_list:
if obj.data_recebimento is None:
obj.data_recebimento = 'Não recebida'\
if obj.data_envio else 'Não enviada'
obj.data_recebimento = 'Não recebida' if obj.data_envio else 'Não enviada'
else:
obj.data_recebimento = timezone.localtime(
obj.data_recebimento)
obj.data_recebimento = formats.date_format(
obj.data_recebimento, "DATETIME_FORMAT")
obj.data_recebimento = timezone.localtime(obj.data_recebimento)
obj.data_recebimento = formats.date_format(obj.data_recebimento, "DATETIME_FORMAT")
if obj.data_envio is None:
obj.data_envio = 'Em elaboração...'
else:
obj.data_envio = timezone.localtime(obj.data_envio)
obj.data_envio = formats.date_format(
obj.data_envio, "DATETIME_FORMAT")
obj.data_envio = formats.date_format(obj.data_envio, "DATETIME_FORMAT")
return [self._as_row(obj) for obj in object_list]
@ -2725,3 +2700,127 @@ class TipoMateriaCrud(CrudAux):
self.object.save()
return fv
def create_zip_docacessorios(materia):
logger = logging.getLogger(__name__)
docs = materia.documentoacessorio_set.\
all().values_list('arquivo', flat=True)
if not docs:
return None, None
docs_path = [os.path.join(MEDIA_ROOT, i) for i in docs]
if not docs_path:
raise FileNotFoundError("Não há arquivos PDF cadastrados em documentos acessorios.")
logger.info("Gerando compilado PDF de documentos acessorios com {} documentos".format(docs_path))
zipfilename = '{}/mat_{}_{}_docacessorios.zip'.format(
get_tempfile_dir(),
materia.pk,
time.mktime(datetime.now().timetuple()))
with zipfile.ZipFile(zipfilename, 'w', zipfile.ZIP_DEFLATED) as zipf:
for f in docs_path:
zipf.write(f, f.split(os.sep)[-1])
external_name = "mat_{}_{}_docacessorios.zip".format(materia.numero, materia.ano)
return external_name, zipfilename
def get_zip_docacessorios(request, pk):
logger = logging.getLogger(__name__)
username = 'Usuário anônimo' if request.user.is_anonymous else request.user.username
materia = get_object_or_404(MateriaLegislativa, pk=pk)
try:
external_name, zipfilename = create_zip_docacessorios(materia)
logger.info("user= {}. Gerou o zip compilado de documento acessorios".format(username))
except FileNotFoundError:
logger.error("user= {}.Não há arquivos cadastrados".format(username))
msg=_('Não há arquivos cadastrados nesses documentos acessórios.')
messages.add_message(request, messages.ERROR, msg)
return redirect(reverse('sapl.materia:documentoacessorio_list',
kwargs={'pk': pk}))
except Exception as e:
logger.error("user={}. Um erro inesperado ocorreu na criação do pdf de documentos acessorios: {}"
.format(username,str(e)))
msg=_('Um erro inesperado ocorreu. Entre em contato com o suporte do SAPL.')
messages.add_message(request, messages.ERROR, msg)
return redirect(reverse('sapl.materia:documentoacessorio_list',
kwargs={'pk': pk}))
if not zipfilename:
msg=_('Não há nenhum documento acessório cadastrado.')
messages.add_message(request, messages.ERROR, msg)
return redirect(reverse('sapl.materia:documentoacessorio_list',
kwargs={'pk': pk}))
with open(os.path.join(get_tempfile_dir(), zipfilename), 'rb') as f:
data = f.read()
response = HttpResponse(data, content_type='application/zip')
response['Content-Disposition'] = ('attachment; filename="%s"'
% external_name)
return response
def create_pdf_docacessorios(materia):
logger = logging.getLogger(__name__)
docs = materia.documentoacessorio_set. \
all().values_list('arquivo', flat=True)
if not docs:
return None, None
# TODO: o for-comprehension abaixo filtra os arquivos não PDF.
# TODO: o que fazer com os arquivos não PDF? converter? ignorar?
docs_path = [os.path.join(MEDIA_ROOT, i) for i in docs if i.lower().endswith('pdf')]
if not docs_path:
raise FileNotFoundError("Não há arquivos PDF cadastrados em documentos acessorios.")
logger.info("Gerando compilado PDF de documentos acessorios com {} documentos"
.format(docs_path))
merged_pdf = '{}/mat_{}_{}_docacessorios.pdf'.format(
get_tempfile_dir(),
materia.pk,
time.mktime(datetime.now().timetuple()))
merger = PdfFileMerger()
for f in docs_path:
merger.append(fileobj=f)
merger.write(fileobj=open(merged_pdf, "wb"))
merger.close()
external_name = "mat_{}_{}_docacessorios.pdf".format(materia.numero, materia.ano)
return external_name, merged_pdf
def get_pdf_docacessorios(request, pk):
materia = get_object_or_404(MateriaLegislativa, pk=pk)
logger = logging.getLogger(__name__)
username = 'Usuário anônimo' if request.user.is_anonymous else request.user.username
try:
external_name, pdffilename = create_pdf_docacessorios(materia)
logger.info("user= {}. Gerou o pdf compilado de documento acessorios".format(username))
except FileNotFoundError:
logger.error("user= {}.Não há arquivos cadastrados".format(username))
msg=_('Não há arquivos cadastrados nesses documentos acessórios.')
messages.add_message(request, messages.ERROR, msg)
return redirect(reverse('sapl.materia:documentoacessorio_list',
kwargs={'pk': pk}))
except Exception as e:
logger.error("user= {}.Um erro inesperado ocorreu na criação do pdf de documentos acessorios: {}"
.format(username,str(e)))
msg=_('Um erro inesperado ocorreu. Entre em contato com o suporte do SAPL.')
messages.add_message(request, messages.ERROR, msg)
return redirect(reverse('sapl.materia:documentoacessorio_list',
kwargs={'pk': pk}))
if not pdffilename:
msg=_('Não há nenhum documento acessório PDF cadastrado.')
messages.add_message(request, messages.ERROR, msg)
return redirect(reverse('sapl.materia:documentoacessorio_list',
kwargs={'pk': pk}))
with open(os.path.join(get_tempfile_dir(), pdffilename), 'rb') as f:
data = f.read()
response = HttpResponse(data, content_type='application/pdf')
response['Content-Disposition'] = ('attachment; filename="%s"'
% external_name)
return response

2
sapl/norma/forms.py

@ -163,7 +163,7 @@ class NormaJuridicaForm(FileFieldCheckMixin, ModelForm):
numero=cleaned_data['numero'],
tipo=cleaned_data['tipo']).exists()
if norma:
self.logger.error("Já existe uma norma de mesmo Tipo ({}), Ano ({}) "
self.logger.warning("Já existe uma norma de mesmo Tipo ({}), Ano ({}) "
"e Número ({}) no sistema."
.format(cleaned_data['tipo'], cleaned_data['ano'], cleaned_data['numero']))
raise ValidationError("Já existe uma norma de mesmo Tipo, Ano "

20
sapl/norma/migrations/0032_auto_20200221_1533.py

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.20 on 2020-02-21 18:33
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('norma', '0031_auto_20200114_1121'),
]
operations = [
migrations.AlterField(
model_name='normajuridica',
name='veiculo_publicacao',
field=models.CharField(blank=True, max_length=200, verbose_name='Veículo de Publicação'),
),
]

20
sapl/norma/migrations/0033_auto_20200416_1538.py

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2020-04-16 18:38
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('norma', '0032_auto_20200221_1533'),
]
operations = [
migrations.AlterField(
model_name='normajuridica',
name='ip',
field=models.CharField(blank=True, default='', max_length=60, verbose_name='IP'),
),
]

4
sapl/norma/models.py

@ -106,7 +106,7 @@ class NormaJuridica(models.Model):
data_publicacao = models.DateField(
blank=True, null=True, verbose_name=_('Data de Publicação'))
veiculo_publicacao = models.CharField(
max_length=30,
max_length=200,
blank=True,
verbose_name=_('Veículo de Publicação'))
pagina_inicio_publicacao = models.PositiveIntegerField(
@ -152,7 +152,7 @@ class NormaJuridica(models.Model):
)
ip = models.CharField(
verbose_name=_('IP'),
max_length=30,
max_length=60,
blank=True,
default=''
)

20
sapl/norma/tests/test_norma.py

@ -1,6 +1,6 @@
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
from model_mommy import mommy
from model_bakery import baker
import pytest
from sapl.base.models import AppConfig
@ -13,10 +13,10 @@ from sapl.norma.models import NormaJuridica, TipoNormaJuridica
@pytest.mark.django_db(transaction=False)
def test_incluir_norma_submit(admin_client):
# Cria um tipo de norma
tipo = mommy.make(TipoNormaJuridica,
tipo = baker.make(TipoNormaJuridica,
sigla='T',
descricao='Teste')
config = mommy.make(AppConfig)
config = baker.make(AppConfig)
# Testa POST
response = admin_client.post(reverse('sapl.norma:normajuridica_create'),
@ -78,12 +78,12 @@ def test_norma_form_invalida():
@pytest.mark.django_db(transaction=False)
def test_norma_juridica_materia_inexistente():
tipo = mommy.make(TipoNormaJuridica)
tipo_materia = mommy.make(TipoMateriaLegislativa, descricao='VETO')
tipo = baker.make(TipoNormaJuridica)
tipo_materia = baker.make(TipoMateriaLegislativa, descricao='VETO')
# cria uma matéria qualquer em 2017 pois, no teste, o campo ano_materia
# está vazio
materia = mommy.make(MateriaLegislativa,
materia = baker.make(MateriaLegislativa,
tipo=tipo_materia,
ano=2017,
numero=1,
@ -109,9 +109,9 @@ def test_norma_juridica_materia_inexistente():
@pytest.mark.django_db(transaction=False)
def test_norma_juridica_materia_existente():
tipo = mommy.make(TipoNormaJuridica)
tipo_materia = mommy.make(TipoMateriaLegislativa)
mommy.make(MateriaLegislativa,
tipo = baker.make(TipoNormaJuridica)
tipo_materia = baker.make(TipoMateriaLegislativa)
baker.make(MateriaLegislativa,
numero=2,
ano=2017,
tipo=tipo_materia)
@ -147,7 +147,7 @@ def test_norma_relacionada_form_campos_obrigatorios():
@pytest.mark.django_db(transaction=False)
def test_norma_pesquisa_form_datas_invalidas():
tipo = mommy.make(TipoNormaJuridica)
tipo = baker.make(TipoNormaJuridica)
form = NormaPesquisaSimplesForm(data={'tipo_norma': str(tipo.pk),
'data_inicial': '10/11/2017',

2
sapl/norma/views.py

@ -329,7 +329,7 @@ def recuperar_norma(request):
response = JsonResponse({'ementa': norma.ementa,
'id': norma.id})
except ObjectDoesNotExist:
logger.error('user=' + username + '. NormaJuridica buscada (tipo={}, ano={}, numero={}) não existe. '
logger.warning('user=' + username + '. NormaJuridica buscada (tipo={}, ano={}, numero={}) não existe. '
'Definida com ementa vazia e id 0.'.format(tipo, ano, numero))
response = JsonResponse({'ementa': '', 'id': 0})

3
sapl/painel/views.py

@ -418,7 +418,8 @@ def get_presentes(pk, response, materia):
'tipo_resultado': materia.resultado,
'observacao_materia': html.unescape(materia.observacao),
'tipo_votacao': tipo_votacao,
'materia_legislativa_texto': str(materia.materia)
'materia_legislativa_texto': str(materia.materia),
'materia_legislativa_ementa': str(materia.materia.ementa)
})
presentes_list = sort_lista_chave(presentes_list, 'nome')

20
sapl/parlamentares/migrations/0031_auto_20200407_1406.py

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2020-04-07 17:06
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('parlamentares', '0030_auto_20190613_1133'),
]
operations = [
migrations.AlterField(
model_name='partido',
name='sigla',
field=models.CharField(max_length=20, verbose_name='Sigla'),
),
]

2
sapl/parlamentares/models.py

@ -104,7 +104,7 @@ def logo_upload_path(instance, filename):
@reversion.register()
class Partido(models.Model):
sigla = models.CharField(max_length=9, verbose_name=_('Sigla'))
sigla = models.CharField(max_length=20, verbose_name=_('Sigla'))
nome = models.CharField(max_length=50, verbose_name=_('Nome'))
data_criacao = models.DateField(
blank=True, null=True, verbose_name=_('Data Criação'))

8
sapl/parlamentares/tests/test_mandato.py

@ -1,7 +1,7 @@
from datetime import datetime
import pytest
from model_mommy import mommy
from model_bakery import baker
from sapl.parlamentares.models import Filiacao, Legislatura, Mandato
@ -13,12 +13,12 @@ def data(valor):
def test_filiacoes():
legislatura = mommy.make(Legislatura,
legislatura = baker.make(Legislatura,
data_inicio=data('2001-01-01'),
data_fim=data('2001-12-31'),
)
mandato = mommy.make(Mandato, legislatura=legislatura)
f1_fora, f2, f3, f4 = [mommy.make(Filiacao,
mandato = baker.make(Mandato, legislatura=legislatura)
f1_fora, f2, f3, f4 = [baker.make(Filiacao,
parlamentar=mandato.parlamentar,
data=ini,
data_desfiliacao=fim)

38
sapl/parlamentares/tests/test_parlamentares.py

@ -1,7 +1,7 @@
import pytest
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
from model_mommy import mommy
from model_bakery import baker
from sapl.parlamentares.forms import FrenteForm, LegislaturaForm, MandatoForm
from sapl.parlamentares.models import (Dependente, Filiacao, Legislatura,
@ -42,8 +42,8 @@ def test_incluir_parlamentar_errors(admin_client):
@pytest.mark.django_db(transaction=False)
def test_filiacao_submit(admin_client):
mommy.make(Parlamentar, pk=14)
mommy.make(Partido, pk=32)
baker.make(Parlamentar, pk=14)
baker.make(Partido, pk=32)
admin_client.post(reverse('sapl.parlamentares:filiacao_create',
kwargs={'pk': 14}),
@ -58,9 +58,9 @@ def test_filiacao_submit(admin_client):
@pytest.mark.django_db(transaction=False)
def test_dependente_submit(admin_client):
mommy.make(Parlamentar, pk=14)
mommy.make(Partido, pk=32)
mommy.make(TipoDependente, pk=3)
baker.make(Parlamentar, pk=14)
baker.make(Partido, pk=32)
baker.make(TipoDependente, pk=3)
admin_client.post(reverse('sapl.parlamentares:dependente_create',
kwargs={'pk': 14}),
@ -77,7 +77,7 @@ def test_dependente_submit(admin_client):
@pytest.mark.django_db(transaction=False)
def test_form_errors_dependente(admin_client):
mommy.make(Parlamentar, pk=14)
baker.make(Parlamentar, pk=14)
response = admin_client.post(
reverse('sapl.parlamentares:dependente_create',
kwargs={'pk': 14}),
@ -94,7 +94,7 @@ def test_form_errors_dependente(admin_client):
@pytest.mark.django_db(transaction=False)
def test_form_errors_filiacao(admin_client):
mommy.make(Parlamentar, pk=14)
baker.make(Parlamentar, pk=14)
response = admin_client.post(reverse('sapl.parlamentares:filiacao_create',
kwargs={'pk': 14}),
@ -110,8 +110,8 @@ def test_form_errors_filiacao(admin_client):
@pytest.mark.django_db(transaction=False)
def test_mandato_submit(admin_client):
mommy.make(Parlamentar, pk=14)
mommy.make(Legislatura, pk=5)
baker.make(Parlamentar, pk=14)
baker.make(Legislatura, pk=5)
admin_client.post(reverse('sapl.parlamentares:mandato_create',
kwargs={'pk': 14}),
@ -134,7 +134,7 @@ def test_mandato_submit(admin_client):
@pytest.mark.django_db(transaction=False)
def test_form_errors_mandato(admin_client):
mommy.make(Parlamentar, pk=14)
baker.make(Parlamentar, pk=14)
response = admin_client.post(reverse('sapl.parlamentares:mandato_create',
kwargs={'pk': 14}),
{'legislatura': '',
@ -158,8 +158,8 @@ def test_mandato_form_invalido():
@pytest.mark.django_db(transaction=False)
def test_mandato_form_duplicado():
parlamentar = mommy.make(Parlamentar, pk=1)
legislatura = mommy.make(Legislatura, pk=1)
parlamentar = baker.make(Parlamentar, pk=1)
legislatura = baker.make(Legislatura, pk=1)
Mandato.objects.create(parlamentar=parlamentar,
legislatura=legislatura,
@ -184,8 +184,8 @@ def test_mandato_form_duplicado():
@pytest.mark.django_db(transaction=False)
def test_mandato_form_datas_invalidas():
parlamentar = mommy.make(Parlamentar, pk=1)
legislatura = mommy.make(Legislatura, pk=1,
parlamentar = baker.make(Parlamentar, pk=1)
legislatura = baker.make(Legislatura, pk=1,
data_inicio='2017-01-01',
data_fim='2021-12-31')
@ -275,7 +275,7 @@ def test_legislatura_form_numeros_invalidos():
assert legislatura_form.is_valid()
legislatura = mommy.make(Legislatura, pk=1,
legislatura = baker.make(Legislatura, pk=1,
numero=5,
data_inicio='2017-02-01',
data_fim='2021-12-31',
@ -306,13 +306,13 @@ def test_legislatura_form_numeros_invalidos():
assert legislatura_form.is_valid()
legislatura = mommy.make(Legislatura, pk=2,
legislatura = baker.make(Legislatura, pk=2,
numero=1,
data_inicio='2002-02-01',
data_fim='2005-12-31',
data_eleicao='2001-11-01')
legislatura2 = mommy.make(Legislatura, pk=3,
legislatura2 = baker.make(Legislatura, pk=3,
numero=3,
data_inicio='2008-02-01',
data_fim='2011-12-31',
@ -344,7 +344,7 @@ def test_valida_campos_obrigatorios_frente_form():
@pytest.mark.django_db(transaction=False)
def test_frente_form_valido():
parlamentares = mommy.make(Parlamentar)
parlamentares = baker.make(Parlamentar)
form = FrenteForm(data={'nome': 'Nome da Frente',
'parlamentar': str(parlamentares.pk),

4
sapl/parlamentares/urls.py

@ -1,6 +1,7 @@
from django.conf.urls import include, url
from sapl.parlamentares.views import (CargoMesaCrud, ColigacaoCrud,
coligacao_legislatura,
ComposicaoColigacaoCrud, DependenteCrud,
FiliacaoCrud, FrenteCrud, FrenteList,
LegislaturaCrud, MandatoCrud,
@ -45,6 +46,9 @@ urlpatterns = [
url(r'^parlamentar/vincular-parlamentar/$',
VincularParlamentarView.as_view(), name='vincular_parlamentar'),
url(r'^parlamentar/coligacao-legislatura/',
coligacao_legislatura, name="coligacao_legislatura"),
url(r'^sistema/coligacao/',
include(ColigacaoCrud.get_urls() +
ComposicaoColigacaoCrud.get_urls())),

19
sapl/parlamentares/views.py

@ -312,6 +312,17 @@ class ColigacaoCrud(CrudAux):
return context
def coligacao_legislatura(request):
try:
coligacoes = Coligacao.objects.filter(legislatura=request.GET['legislatura']).order_by('nome')
except:
coligacoes = []
lista_coligacoes = [(coligacao.id, str(coligacao)) for coligacao in coligacoes]
return JsonResponse({'coligacoes': lista_coligacoes})
def json_date_convert(date):
"""
:param date: recebe a data de uma chamada ajax no formato de
@ -574,8 +585,7 @@ class ParlamentarCrud(Crud):
". Tentando obter id da legislatura.")
return int(self.request.GET['pk'])
except:
self.logger.error(
"user=" + username + ". Legislatura não possui ID. Buscando em todas as entradas.")
self.logger.warning("User=" + username + ". Legislatura não possui ID. Buscando em todas as entradas.")
legislaturas = Legislatura.objects.all()
for l in legislaturas:
if l.atual():
@ -803,7 +813,8 @@ def altera_field_mesa(request):
# é alterado o campo de sessão ou feita alguma operação
# de inclusão/remoção.
if request.GET['sessao']:
sessao_selecionada = request.GET['sessao']
sessao_selecionada = SessaoLegislativa.objects.get(id=request.GET['sessao'])
# Caso a mudança tenha sido no campo legislatura, a sessão
# atual deve ser a primeira daquela legislatura
else:
@ -1124,4 +1135,4 @@ def get_sessoes_legislatura(request):
for s in SessaoLegislativa.objects.filter(legislatura_id=legislatura_id):
json_response['sessoes_legislativas'].append( (s.id, str(s)) )
return JsonResponse(json_response)
return JsonResponse(json_response)

48
sapl/protocoloadm/forms.py

@ -1,3 +1,4 @@
import re
import django_filters
import logging
@ -180,6 +181,7 @@ class DocumentoAdministrativoFilterSet(django_filters.FilterSet):
model = DocumentoAdministrativo
fields = ['tipo',
'numero',
'complemento',
'protocolo__numero',
'numero_externo',
'data',
@ -200,17 +202,21 @@ class DocumentoAdministrativoFilterSet(django_filters.FilterSet):
('o', 4), ])
row2 = to_row(
[('numero', 2),
('ano', 2),
('protocolo__numero', 2),
('numero_externo', 2),
('data', 4)])
[('numero', 5),
('complemento',2),
('ano', 5)])
row3 = to_row(
[('protocolo__numero', 4),
('numero_externo', 4),
('data', 4)
])
row4 = to_row(
[('interessado', 6),
('assunto', 6)])
row4 = to_row(
row5 = to_row(
[
('tramitacao', 2),
('tramitacaoadministrativo__status', 4),
@ -239,7 +245,7 @@ class DocumentoAdministrativoFilterSet(django_filters.FilterSet):
Fieldset(_('Pesquisar Documento'),
row1, row2,
row3, row4,
buttons,)
row5, buttons,)
)
@ -1080,6 +1086,7 @@ class DocumentoAdministrativoForm(FileFieldCheckMixin, ModelForm):
model = DocumentoAdministrativo
fields = ['tipo',
'numero',
'complemento',
'ano',
'data',
'numero_protocolo',
@ -1115,24 +1122,39 @@ class DocumentoAdministrativoForm(FileFieldCheckMixin, ModelForm):
numero_protocolo = self.data['numero_protocolo']
ano_protocolo = self.data['ano_protocolo']
complemento = re.sub('\s+', '', self.data['complemento']).upper()
numero_documento = int(self.cleaned_data['numero'])
tipo_documento = int(self.data['tipo'])
ano_documento = int(self.data['ano'])
# não permite atualizar para numero/ano/tipo existente
if self.instance.pk:
mudanca_doc = numero_documento != self.instance.numero \
or ano_documento != self.instance.ano \
or tipo_documento != self.instance.tipo.pk
or tipo_documento != self.instance.tipo.pk \
or complemento != self.instance.complemento
if not self.instance.pk or mudanca_doc:
doc_exists = DocumentoAdministrativo.objects.filter(numero=numero_documento,
tipo=tipo_documento,
ano=ano_documento).exists()
ano=ano_documento,
complemento=complemento).exists()
if doc_exists:
self.logger.error("DocumentoAdministrativo (numero={}, tipo={} e ano={}) já existe."
.format(numero_documento, tipo_documento, ano_documento))
raise ValidationError(_('Documento já existente'))
self.logger.error("DocumentoAdministrativo "
"(numero={}, tipo={}, ano={}, "
"complemento={}) já existe."
.format(numero_documento,
tipo_documento,
ano_documento,
complemento))
tipo = TipoDocumentoAdministrativo.objects.get(
id=tipo_documento)
raise ValidationError(
_('{}/{} ({}) já existente!'.format(numero_documento,
ano_documento,
tipo)))
# campos opcionais, mas que se informados devem ser válidos
if numero_protocolo and ano_protocolo:
@ -1194,7 +1216,7 @@ class DocumentoAdministrativoForm(FileFieldCheckMixin, ModelForm):
def __init__(self, *args, **kwargs):
row1 = to_row(
[('tipo', 6), ('numero', 3), ('ano', 3)])
[('tipo', 3), ('numero', 3),('complemento', 3), ('ano', 3)])
row2 = to_row(
[('data', 4), ('numero_protocolo', 4), ('ano_protocolo', 4)])

20
sapl/protocoloadm/migrations/0031_documentoadministrativo_caractere_identificador.py

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.26 on 2020-01-15 14:41
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('protocoloadm', '0030_auto_20200114_1121'),
]
operations = [
migrations.AddField(
model_name='documentoadministrativo',
name='complemento',
field=models.CharField(blank=True, max_length=10, verbose_name='Complemento'),
),
]

25
sapl/protocoloadm/migrations/0032_auto_20200416_1538.py

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2020-04-16 18:38
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('protocoloadm', '0031_documentoadministrativo_caractere_identificador'),
]
operations = [
migrations.AlterField(
model_name='documentoadministrativo',
name='ip',
field=models.CharField(blank=True, default='', max_length=60, verbose_name='IP'),
),
migrations.AlterField(
model_name='tramitacaoadministrativo',
name='ip',
field=models.CharField(blank=True, default='', max_length=60, verbose_name='IP'),
),
]

8
sapl/protocoloadm/models.py

@ -134,6 +134,10 @@ class DocumentoAdministrativo(models.Model):
TipoDocumentoAdministrativo, on_delete=models.PROTECT,
verbose_name=_('Tipo Documento'))
numero = models.PositiveIntegerField(verbose_name=_('Número'))
complemento = models.CharField(max_length=10, blank=True,
verbose_name=_('Complemento'))
ano = models.PositiveSmallIntegerField(verbose_name=_('Ano'),
choices=RANGE_ANOS)
protocolo = models.ForeignKey(
@ -195,7 +199,7 @@ class DocumentoAdministrativo(models.Model):
)
ip = models.CharField(
verbose_name=_('IP'),
max_length=30,
max_length=60,
blank=True,
default=''
)
@ -355,7 +359,7 @@ class TramitacaoAdministrativo(models.Model):
null=True,
blank=True)
ip = models.CharField(verbose_name=_('IP'),
max_length=30,
max_length=60,
blank=True,
default='')
ultima_edicao = models.DateTimeField(

105
sapl/protocoloadm/tests/test_protocoloadm.py

@ -4,7 +4,7 @@ from django.core.urlresolvers import reverse
from django.utils import timezone
from django.utils.encoding import force_text
from django.utils.translation import ugettext_lazy as _
from model_mommy import mommy
from model_bakery import baker
from urllib.parse import urlencode
import pytest
@ -34,7 +34,7 @@ def test_anular_protocolo_acessivel(admin_client):
@pytest.mark.django_db(transaction=False)
def test_anular_protocolo_submit(admin_client):
mommy.make(Protocolo, numero='76', ano='2016', anulado=False)
baker.make(Protocolo, numero='76', ano='2016', anulado=False)
# TODO: setar usuario e IP
response = admin_client.post(reverse('sapl.protocoloadm:anular_protocolo'),
@ -69,7 +69,7 @@ def test_form_anular_protocolo_inexistente():
@pytest.mark.django_db(transaction=False)
def test_form_anular_protocolo_valido():
mommy.make(Protocolo, numero='1', ano='2016', anulado=False)
baker.make(Protocolo, numero='1', ano='2016', anulado=False)
form = AnularProtocoloAdmForm({'numero': '1',
'ano': '2016',
'justificativa_anulacao': 'TESTE'})
@ -79,7 +79,7 @@ def test_form_anular_protocolo_valido():
@pytest.mark.django_db(transaction=False)
def test_form_anular_protocolo_anulado():
mommy.make(Protocolo, numero='1', ano='2016', anulado=True)
baker.make(Protocolo, numero='1', ano='2016', anulado=True)
form = AnularProtocoloAdmForm({'numero': '1',
'ano': '2016',
'justificativa_anulacao': 'TESTE'})
@ -89,7 +89,7 @@ def test_form_anular_protocolo_anulado():
@pytest.mark.django_db(transaction=False)
def test_form_anular_protocolo_campos_obrigatorios():
mommy.make(Protocolo, numero='1', ano='2016', anulado=False)
baker.make(Protocolo, numero='1', ano='2016', anulado=False)
# TODO: generalizar para diminuir o tamanho deste método
@ -127,27 +127,27 @@ def test_form_anular_protocolo_campos_obrigatorios():
@pytest.mark.django_db(transaction=False)
def test_create_tramitacao(admin_client):
tipo_doc = mommy.make(
tipo_doc = baker.make(
TipoDocumentoAdministrativo,
descricao='Teste Tipo_DocAdm')
documento_adm = mommy.make(
documento_adm = baker.make(
DocumentoAdministrativo,
tipo=tipo_doc)
unidade_tramitacao_local_1 = mommy.make(
unidade_tramitacao_local_1 = baker.make(
UnidadeTramitacao, pk=1)
unidade_tramitacao_destino_1 = mommy.make(
unidade_tramitacao_destino_1 = baker.make(
UnidadeTramitacao, pk=2)
unidade_tramitacao_destino_2 = mommy.make(
unidade_tramitacao_destino_2 = baker.make(
UnidadeTramitacao, pk=3)
status = mommy.make(
status = baker.make(
StatusTramitacaoAdministrativo)
tramitacao = mommy.make(
tramitacao = baker.make(
TramitacaoAdministrativo,
unidade_tramitacao_local=unidade_tramitacao_local_1,
unidade_tramitacao_destino=unidade_tramitacao_destino_1,
@ -307,7 +307,7 @@ def test_anular_protocolo_form_anula_protocolo_inexistente():
@pytest.mark.django_db(transaction=False)
def test_anular_protocolo_form_anula_protocolo_anulado():
mommy.make(Protocolo, numero=1, ano=2017, anulado=True)
baker.make(Protocolo, numero=1, ano=2017, anulado=True)
form = AnularProtocoloAdmForm(data={'numero': '1',
'ano': '2017',
@ -322,15 +322,15 @@ def test_anular_protocolo_form_anula_protocolo_anulado():
@pytest.mark.django_db(transaction=False)
def test_anular_protocolo_form_anula_protocolo_com_doc_vinculado():
tipo_materia = mommy.make(TipoMateriaLegislativa)
tipo_materia = baker.make(TipoMateriaLegislativa)
mommy.make(Protocolo,
baker.make(Protocolo,
numero=1,
ano=2017,
tipo_materia=tipo_materia,
anulado=False)
mommy.make(MateriaLegislativa,
baker.make(MateriaLegislativa,
ano=2017,
numero_protocolo=1)
@ -345,15 +345,15 @@ def test_anular_protocolo_form_anula_protocolo_com_doc_vinculado():
[_("Protocolo 1/2017 não pode ser removido pois existem "
"documentos vinculados a ele.")]
tipo_documento = mommy.make(TipoDocumentoAdministrativo)
tipo_documento = baker.make(TipoDocumentoAdministrativo)
protocolo_documento = mommy.make(Protocolo,
protocolo_documento = baker.make(Protocolo,
numero=2,
ano=2017,
tipo_documento=tipo_documento,
anulado=False)
mommy.make(DocumentoAdministrativo,
baker.make(DocumentoAdministrativo,
protocolo=protocolo_documento)
form = AnularProtocoloAdmForm(data={'numero': '2',
@ -387,8 +387,8 @@ def test_documento_administrativo_invalido():
@pytest.mark.django_db(transaction=False)
def test_documento_administrativo_protocolo_inexistente():
tipo = mommy.make(TipoDocumentoAdministrativo)
protocolo = mommy.make(Protocolo,
tipo = baker.make(TipoDocumentoAdministrativo)
protocolo = baker.make(Protocolo,
ano=2017,
numero=10,
anulado=False,
@ -398,6 +398,7 @@ def test_documento_administrativo_protocolo_inexistente():
'tipo': str(tipo.pk),
'assunto': 'teste',
'numero': '1',
'complemento':'',
'data': '2017-10-10',
'numero_protocolo': '11',
'ano_protocolo': '2017',
@ -412,7 +413,7 @@ def test_documento_administrativo_protocolo_inexistente():
@pytest.mark.django_db(transaction=False)
def test_protocolo_documento_form_invalido():
config = mommy.make(AppConfig)
config = baker.make(AppConfig)
form = ProtocoloDocumentForm(
data={},
@ -439,7 +440,7 @@ def test_protocolo_documento_form_invalido():
@pytest.mark.django_db(transaction=False)
def test_protocolo_materia_invalido():
config = mommy.make(AppConfig)
config = baker.make(AppConfig)
form = ProtocoloMateriaForm(data={},
initial={
@ -465,25 +466,25 @@ def test_protocolo_materia_invalido():
@pytest.mark.django_db(transaction=False)
def test_lista_documentos_anexados():
tipo_documento = mommy.make(
tipo_documento = baker.make(
TipoDocumentoAdministrativo,
descricao="Tipo_Teste"
)
documento_principal = mommy.make(
documento_principal = baker.make(
DocumentoAdministrativo,
numero=20,
ano=2018,
data="2018-01-04",
tipo=tipo_documento
)
documento_anexado = mommy.make(
documento_anexado = baker.make(
DocumentoAdministrativo,
numero=21,
ano=2019,
data="2019-05-04",
tipo=tipo_documento
)
documento_anexado_anexado = mommy.make(
documento_anexado_anexado = baker.make(
DocumentoAdministrativo,
numero=22,
ano=2020,
@ -491,13 +492,13 @@ def test_lista_documentos_anexados():
tipo=tipo_documento
)
mommy.make(
baker.make(
Anexado,
documento_principal=documento_principal,
documento_anexado=documento_anexado,
data_anexacao="2019-05-11"
)
mommy.make(
baker.make(
Anexado,
documento_principal=documento_anexado,
documento_anexado=documento_anexado_anexado,
@ -514,8 +515,8 @@ def test_lista_documentos_anexados():
@pytest.mark.django_db(transaction=False)
def make_unidade_tramitacao(descricao):
# Cria uma comissão para ser a unidade de tramitação
tipo_comissao = mommy.make(TipoComissao)
comissao = mommy.make(Comissao,
tipo_comissao = baker.make(TipoComissao)
comissao = baker.make(Comissao,
tipo=tipo_comissao,
nome=descricao,
sigla='T',
@ -526,7 +527,7 @@ def make_unidade_tramitacao(descricao):
assert comissao.nome == descricao
# Cria a unidade
unidade = mommy.make(UnidadeTramitacao, comissao=comissao)
unidade = baker.make(UnidadeTramitacao, comissao=comissao)
assert unidade.comissao == comissao
return unidade
@ -535,27 +536,27 @@ def make_unidade_tramitacao(descricao):
@pytest.mark.django_db(transaction=False)
def test_tramitacoes_documentos_anexados(admin_client):
config = mommy.make(AppConfig, tramitacao_documento=True)
config = baker.make(AppConfig, tramitacao_documento=True)
tipo_documento = mommy.make(
tipo_documento = baker.make(
TipoDocumentoAdministrativo,
descricao="Tipo_Teste"
)
documento_principal = mommy.make(
documento_principal = baker.make(
DocumentoAdministrativo,
numero=20,
ano=2018,
data="2018-01-04",
tipo=tipo_documento
)
documento_anexado = mommy.make(
documento_anexado = baker.make(
DocumentoAdministrativo,
numero=21,
ano=2019,
data="2019-05-04",
tipo=tipo_documento
)
documento_anexado_anexado = mommy.make(
documento_anexado_anexado = baker.make(
DocumentoAdministrativo,
numero=22,
ano=2020,
@ -563,13 +564,13 @@ def test_tramitacoes_documentos_anexados(admin_client):
tipo=tipo_documento
)
mommy.make(
baker.make(
Anexado,
documento_principal=documento_principal,
documento_anexado=documento_anexado,
data_anexacao="2019-05-11"
)
mommy.make(
baker.make(
Anexado,
documento_principal=documento_anexado,
documento_anexado=documento_anexado_anexado,
@ -581,7 +582,7 @@ def test_tramitacoes_documentos_anexados(admin_client):
unidade_tramitacao_destino_1 = make_unidade_tramitacao(descricao="Teste 2")
unidade_tramitacao_destino_2 = make_unidade_tramitacao(descricao="Teste 3")
status = mommy.make(
status = baker.make(
StatusTramitacaoAdministrativo,
indicador='R')
@ -807,11 +808,11 @@ def test_tramitacoes_documentos_anexados(admin_client):
@pytest.mark.django_db(transaction=False)
def test_tramitacao_lote_documentos_form(admin_client):
tipo_documento = mommy.make(
tipo_documento = baker.make(
TipoDocumentoAdministrativo,
descricao="Tipo_Teste"
)
documento = mommy.make(
documento = baker.make(
DocumentoAdministrativo,
numero=20,
ano=2018,
@ -822,7 +823,7 @@ def test_tramitacao_lote_documentos_form(admin_client):
unidade_tramitacao_local_1 = make_unidade_tramitacao(descricao="Teste 1")
unidade_tramitacao_destino_1 = make_unidade_tramitacao(descricao="Teste 2")
status = mommy.make(
status = baker.make(
StatusTramitacaoAdministrativo,
indicador='R')
@ -896,27 +897,27 @@ def test_tramitacao_lote_documentos_form(admin_client):
@pytest.mark.django_db(transaction=False)
def test_tramitacao_lote_documentos_views(admin_client):
config = mommy.make(AppConfig, tramitacao_documento=True)
config = baker.make(AppConfig, tramitacao_documento=True)
tipo_documento = mommy.make(
tipo_documento = baker.make(
TipoDocumentoAdministrativo,
descricao="Tipo_Teste"
)
documento_principal = mommy.make(
documento_principal = baker.make(
DocumentoAdministrativo,
numero=20,
ano=2018,
data="2018-01-04",
tipo=tipo_documento
)
documento_anexado = mommy.make(
documento_anexado = baker.make(
DocumentoAdministrativo,
numero=21,
ano=2019,
data="2019-05-04",
tipo=tipo_documento
)
documento_anexado_anexado = mommy.make(
documento_anexado_anexado = baker.make(
DocumentoAdministrativo,
numero=22,
ano=2020,
@ -924,7 +925,7 @@ def test_tramitacao_lote_documentos_views(admin_client):
tipo=tipo_documento
)
documento_sem_anexados = mommy.make(
documento_sem_anexados = baker.make(
DocumentoAdministrativo,
numero=23,
ano=2020,
@ -932,13 +933,13 @@ def test_tramitacao_lote_documentos_views(admin_client):
tipo=tipo_documento
)
mommy.make(
baker.make(
Anexado,
documento_principal=documento_principal,
documento_anexado=documento_anexado,
data_anexacao="2019-05-11"
)
mommy.make(
baker.make(
Anexado,
documento_principal=documento_anexado,
documento_anexado=documento_anexado_anexado,
@ -950,7 +951,7 @@ def test_tramitacao_lote_documentos_views(admin_client):
unidade_tramitacao_destino_2 = make_unidade_tramitacao(descricao="Teste 3")
unidade_tramitacao_destino_3 = make_unidade_tramitacao(descricao="Teste 4")
status = mommy.make(
status = baker.make(
StatusTramitacaoAdministrativo,
indicador='R')

7
sapl/protocoloadm/urls.py

@ -24,7 +24,8 @@ from sapl.protocoloadm.views import (AcompanhamentoDocumentoView,
DesvincularMateriaView,
AnexadoCrud, DocumentoAnexadoEmLoteView,
PrimeiraTramitacaoEmLoteAdmView,
TramitacaoEmLoteAdmView)
TramitacaoEmLoteAdmView,
apaga_protocolos_view)
from .apps import AppConfig
@ -107,6 +108,10 @@ urlpatterns_protocolo = [
url(r'^protocoloadm/tramitacao-em-lote', TramitacaoEmLoteAdmView.as_view(),
name='tramitacao_em_lote_docadm'),
url(r'^protocoloadm/apaga_protocolos', apaga_protocolos_view,
name='apaga_protocolos_view'),
]
urlpatterns_sistema = [

66
sapl/protocoloadm/views.py

@ -1,5 +1,6 @@
from datetime import datetime
import logging
import re
from random import choice
from string import ascii_letters, digits
@ -22,6 +23,7 @@ from django.views.generic import ListView, CreateView, UpdateView
from django.views.generic.base import RedirectView, TemplateView
from django.views.generic.edit import FormView
from django_filters.views import FilterView
from django.contrib.admin.views.decorators import staff_member_required
import sapl
from sapl.base.email_utils import do_envia_email_confirmacao
@ -33,12 +35,15 @@ from sapl.crud.base import (Crud, CrudAux, MasterDetailCrud, make_pagination,
from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa, UnidadeTramitacao
from sapl.materia.views import gerar_pdf_impressos
from sapl.parlamentares.models import Legislatura, Parlamentar
from sapl.protocoloadm.models import Protocolo
from sapl.protocoloadm.models import Protocolo, DocumentoAdministrativo
from sapl.relatorios.views import relatorio_doc_administrativos
from sapl.utils import (create_barcode, get_base_url, get_client_ip,
get_mime_type_from_file_extension, lista_anexados,
show_results_filter_set, mail_service_configured, from_date_to_datetime_utc)
from django.shortcuts import render
from .forms import (AcompanhamentoDocumentoForm, AnularProtocoloAdmForm,
DocumentoAcessorioAdministrativoForm,
DocumentoAdministrativoFilterSet,
@ -365,6 +370,10 @@ class DocumentoAdministrativoCrud(Crud):
def cancel_url(self):
return self.search_url
def form_valid(self, form):
form.instance.complemento = re.sub('\s+', '', form.instance.complemento).upper()
return super().form_valid(form)
class UpdateView(Crud.UpdateView):
form_class = DocumentoAdministrativoForm
layout_key = None
@ -376,7 +385,7 @@ class DocumentoAdministrativoCrud(Crud):
self.object = form.save()
dict_objeto_novo = self.object.__dict__
atributos = [
'tipo_id', 'ano', 'numero', 'data', 'protocolo_id', 'assunto',
'interessado', 'tramitacao', 'restrito', 'texto_integral','numero_externo',
@ -393,6 +402,8 @@ class DocumentoAdministrativoCrud(Crud):
self.object.save()
break
form.instance.complemento = re.sub('\s+', '', form.instance.complemento).upper()
return super().form_valid(form)
@ -1357,9 +1368,9 @@ class TramitacaoAdmCrud(MasterDetailCrud):
if tramitacao.pk != ultima_tramitacao.pk:
username = request.user.username
self.logger.error("user=" + username + ". Não é possível deletar a tramitação de pk={}. "
"Somente a última tramitação (pk={}) pode ser deletada!."
.format(tramitacao.pk, ultima_tramitacao.pk))
self.logger.warning("User={}. Não é possível deletar a tramitação de pk={}. "
"Somente a última tramitação (pk={}) pode ser deletada!."
.format(username, tramitacao.pk, ultima_tramitacao.pk))
msg = _('Somente a última tramitação pode ser deletada!')
messages.add_message(request, messages.ERROR, msg)
return HttpResponseRedirect(url)
@ -1724,3 +1735,48 @@ class TramitacaoEmLoteAdmView(PrimeiraTramitacaoEmLoteAdmView):
status=status,
unidade_tramitacao_destino=destino).distinct().values_list(
'documento_id', flat=True)
def apaga_protocolos(request, ano,numero_protocolo=None):
kwargs = {'ano__in':ano}
if numero_protocolo:
kwargs.update({'numero__gte':numero_protocolo})
all_protocolos = Protocolo.objects.filter(**kwargs)
for doc in DocumentoAdministrativo.objects.filter(protocolo__in=all_protocolos):
doc.protocolo = None
doc.save()
for ml in MateriaLegislativa.objects.filter(ano__in=ano, numero_protocolo__in=all_protocolos.values_list('numero')):
ml.numero_protocolo = None
ml.save()
for deleted_object in all_protocolos:
post_delete_signal.send(sender=None,
instance=deleted_object,
operation='D',
request=request
)
all_protocolos.delete()
@staff_member_required
def apaga_protocolos_view(request):
if request.method == "GET":
if Protocolo.objects.exists():
intervalo_data = Protocolo.objects.all().distinct('ano').values_list('ano', flat=True).order_by('-ano')
else:
intervalo_data = None
return render(request,"protocoloadm/deleta_todos_protocolos.html",{'intervalo_data':intervalo_data})
elif request.method == "POST":
password = request.POST.get('senha')
valid = request.user.check_password(password)
if valid:
anos = request.POST.getlist('ano')
numero_protocolo = request.POST.get('numero_protocolo')
apaga_protocolos(request,anos,numero_protocolo)
return JsonResponse({'type':'success','msg':''})
else:
return JsonResponse({'type':'error','msg':'Senha Incorreta'})

621
sapl/relatorios/views.py

@ -4,7 +4,6 @@ import logging
import re
import tempfile
from django.core.exceptions import ObjectDoesNotExist
from django.http import Http404, HttpResponse
from django.utils import timezone
@ -25,7 +24,7 @@ from sapl.sessao.models import (ExpedienteMateria, ExpedienteSessao,
Orador, OradorExpediente,
OrdemDia, PresencaOrdemDia, SessaoPlenaria,
SessaoPlenariaPresenca, OcorrenciaSessao,
RegistroVotacao, VotoParlamentar, OradorOrdemDia, TipoExpediente)
RegistroVotacao, VotoParlamentar, OradorOrdemDia, TipoExpediente, ResumoOrdenacao)
from sapl.settings import STATIC_ROOT
from sapl.utils import LISTA_DE_UFS, TrocaTag, filiacao_data
@ -58,7 +57,6 @@ def get_kwargs_params(request, fields):
def get_cabecalho(casa):
cabecalho = {}
cabecalho["nom_casa"] = casa.nome
uf_dict = dict(LISTA_DE_UFS)
@ -74,7 +72,6 @@ def get_imagem(casa):
def get_rodape(casa):
if len(casa.cep) == 8:
cep = casa.cep[:4] + "-" + casa.cep[5:]
else:
@ -110,12 +107,11 @@ def get_rodape(casa):
def get_materias(mats):
materias = []
for materia in mats:
dic = {}
dic['titulo'] = materia.tipo.sigla + " " + materia.tipo.descricao \
+ " " + str(materia.numero) + "/" + str(materia.ano)
+ " " + str(materia.numero) + "/" + str(materia.ano)
dic['txt_ementa'] = materia.ementa
dic['nom_autor'] = ', '.join(
@ -250,7 +246,7 @@ def get_capa_processo(prot):
for numera in Numeracao.objects.filter(materia=materia_num):
# FIXME i18n
dic['numeracao'] = 'PROCESSO N&#176; ' + \
str(numera.numero) + '/' + str(numera.ano)
str(numera.numero) + '/' + str(numera.ano)
dic['anulado'] = ''
if p.anulado == 1:
dic['anulado'] = 'Nulo'
@ -294,7 +290,6 @@ def relatorio_capa_processo(request):
def get_ordem_dia(ordem, sessao):
# TODO: fazer implementação de ordem dia
pass
@ -361,7 +356,6 @@ def relatorio_documento_administrativo(request):
def get_documento_administrativo(docs):
documentos = []
for d in docs:
dic = {}
@ -504,148 +498,122 @@ def remove_html_comments(text):
return clean_text if len(clean_text) > 0 else text
def get_sessao_plenaria(sessao, casa):
def is_empty(value):
if not value:
return True
inf_basicas_dic = {}
inf_basicas_dic["num_sessao_plen"] = str(sessao.numero)
inf_basicas_dic["nom_sessao"] = sessao.tipo.nome
inf_basicas_dic["num_legislatura"] = str(sessao.legislatura)
inf_basicas_dic["num_sessao_leg"] = sessao.sessao_legislativa.numero
inf_basicas_dic["dat_inicio_sessao"] = sessao.data_inicio.strftime(
"%d/%m/%Y")
inf_basicas_dic["hr_inicio_sessao"] = sessao.hora_inicio
if sessao.data_fim:
inf_basicas_dic["dat_fim_sessao"] = \
sessao.data_fim.strftime("%d/%m/%Y")
else:
inf_basicas_dic["dat_fim_sessao"] = ''
inf_basicas_dic["hr_fim_sessao"] = sessao.hora_fim
inf_basicas_dic["nom_camara"] = casa.nome
txt = re.sub(r'\s+|<br.*/>|\n|&nbsp;', '', value)
return True if not txt.strip() else False
def get_sessao_plenaria(sessao, casa):
inf_basicas_dic = {
"num_sessao_plen": str(sessao.numero),
"nom_sessao": sessao.tipo.nome,
"num_legislatura": str(sessao.legislatura),
"num_sessao_leg": sessao.sessao_legislativa.numero,
"dat_inicio_sessao": sessao.data_inicio.strftime("%d/%m/%Y"),
"hr_inicio_sessao": sessao.hora_inicio,
"dat_fim_sessao": sessao.data_fim.strftime("%d/%m/%Y") if sessao.data_fim else '',
"hr_fim_sessao": sessao.hora_fim,
"nom_camara": casa.nome
}
if sessao.tipo.nome == 'Solene':
inf_basicas_dic["tema_solene"] = sessao.tema_solene
# Conteudo multimidia
cont_mult_dic = {}
if sessao.url_audio:
cont_mult_dic['multimidia_audio'] = str(sessao.url_audio)
else:
cont_mult_dic['multimidia_audio'] = 'Indisponível'
if sessao.url_video:
cont_mult_dic['multimidia_video'] = str(sessao.url_video)
else:
cont_mult_dic['multimidia_video'] = 'Indisponível'
cont_mult_dic = {
"multimidia_audio": str(sessao.url_audio) if sessao.url_audio else "Indisponível",
"multimidia_video": str(sessao.url_video) if sessao.url_video else "Indisponível"
}
# Lista da composicao da mesa diretora
lst_mesa = []
for composicao in IntegranteMesa.objects.filter(sessao_plenaria=sessao):
for parlamentar in Parlamentar.objects.filter(
id=composicao.parlamentar.id):
for cargo in CargoMesa.objects.filter(id=composicao.cargo.id):
dic_mesa = {}
dic_mesa['nom_parlamentar'] = parlamentar.nome_parlamentar
partido_sigla = Filiacao.objects.filter(
parlamentar=parlamentar).first()
if not partido_sigla:
sigla = ''
else:
sigla = partido_sigla.partido.sigla
dic_mesa['sgl_partido'] = sigla
dic_mesa['des_cargo'] = cargo.descricao
lst_mesa.append(dic_mesa)
for composicao in IntegranteMesa.objects.select_related('parlamentar', 'cargo')\
.filter(sessao_plenaria=sessao)\
.order_by('cargo_id'):
partido_sigla = Filiacao.objects.filter(parlamentar=composicao.parlamentar).first()
sigla = '' if not partido_sigla else partido_sigla.partido.sigla
lst_mesa.append({
'nom_parlamentar': composicao.parlamentar.nome_parlamentar,
'sgl_partido': sigla,
'des_cargo': composicao.cargo.descricao
})
# Lista de presença na sessão
lst_presenca_sessao = []
presenca = SessaoPlenariaPresenca.objects.filter(
sessao_plenaria=sessao).order_by('parlamentar__nome_parlamentar')
presenca = SessaoPlenariaPresenca.objects.filter(sessao_plenaria=sessao).order_by('parlamentar__nome_parlamentar')
for parlamentar in [p.parlamentar for p in presenca]:
dic_presenca = {}
dic_presenca["nom_parlamentar"] = parlamentar.nome_parlamentar
partido_sigla = filiacao_data(parlamentar, sessao.data_inicio)
dic_presenca['sgl_partido'] = partido_sigla
lst_presenca_sessao.append(dic_presenca)
lst_presenca_sessao.append({
"nom_parlamentar": parlamentar.nome_parlamentar,
"sgl_partido": filiacao_data(parlamentar, sessao.data_inicio)
})
# Lista de ausencias na sessão
lst_ausencia_sessao = []
ausencia = JustificativaAusencia.objects.filter(
sessao_plenaria=sessao).order_by('parlamentar__nome_parlamentar')
ausencia = JustificativaAusencia.objects.filter(sessao_plenaria=sessao).order_by('parlamentar__nome_parlamentar')
for ausente in ausencia:
dic_ausencia = {}
dic_ausencia['parlamentar'] = ausente.parlamentar
dic_ausencia['justificativa'] = ausente.tipo_ausencia
if ausente.ausencia == 1:
dic_ausencia['tipo'] = 'Matéria'
else:
dic_ausencia['tipo'] = 'Sessão'
lst_ausencia_sessao.append(dic_ausencia)
lst_ausencia_sessao.append({
"parlamentar": ausente.parlamentar,
"justificativa": ausente.tipo_ausencia,
"tipo": "Matéria" if ausente.ausencia == 1 else "Sessão"
})
# Exibe os Expedientes
lst_expedientes = []
expedientes = ExpedienteSessao.objects.filter(
sessao_plenaria=sessao).order_by('tipo__nome')
expedientes = ExpedienteSessao.objects.filter(sessao_plenaria=sessao).order_by('tipo__nome')
for e in expedientes:
dic_expedientes = {}
dic_expedientes["nom_expediente"] = e.tipo.nome
conteudo = e.conteudo
if not is_empty(conteudo):
# unescape HTML codes
# https://github.com/interlegis/sapl/issues/1046
conteudo = re.sub('style=".*?"', '', conteudo)
conteudo = re.sub('class=".*?"', '', conteudo)
conteudo = re.sub('align=".*?"', '', conteudo) # OSTicket Ticket #796450
conteudo = re.sub('<p\s+>', '<p>', conteudo)
conteudo = re.sub('<br\s+/>', '<br/>', conteudo) # OSTicket Ticket #796450
conteudo = html.unescape(conteudo)
# escape special character '&'
# https://github.com/interlegis/sapl/issues/1009
conteudo = conteudo.replace('&', '&amp;')
# https://github.com/interlegis/sapl/issues/2386
conteudo = remove_html_comments(conteudo)
dic_expedientes = {
"nom_expediente": e.tipo.nome,
"txt_expediente": conteudo
}
# unescape HTML codes
# https://github.com/interlegis/sapl/issues/1046
conteudo = re.sub('style=".*?"', '', conteudo)
conteudo = re.sub('class=".*?"', '', conteudo)
conteudo = re.sub('align=".*?"', '', conteudo) # OSTicket Ticket #796450
conteudo = re.sub('<p\s+>', '<p>', conteudo)
conteudo = re.sub('<br\s+/>', '<br/>', conteudo) # OSTicket Ticket #796450
conteudo = html.unescape(conteudo)
# escape special character '&'
# https://github.com/interlegis/sapl/issues/1009
conteudo = conteudo.replace('&', '&amp;')
# https://github.com/interlegis/sapl/issues/2386
conteudo = remove_html_comments(conteudo)
dic_expedientes["txt_expediente"] = conteudo
if dic_expedientes:
lst_expedientes.append(dic_expedientes)
# Lista das matérias do Expediente, incluindo o resultado das votacoes
lst_expediente_materia = []
for expediente_materia in ExpedienteMateria.objects.filter(
sessao_plenaria=sessao):
for expediente_materia in ExpedienteMateria.objects.filter(sessao_plenaria=sessao):
# seleciona os detalhes de uma matéria
materia = expediente_materia.materia
dic_expediente_materia = {}
dic_expediente_materia["num_ordem"] = expediente_materia.numero_ordem
dic_expediente_materia["id_materia"] = (materia.tipo.sigla + ' ' +
materia.tipo.descricao + ' ' +
str(materia.numero) + '/' +
str(materia.ano))
dic_expediente_materia["des_numeracao"] = ' '
dic_expediente_materia = {
"num_ordem": expediente_materia.numero_ordem,
"id_materia": "{} {} {}/{}".format(materia.tipo.sigla, materia.tipo.descricao, str(materia.numero),
str(materia.ano)),
"des_numeracao": ' ',
"des_turno": get_turno(materia)[0],
"txt_ementa": str(materia.ementa),
"ordem_observacao": expediente_materia.observacao,
"nom_resultado": '',
"nom_autor": '',
"votacao_observacao": ' '
}
numeracao = Numeracao.objects.filter(
materia=expediente_materia.materia).first()
numeracao = Numeracao.objects.filter(materia=expediente_materia.materia).first()
if numeracao:
dic_expediente_materia["des_numeracao"] = (
str(numeracao.numero_materia) + '/' + str(
numeracao.ano_materia))
turno, _ = get_turno(materia)
dic_expediente_materia["des_turno"] = turno
dic_expediente_materia["txt_ementa"] = str(materia.ementa)
dic_expediente_materia["ordem_observacao"] = expediente_materia.observacao
dic_expediente_materia["nom_resultado"] = ''
dic_expediente_materia["des_numeracao"] = (str(numeracao.numero_materia) + '/' + str(numeracao.ano_materia))
dic_expediente_materia["nom_autor"] = ''
autoria = materia.autoria_set.all()
dic_expediente_materia['num_autores'] = 'Autores' if len(
autoria) > 1 else 'Autor'
dic_expediente_materia['num_autores'] = 'Autores' if len(autoria) > 1 else 'Autor'
if autoria:
for a in autoria:
if a.autor.nome:
@ -654,109 +622,90 @@ def get_sessao_plenaria(sessao, casa):
else:
dic_expediente_materia["nom_autor"] = 'Desconhecido'
dic_expediente_materia["votacao_observacao"] = ' '
resultados = expediente_materia.registrovotacao_set.all()
if resultados:
for i in resultados:
dic_expediente_materia["nom_resultado"] = (
i.tipo_resultado_votacao.nome)
dic_expediente_materia["votacao_observacao"] = (
i.observacao)
dic_expediente_materia.update({
"nom_resultado": i.tipo_resultado_votacao.nome,
"votacao_observacao": i.observacao
})
else:
dic_expediente_materia["nom_resultado"] = 'Matéria não votada'
dic_expediente_materia["votacao_observacao"] = ' '
dic_expediente_materia.update({
"nom_resultado": 'Matéria não votada',
"votacao_observacao": ' '
})
lst_expediente_materia.append(dic_expediente_materia)
# Lista dos votos nominais das matérias do Expediente
lst_expediente_materia_vot_nom = []
materias_expediente_votacao_nominal = ExpedienteMateria.objects.filter(
sessao_plenaria=sessao,
tipo_votacao=2).order_by('-materia')
materias_expediente_votacao_nominal = ExpedienteMateria.objects.filter(sessao_plenaria=sessao,tipo_votacao=2)\
.order_by('-materia')
for mevn in materias_expediente_votacao_nominal:
votos_materia = []
titulo_materia = mevn.materia
registro = RegistroVotacao.objects.filter(expediente=mevn)
if registro:
for vp in VotoParlamentar.objects.filter(votacao=registro).order_by('parlamentar'):
votos_materia.append(vp)
dic_expediente_materia_vot_nom = {
'titulo': titulo_materia,
'votos': votos_materia
}
lst_expediente_materia_vot_nom.append(dic_expediente_materia_vot_nom)
lst_expediente_materia_vot_nom.append({
"titulo": titulo_materia,
"votos": votos_materia
})
# Lista dos oradores do Expediente
lst_oradores_expediente = []
for orador_expediente in OradorExpediente.objects.filter(
sessao_plenaria=sessao).order_by('numero_ordem'):
parlamentar = Parlamentar.objects.get(
id=orador_expediente.parlamentar.id)
dic_oradores_expediente = {}
dic_oradores_expediente["num_ordem"] = (
orador_expediente.numero_ordem)
dic_oradores_expediente["nom_parlamentar"] = (
parlamentar.nome_parlamentar)
dic_oradores_expediente["observacao"] = (
orador_expediente.observacao)
partido_sigla = Filiacao.objects.filter(
parlamentar=parlamentar).first()
if not partido_sigla:
sigla = ''
else:
sigla = partido_sigla.partido.sigla
dic_oradores_expediente['sgl_partido'] = sigla
lst_oradores_expediente.append(dic_oradores_expediente)
for orador_expediente in OradorExpediente.objects.filter(sessao_plenaria=sessao).order_by('numero_ordem'):
parlamentar = Parlamentar.objects.get(id=orador_expediente.parlamentar.id)
partido_sigla = Filiacao.objects.filter(parlamentar=parlamentar).first()
lst_oradores_expediente.append({
"num_ordem": orador_expediente.numero_ordem,
"nom_parlamentar": parlamentar.nome_parlamentar,
"observacao": orador_expediente.observacao,
"sgl_partido": "" if not partido_sigla else partido_sigla.partido.sigla
})
# Lista presença na ordem do dia
lst_presenca_ordem_dia = []
presenca_ordem_dia = PresencaOrdemDia.objects.filter(
sessao_plenaria=sessao).order_by('parlamentar__nome_parlamentar')
presenca_ordem_dia = PresencaOrdemDia.objects.filter(sessao_plenaria=sessao)\
.order_by('parlamentar__nome_parlamentar')
for parlamentar in [p.parlamentar for p in presenca_ordem_dia]:
dic_presenca_ordem_dia = {}
dic_presenca_ordem_dia['nom_parlamentar'] = (
parlamentar.nome_parlamentar)
sigla = filiacao_data(parlamentar, sessao.data_inicio)
dic_presenca_ordem_dia['sgl_partido'] = sigla
lst_presenca_ordem_dia.append(dic_presenca_ordem_dia)
lst_presenca_ordem_dia.append({
"nom_parlamentar": parlamentar.nome_parlamentar,
"sgl_partido": filiacao_data(parlamentar, sessao.data_inicio)
})
# Lista das matérias da Ordem do Dia, incluindo o resultado das votacoes
lst_votacao = []
for votacao in OrdemDia.objects.filter(
sessao_plenaria=sessao):
for votacao in OrdemDia.objects.filter(sessao_plenaria=sessao):
# seleciona os detalhes de uma matéria
materia = votacao.materia
dic_votacao = {}
dic_votacao["nom_resultado"] = ''
dic_votacao["num_ordem"] = votacao.numero_ordem
dic_votacao["id_materia"] = (
materia.tipo.sigla + ' ' +
materia.tipo.descricao + ' ' +
str(materia.numero) + '/' +
str(materia.ano))
dic_votacao["des_numeracao"] = ' '
dic_votacao = {
"nom_resultado": '',
"num_ordem": votacao.numero_ordem,
"id_materia": (
materia.tipo.sigla + ' ' +
materia.tipo.descricao + ' ' +
str(materia.numero) + '/' +
str(materia.ano)),
"des_numeracao": ' '
}
numeracao = materia.numeracao_set.first()
if numeracao:
dic_votacao["des_numeracao"] = (str(numeracao.numero_materia) + '/' + str(numeracao.ano_materia))
dic_votacao["des_numeracao"] = (
str(numeracao.numero_materia) +
'/' +
str(numeracao.ano_materia))
turno, _ = get_turno(materia)
dic_votacao.update({
"des_turno": get_turno(materia)[0],
# https://github.com/interlegis/sapl/issues/1009
"txt_ementa": html.unescape(materia.ementa),
"ordem_observacao": html.unescape(votacao.observacao),
"nom_autor": ''
})
dic_votacao["des_turno"] = turno
# https://github.com/interlegis/sapl/issues/1009
dic_votacao["txt_ementa"] = html.unescape(materia.ementa)
dic_votacao["ordem_observacao"] = html.unescape(votacao.observacao)
dic_votacao["nom_autor"] = ''
autoria = materia.autoria_set.all()
dic_votacao['num_autores'] = 'Autores' if len(autoria) > 1 else 'Autor'
if autoria:
@ -781,76 +730,53 @@ def get_sessao_plenaria(sessao, casa):
# Lista dos votos nominais das matérias da Ordem do Dia
lst_votacao_vot_nom = []
materias_ordem_dia_votacao_nominal = OrdemDia.objects.filter(
sessao_plenaria=sessao,
tipo_votacao=2).order_by('-materia')
materias_ordem_dia_votacao_nominal = OrdemDia.objects.filter(sessao_plenaria=sessao, tipo_votacao=2)\
.order_by('-materia')
for modvn in materias_ordem_dia_votacao_nominal:
votos_materia_od = []
t_materia = modvn.materia
registro_od = RegistroVotacao.objects.filter(ordem=modvn)
if registro_od:
for vp_od in VotoParlamentar.objects.filter(votacao=registro_od).order_by('parlamentar'):
votos_materia_od.append(vp_od)
dic_votacao_vot_nom = {
'titulo': t_materia,
'votos': votos_materia_od
}
lst_votacao_vot_nom.append(dic_votacao_vot_nom)
lst_votacao_vot_nom.append({
"titulo": t_materia,
"votos": votos_materia_od
})
# Lista dos oradores da Ordem do Dia
lst_oradores_ordemdia = []
oradores_ordem_dia = OradorOrdemDia.objects.filter(
sessao_plenaria=sessao
).order_by('numero_ordem')
oradores_ordem_dia = OradorOrdemDia.objects.filter(sessao_plenaria=sessao).order_by('numero_ordem')
for orador_ordemdia in oradores_ordem_dia:
parlamentar_orador = Parlamentar.objects.get(
id=orador_ordemdia.parlamentar.id
)
sigla_partido = Filiacao.objects.filter(
parlamentar=parlamentar_orador
).first()
if not sigla_partido:
sigla_p = ""
else:
sigla_p = sigla_partido.partido.sigla
dic_oradores_ordemdia = {
'num_ordem': orador_ordemdia.numero_ordem,
'nome_parlamentar': parlamentar_orador.nome_parlamentar,
'observacao': orador_ordemdia.observacao,
'sigla': sigla_p
}
lst_oradores_ordemdia.append(dic_oradores_ordemdia)
parlamentar_orador = Parlamentar.objects.get(id=orador_ordemdia.parlamentar.id)
sigla_partido = Filiacao.objects.filter(parlamentar=parlamentar_orador).first()
lst_oradores_ordemdia.append({
"num_ordem": orador_ordemdia.numero_ordem,
"nome_parlamentar": parlamentar_orador.nome_parlamentar,
"observacao": orador_ordemdia.observacao,
"sigla": "" if not sigla_partido else sigla_partido.partido.sigla
})
# Lista dos oradores nas Explicações Pessoais
lst_oradores = []
for orador in Orador.objects.filter(
sessao_plenaria=sessao).order_by('numero_ordem'):
for parlamentar in Parlamentar.objects.filter(
id=orador.parlamentar.id):
dic_oradores = {}
dic_oradores["num_ordem"] = orador.numero_ordem
dic_oradores["nom_parlamentar"] = parlamentar.nome_parlamentar
partido_sigla = Filiacao.objects.filter(
parlamentar=parlamentar).first()
if not partido_sigla:
sigla = ''
else:
sigla = partido_sigla.partido.sigla
dic_oradores['sgl_partido'] = sigla
lst_oradores.append(dic_oradores)
for orador in Orador.objects.select_related('parlamentar').filter(sessao_plenaria=sessao).order_by('numero_ordem'):
parlamentar = orador.parlamentar
partido_sigla = orador.parlamentar.filiacao_set.select_related('partido', 'parlamentar').first()
lst_oradores.append({
"num_ordem": orador.numero_ordem,
"nom_parlamentar": parlamentar.nome_parlamentar,
"sgl_partido": "" if not partido_sigla else partido_sigla.partido.sigla
})
# Ocorrências da Sessão
lst_ocorrencias = []
ocorrencias = OcorrenciaSessao.objects.filter(
sessao_plenaria=sessao)
ocorrencias = OcorrenciaSessao.objects.filter(sessao_plenaria=sessao)
for o in ocorrencias:
conteudo = o.conteudo
@ -888,15 +814,16 @@ def get_sessao_plenaria(sessao, casa):
def get_turno(materia):
descricao_turno = ''
descricao_tramitacao = ''
tramitacao = materia.tramitacao_set.last()
tramitacoes = materia.tramitacao_set.all().order_by('-data_tramitacao')
tramitacoes_turno = tramitacoes.exclude(turno="")
if tramitacao:
if tramitacao.turno:
if tramitacoes:
if tramitacoes_turno:
for t in Tramitacao.TURNO_CHOICES:
if t[0] == tramitacao.turno:
if t[0] == tramitacoes_turno.first().turno:
descricao_turno = str(t[1])
break
descricao_tramitacao = tramitacao.status.descricao if tramitacao.status else 'Não informada'
descricao_tramitacao = tramitacoes.first().status.descricao if tramitacoes.first().status else 'Não informada'
return descricao_turno, descricao_tramitacao
@ -939,7 +866,7 @@ def relatorio_sessao_plenaria(request, pk):
lst_presenca_ordem_dia,
lst_votacao,
lst_votacao_vot_nom,
lst_oradores_ordemdia,
lst_oradores_ordemdia,
lst_oradores,
lst_ocorrencias) = get_sessao_plenaria(sessao, casa)
@ -973,7 +900,6 @@ def relatorio_sessao_plenaria(request, pk):
def get_protocolos(prots):
protocolos = []
for protocolo in prots:
dic = {}
@ -983,10 +909,10 @@ def get_protocolos(prots):
ts = timezone.localtime(protocolo.timestamp)
if protocolo.timestamp:
dic['data'] = ts.strftime("%d/%m/%Y") + ' - <b>Horário:</b>' + \
ts.strftime("%H:%m")
ts.strftime("%H:%m")
else:
dic['data'] = protocolo.data.strftime("%d/%m/%Y") + ' - <b>Horário:</b>' \
+ protocolo.hora.strftime("%H:%m")
+ protocolo.hora.strftime("%H:%m")
dic['txt_assunto'] = protocolo.assunto_ementa
@ -1029,7 +955,7 @@ def relatorio_protocolo(request):
response = HttpResponse(content_type='application/pdf')
response[
'Content-Disposition'] = (
'inline; filename="relatorio_protocolo.pdf"')
'inline; filename="relatorio_protocolo.pdf"')
casa = CasaLegislativa.objects.first()
@ -1067,7 +993,7 @@ def relatorio_etiqueta_protocolo(request, nro, ano):
response = HttpResponse(content_type='application/pdf')
response[
'Content-Disposition'] = (
'inline; filename="relatorio_etiqueta_protocolo.pdf"')
'inline; filename="relatorio_etiqueta_protocolo.pdf"')
casa = CasaLegislativa.objects.first()
@ -1090,7 +1016,6 @@ def relatorio_etiqueta_protocolo(request, nro, ano):
def get_etiqueta_protocolos(prots):
protocolos = []
for p in prots:
dic = {}
@ -1113,7 +1038,7 @@ def get_etiqueta_protocolos(prots):
for materia in MateriaLegislativa.objects.filter(
numero_protocolo=p.numero, ano=p.ano):
dic['num_materia'] = materia.tipo.sigla + ' ' + \
str(materia.numero) + '/' + str(materia.ano)
str(materia.numero) + '/' + str(materia.ano)
dic['natureza'] = ''
if p.tipo_processo == 0:
@ -1125,7 +1050,7 @@ def get_etiqueta_protocolos(prots):
for documento in DocumentoAdministrativo.objects.filter(
protocolo=p):
dic['num_documento'] = documento.tipo.sigla + ' ' + \
str(documento.numero) + '/' + str(documento.ano)
str(documento.numero) + '/' + str(documento.ano)
dic['ident_processo'] = dic['num_materia'] or dic['num_documento']
@ -1148,7 +1073,7 @@ def relatorio_pauta_sessao(request, pk):
response = HttpResponse(content_type='application/pdf')
response[
'Content-Disposition'] = (
'inline; filename="relatorio_pauta_sessao.pdf"')
'inline; filename="relatorio_pauta_sessao.pdf"')
casa = CasaLegislativa.objects.first()
@ -1172,7 +1097,6 @@ def relatorio_pauta_sessao(request, pk):
def get_pauta_sessao(sessao, casa):
inf_basicas_dic = {}
inf_basicas_dic["nom_sessao"] = sessao.tipo.nome
inf_basicas_dic["num_sessao_plen"] = sessao.numero
@ -1192,7 +1116,7 @@ def get_pauta_sessao(sessao, casa):
dic_expediente_materia = {}
dic_expediente_materia["tipo_materia"] = materia.tipo.sigla + \
' - ' + materia.tipo.descricao
' - ' + materia.tipo.descricao
dic_expediente_materia["num_ordem"] = str(
expediente_materia.numero_ordem)
dic_expediente_materia["id_materia"] = str(
@ -1234,7 +1158,7 @@ def get_pauta_sessao(sessao, casa):
id=votacao.materia.id).first()
dic_votacao = {}
dic_votacao["tipo_materia"] = materia.tipo.sigla + \
' - ' + materia.tipo.descricao
' - ' + materia.tipo.descricao
dic_votacao["num_ordem"] = votacao.numero_ordem
dic_votacao["id_materia"] = str(
materia.numero) + "/" + str(materia.ano)
@ -1280,7 +1204,8 @@ def get_pauta_sessao(sessao, casa):
inf_basicas_dic,
expedientes)
def make_pdf(base_url,main_template,header_template,main_css='',header_css=''):
def make_pdf(base_url, main_template, header_template, main_css='', header_css=''):
html = HTML(base_url=base_url, string=main_template)
main_doc = html.render(stylesheets=[])
@ -1291,7 +1216,7 @@ def make_pdf(base_url,main_template,header_template,main_css='',header_css=''):
return get_page_body(box.all_children())
# Template of header
html = HTML(base_url=base_url,string=header_template)
html = HTML(base_url=base_url, string=header_template)
header = html.render(stylesheets=[CSS(string='@page {size:A4; margin:1cm;}')])
header_page = header.pages[0]
@ -1301,19 +1226,19 @@ def make_pdf(base_url,main_template,header_template,main_css='',header_css=''):
for page in main_doc.pages:
page_body = get_page_body(page._page_box.all_children())
page_body.children += header_body.all_children()
pdf_file = main_doc.write_pdf()
return pdf_file
def resumo_ata_pdf(request,pk):
def resumo_ata_pdf(request, pk):
base_url = request.build_absolute_uri()
casa = CasaLegislativa.objects.first()
rodape = ' '.join(get_rodape(casa))
sessao_plenaria = SessaoPlenaria.objects.get(pk=pk)
context = {}
context.update(get_identificacao_basica(sessao_plenaria))
context.update(get_mesa_diretora(sessao_plenaria))
@ -1330,13 +1255,13 @@ def resumo_ata_pdf(request,pk):
context.update({'object': sessao_plenaria})
context.update({'data': dt.today().strftime('%d/%m/%Y')})
context.update({'rodape': rodape})
header_context = {"casa": casa, 'logotipo':casa.logotipo, 'MEDIA_URL': MEDIA_URL}
header_context = {"casa": casa, 'logotipo': casa.logotipo, 'MEDIA_URL': MEDIA_URL}
html_template = render_to_string('relatorios/relatorio_ata.html', context)
html_header = render_to_string('relatorios/header_ata.html', header_context)
pdf_file = make_pdf(base_url=base_url,main_template=html_template,header_template=html_header)
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'
@ -1344,6 +1269,7 @@ def resumo_ata_pdf(request,pk):
return response
def cria_relatorio(request, context, html_string, header_info=""):
base_url = request.build_absolute_uri()
casa = CasaLegislativa.objects.first()
@ -1366,62 +1292,80 @@ def cria_relatorio(request, context, html_string, header_info=""):
return response
def relatorio_doc_administrativos(request, context):
return cria_relatorio(request, context, 'relatorios/relatorio_doc_administrativos.html')
def relatorio_materia_em_tramitacao(obj, request, context):
return cria_relatorio(request, context, 'relatorios/relatorio_materias_em_tramitacao.html')
def relatorio_materia_por_autor(obj, request, context):
return cria_relatorio(request, context, 'relatorios/relatorio_materias_por_autor.html')
def relatorio_materia_por_ano_autor(obj, request, context):
return cria_relatorio(request, context, 'relatorios/relatorio_materias_por_ano_autor.html')
def relatorio_presenca_sessao(obj, request, context):
return cria_relatorio(request, context, 'relatorios/relatorio_presenca_sessao.html')
def relatorio_atas(obj, request, context):
return cria_relatorio(request, context, 'relatorios/relatorio_atas.html')
def relatorio_historico_tramitacao(obj, request, context):
return cria_relatorio(request, context, 'relatorios/relatorio_historico_tramitacao.html')
def relatorio_fim_prazo_tramitacao(obj, request, context):
return cria_relatorio(request, context, 'relatorios/relatorio_fim_prazo_tramitacao.html')
def relatorio_reuniao(obj, request, context):
return cria_relatorio(request, context, 'relatorios/relatorio_reuniao.html')
def relatorio_audiencia(obj, request, context):
return cria_relatorio(request, context, 'relatorios/relatorio_audiencia.html')
def relatorio_normas_mes(obj, request, context):
return cria_relatorio(request, context, 'relatorios/relatorio_normas_mes.html')
def relatorio_normas_vigencia(obj, request, context):
return cria_relatorio(request, context, 'relatorios/relatorio_normas_vigencia.html')
def relatorio_historico_tramitacao_adm(obj, request, context):
return cria_relatorio(request, context, 'relatorios/relatorio_historico_tramitacao_adm.html')
def relatorio_estatisticas_acesso_normas(obj, request, context):
return cria_relatorio(request, context, 'relatorios/relatorio_estatisticas_acesso_normas.html')
def relatorio_documento_acessorio(obj, request, context):
return cria_relatorio(request, context, 'relatorios/relatorio_documento_acessorio.html')
def relatorio_normas_por_autor(obj, request, context):
return cria_relatorio(request, context, 'relatorios/relatorio_normas_por_autor.html')
def relatorio_pauta_sessao_weasy(obj, request, context):
sessao = context['object']
info = "Pauta da {} ({} - {}) Legislatura".format(sessao,sessao.legislatura.data_inicio.year,sessao.legislatura.data_fim.year)
return cria_relatorio(request, context, 'relatorios/relatorio_pauta_sessao.html',info)
info = "Pauta da {} ({} - {}) Legislatura".format(sessao, sessao.legislatura.data_inicio.year,
sessao.legislatura.data_fim.year)
return cria_relatorio(request, context, 'relatorios/relatorio_pauta_sessao.html', info)
def relatorio_sessao_plenaria_pdf(request, pk):
base_url=request.build_absolute_uri()
base_url = request.build_absolute_uri()
logger = logging.getLogger(__name__)
username = request.user.username
casa = CasaLegislativa.objects.first()
@ -1441,50 +1385,110 @@ def relatorio_sessao_plenaria_pdf(request, pk):
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')
})
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)
dict_ord_template = {
'cont_mult': 'conteudo_multimidia.html',
'exp': 'expedientes.html',
'id_basica': 'identificacao_basica.html',
'lista_p': 'lista_presenca_sessao.html',
'lista_p_o_d': 'lista_presenca_ordemdia.html',
'mat_exp': 'materias_expediente.html',
'v_n_mat_exp': 'votos_nominais_expediente.html',
'mat_o_d': 'materias_ordemdia.html',
'v_n_mat_o_d': 'votos_nominais_ordemdia.html',
'mesa_d': 'mesa_diretora.html',
'oradores_exped': 'oradores_expediente.html',
'oradores_o_d': 'oradores_ordemdia.html',
'oradores_expli': 'oradores_explicacoes.html',
'ocorr_sessao': 'ocorrencias_sessao.html'
}
context = {
"inf_basicas_dic": inf_basicas_dic,
"cont_mult_dic": cont_mult_dic,
"lst_mesa": lst_mesa,
"lst_expediente_materia_vot_nom": lst_expediente_materia_vot_nom,
"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_ordemdia": lst_oradores_ordemdia,
"lst_votacao_vot_nom": lst_votacao_vot_nom,
"lst_oradores": lst_oradores,
"lst_ocorrencias": lst_ocorrencias,
"rodape": rodape,
"data": dt.today().strftime('%d/%m/%Y')
}
ordenacao = ResumoOrdenacao.objects.get_or_create()[0]
try:
context.update({
'primeiro_ordenacao': dict_ord_template[ordenacao.primeiro],
'segundo_ordenacao': dict_ord_template[ordenacao.segundo],
'terceiro_ordenacao': dict_ord_template[ordenacao.terceiro],
'quarto_ordenacao': dict_ord_template[ordenacao.quarto],
'quinto_ordenacao': dict_ord_template[ordenacao.quinto],
'sexto_ordenacao': dict_ord_template[ordenacao.sexto],
'setimo_ordenacao': dict_ord_template[ordenacao.setimo],
'oitavo_ordenacao': dict_ord_template[ordenacao.oitavo],
'nono_ordenacao': dict_ord_template[ordenacao.nono],
'decimo_ordenacao': dict_ord_template[ordenacao.decimo],
'decimo_primeiro_ordenacao': dict_ord_template[ordenacao.decimo_primeiro],
'decimo_segundo_ordenacao': dict_ord_template[ordenacao.decimo_segundo],
'decimo_terceiro_ordenacao': dict_ord_template[ordenacao.decimo_terceiro],
'decimo_quarto_ordenacao': dict_ord_template[ordenacao.decimo_quarto]
})
except KeyError as e:
# self.logger.error("KeyError: " + str(e) + ". Erro ao tentar utilizar "
# "configuração de ordenação. Utilizando ordenação padrão.")
context.update({
'primeiro_ordenacao': 'identificacao_basica.html',
'segundo_ordenacao': 'conteudo_multimidia.html',
'terceiro_ordenacao': 'mesa_diretora.html',
'quarto_ordenacao': 'lista_presenca_sessao.html',
'quinto_ordenacao': 'expedientes.html',
'sexto_ordenacao': 'materias_expediente.html',
'setimo_ordenacao': 'votos_nominais_expediente.html',
'oitavo_ordenacao': 'oradores_expediente.html',
'nono_ordenacao': 'lista_presenca_ordemdia.html',
'decimo_ordenacao': 'materias_ordemdia.html',
'decimo_primeiro_ordenacao': 'votos_nominais_ordemdia.html',
'decimo_segundo_ordenacao': 'oradores_ordemdia.html',
'decimo_terceiro_ordenacao': 'oradores_explicacoes.html',
'decimo_quarto_ordenacao': 'ocorrencias_sessao.html'
})
html_template = render_to_string('relatorios/relatorio_sessao_plenaria.html', context)
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['nom_sessao'],
inf_basicas_dic['num_sessao_leg'],
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})
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)
@ -1494,4 +1498,3 @@ def relatorio_sessao_plenaria_pdf(request, pk):
response.write(pdf_file)
return response

99
sapl/sessao/forms.py

@ -74,86 +74,35 @@ class SessaoPlenariaForm(FileFieldCheckMixin, ModelForm):
# Condições da verificação
abertura_entre_leg = leg.data_inicio <= abertura <= leg.data_fim
abertura_entre_sl = sl.data_inicio <= abertura <= sl.data_fim
if encerramento is not None:
encerramento_entre_leg = leg.data_inicio <= encerramento <= leg.data_fim
encerramento_entre_sl = sl.data_inicio <= encerramento <= sl.data_fim
# Verificação das datas de abertura e encerramento da Sessão
# Verificações com a data de encerramento preenchidas
if encerramento is not None:
# Verifica se a data de encerramento é anterior a data de abertura
if encerramento < abertura:
raise ValidationError("A data de encerramento não pode ser "
"anterior a data de abertura.")
# Verifica se a data de abertura está entre a data de início e fim
# da legislatura
if abertura_entre_leg and encerramento_entre_leg:
if abertura_entre_sl and encerramento_entre_sl:
pass
elif abertura_entre_sl and not encerramento_entre_sl:
raise ValidationError("A data de encerramento deve estar entre "
"as datas de início e fim da Sessão Legislativa.")
elif not abertura_entre_sl and encerramento_entre_sl:
raise ValidationError("A data de abertura deve estar entre as "
"datas de início e fim da Sessão Legislativa.")
elif not abertura_entre_sl and not encerramento_entre_sl:
raise ValidationError("A data de abertura e de encerramento devem estar "
"entre as datas de início e fim da Sessão Legislativa.")
elif abertura_entre_leg and not encerramento_entre_leg:
if abertura_entre_sl and encerramento_entre_sl:
raise ValidationError("A data de encerramento deve estar entre "
"as datas de início e fim da Legislatura.")
elif abertura_entre_sl and not encerramento_entre_sl:
raise ValidationError("A data de encerramento deve estar entre "
"as datas de início e fim tanto da "
"Legislatura quanto da Sessão Legislativa.")
elif not abertura_entre_sl and encerramento_entre_sl:
raise ValidationError("As datas de abertura e encerramento devem "
"estar entre as "
"datas de início e fim tanto Legislatura "
"quanto da Sessão Legislativa.")
elif not abertura_entre_sl and not encerramento_entre_sl:
raise ValidationError("As datas de abertura e encerramento devem "
"estar entre as "
"datas de início e fim tanto Legislatura "
"quanto da Sessão Legislativa.")
elif not abertura_entre_leg and not encerramento_entre_leg:
if abertura_entre_sl and encerramento_entre_sl:
raise ValidationError("As datas de abertura e encerramento devem "
"estar entre as "
"datas de início e fim da Legislatura.")
elif abertura_entre_sl and not encerramento_entre_sl:
raise ValidationError("As datas de abertura e encerramento devem "
"estar entre as "
"datas de início e fim tanto Legislatura "
"quanto da Sessão Legislativa.")
elif not abertura_entre_sl and encerramento_entre_sl:
raise ValidationError("As datas de abertura e encerramento devem "
"estar entre as "
"datas de início e fim tanto Legislatura "
"quanto da Sessão Legislativa.")
elif not abertura_entre_sl and not encerramento_entre_sl:
raise ValidationError("As datas de abertura e encerramento devem "
"estar entre as "
"datas de início e fim tanto Legislatura "
"quanto da Sessão Legislativa.")
# Verificações com a data de encerramento vazia
encerramento_entre_leg = leg.data_inicio <= encerramento <= leg.data_fim
encerramento_entre_sl = sl.data_inicio <= encerramento <= sl.data_fim
else:
if abertura_entre_leg:
if abertura_entre_sl:
pass
else:
raise ValidationError("A data de abertura da sessão deve estar "
"entre a data de início e fim da Sessão Legislativa.")
else:
if abertura_entre_sl:
raise ValidationError("A data de abertura da sessão deve estar "
"entre a data de início e fim da Legislatura.")
else:
raise ValidationError("A data de abertura da sessão deve estar "
"entre a data de início e fim tanto da "
"Legislatura quanto da Sessão Legislativa.")
encerramento_entre_leg = True
encerramento_entre_sl = True
## Sessões Extraordinárias podem estar fora da sessão legislativa
descricao_tipo = tipo.nome.lower()
if "extraordinária" in descricao_tipo or "especial" in descricao_tipo:
# Ignora checagem de limites para Sessão Legislativa
abertura_entre_sl = True
encerramento_entre_sl = True
if not (abertura_entre_leg and encerramento_entre_leg):
raise ValidationError("A data de abertura e encerramento da Sessão "
"Plenária deve estar compreendida entre a "
"data de abertura e encerramento da Legislatura")
if not (abertura_entre_sl and encerramento_entre_sl):
raise ValidationError("A data de abertura e encerramento da Sessão "
"Plenária deve estar compreendida entre a "
"data de abertura e encerramento da Sessão Legislativa")
upload_pauta = self.cleaned_data.get('upload_pauta', False)
upload_ata = self.cleaned_data.get('upload_ata', False)
@ -628,7 +577,7 @@ class AdicionarVariasMateriasFilterSet(MateriaLegislativaFilterSet):
'Pesquisar Autor',
css_class='btn btn-primary btn-sm'), 2),
(Button('limpar',
'limpar Autor',
'Limpar Autor',
css_class='btn btn-primary btn-sm'), 10)])
row6 = to_row(
[('autoria__autor__tipo', 6),

30
sapl/sessao/migrations/0051_auto_20200416_1538.py

@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2020-04-16 18:38
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('sessao', '0050_auto_20191029_1441'),
]
operations = [
migrations.AlterField(
model_name='registroleitura',
name='ip',
field=models.CharField(blank=True, default='', max_length=60, verbose_name='IP'),
),
migrations.AlterField(
model_name='registrovotacao',
name='ip',
field=models.CharField(blank=True, default='', max_length=60, verbose_name='IP'),
),
migrations.AlterField(
model_name='votoparlamentar',
name='ip',
field=models.CharField(blank=True, default='', max_length=60, verbose_name='IP'),
),
]

6
sapl/sessao/models.py

@ -556,7 +556,7 @@ class RegistroVotacao(models.Model):
null=True,
blank=True)
ip = models.CharField(verbose_name=_('IP'),
max_length=30,
max_length=60,
blank=True,
default='')
data_hora = models.DateTimeField(
@ -607,7 +607,7 @@ class VotoParlamentar(models.Model): # RegistroVotacaoParlamentar
null=True,
blank=True)
ip = models.CharField(verbose_name=_('IP'),
max_length=30,
max_length=60,
blank=True,
default='')
data_hora = models.DateTimeField(
@ -901,7 +901,7 @@ class RegistroLeitura(models.Model):
null=True,
blank=True)
ip = models.CharField(verbose_name=_('IP'),
max_length=30,
max_length=60,
blank=True,
default='')
data_hora = models.DateTimeField(

80
sapl/sessao/tests/test_sessao.py

@ -2,7 +2,7 @@ import pytest
from datetime import datetime
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _
from model_mommy import mommy
from model_bakery import baker
from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa
from sapl.parlamentares.models import Legislatura, Parlamentar, Partido,SessaoLegislativa
@ -33,9 +33,9 @@ def test_valida_campos_obrigatorios_sessao_plenaria_form():
@pytest.mark.django_db(transaction=False)
def test_sessao_plenaria_form_valido():
legislatura = mommy.make(Legislatura)
sessao = mommy.make(SessaoLegislativa)
tipo = mommy.make(TipoSessaoPlenaria)
legislatura = baker.make(Legislatura)
sessao = baker.make(SessaoLegislativa)
tipo = baker.make(TipoSessaoPlenaria)
form = forms.SessaoPlenariaForm(data={'legislatura': str(legislatura.pk),
'numero': '1',
@ -51,10 +51,10 @@ def test_sessao_plenaria_form_valido():
@pytest.mark.django_db(transaction=False)
def test_numero_duplicado_sessao_plenaria_form():
legislatura = mommy.make(Legislatura)
sessao = mommy.make(SessaoLegislativa)
tipo = mommy.make(TipoSessaoPlenaria)
sessao_plenaria = mommy.make(SessaoPlenaria,
legislatura = baker.make(Legislatura)
sessao = baker.make(SessaoLegislativa)
tipo = baker.make(TipoSessaoPlenaria)
sessao_plenaria = baker.make(SessaoPlenaria,
legislatura=legislatura,
sessao_legislativa=sessao,
tipo=tipo,
@ -97,11 +97,11 @@ def data(valor):
@pytest.mark.django_db(transaction=False)
def test_bancada_form_valido():
legislatura = mommy.make(Legislatura,
legislatura = baker.make(Legislatura,
data_inicio=data('2017-11-10'),
data_fim=data('2017-12-31'),
)
partido = mommy.make(Partido)
partido = baker.make(Partido)
form = forms.BancadaForm(data={'legislatura': str(legislatura.pk),
'nome': 'Nome da Bancada',
@ -116,11 +116,11 @@ def test_bancada_form_valido():
@pytest.mark.django_db(transaction=False)
def test_bancada_form_datas_invalidas():
legislatura = mommy.make(Legislatura,
legislatura = baker.make(Legislatura,
data_inicio=data('2017-11-10'),
data_fim=data('2017-12-31'),
)
partido = mommy.make(Partido)
partido = baker.make(Partido)
form = forms.BancadaForm(data={'legislatura': str(legislatura.pk),
'nome': 'Nome da Bancada',
@ -133,12 +133,12 @@ def test_bancada_form_datas_invalidas():
@pytest.mark.django_db(transaction=False)
def test_expediente_materia_form_valido():
tipo_materia = mommy.make(TipoMateriaLegislativa)
materia = mommy.make(MateriaLegislativa, tipo=tipo_materia)
tipo_materia = baker.make(TipoMateriaLegislativa)
materia = baker.make(MateriaLegislativa, tipo=tipo_materia)
sessao = mommy.make(SessaoPlenaria)
sessao = baker.make(SessaoPlenaria)
instance = mommy.make(ExpedienteMateria, sessao_plenaria=sessao,
instance = baker.make(ExpedienteMateria, sessao_plenaria=sessao,
materia=materia)
form = forms.ExpedienteMateriaForm(data={'data_ordem': '28/12/2009',
@ -157,10 +157,10 @@ def test_expediente_materia_form_valido():
def test_registro_votacao_tem_ordem_xor_expediente():
def registro_votacao_com(ordem, expediente):
return mommy.make(RegistroVotacao, ordem=ordem, expediente=expediente)
return baker.make(RegistroVotacao, ordem=ordem, expediente=expediente)
ordem = mommy.make(OrdemDia)
expediente = mommy.make(ExpedienteMateria)
ordem = baker.make(OrdemDia)
expediente = baker.make(ExpedienteMateria)
# a validação funciona com exatamente um dos campos preenchido
registro_votacao_com(ordem, None).full_clean()
@ -175,38 +175,38 @@ def test_registro_votacao_tem_ordem_xor_expediente():
registro_votacao_com(ordem, expediente).full_clean()
def create_sessao_plenaria():
legislatura = mommy.make(Legislatura)
sessao = mommy.make(SessaoLegislativa)
tipo = mommy.make(TipoSessaoPlenaria)
return mommy.make(SessaoPlenaria,
legislatura = baker.make(Legislatura)
sessao = baker.make(SessaoLegislativa)
tipo = baker.make(TipoSessaoPlenaria)
return baker.make(SessaoPlenaria,
legislatura=legislatura,
sessao_legislativa=sessao,
tipo=tipo,
numero=1)
def create_materia_legislativa():
tipo_materia = mommy.make(TipoMateriaLegislativa)
return mommy.make(MateriaLegislativa, tipo=tipo_materia)
tipo_materia = baker.make(TipoMateriaLegislativa)
return baker.make(MateriaLegislativa, tipo=tipo_materia)
@pytest.mark.django_db(transaction=False)
def test_delete_sessao_plenaria_cascade_registro_votacao_ordemdia():
materia = create_materia_legislativa()
sessao_plenaria = create_sessao_plenaria()
ordem = mommy.make(OrdemDia,
ordem = baker.make(OrdemDia,
sessao_plenaria=sessao_plenaria,
materia=materia,
tipo_votacao='2')
tipo_resultado_votacao = mommy.make(TipoResultadoVotacao,
tipo_resultado_votacao = baker.make(TipoResultadoVotacao,
nome='ok',
natureza="A")
registro = mommy.make(RegistroVotacao,
registro = baker.make(RegistroVotacao,
tipo_resultado_votacao=tipo_resultado_votacao,
materia=materia,
ordem=ordem)
presenca = mommy.make(PresencaOrdemDia,
presenca = baker.make(PresencaOrdemDia,
sessao_plenaria=sessao_plenaria)
parlamentar = mommy.make(Parlamentar)
voto_parlamentar = mommy.make(VotoParlamentar,
parlamentar = baker.make(Parlamentar)
voto_parlamentar = baker.make(VotoParlamentar,
votacao=registro,
parlamentar=parlamentar,
ordem=ordem)
@ -234,21 +234,21 @@ def test_delete_sessao_plenaria_cascade_registro_votacao_ordemdia():
def test_delete_sessao_plenaria_cascade_registro_votacao_expediente():
materia = create_materia_legislativa()
sessao_plenaria = create_sessao_plenaria()
expediente = mommy.make(ExpedienteMateria,
expediente = baker.make(ExpedienteMateria,
sessao_plenaria=sessao_plenaria,
materia=materia,
tipo_votacao='2')
tipo_resultado_votacao = mommy.make(TipoResultadoVotacao,
tipo_resultado_votacao = baker.make(TipoResultadoVotacao,
nome='ok',
natureza="A")
registro = mommy.make(RegistroVotacao,
registro = baker.make(RegistroVotacao,
tipo_resultado_votacao=tipo_resultado_votacao,
materia=materia,
expediente=expediente)
presenca = mommy.make(SessaoPlenariaPresenca,
presenca = baker.make(SessaoPlenariaPresenca,
sessao_plenaria=sessao_plenaria)
parlamentar = mommy.make(Parlamentar)
voto_parlamentar = mommy.make(VotoParlamentar,
parlamentar = baker.make(Parlamentar)
voto_parlamentar = baker.make(VotoParlamentar,
votacao=registro,
parlamentar=parlamentar,
expediente=expediente)
@ -274,7 +274,7 @@ def test_delete_sessao_plenaria_cascade_registro_votacao_expediente():
@pytest.mark.django_db(transaction=False)
def test_delete_sessao_plenaria_cascade_integrante_mesa():
sessao_plenaria = create_sessao_plenaria()
mesa = mommy.make(IntegranteMesa,sessao_plenaria=sessao_plenaria)
mesa = baker.make(IntegranteMesa,sessao_plenaria=sessao_plenaria)
sessao_plenaria.delete()
mesa_filter = IntegranteMesa.objects.filter(sessao_plenaria=sessao_plenaria).exists()
assert not mesa_filter
@ -282,7 +282,7 @@ def test_delete_sessao_plenaria_cascade_integrante_mesa():
@pytest.mark.django_db(transaction=False)
def test_delete_sessao_plenaria_cascade_expedientesessao():
sessao_plenaria = create_sessao_plenaria()
expediente_sessao = mommy.make(ExpedienteSessao, sessao_plenaria=sessao_plenaria)
expediente_sessao = baker.make(ExpedienteSessao, sessao_plenaria=sessao_plenaria)
sessao_plenaria.delete()
expediente_sessao_filter = ExpedienteSessao.objects.filter(sessao_plenaria=sessao_plenaria).exists()
assert not expediente_sessao_filter
@ -290,7 +290,7 @@ def test_delete_sessao_plenaria_cascade_expedientesessao():
@pytest.mark.django_db(transaction=False)
def test_delete_sessao_plenaria_cascade_orador():
sessao_plenaria = create_sessao_plenaria()
expediente_sessao = mommy.make(Orador, sessao_plenaria=sessao_plenaria)
expediente_sessao = baker.make(Orador, sessao_plenaria=sessao_plenaria)
sessao_plenaria.delete()
orador_filter = Orador.objects.filter(sessao_plenaria=sessao_plenaria).exists()
assert not orador_filter

30
sapl/sessao/tests/test_sessao_view.py

@ -1,7 +1,7 @@
import pytest
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
from model_mommy import mommy
from model_bakery import baker
from sapl.parlamentares.models import Legislatura, SessaoLegislativa
from sapl.sessao.models import (SessaoPlenaria, TipoSessaoPlenaria,
@ -23,9 +23,9 @@ from sapl.sessao.views import (get_identificacao_basica, get_conteudo_multimidia
@pytest.mark.django_db(transaction=False)
def test_incluir_sessao_plenaria_submit(admin_client):
legislatura = mommy.make(Legislatura)
sessao = mommy.make(SessaoLegislativa)
tipo = mommy.make(TipoSessaoPlenaria, id=1)
legislatura = baker.make(Legislatura)
sessao = baker.make(SessaoLegislativa)
tipo = baker.make(TipoSessaoPlenaria, id=1)
response = admin_client.post(reverse('sapl.sessao:sessaoplenaria_create'),
{'legislatura': str(legislatura.pk),
@ -65,9 +65,9 @@ def test_incluir_sessao_errors(admin_client):
@pytest.mark.django_db(transaction=False)
class TestResumoView():
def setup(self):
self.sessao_plenaria = mommy.make(SessaoPlenaria)
self.parlamentar = mommy.make(Parlamentar)
self.cargo_mesa = mommy.make(CargoMesa)
self.sessao_plenaria = baker.make(SessaoPlenaria)
self.parlamentar = baker.make(Parlamentar)
self.cargo_mesa = baker.make(CargoMesa)
self.integrante_mesa = IntegranteMesa(sessao_plenaria=self.sessao_plenaria,
parlamentar=self.parlamentar,
@ -108,16 +108,16 @@ class TestResumoView():
}]}
def test_get_presenca_sessao(self):
justificativa = mommy.make(JustificativaAusencia,sessao_plenaria=self.sessao_plenaria)
presenca = mommy.make(SessaoPlenariaPresenca,sessao_plenaria=self.sessao_plenaria)
justificativa = baker.make(JustificativaAusencia,sessao_plenaria=self.sessao_plenaria)
presenca = baker.make(SessaoPlenariaPresenca,sessao_plenaria=self.sessao_plenaria)
resposta_presenca = get_presenca_sessao(self.sessao_plenaria)
assert resposta_presenca['presenca_sessao'] == [presenca.parlamentar]
assert resposta_presenca['justificativa_ausencia'][0] == justificativa
def test_get_expedientes(self):
tipo_expediente = mommy.make(TipoExpediente)
expediente = mommy.make(ExpedienteSessao,sessao_plenaria=self.sessao_plenaria,tipo=tipo_expediente)
tipo_expediente = baker.make(TipoExpediente)
expediente = baker.make(ExpedienteSessao,sessao_plenaria=self.sessao_plenaria,tipo=tipo_expediente)
resposta_expediente = get_expedientes(self.sessao_plenaria)
@ -130,9 +130,9 @@ class TestResumoView():
pass
def test_get_oradores_explicacoes_pessoais(self):
parlamentar = mommy.make(Parlamentar)
partido_sigla = mommy.make(Filiacao, parlamentar=parlamentar)
orador = mommy.make(Orador,sessao_plenaria=self.sessao_plenaria,parlamentar=parlamentar)
parlamentar = baker.make(Parlamentar)
partido_sigla = baker.make(Filiacao, parlamentar=parlamentar)
orador = baker.make(Orador,sessao_plenaria=self.sessao_plenaria,parlamentar=parlamentar)
resultado_get_oradores = get_oradores_explicacoes_pessoais(self.sessao_plenaria)
@ -143,7 +143,7 @@ class TestResumoView():
}]
def test_get_ocorrencias_da_sessao(self):
ocorrencia = mommy.make(OcorrenciaSessao, sessao_plenaria=self.sessao_plenaria)
ocorrencia = baker.make(OcorrenciaSessao, sessao_plenaria=self.sessao_plenaria)
resultado_get_ocorrencia = get_ocorrencias_da_sessao(self.sessao_plenaria)
assert resultado_get_ocorrencia['ocorrencias_da_sessao'][0] == ocorrencia

17
sapl/sessao/urls.py

@ -27,10 +27,7 @@ from sapl.sessao.views import (AdicionarVariasMateriasExpediente,
mudar_ordem_materia_sessao, recuperar_materia,
recuperar_numero_sessao_view,
remove_parlamentar_composicao,
reordernar_materias_expediente,
reordernar_materias_ordem,
renumerar_materias_ordem,
renumerar_materias_expediente,
reordena_materias,
sessao_legislativa_legislatura_ajax,
VotacaoEmBlocoOrdemDia, VotacaoEmBlocoExpediente,
VotacaoEmBlocoSimbolicaView, VotacaoEmBlocoNominalView,
@ -82,15 +79,9 @@ urlpatterns = [
url(r'^sessao/(?P<pk>\d+)/(?P<spk>\d+)/abrir-votacao$',
abrir_votacao,
name="abrir_votacao"),
url(r'^sessao/(?P<pk>\d+)/reordenar-expediente$',
reordernar_materias_expediente,
name="reordenar_expediente"),
url(r'^sessao/(?P<pk>\d+)/reordenar-ordem$', reordernar_materias_ordem,
name="reordenar_ordem"),
url(r'^sessao/(?P<pk>\d+)/renumerar-ordem$', renumerar_materias_ordem,
name="renumerar_ordem"),
url(r'^sessao/(?P<pk>\d+)/renumerar-materias-expediente$', renumerar_materias_expediente,
name="renumerar_materias_expediente"),
url(r'^sessao/(?P<pk>\d+)/reordena/(?P<tipo>[\w\-]+)/(?P<ordenacao>\d+)/$', reordena_materias, name="reordena_materias"),
url(r'^sistema/sessao-plenaria/tipo/',
include(TipoSessaoCrud.get_urls())),
url(r'^sistema/sessao-plenaria/tipo-resultado-votacao/',

132
sapl/sessao/views.py

@ -1,5 +1,6 @@
import logging
from collections import OrderedDict
from re import sub
from django.contrib import messages
@ -66,68 +67,45 @@ SECRETA = 3
LEITURA = 4
def reordernar_materias_expediente(request, pk):
expedientes = ExpedienteMateria.objects.filter(
sessao_plenaria_id=pk
).order_by(
'materia__tipo__sequencia_regimental',
'materia__ano',
'materia__numero'
)
def reordena_materias(request, pk, tipo, ordenacao):
TIPOS_MATERIAS = {
"expediente": ExpedienteMateria,
"ordemdia": OrdemDia
}
for exp_num, e in enumerate(expedientes, 1):
e.numero_ordem = exp_num
e.save()
TIPOS_ORDENACAO = {
"1": ("materia__tipo__sequencia_regimental", "materia__ano", "materia__numero"),
"2": ("materia__ano", "materia__numero"),
"3": ("-materia__ano", "materia__numero"),
"4": ("materia__autores", "materia__ano", "materia__numero")
}
return HttpResponseRedirect(
reverse('sapl.sessao:expedientemateria_list', kwargs={'pk': pk}))
TIPOS_URLS_SUCESSO = {
"expediente": "sapl.sessao:expedientemateria_list",
"ordemdia": "sapl.sessao:ordemdia_list"
}
materias = TIPOS_MATERIAS[tipo].objects.filter(sessao_plenaria_id=pk).order_by(*TIPOS_ORDENACAO[ordenacao])
materias = OrderedDict.fromkeys(materias)
def reordernar_materias_ordem(request, pk):
ordens = OrdemDia.objects.filter(
sessao_plenaria_id=pk
).order_by(
'materia__tipo__sequencia_regimental',
'materia__ano',
'materia__numero'
)
for ordem_num, o in enumerate(ordens, 1):
o.numero_ordem = ordem_num
o.save()
for numero, materia in enumerate(materias, 1):
materia.numero_ordem = numero
materia.save()
return HttpResponseRedirect(
reverse('sapl.sessao:ordemdia_list', kwargs={'pk': pk}))
return HttpResponseRedirect(reverse(TIPOS_URLS_SUCESSO[tipo], kwargs={'pk': pk}))
def renumerar_materias_ordem(request, pk):
ordens = OrdemDia.objects.filter(sessao_plenaria_id=pk)
for ordem_num, o in enumerate(ordens, 1):
o.numero_ordem = ordem_num
o.save()
return HttpResponseRedirect(
reverse('sapl.sessao:ordemdia_list', kwargs={'pk': pk}))
def renumerar_materias_expediente(request, pk):
expedientes = ExpedienteMateria.objects.filter(sessao_plenaria_id=pk)
for exp_num, e in enumerate(expedientes, 1):
e.numero_ordem = exp_num
e.save()
return HttpResponseRedirect(
reverse('sapl.sessao:expedientemateria_list', kwargs={'pk': pk}))
def verifica_presenca(request, model, spk):
def verifica_presenca(request, model, spk, is_leitura=False):
logger = logging.getLogger(__name__)
if not model.objects.filter(sessao_plenaria_id=spk).exists():
username = request.user.username
logger.error("user=" + username +
". Votação não pode ser aberta sem presenças (sessao_plenaria_id={}).".format(spk))
msg = _('Votação não pode ser aberta sem presenças')
if is_leitura:
text = 'Leitura não pode ser feita sem presenças'
else:
text = 'Votação não pode ser aberta sem presenças'
logger.error("user={}. {} (sessao_plenaria_id={}).".format(username,text, spk))
msg = _(text)
messages.add_message(request, messages.ERROR, msg)
return False
return True
@ -161,17 +139,18 @@ def verifica_votacoes_abertas(request):
return True
def verifica_sessao_iniciada(request, spk):
def verifica_sessao_iniciada(request, spk, is_leitura=False):
logger = logging.getLogger(__name__)
sessao = SessaoPlenaria.objects.get(id=spk)
if not sessao.iniciada or sessao.finalizada:
username = request.user.username
logger.info('user=' + username + '. Não é possível abrir matérias para votação. '
'Esta SessaoPlenaria (id={}) não foi iniciada ou está finalizada.'.format(spk))
msg = _('Não é possível abrir matérias para votação. '
aux_text = 'leitura' if is_leitura else 'votação'
logger.info('user=' + username + '. Não é possível abrir matérias para {}. '
'Esta SessaoPlenaria (id={}) não foi iniciada ou está finalizada.'.format(aux_text, spk))
msg = _('Não é possível abrir matérias para {}. '
'Esta Sessão Plenária não foi iniciada ou está finalizada.'
' Vá em "Abertura"->"Dados Básicos" e altere os valores dos campos necessários.')
' Vá em "Abertura"->"Dados Básicos" e altere os valores dos campos necessários.'.format(aux_text))
messages.add_message(request, messages.INFO, msg)
return False
@ -197,10 +176,11 @@ def abrir_votacao(request, pk, spk):
query_params = "?"
if (verifica_presenca(request, presenca_model, spk) and
materia_votacao = model.objects.get(id=pk)
is_leitura = materia_votacao.tipo_votacao == 4
if (verifica_presenca(request, presenca_model, spk, is_leitura) and
verifica_votacoes_abertas(request) and
verifica_sessao_iniciada(request, spk)):
materia_votacao = model.objects.get(id=pk)
verifica_sessao_iniciada(request, spk, is_leitura)):
materia_votacao.votacao_aberta = True
sessao = SessaoPlenaria.objects.get(id=spk)
sessao.painel_aberto = True
@ -1740,7 +1720,10 @@ def get_materias_expediente(sessao_plenaria):
resultado = rp.tipo_de_retirada.descricao
resultado_observacao = rp.observacao
else:
resultado = _('Matéria não votada')
if m.tipo_votacao == 4:
resultado = _('Matéria lida')
else:
resultado = _('Matéria não votada')
resultado_observacao = _(' ')
autoria = Autoria.objects.filter(materia_id=m.materia_id)
@ -1854,13 +1837,14 @@ def get_materias_ordem_do_dia(sessao_plenaria):
if rv:
resultado = rv.tipo_resultado_votacao.nome
resultado_observacao = rv.observacao
elif rp:
resultado = rp.tipo_de_retirada.descricao
resultado_observacao = rp.observacao
else:
resultado = _('Matéria não votada')
if o.tipo_votacao == 4:
resultado = _('Matéria lida')
else:
resultado = _('Matéria não votada')
resultado_observacao = _(' ')
voto_sim = ""
@ -2817,8 +2801,8 @@ class VotacaoNominalAbstract(SessaoPermissionMixin):
parlamentar=parlamentar)
except ObjectDoesNotExist:
username = self.request.user.username
self.logger.error('user=' + username + '. Objeto voto_parlamentar do ' +
'parlamentar de id={} não existe.'.format(parlamentar.pk))
self.logger.warning('User={}. Objeto voto_parlamentar do parlamentar de id={} não existe.'
.format(username, parlamentar.pk))
yield [parlamentar, None]
else:
yield [parlamentar, voto.voto]
@ -3466,11 +3450,13 @@ class PautaSessaoDetailView(DetailView):
expedientes = []
for e in expediente:
tipo = e.tipo
conteudo = sub(
'&nbsp;', ' ', e.conteudo)
ex = {'tipo': tipo, 'conteudo': conteudo}
expedientes.append(ex)
conteudo = e.conteudo
from sapl.relatorios.views import is_empty
if not is_empty(conteudo):
tipo = e.tipo
conteudo = sub('&nbsp;', ' ', conteudo)
ex = {'tipo': tipo, 'conteudo': conteudo}
expedientes.append(ex)
context.update({'expedientes': expedientes})
# =====================================================================
@ -4412,8 +4398,8 @@ class VotacaoEmBlocoNominalView(PermissionRequiredForAppCrudMixin, TemplateView)
parlamentar=parlamentar)
except ObjectDoesNotExist:
username = self.request.user.username
self.logger.error('user=' + username + '. Objeto voto_parlamentar do ' +
'parlamentar de id={} não existe.'.format(parlamentar.pk))
self.logger.warning('User={}. Objeto voto_parlamentar do parlamentar de id={} não existe.'
.format(username, parlamentar.pk))
yield [parlamentar, None]
else:
yield [parlamentar, voto.voto]

21
sapl/settings.py

@ -41,7 +41,7 @@ ALLOWED_HOSTS = ['*']
LOGIN_REDIRECT_URL = '/'
LOGIN_URL = '/login/?next='
SAPL_VERSION = '3.1.160-RC12'
SAPL_VERSION = '3.1.161-RC3'
if DEBUG:
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
@ -86,6 +86,7 @@ INSTALLED_APPS = (
'drf_yasg',
#'rest_framework_swagger',
'rest_framework',
'rest_framework.authtoken',
'django_filters',
'easy_thumbnails',
@ -139,7 +140,6 @@ MIDDLEWARE = [
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware',
'speedinfo.middleware.ProfilerMiddleware',
]
if DEBUG:
INSTALLED_APPS += ('debug_toolbar', )
@ -148,14 +148,6 @@ if DEBUG:
SITE_URL = config('SITE_URL', cast=str, default='')
CACHES = {
'default': {
'BACKEND': 'speedinfo.backends.proxy_cache',
'CACHE_BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': '/var/tmp/django_cache',
}
}
REST_FRAMEWORK = {
"UNICODE_JSON": False,
"DEFAULT_PARSER_CLASSES": (
@ -168,6 +160,7 @@ REST_FRAMEWORK = {
"sapl.api.permissions.SaplModelPermissions",
),
"DEFAULT_AUTHENTICATION_CLASSES": (
'rest_framework.authentication.TokenAuthentication',
"rest_framework.authentication.SessionAuthentication",
),
"DEFAULT_PAGINATION_CLASS": "sapl.api.pagination.StandardPagination",
@ -176,6 +169,14 @@ REST_FRAMEWORK = {
'django_filters.rest_framework.DjangoFilterBackend',
),
}
CACHES = {
'default': {
'BACKEND': 'speedinfo.backends.proxy_cache',
'CACHE_BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': '/var/tmp/django_cache',
}
}
ROOT_URLCONF = 'sapl.urls'

15
sapl/static/sapl/css/ancora.css

@ -0,0 +1,15 @@
.ancora{
padding: 10px;
border: 1px solid #a2a9b1;
background-color: #f8f9fa;
width: 180px;
height: 130px;
margin-left: 15px;
font-size: 95%;
}
.titulo-conteudo{
text-align: center;
font-weight: bold;
padding-bottom: 15px;
}

3
sapl/static/sapl/css/relatorio.css

@ -38,6 +38,7 @@ html body section dl {
html body section dt{
width: 50px;
text-align: left;
}
html body section dd {
@ -76,7 +77,7 @@ table.grayTable tbody td {
text-align: justify;
}
table.grayTable tr:nth-child(even) {
background: #dddddd;
background: #ffffff;
}
table.grayTable thead {
background: #BBBBBB;

126
sapl/static/sapl/frontend/css/chunk-vendors.42151acc.css

File diff suppressed because one or more lines are too long

BIN
sapl/static/sapl/frontend/css/chunk-vendors.42151acc.css.gz

Binary file not shown.

126
sapl/static/sapl/frontend/css/chunk-vendors.aa0d128d.css

File diff suppressed because one or more lines are too long

BIN
sapl/static/sapl/frontend/css/chunk-vendors.aa0d128d.css.gz

Binary file not shown.

1
sapl/static/sapl/frontend/css/global.278b5d61.css

File diff suppressed because one or more lines are too long

BIN
sapl/static/sapl/frontend/css/global.278b5d61.css.gz

Binary file not shown.

1
sapl/static/sapl/frontend/css/global.3b8f6afb.css

File diff suppressed because one or more lines are too long

BIN
sapl/static/sapl/frontend/css/global.3b8f6afb.css.gz

Binary file not shown.

BIN
sapl/static/sapl/frontend/fonts/fa-brands-400.06147b6c.ttf.gz

Binary file not shown.

BIN
sapl/static/sapl/frontend/fonts/fa-brands-400.5063b105.eot → sapl/static/sapl/frontend/fonts/fa-brands-400.13685372.ttf

Binary file not shown.

BIN
sapl/static/sapl/frontend/fonts/fa-brands-400.13685372.ttf.gz

Binary file not shown.

BIN
sapl/static/sapl/frontend/fonts/fa-brands-400.5063b105.eot.gz

Binary file not shown.

BIN
sapl/static/sapl/frontend/fonts/fa-brands-400.a06da7f0.woff2

Binary file not shown.

BIN
sapl/static/sapl/frontend/fonts/fa-brands-400.06147b6c.ttf → sapl/static/sapl/frontend/fonts/fa-brands-400.c1868c95.eot

Binary file not shown.

BIN
sapl/static/sapl/frontend/fonts/fa-brands-400.c1868c95.eot.gz

Binary file not shown.

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save