From d08fb9e963e14f54a7bddd8b653cfd17f451a5d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Cantu=C3=A1ria?= Date: Wed, 19 Aug 2020 10:36:19 -0300 Subject: [PATCH] =?UTF-8?q?Atualiza=20a=20vers=C3=A3o=20do=20Django=20para?= =?UTF-8?q?=202.2=20(#3165)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: UlyssesBML Co-authored-by: eribeiro Co-authored-by: UlyssesBML Co-authored-by: eribeiro --- .gitignore | 3 + Dockerfile.dev | 1 + requirements/dev-requirements.txt | 2 +- requirements/requirements.txt | 11 +- sapl/api/permissions.py | 2 +- sapl/api/views.py | 14 +- sapl/audiencia/tests/test_audiencia.py | 2 +- sapl/audiencia/views.py | 2 +- sapl/base/email_utils.py | 2 +- sapl/base/forms.py | 17 +- .../migrations/0040_auto_20200521_1534.py | 24 ++ .../migrations/0041_merge_20200818_1256.py | 14 + sapl/base/models.py | 20 +- sapl/base/templatetags/menus.py | 2 +- sapl/base/tests/test_view_base.py | 2 +- sapl/base/urls.py | 87 ++--- sapl/base/views.py | 102 +++--- sapl/comissoes/tests/test_comissoes.py | 5 +- sapl/comissoes/views.py | 2 +- sapl/compilacao/apps.py | 15 +- .../migrations/0014_auto_20200521_1534.py | 105 ++++++ .../migrations/0015_auto_20200609_1501.py | 18 + sapl/compilacao/models.py | 336 ++++++++++++++---- sapl/compilacao/views.py | 48 ++- sapl/crispy_layout_mixin.py | 9 +- sapl/crud/base.py | 7 +- sapl/crud/tests/test_base.py | 2 - sapl/crud/tests/test_masterdetail.py | 2 +- sapl/materia/forms.py | 2 +- .../migrations/0070_auto_20200521_1534.py | 19 + .../migrations/0071_auto_20200609_1503.py | 23 ++ sapl/materia/models.py | 174 ++++++--- sapl/materia/tests/test_materia.py | 2 +- sapl/materia/tests/test_materia_form.py | 8 +- sapl/materia/tests/test_materia_urls.py | 2 +- sapl/materia/views.py | 2 +- .../migrations/0034_auto_20200609_1453.py | 18 + .../migrations/0035_auto_20200609_1501.py | 18 + sapl/norma/models.py | 10 +- sapl/norma/tests/test_norma.py | 2 +- sapl/norma/views.py | 2 +- sapl/painel/views.py | 2 +- .../parlamentares/tests/test_parlamentares.py | 13 +- sapl/parlamentares/views.py | 2 +- .../migrations/0033_auto_20200521_1534.py | 18 + ...708_1312.py => 0034_auto_20200708_1312.py} | 2 +- sapl/protocoloadm/tests/test_protocoloadm.py | 8 +- sapl/protocoloadm/views.py | 8 +- sapl/redireciona_urls/tests.py | 2 +- sapl/redireciona_urls/views.py | 2 +- sapl/rules/apps.py | 13 +- .../migrations/0052_auto_20200521_1534.py | 18 + .../migrations/0053_auto_20200609_1501.py | 48 +++ sapl/sessao/models.py | 46 ++- sapl/sessao/tests/test_sessao_view.py | 2 +- sapl/sessao/views.py | 4 +- sapl/settings.py | 5 +- sapl/test_urls.py | 9 +- sapl/urls.py | 2 +- scripts/lista_urls.py | 4 +- 60 files changed, 960 insertions(+), 386 deletions(-) create mode 100644 sapl/base/migrations/0040_auto_20200521_1534.py create mode 100644 sapl/base/migrations/0041_merge_20200818_1256.py create mode 100644 sapl/compilacao/migrations/0014_auto_20200521_1534.py create mode 100644 sapl/compilacao/migrations/0015_auto_20200609_1501.py create mode 100644 sapl/materia/migrations/0070_auto_20200521_1534.py create mode 100644 sapl/materia/migrations/0071_auto_20200609_1503.py create mode 100644 sapl/norma/migrations/0034_auto_20200609_1453.py create mode 100644 sapl/norma/migrations/0035_auto_20200609_1501.py create mode 100644 sapl/protocoloadm/migrations/0033_auto_20200521_1534.py rename sapl/protocoloadm/migrations/{0033_auto_20200708_1312.py => 0034_auto_20200708_1312.py} (90%) create mode 100644 sapl/sessao/migrations/0052_auto_20200521_1534.py create mode 100644 sapl/sessao/migrations/0053_auto_20200609_1501.py diff --git a/.gitignore b/.gitignore index fc095f5ef..712f1ae1b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,9 @@ __pycache__/ *.py[cod] *$py.class +.pytest_cache/ +.DS_Store + # C extensions *.so diff --git a/Dockerfile.dev b/Dockerfile.dev index fc02a7870..43b636abc 100644 --- a/Dockerfile.dev +++ b/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 diff --git a/requirements/dev-requirements.txt b/requirements/dev-requirements.txt index dba8788b2..ee3dd5577 100644 --- a/requirements/dev-requirements.txt +++ b/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 diff --git a/requirements/requirements.txt b/requirements/requirements.txt index b45b64d9e..27b404c0a 100644 --- a/requirements/requirements.txt +++ b/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 diff --git a/sapl/api/permissions.py b/sapl/api/permissions.py index b7df6c63a..ae39f8ca9 100644 --- a/sapl/api/permissions.py +++ b/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) ) diff --git a/sapl/api/views.py b/sapl/api/views.py index 8cbe13e8c..eb58616dc 100644 --- a/sapl/api/views.py +++ b/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) diff --git a/sapl/audiencia/tests/test_audiencia.py b/sapl/audiencia/tests/test_audiencia.py index d217bf460..930d1288b 100644 --- a/sapl/audiencia/tests/test_audiencia.py +++ b/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) diff --git a/sapl/audiencia/views.py b/sapl/audiencia/views.py index 2ef1a334a..281075df1 100755 --- a/sapl/audiencia/views.py +++ b/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 diff --git a/sapl/base/email_utils.py b/sapl/base/email_utils.py index 7c23dd2da..48b0e3adb 100644 --- a/sapl/base/email_utils.py +++ b/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 diff --git a/sapl/base/forms.py b/sapl/base/forms.py index 88d4081a4..399786dd1 100644 --- a/sapl/base/forms.py +++ b/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( diff --git a/sapl/base/migrations/0040_auto_20200521_1534.py b/sapl/base/migrations/0040_auto_20200521_1534.py new file mode 100644 index 000000000..ae19b9f01 --- /dev/null +++ b/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'), + ), + ] diff --git a/sapl/base/migrations/0041_merge_20200818_1256.py b/sapl/base/migrations/0041_merge_20200818_1256.py new file mode 100644 index 000000000..94ad51f86 --- /dev/null +++ b/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 = [ + ] diff --git a/sapl/base/models.py b/sapl/base/models.py index 4790e5d0f..b619a1e97 100644 --- a/sapl/base/models.py +++ b/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') diff --git a/sapl/base/templatetags/menus.py b/sapl/base/templatetags/menus.py index 5b526002a..5c5d61193 100644 --- a/sapl/base/templatetags/menus.py +++ b/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 diff --git a/sapl/base/tests/test_view_base.py b/sapl/base/tests/test_view_base.py index 1020da2f4..3a38d379c 100644 --- a/sapl/base/tests/test_view_base.py +++ b/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 diff --git a/sapl/base/urls.py b/sapl/base/urls.py index 2630f5114..0bed621c9 100644 --- a/sapl/base/urls.py +++ b/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[0-9A-Za-z_\-]+)/(?P.+)$', - 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[0-9A-Za-z_\-]+)/(?P.+)/$', 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'), diff --git a/sapl/base/views.py b/sapl/base/views.py index b2aa7a0e8..a9ba56802 100644 --- a/sapl/base/views.py +++ b/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') ) @@ -2104,7 +2116,7 @@ class AppConfigCrud(CrudAux): class UpdateView(CrudAux.UpdateView): - + form_class = ConfiguracoesAppForm def form_valid(self, form): @@ -2113,7 +2125,7 @@ class AppConfigCrud(CrudAux): self.object = form.save() numeracao_nova = self.object.inicio_numeracao_protocolo - + if numeracao_nova != numeracao_antiga: if numeracao == 'A': numero_max = Protocolo.objects.filter( @@ -2132,7 +2144,7 @@ class AppConfigCrud(CrudAux): data_fim_utc = from_date_to_datetime_utc(data_fim) numero_max = Protocolo.objects.filter( - Q(data__isnull=False, data__gte=data_inicio, data__lte=data_fim) | + Q(data__isnull=False, data__gte=data_inicio, data__lte=data_fim) | Q( timestamp__isnull=False, timestamp__gte=data_inicio_utc, timestamp__lte=data_fim_utc diff --git a/sapl/comissoes/tests/test_comissoes.py b/sapl/comissoes/tests/test_comissoes.py index 705ff8a57..72b839a66 100644 --- a/sapl/comissoes/tests/test_comissoes.py +++ b/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,12 +88,13 @@ 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'}, follow=True) assert response.status_code == 200 - + comissao = Comissao.objects.first() assert comissao.nome == 'Comissão Teste' assert comissao.tipo == tipo diff --git a/sapl/comissoes/views.py b/sapl/comissoes/views.py index 019cca739..48a19ddf6 100644 --- a/sapl/comissoes/views.py +++ b/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 diff --git a/sapl/compilacao/apps.py b/sapl/compilacao/apps.py index fae56b631..0e8105a64 100644 --- a/sapl/compilacao/apps.py +++ b/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() diff --git a/sapl/compilacao/migrations/0014_auto_20200521_1534.py b/sapl/compilacao/migrations/0014_auto_20200521_1534.py new file mode 100644 index 000000000..97d2dd3f9 --- /dev/null +++ b/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'), + ), + ] diff --git a/sapl/compilacao/migrations/0015_auto_20200609_1501.py b/sapl/compilacao/migrations/0015_auto_20200609_1501.py new file mode 100644 index 000000000..dd1691839 --- /dev/null +++ b/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'), + ), + ] diff --git a/sapl/compilacao/models.py b/sapl/compilacao/models.py index 7161df37a..a20053af8 100644 --- a/sapl/compilacao/models.py +++ b/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')) diff --git a/sapl/compilacao/views.py b/sapl/compilacao/views.py index 9f0ed2b46..0234d3c52 100644 --- a/sapl/compilacao/views.py +++ b/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': '↷ ', - 'action': 'json_add_next', - 'itens': []}, - {'tipo_insert': force_text(string_concat( - _('Inserir em'), - ' ', - base.tipo_dispositivo.nome)), - 'icone': '⇲ ', - 'action': 'json_add_in', - 'itens': []}, - {'tipo_insert': force_text(_('Inserir Antes')), - 'icone': '↶ ', - 'action': 'json_add_prior', - 'itens': []}] + result = [ + { + 'tipo_insert': force_text("{} {}".format(_('Inserir Após'), base.tipo_dispositivo.nome)), + 'icone': '↷ ', + 'action': 'json_add_next', + 'itens': []}, + { + 'tipo_insert': force_text("{} {}".format(_('Inserir em'), base.tipo_dispositivo.nome)), + 'icone': '⇲ ', + 'action': 'json_add_in', + 'itens': [] + }, + { + 'tipo_insert': force_text(_('Inserir Antes')), + 'icone': '↶ ', + 'action': 'json_add_prior', + 'itens': [] + } + ] perfil_pk = request.session['perfil_estrutural'] diff --git a/sapl/crispy_layout_mixin.py b/sapl/crispy_layout_mixin.py index 2574a4a46..2f212740a 100644 --- a/sapl/crispy_layout_mixin.py +++ b/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) diff --git a/sapl/crud/base.py b/sapl/crud/base.py index 4a9ab196c..e77e341bb 100644 --- a/sapl/crud/base.py +++ b/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: diff --git a/sapl/crud/tests/test_base.py b/sapl/crud/tests/test_base.py index 89e485fae..bd5e0d95c 100644 --- a/sapl/crud/tests/test_base.py +++ b/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 diff --git a/sapl/crud/tests/test_masterdetail.py b/sapl/crud/tests/test_masterdetail.py index b9d0d8ade..781faea23 100644 --- a/sapl/crud/tests/test_masterdetail.py +++ b/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', [ diff --git a/sapl/materia/forms.py b/sapl/materia/forms.py index 182bb6733..f6eeb5542 100644 --- a/sapl/materia/forms.py +++ b/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 diff --git a/sapl/materia/migrations/0070_auto_20200521_1534.py b/sapl/materia/migrations/0070_auto_20200521_1534.py new file mode 100644 index 000000000..cf5ef1839 --- /dev/null +++ b/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'), + ), + ] diff --git a/sapl/materia/migrations/0071_auto_20200609_1503.py b/sapl/materia/migrations/0071_auto_20200609_1503.py new file mode 100644 index 000000000..9adec696f --- /dev/null +++ b/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?'), + ), + ] diff --git a/sapl/materia/models.py b/sapl/materia/models.py index fe0c45c39..ba7626a24 100644 --- a/sapl/materia/models.py +++ b/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') + ) - """# Ao ser recebida, irá gerar uma nova matéria ou um documento acessorio - # de uma já existente + 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 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 diff --git a/sapl/materia/tests/test_materia.py b/sapl/materia/tests/test_materia.py index e9c7ae2db..b8411c7c5 100644 --- a/sapl/materia/tests/test_materia.py +++ b/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 diff --git a/sapl/materia/tests/test_materia_form.py b/sapl/materia/tests/test_materia_form.py index 6e1d7b3ba..435d9dde6 100644 --- a/sapl/materia/tests/test_materia_form.py +++ b/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) diff --git a/sapl/materia/tests/test_materia_urls.py b/sapl/materia/tests/test_materia_urls.py index a2f0fa667..84a421e1b 100644 --- a/sapl/materia/tests/test_materia_urls.py +++ b/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", [ diff --git a/sapl/materia/views.py b/sapl/materia/views.py index dedab0ed0..60c9c2e91 100644 --- a/sapl/materia/views.py +++ b/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 diff --git a/sapl/norma/migrations/0034_auto_20200609_1453.py b/sapl/norma/migrations/0034_auto_20200609_1453.py new file mode 100644 index 000000000..70be6325d --- /dev/null +++ b/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 ?'), + ), + ] diff --git a/sapl/norma/migrations/0035_auto_20200609_1501.py b/sapl/norma/migrations/0035_auto_20200609_1501.py new file mode 100644 index 000000000..401932e08 --- /dev/null +++ b/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 ?'), + ), + ] diff --git a/sapl/norma/models.py b/sapl/norma/models.py index ca0846eca..099ae6ade 100644 --- a/sapl/norma/models.py +++ b/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, diff --git a/sapl/norma/tests/test_norma.py b/sapl/norma/tests/test_norma.py index efbab6531..52f810b91 100644 --- a/sapl/norma/tests/test_norma.py +++ b/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 diff --git a/sapl/norma/views.py b/sapl/norma/views.py index 21975f77a..fa7c0bbde 100644 --- a/sapl/norma/views.py +++ b/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 diff --git a/sapl/painel/views.py b/sapl/painel/views.py index fcdd2d8cf..bf7d57116 100644 --- a/sapl/painel/views.py +++ b/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 diff --git a/sapl/parlamentares/tests/test_parlamentares.py b/sapl/parlamentares/tests/test_parlamentares.py index 3cb2b7d90..4e69cd375 100644 --- a/sapl/parlamentares/tests/test_parlamentares.py +++ b/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' diff --git a/sapl/parlamentares/views.py b/sapl/parlamentares/views.py index 50f042dec..7ea6a9dc0 100644 --- a/sapl/parlamentares/views.py +++ b/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 diff --git a/sapl/protocoloadm/migrations/0033_auto_20200521_1534.py b/sapl/protocoloadm/migrations/0033_auto_20200521_1534.py new file mode 100644 index 000000000..1d3ed894d --- /dev/null +++ b/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'), + ), + ] diff --git a/sapl/protocoloadm/migrations/0033_auto_20200708_1312.py b/sapl/protocoloadm/migrations/0034_auto_20200708_1312.py similarity index 90% rename from sapl/protocoloadm/migrations/0033_auto_20200708_1312.py rename to sapl/protocoloadm/migrations/0034_auto_20200708_1312.py index 1edf18073..8151e3d7f 100644 --- a/sapl/protocoloadm/migrations/0033_auto_20200708_1312.py +++ b/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 = [ diff --git a/sapl/protocoloadm/tests/test_protocoloadm.py b/sapl/protocoloadm/tests/test_protocoloadm.py index 0857a3d47..bfc5d769e 100644 --- a/sapl/protocoloadm/tests/test_protocoloadm.py +++ b/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() diff --git a/sapl/protocoloadm/views.py b/sapl/protocoloadm/views.py index 026b5528f..ed2f0d78f 100755 --- a/sapl/protocoloadm/views.py +++ b/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() diff --git a/sapl/redireciona_urls/tests.py b/sapl/redireciona_urls/tests.py index d8d713a2e..d641afc6c 100644 --- a/sapl/redireciona_urls/tests.py +++ b/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 diff --git a/sapl/redireciona_urls/views.py b/sapl/redireciona_urls/views.py index ee0212661..ad3cae93c 100644 --- a/sapl/redireciona_urls/views.py +++ b/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 diff --git a/sapl/rules/apps.py b/sapl/rules/apps.py index 2247e5c94..92126dcc6 100644 --- a/sapl/rules/apps.py +++ b/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'] diff --git a/sapl/sessao/migrations/0052_auto_20200521_1534.py b/sapl/sessao/migrations/0052_auto_20200521_1534.py new file mode 100644 index 000000000..5869c8253 --- /dev/null +++ b/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?'), + ), + ] diff --git a/sapl/sessao/migrations/0053_auto_20200609_1501.py b/sapl/sessao/migrations/0053_auto_20200609_1501.py new file mode 100644 index 000000000..ed1f61f83 --- /dev/null +++ b/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'), + ), + ] diff --git a/sapl/sessao/models.py b/sapl/sessao/models.py index 84f493acd..8585be2ad 100644 --- a/sapl/sessao/models.py +++ b/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 diff --git a/sapl/sessao/tests/test_sessao_view.py b/sapl/sessao/tests/test_sessao_view.py index 322d7fd5f..00284465b 100644 --- a/sapl/sessao/tests/test_sessao_view.py +++ b/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 diff --git a/sapl/sessao/views.py b/sapl/sessao/views.py index 3d16d9298..083bbe76f 100755 --- a/sapl/sessao/views.py +++ b/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' diff --git a/sapl/settings.py b/sapl/settings.py index 7a378f028..b518ad98f 100644 --- a/sapl/settings.py +++ b/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() diff --git a/sapl/test_urls.py b/sapl/test_urls.py index 23e0c9544..78d0388f4 100644 --- a/sapl/test_urls.py +++ b/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))) diff --git a/sapl/urls.py b/sapl/urls.py index 12528e07e..a1fafc81a 100644 --- a/sapl/urls.py +++ b/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)), diff --git a/scripts/lista_urls.py b/scripts/lista_urls.py index 769268e1b..25edd8c3f 100644 --- a/scripts/lista_urls.py +++ b/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():