Browse Source

Atualiza a versão do Django para 2.2 (#3165)

Co-authored-by: UlyssesBML <ulysses3353@gmail.com>
Co-authored-by: eribeiro <edwardr@senado.leg.br>

Co-authored-by: UlyssesBML <ulysses3353@gmail.com>
Co-authored-by: eribeiro <edwardr@senado.leg.br>
pull/3246/head
Vinícius Cantuária 4 years ago
committed by GitHub
parent
commit
d08fb9e963
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      .gitignore
  2. 1
      Dockerfile.dev
  3. 2
      requirements/dev-requirements.txt
  4. 11
      requirements/requirements.txt
  5. 2
      sapl/api/permissions.py
  6. 14
      sapl/api/views.py
  7. 2
      sapl/audiencia/tests/test_audiencia.py
  8. 2
      sapl/audiencia/views.py
  9. 2
      sapl/base/email_utils.py
  10. 17
      sapl/base/forms.py
  11. 24
      sapl/base/migrations/0040_auto_20200521_1534.py
  12. 14
      sapl/base/migrations/0041_merge_20200818_1256.py
  13. 20
      sapl/base/models.py
  14. 2
      sapl/base/templatetags/menus.py
  15. 2
      sapl/base/tests/test_view_base.py
  16. 87
      sapl/base/urls.py
  17. 96
      sapl/base/views.py
  18. 3
      sapl/comissoes/tests/test_comissoes.py
  19. 2
      sapl/comissoes/views.py
  20. 15
      sapl/compilacao/apps.py
  21. 105
      sapl/compilacao/migrations/0014_auto_20200521_1534.py
  22. 18
      sapl/compilacao/migrations/0015_auto_20200609_1501.py
  23. 336
      sapl/compilacao/models.py
  24. 48
      sapl/compilacao/views.py
  25. 9
      sapl/crispy_layout_mixin.py
  26. 7
      sapl/crud/base.py
  27. 2
      sapl/crud/tests/test_base.py
  28. 2
      sapl/crud/tests/test_masterdetail.py
  29. 2
      sapl/materia/forms.py
  30. 19
      sapl/materia/migrations/0070_auto_20200521_1534.py
  31. 23
      sapl/materia/migrations/0071_auto_20200609_1503.py
  32. 174
      sapl/materia/models.py
  33. 2
      sapl/materia/tests/test_materia.py
  34. 8
      sapl/materia/tests/test_materia_form.py
  35. 2
      sapl/materia/tests/test_materia_urls.py
  36. 2
      sapl/materia/views.py
  37. 18
      sapl/norma/migrations/0034_auto_20200609_1453.py
  38. 18
      sapl/norma/migrations/0035_auto_20200609_1501.py
  39. 10
      sapl/norma/models.py
  40. 2
      sapl/norma/tests/test_norma.py
  41. 2
      sapl/norma/views.py
  42. 2
      sapl/painel/views.py
  43. 13
      sapl/parlamentares/tests/test_parlamentares.py
  44. 2
      sapl/parlamentares/views.py
  45. 18
      sapl/protocoloadm/migrations/0033_auto_20200521_1534.py
  46. 2
      sapl/protocoloadm/migrations/0034_auto_20200708_1312.py
  47. 8
      sapl/protocoloadm/tests/test_protocoloadm.py
  48. 8
      sapl/protocoloadm/views.py
  49. 2
      sapl/redireciona_urls/tests.py
  50. 2
      sapl/redireciona_urls/views.py
  51. 13
      sapl/rules/apps.py
  52. 18
      sapl/sessao/migrations/0052_auto_20200521_1534.py
  53. 48
      sapl/sessao/migrations/0053_auto_20200609_1501.py
  54. 46
      sapl/sessao/models.py
  55. 2
      sapl/sessao/tests/test_sessao_view.py
  56. 4
      sapl/sessao/views.py
  57. 5
      sapl/settings.py
  58. 9
      sapl/test_urls.py
  59. 2
      sapl/urls.py
  60. 4
      scripts/lista_urls.py

3
.gitignore

@ -3,6 +3,9 @@ __pycache__/
*.py[cod]
*$py.class
.pytest_cache/
.DS_Store
# C extensions
*.so

1
Dockerfile.dev

@ -4,5 +4,6 @@ WORKDIR /sapl-dev
COPY requirements ./requirements/
RUN apt update && \
apt -y install graphviz-dev && \
pip install --upgrade pip && \
pip install -r ./requirements/dev-requirements.txt
EXPOSE 8000

2
requirements/dev-requirements.txt

@ -1,7 +1,7 @@
-r test-requirements.txt
autopep8==1.2.4
beautifulsoup4==4.6.0
beautifulsoup4==4.9.1
django-debug-toolbar==1.8
ipdb==0.10.1
pdbpp==0.9.2

11
requirements/requirements.txt

@ -1,11 +1,11 @@
django>=1.11.29,<2.0
Django==2.2
django-haystack==2.8.1
django-filter==2.0.0
djangorestframework==3.9.1
dj-database-url==0.5.0
django-braces==1.9.0
django-braces==1.14.0
django-crispy-forms==1.7.2
django-floppyforms==1.7.0
django-floppyforms==1.8.0
django-extra-views==0.12.0
django-model-utils==3.1.2
django-reversion==3.0.2
@ -19,9 +19,9 @@ ruamel.yaml>=0.15.34,<0.16.0
easy-thumbnails==2.5
python-decouple==3.1
psycopg2-binary==2.7.6.1
pyyaml==4.2b1
pyyaml==5.3.1
pytz==2019.3
rtyaml==0.0.5
rtyaml==1.0.0
python-magic==0.4.15
unipath==1.1
WeasyPrint==51
@ -32,6 +32,7 @@ pysolr==3.6.0
PyPDF4==1.27.0
pyoai==2.5.0
Unidecode==1.1.1
whitenoise==5.1.0
git+https://github.com/interlegis/trml2pdf
git+https://github.com/interlegis/django-admin-bootstrapped

2
sapl/api/permissions.py

@ -48,6 +48,6 @@ class SaplModelPermissions(DjangoModelPermissions):
return (
request.user and
(request.user.is_authenticated() or not self.authenticated_users_only) and
(request.user.is_authenticated or not self.authenticated_users_only) and
request.user.has_perms(perms)
)

14
sapl/api/views.py

@ -3,7 +3,7 @@ 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.urls 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
@ -448,7 +448,7 @@ class _ProposicaoViewSet():
qs = super().get_queryset()
q = Q(data_recebimento__isnull=False, object_id__isnull=False)
if not self.request.user.is_anonymous():
if not self.request.user.is_anonymous:
q |= Q(autor__user=self.request.user)
qs = qs.filter(q)
@ -526,7 +526,7 @@ class _DocumentoAdministrativoViewSet:
"""
qs = super().get_queryset()
if self.request.user.is_anonymous():
if self.request.user.is_anonymous:
qs = qs.exclude(restrito=True)
return qs
@ -540,7 +540,7 @@ class _DocumentoAcessorioAdministrativoViewSet:
def get_queryset(self):
qs = super().get_queryset()
if self.request.user.is_anonymous():
if self.request.user.is_anonymous:
qs = qs.exclude(documento__restrito=True)
return qs
@ -555,7 +555,7 @@ class _TramitacaoAdministrativoViewSet(BusinessRulesNotImplementedMixin):
def get_queryset(self):
qs = super().get_queryset()
if self.request.user.is_anonymous():
if self.request.user.is_anonymous:
qs = qs.exclude(documento__restrito=True)
return qs
@ -569,7 +569,7 @@ class _AnexadoViewSet(BusinessRulesNotImplementedMixin):
def get_queryset(self):
qs = super().get_queryset()
if self.request.user.is_anonymous():
if self.request.user.is_anonymous:
qs = qs.exclude(documento__restrito=True)
return qs
@ -617,7 +617,7 @@ class AppVersionView(APIView):
'description': 'Sistema de Apoio ao Processo Legislativo',
'version': settings.SAPL_VERSION,
'user': request.user.username,
'is_authenticated': request.user.is_authenticated(),
'is_authenticated': request.user.is_authenticated,
}
return Response(content)

2
sapl/audiencia/tests/test_audiencia.py

@ -71,7 +71,7 @@ def test_valida_campos_obrigatorios_audiencia_form():
assert errors['data'] == [_('Este campo é obrigatório.')]
assert errors['hora_inicio'] == [_('Este campo é obrigatório.')]
assert len(errors) == 5
assert len(errors) == 6
@pytest.mark.django_db(transaction=False)

2
sapl/audiencia/views.py

@ -1,7 +1,7 @@
import sapl
from django.http import HttpResponse
from django.core.urlresolvers import reverse
from django.urls import reverse
from django.views.decorators.clickjacking import xframe_options_exempt
from django.views.generic import UpdateView
from sapl.crud.base import RP_DETAIL, RP_LIST, Crud, MasterDetailCrud

2
sapl/base/email_utils.py

@ -2,7 +2,7 @@ from datetime import datetime as dt
import logging
from django.core.mail import EmailMultiAlternatives, get_connection, send_mail
from django.core.urlresolvers import reverse
from django.urls import reverse
from django.template import Context, loader
from django.utils import timezone

17
sapl/base/forms.py

@ -15,7 +15,6 @@ from django.db import models, transaction
from django.db.models import Q
from django.forms import Form, ModelForm
from django.utils import timezone
from django.utils.translation import string_concat
from django.utils.translation import ugettext_lazy as _
import django_filters
@ -551,19 +550,15 @@ class AutorForm(ModelForm):
get_user_model().USERNAME_FIELD)
self.fields['action_user'].initial = 'A'
self.fields['username'].label = string_concat(
self.fields['username'].label,
' (', getattr(
self.instance.user,
get_user_model().USERNAME_FIELD), ')')
self.fields['username'].label = "{} ({})".format(self.fields['username'].label,
getattr(self.instance.user,
get_user_model().USERNAME_FIELD))
if 'status_user' in self.Meta.fields:
self.fields['status_user'].initial = 'R'
self.fields['status_user'].label = string_concat(
self.fields['status_user'].label,
' (', getattr(
self.instance.user,
get_user_model().USERNAME_FIELD), ')')
self.fields['status_user'].label = "{} ({})".format(self.fields['status_user'].label,
getattr(self.instance.user,
get_user_model().USERNAME_FIELD))
self.fields['username'].widget.attrs.update({
'data': getattr(

24
sapl/base/migrations/0040_auto_20200521_1534.py

@ -0,0 +1,24 @@
# Generated by Django 2.2 on 2020-05-21 18:34
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('base', '0039_auto_20191202_1114'),
]
operations = [
migrations.AlterField(
model_name='autor',
name='content_type',
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.PROTECT, to='contenttypes.ContentType'),
),
migrations.AlterField(
model_name='tipoautor',
name='content_type',
field=models.OneToOneField(default=None, null=True, on_delete=django.db.models.deletion.PROTECT, to='contenttypes.ContentType', verbose_name='Modelagem no SAPL'),
),
]

14
sapl/base/migrations/0041_merge_20200818_1256.py

@ -0,0 +1,14 @@
# Generated by Django 2.2 on 2020-08-18 15:56
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('base', '0040_appconfig_inicio_numeracao_protocolo'),
('base', '0040_auto_20200521_1534'),
]
operations = [
]

20
sapl/base/models.py

@ -216,14 +216,17 @@ class AppConfig(models.Model):
@reversion.register()
class TipoAutor(models.Model):
descricao = models.CharField(
max_length=50, verbose_name=_('Descrição'),
help_text=_('Obs: Não crie tipos de autores '
'semelhante aos tipos fixos. '))
max_length=50,
verbose_name=_('Descrição'),
help_text=_('Obs: Não crie tipos de autores semelhante aos tipos fixos. ')
)
content_type = models.OneToOneField(
ContentType,
null=True, default=None,
verbose_name=_('Modelagem no SAPL'))
null=True,
default=None,
verbose_name=_('Modelagem no SAPL'),
on_delete=models.PROTECT)
class Meta:
ordering = ['descricao']
@ -248,7 +251,8 @@ class Autor(models.Model):
ContentType,
blank=True,
null=True,
default=None)
default=None,
on_delete=models.PROTECT)
object_id = models.PositiveIntegerField(
blank=True,
null=True,
@ -258,7 +262,9 @@ class Autor(models.Model):
max_length=120,
blank=True,
verbose_name=_('Nome do Autor'))
cargo = models.CharField(max_length=50, blank=True)
cargo = models.CharField(
max_length=50,
blank=True)
class Meta:
verbose_name = _('Autor')

2
sapl/base/templatetags/menus.py

@ -1,7 +1,7 @@
import logging
from django import template
from django.core.urlresolvers import reverse
from django.urls import reverse
from django.utils.translation import ugettext_lazy as _
import yaml

2
sapl/base/tests/test_view_base.py

@ -1,7 +1,7 @@
import pytest
from model_bakery import baker
import datetime
from django.core.urlresolvers import reverse
from django.urls import reverse
from django.utils.translation import ugettext_lazy as _
from model_bakery import baker

87
sapl/base/urls.py

@ -3,42 +3,32 @@ import os
from django.conf.urls import include, url
from django.contrib.auth import views
from django.contrib.auth.decorators import permission_required
from django.contrib.auth.views import (password_reset, password_reset_complete,
password_reset_confirm,
password_reset_done)
from django.contrib.auth.views import (PasswordResetView, PasswordResetCompleteView, PasswordResetConfirmView,
PasswordResetDoneView)
from django.urls.base import reverse_lazy
from django.views.generic.base import RedirectView, TemplateView
from sapl.base.views import AutorCrud, ConfirmarEmailView, TipoAutorCrud, get_estatistica, DetailUsuarioView, \
PesquisarAutorView
from sapl.settings import EMAIL_SEND_USER, MEDIA_URL
from sapl.base.views import (AutorCrud, ConfirmarEmailView, TipoAutorCrud, get_estatistica, DetailUsuarioView,
PesquisarAutorView, RecuperarSenhaEmailView, RecuperarSenhaFinalizadoView,
RecuperarSenhaConfirmaView, RecuperarSenhaCompletoView)
from sapl.settings import EMAIL_SEND_USER, MEDIA_URL, LOGOUT_REDIRECT_URL
from .apps import AppConfig
from .forms import LoginForm, NovaSenhaForm, RecuperarSenhaForm
from .views import (AlterarSenha, AppConfigCrud, CasaLegislativaCrud,
CreateUsuarioView, DeleteUsuarioView, EditUsuarioView,
HelpTopicView, PesquisarUsuarioView, LogotipoView,
RelatorioAtasView, RelatorioAudienciaView,
RelatorioDataFimPrazoTramitacaoView,
RelatorioHistoricoTramitacaoView,
RelatorioMateriasPorAnoAutorTipoView,
RelatorioMateriasPorAutorView,
RelatorioMateriasTramitacaoView,
RelatorioPresencaSessaoView,
RelatorioReuniaoView, SaplSearchView,
RelatorioNormasPublicadasMesView,
RelatorioNormasVigenciaView,
EstatisticasAcessoNormas,
RelatoriosListView,
ListarInconsistenciasView, ListarProtocolosDuplicadosView,
ListarProtocolosComMateriasView, ListarMatProtocoloInexistenteView,
ListarParlamentaresDuplicadosView,
ListarFiliacoesSemDataFiliacaoView, ListarMandatoSemDataInicioView,
ListarParlMandatosIntersecaoView, ListarParlFiliacoesIntersecaoView,
ListarAutoresDuplicadosView, ListarBancadaComissaoAutorExternoView,
ListarLegislaturaInfindavelView, ListarAnexadasCiclicasView,
ListarAnexadosCiclicosView, pesquisa_textual,
RelatorioHistoricoTramitacaoAdmView, RelatorioDocumentosAcessoriosView,
RelatorioNormasPorAutorView)
from .views import (AlterarSenha, AppConfigCrud, CasaLegislativaCrud, CreateUsuarioView, DeleteUsuarioView,
EditUsuarioView, HelpTopicView, PesquisarUsuarioView, LogotipoView, RelatorioAtasView,
RelatorioAudienciaView, RelatorioDataFimPrazoTramitacaoView, RelatorioHistoricoTramitacaoView,
RelatorioMateriasPorAnoAutorTipoView, RelatorioMateriasPorAutorView,
RelatorioMateriasTramitacaoView, RelatorioPresencaSessaoView, RelatorioReuniaoView, SaplSearchView,
RelatorioNormasPublicadasMesView, RelatorioNormasVigenciaView,
EstatisticasAcessoNormas, RelatoriosListView, ListarInconsistenciasView,
ListarProtocolosDuplicadosView, ListarProtocolosComMateriasView, ListarMatProtocoloInexistenteView,
ListarParlamentaresDuplicadosView, ListarFiliacoesSemDataFiliacaoView,
ListarMandatoSemDataInicioView, ListarParlMandatosIntersecaoView, ListarParlFiliacoesIntersecaoView,
ListarAutoresDuplicadosView, ListarBancadaComissaoAutorExternoView, ListarLegislaturaInfindavelView,
ListarAnexadasCiclicasView, ListarAnexadosCiclicosView, pesquisa_textual,
RelatorioHistoricoTramitacaoAdmView, RelatorioDocumentosAcessoriosView, RelatorioNormasPorAutorView)
app_name = AppConfig.name
@ -59,35 +49,13 @@ alterar_senha = [
]
recuperar_senha = [
url(r'^recuperar-senha/email/$',
password_reset,
{'post_reset_redirect': 'sapl.base:recuperar_senha_finalizado',
'email_template_name': 'base/recuperar_senha_email.html',
'html_email_template_name': 'base/recuperar_senha_email.html',
'template_name': 'base/recuperar_senha_email_form.html',
'from_email': EMAIL_SEND_USER,
'password_reset_form': RecuperarSenhaForm},
name='recuperar_senha_email'),
url(r'^recuperar-senha/finalizado/$',
password_reset_done,
{'template_name': 'base/recupera_senha_email_enviado.html'},
name='recuperar_senha_finalizado'),
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',
'set_password_form': NovaSenhaForm},
url(r'^recuperar-senha/email/$', RecuperarSenhaEmailView.as_view(), name='recuperar_senha_email'),
url(r'^recuperar-senha/finalizado/$', RecuperarSenhaFinalizadoView.as_view(), name='recuperar_senha_finalizado'),
url(r'^recuperar-senha/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>.+)/$', RecuperarSenhaConfirmaView.as_view(),
name='recuperar_senha_confirma'),
url(r'^recuperar-senha/completo/$',
password_reset_complete,
{'template_name': 'base/recuperar_senha_completo.html'},
name='recuperar_senha_completo'),
url(r'^recuperar-senha/completo/$', RecuperarSenhaCompletoView.as_view(), name='recuperar_senha_completo'),
]
urlpatterns = [
url(r'^sistema/autor/tipo/', include(TipoAutorCrud.get_urls())),
url(r'^sistema/autor/', include(AutorCrud.get_urls())),
@ -203,10 +171,9 @@ urlpatterns = [
(TemplateView.as_view(template_name='sistema.html')),
name='sistema'),
url(r'^login/$', views.login, {
'template_name': 'base/login.html', 'authentication_form': LoginForm},
url(r'^login/$', views.LoginView.as_view(template_name= 'base/login.html', authentication_form= LoginForm),
name='login'),
url(r'^logout/$', views.logout, {'next_page': '/login'}, name='logout'),
url(r'^logout/$', views.LogoutView.as_view(), {'next_page': LOGOUT_REDIRECT_URL}, name='logout'),
url(r'^sistema/search/', SaplSearchView(), name='haystack_search'),

96
sapl/base/views.py

@ -10,9 +10,11 @@ from django.contrib.auth import get_user_model
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.contrib.auth.models import Group, User
from django.contrib.auth.tokens import default_token_generator
from django.contrib.auth.views import (PasswordResetView,PasswordResetConfirmView, PasswordResetCompleteView,
PasswordResetDoneView)
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied, ValidationError
from django.core.mail import send_mail
from django.core.urlresolvers import reverse, reverse_lazy
from django.urls import reverse, reverse_lazy
from django.db import connection
from django.db.models import Count, Q, ProtectedError, Max
from django.shortcuts import render
@ -22,7 +24,6 @@ from django.template.loader import get_template
from django.utils import timezone
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, DetailView, DeleteView, FormView, ListView, UpdateView)
from django.views.generic.base import RedirectView, TemplateView
@ -32,49 +33,37 @@ from haystack.query import SearchQuerySet
from sapl.relatorios.views import (relatorio_materia_em_tramitacao, relatorio_materia_por_autor,
relatorio_materia_por_ano_autor, relatorio_presenca_sessao,
relatorio_historico_tramitacao, relatorio_fim_prazo_tramitacao,
relatorio_atas, relatorio_audiencia, relatorio_normas_mes,
relatorio_normas_vigencia, relatorio_historico_tramitacao_adm,
relatorio_reuniao, relatorio_estatisticas_acesso_normas,
relatorio_normas_por_autor, relatorio_documento_acessorio)
relatorio_historico_tramitacao, relatorio_fim_prazo_tramitacao, relatorio_atas,
relatorio_audiencia, relatorio_normas_mes, relatorio_normas_vigencia,
relatorio_historico_tramitacao_adm, relatorio_reuniao,
relatorio_estatisticas_acesso_normas, relatorio_normas_por_autor,
relatorio_documento_acessorio)
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, AutorFilterSet
from sapl.base.forms import (AutorForm, AutorFormForAdmin, TipoAutorForm, AutorFilterSet, RecuperarSenhaForm,
NovaSenhaForm)
from sapl.comissoes.models import Comissao, Reuniao
from sapl.crud.base import CrudAux, make_pagination
from sapl.materia.models import (Anexada, Autoria, DocumentoAcessorio,
MateriaEmTramitacao, MateriaLegislativa, Proposicao,
StatusTramitacao, TipoDocumento,
TipoMateriaLegislativa, UnidadeTramitacao, Tramitacao)
from sapl.materia.models import (Anexada, Autoria, DocumentoAcessorio, MateriaEmTramitacao, MateriaLegislativa,
Proposicao, StatusTramitacao, TipoDocumento, TipoMateriaLegislativa, UnidadeTramitacao,
Tramitacao)
from sapl.norma.models import NormaJuridica, TipoNormaJuridica
from sapl.parlamentares.models import (Filiacao, Legislatura, Mandato, Parlamentar,
SessaoLegislativa)
from sapl.protocoloadm.models import (Anexado, DocumentoAdministrativo, Protocolo,
StatusTramitacaoAdministrativo,
from sapl.parlamentares.models import (Filiacao, Legislatura, Mandato, Parlamentar, SessaoLegislativa)
from sapl.protocoloadm.models import (Anexado, DocumentoAdministrativo, Protocolo, StatusTramitacaoAdministrativo,
TipoDocumentoAdministrativo)
from sapl.sessao.models import (Bancada, PresencaOrdemDia, SessaoPlenaria,
SessaoPlenariaPresenca, TipoSessaoPlenaria)
from sapl.utils import (from_date_to_datetime_utc, gerar_hash_arquivo, intervalos_tem_intersecao,
mail_service_configured, parlamentares_ativos, SEPARADOR_HASH_PROPOSICAO,
show_results_filter_set, num_materias_por_tipo)
from .forms import (AlterarSenhaForm, CasaLegislativaForm,
ConfiguracoesAppForm, RelatorioAtasFilterSet,
RelatorioAudienciaFilterSet,
RelatorioDataFimPrazoTramitacaoFilterSet,
RelatorioHistoricoTramitacaoFilterSet,
RelatorioMateriasPorAnoAutorTipoFilterSet,
RelatorioMateriasPorAutorFilterSet,
RelatorioMateriasTramitacaoFilterSet,
RelatorioPresencaSessaoFilterSet,
RelatorioReuniaoFilterSet, UsuarioCreateForm,
UsuarioEditForm, RelatorioNormasMesFilterSet,
RelatorioNormasVigenciaFilterSet,
EstatisticasAcessoNormasForm, UsuarioFilterSet,
RelatorioHistoricoTramitacaoAdmFilterSet,
RelatorioDocumentosAcessoriosFilterSet,
from sapl.sessao.models import (Bancada, PresencaOrdemDia, SessaoPlenaria, SessaoPlenariaPresenca, TipoSessaoPlenaria)
from sapl.settings import EMAIL_SEND_USER
from sapl.utils import (gerar_hash_arquivo, intervalos_tem_intersecao, mail_service_configured, parlamentares_ativos,
SEPARADOR_HASH_PROPOSICAO, show_results_filter_set, num_materias_por_tipo)
from .forms import (AlterarSenhaForm, CasaLegislativaForm, ConfiguracoesAppForm, RelatorioAtasFilterSet,
RelatorioAudienciaFilterSet, RelatorioDataFimPrazoTramitacaoFilterSet,
RelatorioHistoricoTramitacaoFilterSet, RelatorioMateriasPorAnoAutorTipoFilterSet,
RelatorioMateriasPorAutorFilterSet, RelatorioMateriasTramitacaoFilterSet,
RelatorioPresencaSessaoFilterSet, RelatorioReuniaoFilterSet, UsuarioCreateForm, UsuarioEditForm,
RelatorioNormasMesFilterSet, RelatorioNormasVigenciaFilterSet, EstatisticasAcessoNormasForm,
UsuarioFilterSet, RelatorioHistoricoTramitacaoAdmFilterSet, RelatorioDocumentosAcessoriosFilterSet,
RelatorioNormasPorAutorFilterSet)
from .models import AppConfig, CasaLegislativa
@ -97,6 +86,29 @@ class ConfirmarEmailView(TemplateView):
return self.render_to_response(context)
class RecuperarSenhaEmailView(PasswordResetView):
success_url = reverse_lazy('sapl.base:recuperar_senha_finalizado')
email_template_name = 'base/recuperar_senha_email.html'
html_email_template_name = 'base/recuperar_senha_email.html'
template_name = 'base/recuperar_senha_email_form.html'
from_email = EMAIL_SEND_USER
form_class = RecuperarSenhaForm
class RecuperarSenhaFinalizadoView(PasswordResetDoneView):
template_name = 'base/recupera_senha_email_enviado.html'
class RecuperarSenhaConfirmaView(PasswordResetConfirmView):
success_url = reverse_lazy('sapl.base:recuperar_senha_completo')
template_name = 'base/nova_senha_form.html'
form_class = NovaSenhaForm
class RecuperarSenhaCompletoView(PasswordResetCompleteView):
template_name = 'base/recuperar_senha_completo.html'
class TipoAutorCrud(CrudAux):
model = TipoAutor
help_topic = 'tipo-autor'
@ -108,7 +120,7 @@ class TipoAutorCrud(CrudAux):
@property
def verbose_name(self):
vn = super().verbose_name
vn = string_concat(vn, ' ', _('Externo ao SAPL'))
vn = "{} {}".format(vn, _('Externo ao SAPL'))
return vn
class ListView(CrudAux.ListView):
@ -1433,20 +1445,20 @@ def bancada_comissao_autor_externo():
lista_bancada_autor_externo = []
for bancada in Bancada.objects.all().order_by('nome'):
autor_externo = bancada.autor.filter(tipo=tipo_autor_externo)
autor_externo = bancada.autor.filter(tipo__in=tipo_autor_externo)
if autor_externo:
q_autor_externo = bancada.autor.get(tipo=tipo_autor_externo)
q_autor_externo = bancada.autor.get(tipo__in=tipo_autor_externo)
lista_bancada_autor_externo.append(
(q_autor_externo, bancada, 'Bancada', 'sistema/bancada')
)
lista_comissao_autor_externo = []
for comissao in Comissao.objects.all().order_by('nome'):
autor_externo = comissao.autor.filter(tipo=tipo_autor_externo)
autor_externo = comissao.autor.filter(tipo__in=tipo_autor_externo)
if autor_externo:
q_autor_externo = comissao.autor.get(tipo=tipo_autor_externo)
q_autor_externo = comissao.autor.get(tipo__in=tipo_autor_externo)
lista_comissao_autor_externo.append(
(q_autor_externo, comissao, 'Comissão', 'comissao')
)

3
sapl/comissoes/tests/test_comissoes.py

@ -1,5 +1,5 @@
import pytest
from django.core.urlresolvers import reverse
from django.urls import reverse
from django.utils.translation import ugettext as _
from model_bakery import baker
@ -88,6 +88,7 @@ def test_incluir_comissao_submit(admin_client):
{'tipo': tipo.pk,
'nome': 'Comissão Teste',
'sigla': 'CT',
'ativa': True,
'data_criacao': '2016-03-22',
'unidade_deliberativa': True,
'salvar': 'salvar'},

2
sapl/comissoes/views.py

@ -2,7 +2,7 @@ import logging
from django.contrib import messages
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.core.urlresolvers import reverse
from django.urls import reverse
from django.db.models import F
from django.http.response import HttpResponseRedirect, JsonResponse
from django.views.decorators.clickjacking import xframe_options_exempt

15
sapl/compilacao/apps.py

@ -3,7 +3,6 @@ from django import apps
from django.conf import settings
from django.db import connection, models
from django.db.utils import DEFAULT_DB_ALIAS, IntegrityError
from django.utils.translation import string_concat
from django.utils.translation import ugettext_lazy as _
@ -37,11 +36,7 @@ class AppConfig(apps.AppConfig):
cursor.execute(line)
except IntegrityError as e:
if not settings.DEBUG:
print(
string_concat(
_('Ocorreu erro na importação: '),
line,
str(e)))
print("{} {} {}".format(_('Ocorreu erro na importação:'), line, str(e)))
except Exception as ee:
print(ee)
@ -72,9 +67,7 @@ class AppConfig(apps.AppConfig):
tipo.save()
except IntegrityError as e:
if not settings.DEBUG:
print(string_concat(
_('Ocorreu erro na criação tipo de ta: '),
str(e)))
print("{} {}".format(_('Ocorreu erro na criação tipo de ta:'), str(e)))
def init_compilacao_base(app_config, verbosity=2, interactive=True,
@ -86,9 +79,7 @@ def init_compilacao_base(app_config, verbosity=2, interactive=True,
if not TipoDispositivo.objects.exists():
print('')
print(string_concat('\033[93m\033[1m',
_('Iniciando Textos Articulados...'),
'\033[0m'))
print("\033[93m\033[1m{}\033[0m".format(_('Iniciando Textos Articulados...')))
AppConfig.import_pattern()

105
sapl/compilacao/migrations/0014_auto_20200521_1534.py

@ -0,0 +1,105 @@
# Generated by Django 2.2 on 2020-05-21 18:34
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('compilacao', '0013_auto_20190924_0830'),
]
operations = [
migrations.AlterField(
model_name='dispositivo',
name='dispositivo_atualizador',
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='dispositivos_alterados_set', to='compilacao.Dispositivo', verbose_name='Dispositivo Atualizador'),
),
migrations.AlterField(
model_name='dispositivo',
name='dispositivo_pai',
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='dispositivos_filhos_set', to='compilacao.Dispositivo', verbose_name='Dispositivo Pai'),
),
migrations.AlterField(
model_name='dispositivo',
name='dispositivo_raiz',
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='nodes', to='compilacao.Dispositivo', verbose_name='Dispositivo Raiz'),
),
migrations.AlterField(
model_name='dispositivo',
name='publicacao',
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.PROTECT, to='compilacao.Publicacao', verbose_name='Publicação'),
),
migrations.AlterField(
model_name='nota',
name='dispositivo',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='dispositivo_nota_set', to='compilacao.Dispositivo', verbose_name='Dispositivo da Nota'),
),
migrations.AlterField(
model_name='nota',
name='owner',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL, verbose_name='Dono da Nota'),
),
migrations.AlterField(
model_name='nota',
name='tipo',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='compilacao.TipoNota', verbose_name='Tipo da Nota'),
),
migrations.AlterField(
model_name='publicacao',
name='ta',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='compilacao.TextoArticulado', verbose_name='Texto Articulado'),
),
migrations.AlterField(
model_name='publicacao',
name='tipo_publicacao',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='compilacao.TipoPublicacao', verbose_name='Tipo de Publicação'),
),
migrations.AlterField(
model_name='publicacao',
name='veiculo_publicacao',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='compilacao.VeiculoPublicacao', verbose_name='Veículo de Publicação'),
),
migrations.AlterField(
model_name='textoarticulado',
name='content_type',
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.PROTECT, to='contenttypes.ContentType'),
),
migrations.AlterField(
model_name='textoarticulado',
name='tipo_ta',
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.PROTECT, to='compilacao.TipoTextoArticulado', verbose_name='Tipo de Texto Articulado'),
),
migrations.AlterField(
model_name='tipodispositivorelationship',
name='filho_permitido',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='possiveis_pais', to='compilacao.TipoDispositivo'),
),
migrations.AlterField(
model_name='tipodispositivorelationship',
name='pai',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='filhos_permitidos', to='compilacao.TipoDispositivo'),
),
migrations.AlterField(
model_name='tipodispositivorelationship',
name='perfil',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='compilacao.PerfilEstruturalTextoArticulado'),
),
migrations.AlterField(
model_name='vide',
name='dispositivo_base',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='dispositivo_base_set', to='compilacao.Dispositivo', verbose_name='Dispositivo Base'),
),
migrations.AlterField(
model_name='vide',
name='dispositivo_ref',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='dispositivo_citado_set', to='compilacao.Dispositivo', verbose_name='Dispositivo Referido'),
),
migrations.AlterField(
model_name='vide',
name='tipo',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='compilacao.TipoVide', verbose_name='Tipo do Vide'),
),
]

18
sapl/compilacao/migrations/0015_auto_20200609_1501.py

@ -0,0 +1,18 @@
# Generated by Django 2.2 on 2020-06-09 18:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('compilacao', '0014_auto_20200521_1534'),
]
operations = [
migrations.AlterField(
model_name='textoarticulado',
name='participacao_social',
field=models.BooleanField(blank=True, choices=[(None, 'Padrão definido no Tipo'), (True, 'Sim'), (False, 'Não')], default=False, null=True, verbose_name='Participação Social'),
),
]

336
sapl/compilacao/models.py

@ -191,47 +191,81 @@ PRIVACIDADE_STATUS = (
@reversion.register()
class TextoArticulado(TimestampedMixin):
data = models.DateField(blank=True, null=True, verbose_name=_('Data'))
data = models.DateField(
blank=True,
null=True,
verbose_name=_('Data')
)
ementa = models.TextField(verbose_name=_('Ementa'))
observacao = models.TextField(blank=True, verbose_name=_('Observação'))
observacao = models.TextField(
blank=True,
verbose_name=_('Observação')
)
numero = models.CharField(
max_length=8, verbose_name=_('Número'))
max_length=8,
verbose_name=_('Número')
)
ano = models.PositiveSmallIntegerField(verbose_name=_('Ano'))
tipo_ta = models.ForeignKey(
TipoTextoArticulado,
blank=True, null=True, default=None,
verbose_name=_('Tipo de Texto Articulado'))
participacao_social = models.NullBooleanField(
blank=True,
null=True,
default=None,
blank=True, null=True,
verbose_name=_('Tipo de Texto Articulado'),
on_delete=models.PROTECT
)
participacao_social = models.BooleanField(
blank=True,
null=True,
default=False,
choices=PARTICIPACAO_SOCIAL_CHOICES,
verbose_name=_('Participação Social'))
verbose_name=_('Participação Social')
)
content_type = models.ForeignKey(
ContentType,
blank=True, null=True, default=None)
blank=True,
null=True,
default=None,
on_delete=models.PROTECT
)
object_id = models.PositiveIntegerField(
blank=True, null=True, default=None)
blank=True,
null=True,
default=None)
content_object = GenericForeignKey('content_type', 'object_id')
owners = models.ManyToManyField(
get_settings_auth_user_model(),
blank=True, verbose_name=_('Donos do Texto Articulado'))
blank=True,
verbose_name=_('Donos do Texto Articulado')
)
editable_only_by_owners = models.BooleanField(
choices=YES_NO_CHOICES,
default=True,
verbose_name=_('Editável apenas pelos donos do Texto Articulado?'))
verbose_name=_('Editável apenas pelos donos do Texto Articulado?')
)
editing_locked = models.BooleanField(
choices=YES_NO_CHOICES,
default=True,
verbose_name=_('Texto Articulado em Edição?'))
verbose_name=_('Texto Articulado em Edição?')
)
privacidade = models.IntegerField(
_('Privacidade'),
choices=PRIVACIDADE_STATUS,
default=STATUS_TA_PRIVATE)
default=STATUS_TA_PRIVATE
)
class Meta:
verbose_name = _('Texto Articulado')
@ -775,23 +809,39 @@ class TipoDispositivo(BaseModel):
@reversion.register()
class TipoDispositivoRelationship(BaseModel):
pai = models.ForeignKey(TipoDispositivo, related_name='filhos_permitidos')
pai = models.ForeignKey(
TipoDispositivo,
related_name='filhos_permitidos',
on_delete=models.PROTECT
)
filho_permitido = models.ForeignKey(
TipoDispositivo,
related_name='possiveis_pais')
perfil = models.ForeignKey(PerfilEstruturalTextoArticulado)
related_name='possiveis_pais',
on_delete=models.PROTECT
)
perfil = models.ForeignKey(
PerfilEstruturalTextoArticulado,
on_delete=models.PROTECT
)
filho_de_insercao_automatica = models.BooleanField(
default=False,
choices=YES_NO_CHOICES,
verbose_name=_('Filho de Inserção Automática'))
verbose_name=_('Filho de Inserção Automática')
)
permitir_variacao = models.BooleanField(
default=True,
choices=YES_NO_CHOICES,
verbose_name=_('Permitir Variação Numérica'))
verbose_name=_('Permitir Variação Numérica')
)
quantidade_permitida = models.IntegerField(
default=-1,
verbose_name=_('Quantidade permitida nesta relação'))
verbose_name=_('Quantidade permitida nesta relação')
)
class Meta:
verbose_name = _('Relação Direta Permitida')
@ -837,33 +887,66 @@ class VeiculoPublicacao(models.Model):
@reversion.register()
class Publicacao(TimestampedMixin):
ta = models.ForeignKey(
TextoArticulado, verbose_name=_('Texto Articulado'))
TextoArticulado,
verbose_name=_('Texto Articulado'),
on_delete=models.PROTECT
)
veiculo_publicacao = models.ForeignKey(
VeiculoPublicacao, verbose_name=_('Veículo de Publicação'))
VeiculoPublicacao,
verbose_name=_('Veículo de Publicação'),
on_delete=models.PROTECT
)
tipo_publicacao = models.ForeignKey(
TipoPublicacao, verbose_name=_('Tipo de Publicação'))
TipoPublicacao,
verbose_name=_('Tipo de Publicação'),
on_delete=models.PROTECT
)
data = models.DateField(verbose_name=_('Data de Publicação'))
hora = models.TimeField(
blank=True, null=True, verbose_name=_('Horário de Publicação'))
blank=True,
null=True,
verbose_name=_('Horário de Publicação')
)
numero = models.PositiveIntegerField(
blank=True, null=True, verbose_name=_('Número'))
blank=True,
null=True,
verbose_name=_('Número')
)
ano = models.PositiveIntegerField(
blank=True, null=True, verbose_name=_('Ano'))
blank=True,
null=True,
verbose_name=_('Ano')
)
edicao = models.PositiveIntegerField(
blank=True, null=True, verbose_name=_('Edição'))
blank=True,
null=True,
verbose_name=_('Edição')
)
url_externa = models.URLField(
max_length=1024,
blank=True,
verbose_name=_('Link para Versão Eletrônica'))
verbose_name=_('Link para Versão Eletrônica')
)
pagina_inicio = models.PositiveIntegerField(
blank=True, null=True, verbose_name=_('Pg. Início'))
blank=True,
null=True,
verbose_name=_('Pg. Início')
)
pagina_fim = models.PositiveIntegerField(
blank=True, null=True, verbose_name=_('Pg. Fim'))
blank=True,
null=True,
verbose_name=_('Pg. Fim')
)
class Meta:
verbose_name = _('Publicação')
@ -880,150 +963,222 @@ class Publicacao(TimestampedMixin):
class Dispositivo(BaseModel, TimestampedMixin):
TEXTO_PADRAO_DISPOSITIVO_REVOGADO = force_text(_('(Revogado)'))
INTERVALO_ORDEM = 1000
ordem = models.PositiveIntegerField(
default=0,
verbose_name=_('Ordem de Renderização'))
verbose_name=_('Ordem de Renderização')
)
ordem_bloco_atualizador = models.PositiveIntegerField(
default=0,
verbose_name=_('Ordem de Renderização no Bloco Atualizador'))
verbose_name=_('Ordem de Renderização no Bloco Atualizador')
)
# apenas articulacao recebe nivel zero
nivel = models.PositiveIntegerField(
default=0,
blank=True,
null=True,
verbose_name=_('Nível Estrutural'))
verbose_name=_('Nível Estrutural')
)
dispositivo0 = models.PositiveIntegerField(
default=0,
verbose_name=_('Número do Dispositivo'))
verbose_name=_('Número do Dispositivo')
)
dispositivo1 = models.PositiveIntegerField(
default=0,
blank=True,
null=True,
verbose_name=_('Primeiro Nível de Variação'))
verbose_name=_('Primeiro Nível de Variação')
)
dispositivo2 = models.PositiveIntegerField(
default=0,
blank=True,
null=True,
verbose_name=_('Segundo Nível de Variação'))
verbose_name=_('Segundo Nível de Variação')
)
dispositivo3 = models.PositiveIntegerField(
default=0,
blank=True,
null=True,
verbose_name=_('Terceiro Nível de Variação'))
verbose_name=_('Terceiro Nível de Variação')
)
dispositivo4 = models.PositiveIntegerField(
default=0,
blank=True,
null=True,
verbose_name=_('Quarto Nível de Variação'))
verbose_name=_('Quarto Nível de Variação')
)
dispositivo5 = models.PositiveIntegerField(
default=0,
blank=True,
null=True,
verbose_name=_('Quinto Nível de Variação'))
verbose_name=_('Quinto Nível de Variação')
)
rotulo = models.CharField(
max_length=50,
blank=True,
default='',
verbose_name=_('Rótulo'))
verbose_name=_('Rótulo')
)
texto = models.TextField(
blank=True,
default='',
verbose_name=_('Texto do Dispositivo'))
verbose_name=_('Texto do Dispositivo')
)
texto_atualizador = models.TextField(
blank=True,
default='',
verbose_name=_('Texto do Dispositivo no Dispositivo Atualizador'))
verbose_name=_('Texto do Dispositivo no Dispositivo Atualizador')
)
inicio_vigencia = models.DateField(verbose_name=_('Início de Vigência'))
inicio_vigencia = models.DateField(
verbose_name=_('Início de Vigência'))
fim_vigencia = models.DateField(
blank=True, null=True, verbose_name=_('Fim de Vigência'))
blank=True,
null=True,
verbose_name=_('Fim de Vigência')
)
inicio_eficacia = models.DateField(verbose_name=_('Início de Eficácia'))
inicio_eficacia = models.DateField(
verbose_name=_('Início de Eficácia'))
fim_eficacia = models.DateField(
blank=True, null=True, verbose_name=_('Fim de Eficácia'))
blank=True,
null=True,
verbose_name=_('Fim de Eficácia')
)
inconstitucionalidade = models.BooleanField(
default=False,
choices=YES_NO_CHOICES,
verbose_name=_('Declarado Inconstitucional'))
verbose_name=_('Declarado Inconstitucional')
)
auto_inserido = models.BooleanField(
default=False,
choices=YES_NO_CHOICES,
verbose_name=_('Auto Inserido'))
verbose_name=_('Auto Inserido')
)
visibilidade = models.BooleanField(
default=False,
choices=YES_NO_CHOICES,
verbose_name=_('Visibilidade no Texto Articulado Publicado'))
verbose_name=_('Visibilidade no Texto Articulado Publicado')
)
dispositivo_de_revogacao = models.BooleanField(
default=False,
choices=YES_NO_CHOICES,
verbose_name=_('Dispositivo de Revogação'))
verbose_name=_('Dispositivo de Revogação')
)
tipo_dispositivo = models.ForeignKey(
TipoDispositivo,
on_delete=models.PROTECT,
related_name='dispositivos_do_tipo_set',
verbose_name=_('Tipo do Dispositivo'))
verbose_name=_('Tipo do Dispositivo')
)
publicacao = models.ForeignKey(
Publicacao,
blank=True, null=True, default=None, verbose_name=_('Publicação'))
blank=True,
null=True,
default=None,
verbose_name=_('Publicação'),
on_delete=models.PROTECT
)
ta = models.ForeignKey(
TextoArticulado,
on_delete=models.CASCADE,
related_name='dispositivos_set',
verbose_name=_('Texto Articulado'))
verbose_name=_('Texto Articulado'),
)
ta_publicado = models.ForeignKey(
TextoArticulado,
on_delete=models.PROTECT,
blank=True, null=True, default=None,
blank=True,
null=True,
default=None,
related_name='dispositivos_alterados_pelo_ta_set',
verbose_name=_('Texto Articulado Publicado'))
verbose_name=_('Texto Articulado Publicado')
)
dispositivo_subsequente = models.ForeignKey(
'self',
blank=True, null=True, default=None,
blank=True,
null=True,
default=None,
related_name='dispositivo_subsequente_set',
on_delete=models.SET_NULL,
verbose_name=_('Dispositivo Subsequente'))
verbose_name=_('Dispositivo Subsequente')
)
dispositivo_substituido = models.ForeignKey(
'self',
blank=True, null=True, default=None,
blank=True,
null=True,
default=None,
related_name='dispositivo_substituido_set',
on_delete=models.SET_NULL,
verbose_name=_('Dispositivo Substituido'))
verbose_name=_('Dispositivo Substituido')
)
dispositivo_pai = models.ForeignKey(
'self',
blank=True, null=True, default=None,
blank=True,
null=True,
default=None,
related_name='dispositivos_filhos_set',
verbose_name=_('Dispositivo Pai'))
verbose_name=_('Dispositivo Pai'),
on_delete=models.PROTECT
)
dispositivo_raiz = models.ForeignKey(
'self',
blank=True, null=True, default=None,
blank=True,
null=True,
default=None,
related_name='nodes',
verbose_name=_('Dispositivo Raiz'))
verbose_name=_('Dispositivo Raiz'),
on_delete=models.PROTECT
)
dispositivo_vigencia = models.ForeignKey(
'self',
blank=True, null=True, default=None,
blank=True,
null=True,
default=None,
on_delete=models.SET_NULL,
related_name='dispositivos_vigencias_set',
verbose_name=_('Dispositivo de Vigência'))
verbose_name=_('Dispositivo de Vigência')
)
dispositivo_atualizador = models.ForeignKey(
'self',
blank=True, null=True, default=None,
blank=True,
null=True,
default=None,
related_name='dispositivos_alterados_set',
verbose_name=_('Dispositivo Atualizador'))
verbose_name=_('Dispositivo Atualizador'),
on_delete=models.PROTECT
)
contagem_continua = models.BooleanField(
default=False,
choices=YES_NO_CHOICES, verbose_name=_('Contagem contínua'))
choices=YES_NO_CHOICES,
verbose_name=_('Contagem contínua')
)
class Meta:
verbose_name = _('Dispositivo')
@ -1740,16 +1895,25 @@ class Dispositivo(BaseModel, TimestampedMixin):
class Vide(TimestampedMixin):
texto = models.TextField(verbose_name=_('Texto do Vide'))
tipo = models.ForeignKey(TipoVide, verbose_name=_('Tipo do Vide'))
tipo = models.ForeignKey(
TipoVide,
verbose_name=_('Tipo do Vide'),
on_delete=models.PROTECT
)
dispositivo_base = models.ForeignKey(
Dispositivo,
verbose_name=_('Dispositivo Base'),
related_name='dispositivo_base_set')
related_name='dispositivo_base_set',
on_delete=models.PROTECT
)
dispositivo_ref = models.ForeignKey(
Dispositivo,
related_name='dispositivo_citado_set',
verbose_name=_('Dispositivo Referido'))
verbose_name=_('Dispositivo Referido'),
on_delete=models.PROTECT
)
class Meta:
verbose_name = _('Vide')
@ -1784,24 +1948,40 @@ class Nota(TimestampedMixin):
verbose_name=_('Título'),
max_length=100,
default='',
blank=True)
blank=True
)
texto = models.TextField(verbose_name=_('Texto'))
url_externa = models.URLField(
max_length=1024,
blank=True,
verbose_name=_('Url externa'))
verbose_name=_('Url externa')
)
publicacao = models.DateTimeField(verbose_name=_('Data de Publicação'))
efetividade = models.DateTimeField(verbose_name=_('Data de Efeito'))
tipo = models.ForeignKey(TipoNota, verbose_name=_('Tipo da Nota'))
tipo = models.ForeignKey(
TipoNota,
verbose_name=_('Tipo da Nota'),
on_delete=models.PROTECT
)
dispositivo = models.ForeignKey(
Dispositivo,
verbose_name=_('Dispositivo da Nota'),
related_name='dispositivo_nota_set')
related_name='dispositivo_nota_set',
on_delete=models.PROTECT
)
owner = models.ForeignKey(
get_settings_auth_user_model(), verbose_name=_('Dono da Nota'))
get_settings_auth_user_model(),
verbose_name=_('Dono da Nota'),
on_delete=models.PROTECT
)
publicidade = models.PositiveSmallIntegerField(
choices=NOTAS_PUBLICIDADE_CHOICES,
verbose_name=_('Nível de Publicidade'))

48
sapl/compilacao/views.py

@ -12,7 +12,7 @@ from django.contrib.auth.mixins import PermissionRequiredMixin
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError, PermissionDenied
from django.core.signing import Signer
from django.core.urlresolvers import reverse, reverse_lazy
from django.urls import reverse, reverse_lazy
from django.db import transaction
from django.db.models import Q
from django.db.models.query import QuerySet
@ -21,7 +21,6 @@ from django.http.response import (HttpResponse, HttpResponseRedirect,
from django.shortcuts import get_object_or_404, redirect
from django.utils.dateparse import parse_date
from django.utils.encoding import force_text
from django.utils.translation import string_concat
from django.utils.translation import ugettext_lazy as _
from django.views.generic.base import TemplateView
from django.views.generic.detail import DetailView
@ -121,11 +120,7 @@ class IntegracaoTaView(TemplateView):
tipo_ta.save()
except Exception as e:
print(
string_concat(
_('Ocorreu erro na importação do arquivo base dos Tipos de'
'Dispositivos, entre outras informações iniciais.'),
str(e)))
print("{} {}".format(_('Ocorreu erro na importação do arquivo base dos Tipos de Dispositivos, entre outras informações iniciais.'), str(e)))
return self.get_redirect_deactivated()
assert hasattr(self, 'map_fields'), _(
@ -187,7 +182,7 @@ class IntegracaoTaView(TemplateView):
if not ta_exists:
if ta.editable_only_by_owners and\
not self.request.user.is_anonymous():
not self.request.user.is_anonymous:
ta.owners.add(self.request.user)
if not Dispositivo.objects.filter(ta_id=ta.pk).exists() and\
@ -1876,24 +1871,25 @@ class ActionDispositivoCreateMixin(ActionsCommonsMixin):
base = Dispositivo.objects.get(
pk=self.kwargs['dispositivo_id'] if not _base else _base)
result = [{'tipo_insert': force_text(string_concat(
_('Inserir Após'),
' ',
base.tipo_dispositivo.nome)),
'icone': '&#8631;&nbsp;',
'action': 'json_add_next',
'itens': []},
{'tipo_insert': force_text(string_concat(
_('Inserir em'),
' ',
base.tipo_dispositivo.nome)),
'icone': '&#8690;&nbsp;',
'action': 'json_add_in',
'itens': []},
{'tipo_insert': force_text(_('Inserir Antes')),
'icone': '&#8630;&nbsp;',
'action': 'json_add_prior',
'itens': []}]
result = [
{
'tipo_insert': force_text("{} {}".format(_('Inserir Após'), base.tipo_dispositivo.nome)),
'icone': '&#8631;&nbsp;',
'action': 'json_add_next',
'itens': []},
{
'tipo_insert': force_text("{} {}".format(_('Inserir em'), base.tipo_dispositivo.nome)),
'icone': '&#8690;&nbsp;',
'action': 'json_add_in',
'itens': []
},
{
'tipo_insert': force_text(_('Inserir Antes')),
'icone': '&#8630;&nbsp;',
'action': 'json_add_prior',
'itens': []
}
]
perfil_pk = request.session['perfil_estrutural']

9
sapl/crispy_layout_mixin.py

@ -4,7 +4,7 @@ from crispy_forms.bootstrap import FormActions
from crispy_forms.helper import FormHelper
from crispy_forms.layout import HTML, Div, Fieldset, Layout, Submit
from django import template
from django.core.urlresolvers import reverse, reverse_lazy
from django.urls import reverse, reverse_lazy
from django.utils import formats
from django.utils.translation import ugettext as _
import rtyaml
@ -330,11 +330,18 @@ class CrispyLayoutFormMixin:
def read_yaml_from_file(yaml_layout):
from django.utils.safestring import SafeText
# TODO cache this at application level
t = template.loader.get_template(yaml_layout)
# aqui é importante converter para str pois, dependendo do ambiente,
# o rtyaml pode usar yaml.CSafeLoader, que exige str ou stream
rendered = str(t.render())
# Força conversão para string caso seja SafeText.
if isinstance(rendered, SafeText):
rendered = rendered.strip()
return rtyaml.load(rendered)

7
sapl/crud/base.py

@ -9,14 +9,13 @@ from django.conf.urls import url
from django.contrib import messages
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.core.exceptions import ObjectDoesNotExist
from django.core.urlresolvers import reverse
from django.urls import reverse
from django.db import models
from django.db.models.fields.related import ForeignKey, ManyToManyField
from django.http.response import Http404
from django.shortcuts import redirect
from django.utils.decorators import classonlymethod
from django.utils.encoding import force_text
from django.utils.translation import string_concat
from django.utils.translation import ugettext_lazy as _
from django.views.generic import (CreateView, DeleteView, DetailView, ListView,
UpdateView)
@ -613,7 +612,7 @@ class CrudListView(PermissionRequiredContainerCrudMixin, ListView):
)
# print(queryset.query)
if not self.request.user.is_authenticated():
if not self.request.user.is_authenticated:
return queryset
if self.container_field:
@ -816,7 +815,7 @@ class CrudDetailView(PermissionRequiredContainerCrudMixin,
else:
queryset = super().get_queryset()
if not self.request.user.is_authenticated():
if not self.request.user.is_authenticated:
return queryset
if self.container_field_set:

2
sapl/crud/tests/test_base.py

@ -1,8 +1,6 @@
import pytest
from django.core.urlresolvers import reverse
from model_bakery import baker
from sapl.crud.base import (CrispyLayoutFormMixin, CrudListView, from_to,
get_field_display, make_pagination)
from sapl.crud.tests.stub_app.models import Continent, Country
from sapl.crud.tests.stub_app.views import CountryCrud

2
sapl/crud/tests/test_masterdetail.py

@ -1,5 +1,5 @@
import pytest
from django.core.urlresolvers import reverse
from django.urls import reverse
@pytest.mark.parametrize('path_name', [

2
sapl/materia/forms.py

@ -10,7 +10,7 @@ from django import forms
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.core.files.base import File
from django.core.urlresolvers import reverse
from django.urls import reverse
from django.db import models, transaction
from django.db.models import F, Max, Q
from django.forms import ModelChoiceField, ModelForm, widgets

19
sapl/materia/migrations/0070_auto_20200521_1534.py

@ -0,0 +1,19 @@
# Generated by Django 2.2 on 2020-05-21 18:34
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('materia', '0069_auto_20200518_1519'),
]
operations = [
migrations.AlterField(
model_name='proposicao',
name='content_type',
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.PROTECT, to='contenttypes.ContentType', verbose_name='Tipo de Material Gerado'),
),
]

23
sapl/materia/migrations/0071_auto_20200609_1503.py

@ -0,0 +1,23 @@
# Generated by Django 2.2 on 2020-06-09 18:03
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('materia', '0070_auto_20200521_1534'),
]
operations = [
migrations.AlterField(
model_name='materialegislativa',
name='complementar',
field=models.BooleanField(blank=True, default=False, null=True, verbose_name='É Complementar?'),
),
migrations.AlterField(
model_name='materialegislativa',
name='polemica',
field=models.BooleanField(blank=True, default=False, null=True, verbose_name='Matéria Polêmica?'),
),
]

174
sapl/materia/models.py

@ -234,12 +234,20 @@ class MateriaLegislativa(models.Model):
verbose_name=_('Em Tramitação?'),
default=False,
choices=YES_NO_CHOICES)
polemica = models.NullBooleanField(
blank=True, verbose_name=_('Matéria Polêmica?'))
polemica = models.BooleanField(
null=True,
blank=True,
default=False,
verbose_name=_('Matéria Polêmica?')
)
objeto = models.CharField(
max_length=150, blank=True, verbose_name=_('Objeto'))
complementar = models.NullBooleanField(
blank=True, verbose_name=_('É Complementar?'))
complementar = models.BooleanField(
null=True,
blank=True,
default=False,
verbose_name=_('É Complementar?')
)
ementa = models.TextField(verbose_name=_('Ementa'))
indexacao = models.TextField(
blank=True, verbose_name=_('Indexação'))
@ -754,42 +762,73 @@ class Parecer(models.Model):
@reversion.register()
class Proposicao(models.Model):
autor = models.ForeignKey(Autor,
null=True,
blank=True,
on_delete=models.PROTECT)
tipo = models.ForeignKey(TipoProposicao, on_delete=models.PROTECT,
blank=False,
null=True,
verbose_name=_('Tipo'))
autor = models.ForeignKey(
Autor,
null=True,
blank=True,
on_delete=models.PROTECT
)
tipo = models.ForeignKey(
TipoProposicao,
on_delete=models.PROTECT,
blank=False,
null=True,
verbose_name=_('Tipo')
)
# XXX data_envio was not null, but actual data said otherwise!!!
data_envio = models.DateTimeField(
blank=False, null=True, verbose_name=_('Data de Envio'))
blank=False,
null=True,
verbose_name=_('Data de Envio')
)
data_recebimento = models.DateTimeField(
blank=True, null=True, verbose_name=_('Data de Recebimento'))
blank=True,
null=True,
verbose_name=_('Data de Recebimento')
)
data_devolucao = models.DateTimeField(
blank=True, null=True, verbose_name=_('Data de Devolução'))
blank=True,
null=True,
verbose_name=_('Data de Devolução')
)
descricao = models.TextField(verbose_name=_('Ementa'))
justificativa_devolucao = models.CharField(
max_length=200,
blank=True,
verbose_name=_('Justificativa da Devolução'))
verbose_name=_('Justificativa da Devolução')
)
ano = models.PositiveSmallIntegerField(verbose_name=_('Ano'),
default=None, blank=True, null=True,
choices=RANGE_ANOS)
ano = models.PositiveSmallIntegerField(
verbose_name=_('Ano'),
default=None,
blank=True,
null=True,
choices=RANGE_ANOS
)
numero_proposicao = models.PositiveIntegerField(
blank=True, null=True, verbose_name=_('Número'))
blank=True,
null=True,
verbose_name=_('Número')
)
numero_materia_futuro = models.PositiveIntegerField(
blank=True, null=True, verbose_name=_('Número Matéria'))
blank=True,
null=True,
verbose_name=_('Número Matéria')
)
hash_code = models.CharField(verbose_name=_('Código do Documento'),
max_length=200,
blank=True)
hash_code = models.CharField(
verbose_name=_('Código do Documento'),
max_length=200,
blank=True
)
"""
FIXME Campo não é necessário na modelagem e implementação atual para o
@ -807,12 +846,15 @@ class Proposicao(models.Model):
sua proposição ou resolva excluir.
"""
# ind_enviado and ind_devolvido collapsed as char field (status)
status = models.CharField(blank=True,
max_length=1,
choices=(('E', 'Enviada'),
('R', 'Recebida'),
('I', 'Incorporada')),
verbose_name=_('Status Proposição'))
status = models.CharField(
blank=True,
max_length=1,
choices=(('E', 'Enviada'),
('R', 'Recebida'),
('I', 'Incorporada')),
verbose_name=_('Status Proposição')
)
texto_original = models.FileField(
max_length=300,
upload_to=materia_upload_path,
@ -820,37 +862,70 @@ class Proposicao(models.Model):
null=True,
verbose_name=_('Texto Original'),
storage=OverwriteStorage(),
validators=[restringe_tipos_de_arquivo_txt])
validators=[restringe_tipos_de_arquivo_txt]
)
texto_articulado = GenericRelation(
TextoArticulado, related_query_name='texto_articulado')
TextoArticulado,
related_query_name='texto_articulado'
)
materia_de_vinculo = models.ForeignKey(
MateriaLegislativa, blank=True, null=True,
MateriaLegislativa,
blank=True,
null=True,
on_delete=models.CASCADE,
verbose_name=_('Matéria anexadora'),
related_name=_('proposicao_set'))
related_name=_('proposicao_set')
)
content_type = models.ForeignKey(
ContentType, default=None, blank=True, null=True,
verbose_name=_('Tipo de Material Gerado'))
ContentType,
default=None,
blank=True,
null=True,
verbose_name=_('Tipo de Material Gerado'),
on_delete=models.PROTECT
)
object_id = models.PositiveIntegerField(
blank=True, null=True, default=None)
blank=True,
null=True,
default=None
)
conteudo_gerado_related = SaplGenericForeignKey(
'content_type', 'object_id', verbose_name=_('Conteúdo Gerado'))
'content_type',
'object_id',
verbose_name=_('Conteúdo Gerado')
)
observacao = models.TextField(
blank=True, verbose_name=_('Observação'))
cancelado = models.BooleanField(verbose_name=_('Cancelada ?'),
choices=YES_NO_CHOICES,
default=False)
blank=True,
verbose_name=_('Observação')
)
cancelado = models.BooleanField(
verbose_name=_('Cancelada ?'),
choices=YES_NO_CHOICES,
default=False
)
"""
Ao ser recebida, irá gerar uma nova matéria ou um documento acessorio de uma existente
"""# Ao ser recebida, irá gerar uma nova matéria ou um documento acessorio
# de uma já existente
materia_gerada = models.ForeignKey(
MateriaLegislativa, blank=True, null=True,
related_name=_('materia_gerada'))
MateriaLegislativa,
blank=True,
null=True,
related_name=_('materia_gerada')
)
documento_gerado = models.ForeignKey(
DocumentoAcessorio, blank=True, null=True)"""
DocumentoAcessorio,
blank=True,
null=True
)
"""
user = models.ForeignKey(
get_settings_auth_user_model(),
@ -859,15 +934,18 @@ class Proposicao(models.Model):
null=True,
blank=True
)
ip = models.CharField(
verbose_name=_('IP'),
max_length=60,
blank=True,
default=''
)
ultima_edicao = models.DateTimeField(
verbose_name=_('Data e Hora da Edição'),
blank=True, null=True
blank=True,
null=True
)
@property

2
sapl/materia/tests/test_materia.py

@ -2,7 +2,7 @@ from datetime import date
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from django.core.files.uploadedfile import SimpleUploadedFile
from django.core.urlresolvers import reverse
from django.urls import reverse
from django.db.models import Max
from model_bakery import baker
import pytest

8
sapl/materia/tests/test_materia_form.py

@ -82,8 +82,8 @@ def test_valida_campos_obrigatorios_materialegislativa_form():
assert errors['numero'] == [_('Este campo é obrigatório.')]
assert errors['ementa'] == [_('Este campo é obrigatório.')]
assert errors['regime_tramitacao'] == [_('Este campo é obrigatório.')]
assert len(errors) == 6
assert errors['em_tramitacao'] == [_('Este campo é obrigatório.')]
assert len(errors) == 7
@pytest.mark.django_db(transaction=False)
@ -104,8 +104,8 @@ def test_valida_campos_obrigatorios_orgao_form():
assert errors['nome'] == [_('Este campo é obrigatório.')]
assert errors['sigla'] == [_('Este campo é obrigatório.')]
assert len(errors) == 2
assert errors['unidade_deliberativa'] == [_('Este campo é obrigatório.')]
assert len(errors) == 3
@pytest.mark.django_db(transaction=False)

2
sapl/materia/tests/test_materia_urls.py

@ -1,5 +1,5 @@
import pytest
from django.core.urlresolvers import reverse
from django.urls import reverse
@pytest.mark.parametrize("test_input,kwargs,expected", [

2
sapl/materia/views.py

@ -21,7 +21,7 @@ from django.contrib import messages
from django.contrib.auth.decorators import permission_required
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned, ValidationError
from django.core.urlresolvers import reverse
from django.urls import reverse
from django.db.models import Max, Q
from django.http import HttpResponse, JsonResponse
from django.http.response import Http404, HttpResponseRedirect

18
sapl/norma/migrations/0034_auto_20200609_1453.py

@ -0,0 +1,18 @@
# Generated by Django 2.2 on 2020-06-09 17:53
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('norma', '0033_auto_20200416_1538'),
]
operations = [
migrations.AlterField(
model_name='normajuridica',
name='complemento',
field=models.BooleanField(blank=True, choices=[(True, 'Sim'), (False, 'Não')], default=None, null=True, verbose_name='Complementar ?'),
),
]

18
sapl/norma/migrations/0035_auto_20200609_1501.py

@ -0,0 +1,18 @@
# Generated by Django 2.2 on 2020-06-09 18:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('norma', '0034_auto_20200609_1453'),
]
operations = [
migrations.AlterField(
model_name='normajuridica',
name='complemento',
field=models.BooleanField(blank=True, choices=[(True, 'Sim'), (False, 'Não')], default=False, null=True, verbose_name='Complementar ?'),
),
]

10
sapl/norma/models.py

@ -118,9 +118,13 @@ class NormaJuridica(models.Model):
blank=True, verbose_name=_('Indexação'))
observacao = models.TextField(
blank=True, verbose_name=_('Observação'))
complemento = models.NullBooleanField(
blank=True, verbose_name=_('Complementar ?'),
choices=YES_NO_CHOICES)
complemento = models.BooleanField(
null=True,
blank=True,
default=False,
verbose_name=_('Complementar ?'),
choices=YES_NO_CHOICES
)
# XXX was a CharField (attention on migrate)
assuntos = models.ManyToManyField(
AssuntoNorma, blank=True,

2
sapl/norma/tests/test_norma.py

@ -1,4 +1,4 @@
from django.core.urlresolvers import reverse
from django.urls import reverse
from django.utils.translation import ugettext_lazy as _
from model_bakery import baker
import pytest

2
sapl/norma/views.py

@ -4,7 +4,7 @@ import re
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.core.exceptions import ObjectDoesNotExist
from django.core.urlresolvers import reverse
from django.urls import reverse
from django.http import HttpResponse, JsonResponse
from django.template import RequestContext, loader
from django.utils import timezone

2
sapl/painel/views.py

@ -5,7 +5,7 @@ import logging
from django.contrib import messages
from django.contrib.auth.decorators import user_passes_test
from django.core.exceptions import ObjectDoesNotExist
from django.core.urlresolvers import reverse
from django.urls import reverse
from django.db.models import Q
from django.http import HttpResponse, JsonResponse
from django.http.response import Http404, HttpResponseRedirect

13
sapl/parlamentares/tests/test_parlamentares.py

@ -1,5 +1,5 @@
import pytest
from django.core.urlresolvers import reverse
from django.urls import reverse
from django.utils.translation import ugettext_lazy as _
from model_bakery import baker
@ -33,7 +33,8 @@ def test_incluir_parlamentar_errors(admin_client):
url = reverse('sapl.parlamentares:parlamentar_create')
response = admin_client.post(url)
erros_esperados = {campo: ['Este campo é obrigatório.']
for campo in ['nome_parlamentar',
for campo in ['ativo',
'nome_parlamentar',
'nome_completo',
'sexo',
]}
@ -113,7 +114,7 @@ def test_mandato_submit(admin_client):
baker.make(Parlamentar, pk=14)
baker.make(Legislatura, pk=5)
admin_client.post(reverse('sapl.parlamentares:mandato_create',
response = admin_client.post(reverse('sapl.parlamentares:mandato_create',
kwargs={'pk': 14}),
{
'parlamentar': 14, # hidden field
@ -124,10 +125,10 @@ def test_mandato_submit(admin_client):
Legislatura.objects.get(id=5).data_fim,
'data_expedicao_diploma': '2016-03-22',
'observacao': 'Observação do mandato',
'salvar': 'salvar'
'salvar': 'salvar',
'titular': True
},
follow=True)
mandato = Mandato.objects.first()
assert str(_('Observação do mandato')) == str(_(mandato.observacao))
@ -192,6 +193,7 @@ def test_mandato_form_datas_invalidas():
form = MandatoForm(data={
'parlamentar': str(parlamentar.pk),
'legislatura': str(legislatura.pk),
'titular': True,
'data_expedicao_diploma': '2016-11-01',
'data_inicio_mandato': '2016-12-12',
'data_fim_mandato': '2019-10-09'
@ -204,6 +206,7 @@ def test_mandato_form_datas_invalidas():
form = MandatoForm(data={
'parlamentar': str(parlamentar.pk),
'legislatura': str(legislatura.pk),
'titular': True,
'data_expedicao_diploma': '2016-11-01',
'data_inicio_mandato': '2017-02-02',
'data_fim_mandato': '2022-01-01'

2
sapl/parlamentares/views.py

@ -6,7 +6,7 @@ from django.contrib import messages
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist
from django.core.urlresolvers import reverse, reverse_lazy
from django.urls import reverse, reverse_lazy
from django.db.models import F, Q
from django.db.models.aggregates import Count
from django.http import JsonResponse

18
sapl/protocoloadm/migrations/0033_auto_20200521_1534.py

@ -0,0 +1,18 @@
# Generated by Django 2.2 on 2020-05-21 18:34
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('protocoloadm', '0032_auto_20200416_1538'),
]
operations = [
migrations.AlterField(
model_name='documentoadministrativo',
name='restrito',
field=models.BooleanField(blank=True, default=False, verbose_name='Acesso Restrito'),
),
]

2
sapl/protocoloadm/migrations/0033_auto_20200708_1312.py → sapl/protocoloadm/migrations/0034_auto_20200708_1312.py

@ -9,7 +9,7 @@ import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('protocoloadm', '0032_auto_20200416_1538'),
('protocoloadm', '0033_auto_20200521_1534'),
]
operations = [

8
sapl/protocoloadm/tests/test_protocoloadm.py

@ -1,6 +1,6 @@
from datetime import date, timedelta
from django.core.urlresolvers import reverse
from django.urls import reverse
from django.utils import timezone
from django.utils.encoding import force_text
from django.utils.translation import ugettext_lazy as _
@ -380,8 +380,9 @@ def test_documento_administrativo_invalido():
assert errors['numero'] == [_('Este campo é obrigatório.')]
assert errors['data'] == [_('Este campo é obrigatório.')]
assert errors['restrito'] == [_('Este campo é obrigatório.')]
assert errors['tramitacao'] == [_('Este campo é obrigatório.')]
assert len(errors) == 6
assert len(errors) == 7
@pytest.mark.django_db(transaction=False)
@ -402,7 +403,8 @@ def test_documento_administrativo_protocolo_inexistente():
'data': '2017-10-10',
'numero_protocolo': '11',
'ano_protocolo': '2017',
'restrito': False
'restrito': False,
'tramitacao': False
})
assert not form.is_valid()

8
sapl/protocoloadm/views.py

@ -11,7 +11,7 @@ from django.contrib.auth.decorators import permission_required
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist
from django.core.urlresolvers import reverse
from django.urls import reverse
from django.db import transaction
from django.db.models import Max, Q
from django.http import Http404, HttpResponse, JsonResponse
@ -95,7 +95,7 @@ def recuperar_materia_protocolo(request):
def doc_texto_integral(request, pk):
can_see = True
if not request.user.is_authenticated():
if not request.user.is_authenticated:
app_config = AppConfig.objects.last()
if app_config and app_config.documentos_administrativos == 'R':
can_see = False
@ -414,7 +414,7 @@ class DocumentoAdministrativoCrud(Crud):
def get(self, *args, **kwargs):
pk = self.kwargs['pk']
documento = DocumentoAdministrativo.objects.get(id=pk)
if documento.restrito and self.request.user.is_anonymous():
if documento.restrito and self.request.user.is_anonymous:
return redirect('/')
return super(Crud.DetailView, self).get(args, kwargs)
@ -1024,7 +1024,7 @@ class PesquisarDocumentoAdministrativoView(DocumentoAdministrativoMixin,
# é usada essa verificação anônima para quando os documentos administrativos
# estão no modo ostensivo, mas podem existir documentos administrativos
# restritos
if request.user.is_anonymous():
if request.user.is_anonymous:
length = self.object_list.filter(restrito=False).count()
else:
length = self.object_list.count()

2
sapl/redireciona_urls/tests.py

@ -1,4 +1,4 @@
from django.core.urlresolvers import reverse
from django.urls import reverse
from django.test import TestCase
MovedPermanentlyHTTPStatusCode = 301

2
sapl/redireciona_urls/views.py

@ -1,6 +1,6 @@
import logging
from django.core.urlresolvers import NoReverseMatch, reverse
from django.urls import NoReverseMatch, reverse
from django.views.generic import RedirectView
from sapl.audiencia.apps import AppConfig as audienciaConfig

13
sapl/rules/apps.py

@ -8,7 +8,6 @@ from django.contrib.auth.management import _get_all_permissions
from django.core import exceptions
from django.db import models, router
from django.db.utils import DEFAULT_DB_ALIAS
from django.utils.translation import string_concat
from django.utils.translation import ugettext_lazy as _
import reversion
@ -55,13 +54,9 @@ def create_proxy_permissions(
opts = klass._meta
permissions = (
("list_" + opts.model_name,
string_concat(
_('Visualizaçao da lista de'), ' ',
opts.verbose_name_plural)),
"{} {}".format(_('Visualizaçao da lista de'), opts.verbose_name_plural)),
("detail_" + opts.model_name,
string_concat(
_('Visualização dos detalhes de'), ' ',
opts.verbose_name_plural)),
"{} {}".format(_('Visualização dos detalhes de'), opts.verbose_name_plural)),
)
opts.permissions = tuple(
set(list(permissions) + list(opts.permissions)))
@ -216,9 +211,7 @@ def get_rules():
def update_groups(self):
print('')
print(string_concat('\033[93m\033[1m',
_('Atualizando grupos do SAPL:'),
'\033[0m'))
print("\033[93m\033[1m{}\033[0m".format(_('Atualizando grupos do SAPL:')))
for rules_group in self.rules_patterns:
group_name = rules_group['group']
rules_list = rules_group['rules']

18
sapl/sessao/migrations/0052_auto_20200521_1534.py

@ -0,0 +1,18 @@
# Generated by Django 2.2 on 2020-05-21 18:34
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('sessao', '0051_auto_20200416_1538'),
]
operations = [
migrations.AlterField(
model_name='sessaoplenaria',
name='painel_aberto',
field=models.BooleanField(blank=True, default=False, verbose_name='Painel está aberto?'),
),
]

48
sapl/sessao/migrations/0053_auto_20200609_1501.py

@ -0,0 +1,48 @@
# Generated by Django 2.2 on 2020-06-09 18:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('sessao', '0052_auto_20200521_1534'),
]
operations = [
migrations.AlterField(
model_name='expedientemateria',
name='registro_aberto',
field=models.BooleanField(blank=True, choices=[(True, 'Sim'), (False, 'Não')], default=False, null=True, verbose_name='Registro de Votação Iniciado?'),
),
migrations.AlterField(
model_name='expedientemateria',
name='votacao_aberta',
field=models.BooleanField(blank=True, choices=[(True, 'Sim'), (False, 'Não')], default=False, null=True, verbose_name='Votação iniciada?'),
),
migrations.AlterField(
model_name='ordemdia',
name='registro_aberto',
field=models.BooleanField(blank=True, choices=[(True, 'Sim'), (False, 'Não')], default=False, null=True, verbose_name='Registro de Votação Iniciado?'),
),
migrations.AlterField(
model_name='ordemdia',
name='votacao_aberta',
field=models.BooleanField(blank=True, choices=[(True, 'Sim'), (False, 'Não')], default=False, null=True, verbose_name='Votação iniciada?'),
),
migrations.AlterField(
model_name='sessaoplenaria',
name='finalizada',
field=models.BooleanField(blank=True, choices=[(True, 'Sim'), (False, 'Não')], default=False, null=True, verbose_name='Sessão finalizada?'),
),
migrations.AlterField(
model_name='sessaoplenaria',
name='iniciada',
field=models.BooleanField(blank=True, choices=[(True, 'Sim'), (False, 'Não')], default=True, null=True, verbose_name='Sessão iniciada?'),
),
migrations.AlterField(
model_name='sessaoplenaria',
name='interativa',
field=models.BooleanField(blank=True, choices=[(True, 'Sim'), (False, 'Não')], default=False, null=True, verbose_name='Sessão interativa'),
),
]

46
sapl/sessao/models.py

@ -204,17 +204,27 @@ class SessaoPlenaria(models.Model):
storage=OverwriteStorage(),
upload_to=anexo_upload_path,
verbose_name=_('Anexo da Sessão'))
iniciada = models.NullBooleanField(blank=True,
choices=YES_NO_CHOICES,
verbose_name=_('Sessão iniciada?'),
default=True)
finalizada = models.NullBooleanField(blank=True,
choices=YES_NO_CHOICES,
verbose_name=_('Sessão finalizada?'),
default=False)
interativa = models.NullBooleanField(blank=True,
choices=YES_NO_CHOICES,
verbose_name=_('Sessão interativa'))
iniciada = models.BooleanField(
null=True,
blank=True,
default=True,
choices=YES_NO_CHOICES,
verbose_name=_('Sessão iniciada?')
)
finalizada = models.BooleanField(
null=True,
blank=True,
default=False,
choices=YES_NO_CHOICES,
verbose_name=_('Sessão finalizada?')
)
interativa = models.BooleanField(
null=True,
blank=True,
default=False,
choices=YES_NO_CHOICES,
verbose_name=_('Sessão interativa')
)
tema_solene = models.TextField(
blank=True, max_length=500, verbose_name=_('Tema da Sessão Solene'))
@ -326,14 +336,20 @@ class AbstractOrdemDia(models.Model):
resultado = models.TextField(blank=True, verbose_name=_('Resultado'))
tipo_votacao = models.PositiveIntegerField(
verbose_name=_('Tipo de votação'), choices=TIPO_VOTACAO_CHOICES, default=1)
votacao_aberta = models.NullBooleanField(
votacao_aberta = models.BooleanField(
null=True,
blank=True,
default=False,
choices=YES_NO_CHOICES,
verbose_name=_('Votação iniciada?'))
registro_aberto = models.NullBooleanField(
verbose_name=_('Votação iniciada?')
)
registro_aberto = models.BooleanField(
null=True,
blank=True,
default=False,
choices=YES_NO_CHOICES,
verbose_name=_('Registro de Votação Iniciado?'))
verbose_name=_('Registro de Votação Iniciado?')
)
class Meta:
abstract = True

2
sapl/sessao/tests/test_sessao_view.py

@ -1,5 +1,5 @@
import pytest
from django.core.urlresolvers import reverse
from django.urls import reverse
from django.utils.translation import ugettext_lazy as _
from model_bakery import baker

4
sapl/sessao/views.py

@ -7,7 +7,7 @@ from django.contrib import messages
from django.contrib.auth.decorators import permission_required
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.core.exceptions import ObjectDoesNotExist
from django.core.urlresolvers import reverse
from django.urls import reverse
from django.db.models import Max, Q
from django.http import JsonResponse
from django.http.response import Http404, HttpResponseRedirect
@ -1364,7 +1364,7 @@ class PainelView(PermissionRequiredForAppCrudMixin, TemplateView):
logger = logging.getLogger(__name__)
def get(self, request, *args, **kwargs):
if request.user.is_anonymous():
if request.user.is_anonymous:
self.template_name = 'painel/index.html'
request.session['discurso'] = 'stop'

5
sapl/settings.py

@ -136,10 +136,10 @@ MIDDLEWARE = [
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware',
]
if DEBUG:
INSTALLED_APPS += ('debug_toolbar', )
@ -311,6 +311,8 @@ CRISPY_ALLOWED_TEMPLATE_PACKS = 'bootstrap4'
CRISPY_FAIL_SILENTLY = not DEBUG
FLOPPY_FORMS_USE_GIS = False
FORM_RENDERER = 'django.forms.renderers.DjangoTemplates'
# suprime texto de ajuda default do django-filter
FILTERS_HELP_TEXT_FILTER = False
@ -367,5 +369,6 @@ def remove_warnings():
message='Unable to import floppyforms.gis'
)
LOGOUT_REDIRECT_URL = '/login'
remove_warnings()

9
sapl/test_urls.py

@ -6,7 +6,6 @@ from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.db import transaction
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import string_concat
from sapl.crud.base import PermissionRequiredForAppCrudMixin
from sapl.rules.apps import AppConfig, update_groups
@ -30,13 +29,9 @@ def create_perms_post_migrate(sapl_app_config):
opts = klass._meta
permissions = (
("list_" + opts.model_name,
string_concat(
_('Visualizaçao da lista de'), ' ',
opts.verbose_name_plural)),
"{} {}".format(_('Visualização da lista de'), opts.verbose_name_plural)),
("detail_" + opts.model_name,
string_concat(
_('Visualização dos detalhes de'), ' ',
opts.verbose_name_plural)),
"{} {}".format(_('Visualização dos detalhes de'), opts.verbose_name_plural)),
)
opts.permissions = tuple(
set(list(permissions) + list(opts.permissions)))

2
sapl/urls.py

@ -39,7 +39,7 @@ urlpatterns = [
url(r'^$', TemplateView.as_view(template_name='index.html'),
name='sapl_index'),
url(r'^message$', TemplateView.as_view(template_name='base.html')),
url(r'^admin/', include(admin.site.urls)),
url(r'^admin/', admin.site.urls),
url(r'', include(sapl.comissoes.urls)),
url(r'', include(sapl.sessao.urls)),

4
scripts/lista_urls.py

@ -9,7 +9,7 @@ if __name__ == '__main__':
if True:
from sapl.urls import urlpatterns
from django.core.urlresolvers import RegexURLResolver
from django.urls.resolvers import URLResolver
class ListaUrls():
@ -17,7 +17,7 @@ class ListaUrls():
def lista_urls(self, _urls):
urls = []
for item in _urls:
if isinstance(item, RegexURLResolver) and \
if isinstance(item, URLResolver) and \
item.app_name.startswith('sapl'):
for key, value in item.reverse_dict.items():

Loading…
Cancel
Save