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. 34
      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. 154
      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. 40
      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[cod]
*$py.class *$py.class
.pytest_cache/
.DS_Store
# C extensions # C extensions
*.so *.so

1
Dockerfile.dev

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

2
requirements/dev-requirements.txt

@ -1,7 +1,7 @@
-r test-requirements.txt -r test-requirements.txt
autopep8==1.2.4 autopep8==1.2.4
beautifulsoup4==4.6.0 beautifulsoup4==4.9.1
django-debug-toolbar==1.8 django-debug-toolbar==1.8
ipdb==0.10.1 ipdb==0.10.1
pdbpp==0.9.2 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-haystack==2.8.1
django-filter==2.0.0 django-filter==2.0.0
djangorestframework==3.9.1 djangorestframework==3.9.1
dj-database-url==0.5.0 dj-database-url==0.5.0
django-braces==1.9.0 django-braces==1.14.0
django-crispy-forms==1.7.2 django-crispy-forms==1.7.2
django-floppyforms==1.7.0 django-floppyforms==1.8.0
django-extra-views==0.12.0 django-extra-views==0.12.0
django-model-utils==3.1.2 django-model-utils==3.1.2
django-reversion==3.0.2 django-reversion==3.0.2
@ -19,9 +19,9 @@ ruamel.yaml>=0.15.34,<0.16.0
easy-thumbnails==2.5 easy-thumbnails==2.5
python-decouple==3.1 python-decouple==3.1
psycopg2-binary==2.7.6.1 psycopg2-binary==2.7.6.1
pyyaml==4.2b1 pyyaml==5.3.1
pytz==2019.3 pytz==2019.3
rtyaml==0.0.5 rtyaml==1.0.0
python-magic==0.4.15 python-magic==0.4.15
unipath==1.1 unipath==1.1
WeasyPrint==51 WeasyPrint==51
@ -32,6 +32,7 @@ pysolr==3.6.0
PyPDF4==1.27.0 PyPDF4==1.27.0
pyoai==2.5.0 pyoai==2.5.0
Unidecode==1.1.1 Unidecode==1.1.1
whitenoise==5.1.0
git+https://github.com/interlegis/trml2pdf git+https://github.com/interlegis/trml2pdf
git+https://github.com/interlegis/django-admin-bootstrapped git+https://github.com/interlegis/django-admin-bootstrapped

2
sapl/api/permissions.py

@ -48,6 +48,6 @@ class SaplModelPermissions(DjangoModelPermissions):
return ( return (
request.user and 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) request.user.has_perms(perms)
) )

14
sapl/api/views.py

@ -3,7 +3,7 @@ import logging
from django import apps from django import apps
from django.conf import settings from django.conf import settings
from django.contrib.contenttypes.models import ContentType 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 import Q
from django.db.models.fields.files import FileField from django.db.models.fields.files import FileField
from django.db.models.signals import post_save from django.db.models.signals import post_save
@ -448,7 +448,7 @@ class _ProposicaoViewSet():
qs = super().get_queryset() qs = super().get_queryset()
q = Q(data_recebimento__isnull=False, object_id__isnull=False) 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) q |= Q(autor__user=self.request.user)
qs = qs.filter(q) qs = qs.filter(q)
@ -526,7 +526,7 @@ class _DocumentoAdministrativoViewSet:
""" """
qs = super().get_queryset() qs = super().get_queryset()
if self.request.user.is_anonymous(): if self.request.user.is_anonymous:
qs = qs.exclude(restrito=True) qs = qs.exclude(restrito=True)
return qs return qs
@ -540,7 +540,7 @@ class _DocumentoAcessorioAdministrativoViewSet:
def get_queryset(self): def get_queryset(self):
qs = super().get_queryset() qs = super().get_queryset()
if self.request.user.is_anonymous(): if self.request.user.is_anonymous:
qs = qs.exclude(documento__restrito=True) qs = qs.exclude(documento__restrito=True)
return qs return qs
@ -555,7 +555,7 @@ class _TramitacaoAdministrativoViewSet(BusinessRulesNotImplementedMixin):
def get_queryset(self): def get_queryset(self):
qs = super().get_queryset() qs = super().get_queryset()
if self.request.user.is_anonymous(): if self.request.user.is_anonymous:
qs = qs.exclude(documento__restrito=True) qs = qs.exclude(documento__restrito=True)
return qs return qs
@ -569,7 +569,7 @@ class _AnexadoViewSet(BusinessRulesNotImplementedMixin):
def get_queryset(self): def get_queryset(self):
qs = super().get_queryset() qs = super().get_queryset()
if self.request.user.is_anonymous(): if self.request.user.is_anonymous:
qs = qs.exclude(documento__restrito=True) qs = qs.exclude(documento__restrito=True)
return qs return qs
@ -617,7 +617,7 @@ class AppVersionView(APIView):
'description': 'Sistema de Apoio ao Processo Legislativo', 'description': 'Sistema de Apoio ao Processo Legislativo',
'version': settings.SAPL_VERSION, 'version': settings.SAPL_VERSION,
'user': request.user.username, 'user': request.user.username,
'is_authenticated': request.user.is_authenticated(), 'is_authenticated': request.user.is_authenticated,
} }
return Response(content) 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['data'] == [_('Este campo é obrigatório.')]
assert errors['hora_inicio'] == [_('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) @pytest.mark.django_db(transaction=False)

2
sapl/audiencia/views.py

@ -1,7 +1,7 @@
import sapl import sapl
from django.http import HttpResponse 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.decorators.clickjacking import xframe_options_exempt
from django.views.generic import UpdateView from django.views.generic import UpdateView
from sapl.crud.base import RP_DETAIL, RP_LIST, Crud, MasterDetailCrud 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 import logging
from django.core.mail import EmailMultiAlternatives, get_connection, send_mail 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.template import Context, loader
from django.utils import timezone 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.db.models import Q
from django.forms import Form, ModelForm from django.forms import Form, ModelForm
from django.utils import timezone from django.utils import timezone
from django.utils.translation import string_concat
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
import django_filters import django_filters
@ -551,19 +550,15 @@ class AutorForm(ModelForm):
get_user_model().USERNAME_FIELD) get_user_model().USERNAME_FIELD)
self.fields['action_user'].initial = 'A' self.fields['action_user'].initial = 'A'
self.fields['username'].label = string_concat( self.fields['username'].label = "{} ({})".format(self.fields['username'].label,
self.fields['username'].label, getattr(self.instance.user,
' (', getattr( get_user_model().USERNAME_FIELD))
self.instance.user,
get_user_model().USERNAME_FIELD), ')')
if 'status_user' in self.Meta.fields: if 'status_user' in self.Meta.fields:
self.fields['status_user'].initial = 'R' self.fields['status_user'].initial = 'R'
self.fields['status_user'].label = string_concat( self.fields['status_user'].label = "{} ({})".format(self.fields['status_user'].label,
self.fields['status_user'].label, getattr(self.instance.user,
' (', getattr( get_user_model().USERNAME_FIELD))
self.instance.user,
get_user_model().USERNAME_FIELD), ')')
self.fields['username'].widget.attrs.update({ self.fields['username'].widget.attrs.update({
'data': getattr( '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() @reversion.register()
class TipoAutor(models.Model): class TipoAutor(models.Model):
descricao = models.CharField( descricao = models.CharField(
max_length=50, verbose_name=_('Descrição'), max_length=50,
help_text=_('Obs: Não crie tipos de autores ' verbose_name=_('Descrição'),
'semelhante aos tipos fixos. ')) help_text=_('Obs: Não crie tipos de autores semelhante aos tipos fixos. ')
)
content_type = models.OneToOneField( content_type = models.OneToOneField(
ContentType, ContentType,
null=True, default=None, null=True,
verbose_name=_('Modelagem no SAPL')) default=None,
verbose_name=_('Modelagem no SAPL'),
on_delete=models.PROTECT)
class Meta: class Meta:
ordering = ['descricao'] ordering = ['descricao']
@ -248,7 +251,8 @@ class Autor(models.Model):
ContentType, ContentType,
blank=True, blank=True,
null=True, null=True,
default=None) default=None,
on_delete=models.PROTECT)
object_id = models.PositiveIntegerField( object_id = models.PositiveIntegerField(
blank=True, blank=True,
null=True, null=True,
@ -258,7 +262,9 @@ class Autor(models.Model):
max_length=120, max_length=120,
blank=True, blank=True,
verbose_name=_('Nome do Autor')) verbose_name=_('Nome do Autor'))
cargo = models.CharField(max_length=50, blank=True) cargo = models.CharField(
max_length=50,
blank=True)
class Meta: class Meta:
verbose_name = _('Autor') verbose_name = _('Autor')

2
sapl/base/templatetags/menus.py

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

2
sapl/base/tests/test_view_base.py

@ -1,7 +1,7 @@
import pytest import pytest
from model_bakery import baker from model_bakery import baker
import datetime import datetime
from django.core.urlresolvers import reverse from django.urls import reverse
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from model_bakery import baker 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.conf.urls import include, url
from django.contrib.auth import views from django.contrib.auth import views
from django.contrib.auth.decorators import permission_required from django.contrib.auth.decorators import permission_required
from django.contrib.auth.views import (password_reset, password_reset_complete, from django.contrib.auth.views import (PasswordResetView, PasswordResetCompleteView, PasswordResetConfirmView,
password_reset_confirm, PasswordResetDoneView)
password_reset_done)
from django.urls.base import reverse_lazy
from django.views.generic.base import RedirectView, TemplateView from django.views.generic.base import RedirectView, TemplateView
from sapl.base.views import AutorCrud, ConfirmarEmailView, TipoAutorCrud, get_estatistica, DetailUsuarioView, \ from sapl.base.views import (AutorCrud, ConfirmarEmailView, TipoAutorCrud, get_estatistica, DetailUsuarioView,
PesquisarAutorView PesquisarAutorView, RecuperarSenhaEmailView, RecuperarSenhaFinalizadoView,
from sapl.settings import EMAIL_SEND_USER, MEDIA_URL RecuperarSenhaConfirmaView, RecuperarSenhaCompletoView)
from sapl.settings import EMAIL_SEND_USER, MEDIA_URL, LOGOUT_REDIRECT_URL
from .apps import AppConfig from .apps import AppConfig
from .forms import LoginForm, NovaSenhaForm, RecuperarSenhaForm from .forms import LoginForm, NovaSenhaForm, RecuperarSenhaForm
from .views import (AlterarSenha, AppConfigCrud, CasaLegislativaCrud, from .views import (AlterarSenha, AppConfigCrud, CasaLegislativaCrud, CreateUsuarioView, DeleteUsuarioView,
CreateUsuarioView, DeleteUsuarioView, EditUsuarioView, EditUsuarioView, HelpTopicView, PesquisarUsuarioView, LogotipoView, RelatorioAtasView,
HelpTopicView, PesquisarUsuarioView, LogotipoView, RelatorioAudienciaView, RelatorioDataFimPrazoTramitacaoView, RelatorioHistoricoTramitacaoView,
RelatorioAtasView, RelatorioAudienciaView, RelatorioMateriasPorAnoAutorTipoView, RelatorioMateriasPorAutorView,
RelatorioDataFimPrazoTramitacaoView, RelatorioMateriasTramitacaoView, RelatorioPresencaSessaoView, RelatorioReuniaoView, SaplSearchView,
RelatorioHistoricoTramitacaoView, RelatorioNormasPublicadasMesView, RelatorioNormasVigenciaView,
RelatorioMateriasPorAnoAutorTipoView, EstatisticasAcessoNormas, RelatoriosListView, ListarInconsistenciasView,
RelatorioMateriasPorAutorView, ListarProtocolosDuplicadosView, ListarProtocolosComMateriasView, ListarMatProtocoloInexistenteView,
RelatorioMateriasTramitacaoView, ListarParlamentaresDuplicadosView, ListarFiliacoesSemDataFiliacaoView,
RelatorioPresencaSessaoView, ListarMandatoSemDataInicioView, ListarParlMandatosIntersecaoView, ListarParlFiliacoesIntersecaoView,
RelatorioReuniaoView, SaplSearchView, ListarAutoresDuplicadosView, ListarBancadaComissaoAutorExternoView, ListarLegislaturaInfindavelView,
RelatorioNormasPublicadasMesView, ListarAnexadasCiclicasView, ListarAnexadosCiclicosView, pesquisa_textual,
RelatorioNormasVigenciaView, RelatorioHistoricoTramitacaoAdmView, RelatorioDocumentosAcessoriosView, RelatorioNormasPorAutorView)
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 app_name = AppConfig.name
@ -59,35 +49,13 @@ alterar_senha = [
] ]
recuperar_senha = [ recuperar_senha = [
url(r'^recuperar-senha/email/$', url(r'^recuperar-senha/email/$', RecuperarSenhaEmailView.as_view(), name='recuperar_senha_email'),
password_reset, url(r'^recuperar-senha/finalizado/$', RecuperarSenhaFinalizadoView.as_view(), name='recuperar_senha_finalizado'),
{'post_reset_redirect': 'sapl.base:recuperar_senha_finalizado', url(r'^recuperar-senha/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>.+)/$', RecuperarSenhaConfirmaView.as_view(),
'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},
name='recuperar_senha_confirma'), name='recuperar_senha_confirma'),
url(r'^recuperar-senha/completo/$', RecuperarSenhaCompletoView.as_view(), name='recuperar_senha_completo'),
url(r'^recuperar-senha/completo/$',
password_reset_complete,
{'template_name': 'base/recuperar_senha_completo.html'},
name='recuperar_senha_completo'),
] ]
urlpatterns = [ urlpatterns = [
url(r'^sistema/autor/tipo/', include(TipoAutorCrud.get_urls())), url(r'^sistema/autor/tipo/', include(TipoAutorCrud.get_urls())),
url(r'^sistema/autor/', include(AutorCrud.get_urls())), url(r'^sistema/autor/', include(AutorCrud.get_urls())),
@ -203,10 +171,9 @@ urlpatterns = [
(TemplateView.as_view(template_name='sistema.html')), (TemplateView.as_view(template_name='sistema.html')),
name='sistema'), name='sistema'),
url(r'^login/$', views.login, { url(r'^login/$', views.LoginView.as_view(template_name= 'base/login.html', authentication_form= LoginForm),
'template_name': 'base/login.html', 'authentication_form': LoginForm},
name='login'), 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'), 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.mixins import PermissionRequiredMixin
from django.contrib.auth.models import Group, User from django.contrib.auth.models import Group, User
from django.contrib.auth.tokens import default_token_generator 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.exceptions import ObjectDoesNotExist, PermissionDenied, ValidationError
from django.core.mail import send_mail 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 import connection
from django.db.models import Count, Q, ProtectedError, Max from django.db.models import Count, Q, ProtectedError, Max
from django.shortcuts import render from django.shortcuts import render
@ -22,7 +24,6 @@ from django.template.loader import get_template
from django.utils import timezone from django.utils import timezone
from django.utils.encoding import force_bytes from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_decode, urlsafe_base64_encode 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.utils.translation import ugettext_lazy as _
from django.views.generic import (CreateView, DetailView, DeleteView, FormView, ListView, UpdateView) from django.views.generic import (CreateView, DetailView, DeleteView, FormView, ListView, UpdateView)
from django.views.generic.base import RedirectView, TemplateView from django.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, from sapl.relatorios.views import (relatorio_materia_em_tramitacao, relatorio_materia_por_autor,
relatorio_materia_por_ano_autor, relatorio_presenca_sessao, relatorio_materia_por_ano_autor, relatorio_presenca_sessao,
relatorio_historico_tramitacao, relatorio_fim_prazo_tramitacao, relatorio_historico_tramitacao, relatorio_fim_prazo_tramitacao, relatorio_atas,
relatorio_atas, relatorio_audiencia, relatorio_normas_mes, relatorio_audiencia, relatorio_normas_mes, relatorio_normas_vigencia,
relatorio_normas_vigencia, relatorio_historico_tramitacao_adm, relatorio_historico_tramitacao_adm, relatorio_reuniao,
relatorio_reuniao, relatorio_estatisticas_acesso_normas, relatorio_estatisticas_acesso_normas, relatorio_normas_por_autor,
relatorio_normas_por_autor, relatorio_documento_acessorio) relatorio_documento_acessorio)
from sapl import settings from sapl import settings
from sapl.audiencia.models import AudienciaPublica, TipoAudienciaPublica from sapl.audiencia.models import AudienciaPublica, TipoAudienciaPublica
from sapl.base.models import Autor, TipoAutor 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.comissoes.models import Comissao, Reuniao
from sapl.crud.base import CrudAux, make_pagination from sapl.crud.base import CrudAux, make_pagination
from sapl.materia.models import (Anexada, Autoria, DocumentoAcessorio, from sapl.materia.models import (Anexada, Autoria, DocumentoAcessorio, MateriaEmTramitacao, MateriaLegislativa,
MateriaEmTramitacao, MateriaLegislativa, Proposicao, Proposicao, StatusTramitacao, TipoDocumento, TipoMateriaLegislativa, UnidadeTramitacao,
StatusTramitacao, TipoDocumento, Tramitacao)
TipoMateriaLegislativa, UnidadeTramitacao, Tramitacao)
from sapl.norma.models import NormaJuridica, TipoNormaJuridica from sapl.norma.models import NormaJuridica, TipoNormaJuridica
from sapl.parlamentares.models import (Filiacao, Legislatura, Mandato, Parlamentar, from sapl.parlamentares.models import (Filiacao, Legislatura, Mandato, Parlamentar, SessaoLegislativa)
SessaoLegislativa) from sapl.protocoloadm.models import (Anexado, DocumentoAdministrativo, Protocolo, StatusTramitacaoAdministrativo,
from sapl.protocoloadm.models import (Anexado, DocumentoAdministrativo, Protocolo,
StatusTramitacaoAdministrativo,
TipoDocumentoAdministrativo) TipoDocumentoAdministrativo)
from sapl.sessao.models import (Bancada, PresencaOrdemDia, SessaoPlenaria, from sapl.sessao.models import (Bancada, PresencaOrdemDia, SessaoPlenaria, SessaoPlenariaPresenca, TipoSessaoPlenaria)
SessaoPlenariaPresenca, TipoSessaoPlenaria) from sapl.settings import EMAIL_SEND_USER
from sapl.utils import (from_date_to_datetime_utc, gerar_hash_arquivo, intervalos_tem_intersecao, from sapl.utils import (gerar_hash_arquivo, intervalos_tem_intersecao, mail_service_configured, parlamentares_ativos,
mail_service_configured, parlamentares_ativos, SEPARADOR_HASH_PROPOSICAO, SEPARADOR_HASH_PROPOSICAO, show_results_filter_set, num_materias_por_tipo)
show_results_filter_set, num_materias_por_tipo) from .forms import (AlterarSenhaForm, CasaLegislativaForm, ConfiguracoesAppForm, RelatorioAtasFilterSet,
RelatorioAudienciaFilterSet, RelatorioDataFimPrazoTramitacaoFilterSet,
from .forms import (AlterarSenhaForm, CasaLegislativaForm, RelatorioHistoricoTramitacaoFilterSet, RelatorioMateriasPorAnoAutorTipoFilterSet,
ConfiguracoesAppForm, RelatorioAtasFilterSet, RelatorioMateriasPorAutorFilterSet, RelatorioMateriasTramitacaoFilterSet,
RelatorioAudienciaFilterSet, RelatorioPresencaSessaoFilterSet, RelatorioReuniaoFilterSet, UsuarioCreateForm, UsuarioEditForm,
RelatorioDataFimPrazoTramitacaoFilterSet, RelatorioNormasMesFilterSet, RelatorioNormasVigenciaFilterSet, EstatisticasAcessoNormasForm,
RelatorioHistoricoTramitacaoFilterSet, UsuarioFilterSet, RelatorioHistoricoTramitacaoAdmFilterSet, RelatorioDocumentosAcessoriosFilterSet,
RelatorioMateriasPorAnoAutorTipoFilterSet,
RelatorioMateriasPorAutorFilterSet,
RelatorioMateriasTramitacaoFilterSet,
RelatorioPresencaSessaoFilterSet,
RelatorioReuniaoFilterSet, UsuarioCreateForm,
UsuarioEditForm, RelatorioNormasMesFilterSet,
RelatorioNormasVigenciaFilterSet,
EstatisticasAcessoNormasForm, UsuarioFilterSet,
RelatorioHistoricoTramitacaoAdmFilterSet,
RelatorioDocumentosAcessoriosFilterSet,
RelatorioNormasPorAutorFilterSet) RelatorioNormasPorAutorFilterSet)
from .models import AppConfig, CasaLegislativa from .models import AppConfig, CasaLegislativa
@ -97,6 +86,29 @@ class ConfirmarEmailView(TemplateView):
return self.render_to_response(context) 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): class TipoAutorCrud(CrudAux):
model = TipoAutor model = TipoAutor
help_topic = 'tipo-autor' help_topic = 'tipo-autor'
@ -108,7 +120,7 @@ class TipoAutorCrud(CrudAux):
@property @property
def verbose_name(self): def verbose_name(self):
vn = super().verbose_name vn = super().verbose_name
vn = string_concat(vn, ' ', _('Externo ao SAPL')) vn = "{} {}".format(vn, _('Externo ao SAPL'))
return vn return vn
class ListView(CrudAux.ListView): class ListView(CrudAux.ListView):
@ -1433,20 +1445,20 @@ def bancada_comissao_autor_externo():
lista_bancada_autor_externo = [] lista_bancada_autor_externo = []
for bancada in Bancada.objects.all().order_by('nome'): 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: 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( lista_bancada_autor_externo.append(
(q_autor_externo, bancada, 'Bancada', 'sistema/bancada') (q_autor_externo, bancada, 'Bancada', 'sistema/bancada')
) )
lista_comissao_autor_externo = [] lista_comissao_autor_externo = []
for comissao in Comissao.objects.all().order_by('nome'): 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: 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( lista_comissao_autor_externo.append(
(q_autor_externo, comissao, 'Comissão', 'comissao') (q_autor_externo, comissao, 'Comissão', 'comissao')
) )

3
sapl/comissoes/tests/test_comissoes.py

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

2
sapl/comissoes/views.py

@ -2,7 +2,7 @@ import logging
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.mixins import PermissionRequiredMixin 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.db.models import F
from django.http.response import HttpResponseRedirect, JsonResponse from django.http.response import HttpResponseRedirect, JsonResponse
from django.views.decorators.clickjacking import xframe_options_exempt 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.conf import settings
from django.db import connection, models from django.db import connection, models
from django.db.utils import DEFAULT_DB_ALIAS, IntegrityError from django.db.utils import DEFAULT_DB_ALIAS, IntegrityError
from django.utils.translation import string_concat
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
@ -37,11 +36,7 @@ class AppConfig(apps.AppConfig):
cursor.execute(line) cursor.execute(line)
except IntegrityError as e: except IntegrityError as e:
if not settings.DEBUG: if not settings.DEBUG:
print( print("{} {} {}".format(_('Ocorreu erro na importação:'), line, str(e)))
string_concat(
_('Ocorreu erro na importação: '),
line,
str(e)))
except Exception as ee: except Exception as ee:
print(ee) print(ee)
@ -72,9 +67,7 @@ class AppConfig(apps.AppConfig):
tipo.save() tipo.save()
except IntegrityError as e: except IntegrityError as e:
if not settings.DEBUG: if not settings.DEBUG:
print(string_concat( print("{} {}".format(_('Ocorreu erro na criação tipo de ta:'), str(e)))
_('Ocorreu erro na criação tipo de ta: '),
str(e)))
def init_compilacao_base(app_config, verbosity=2, interactive=True, 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(): if not TipoDispositivo.objects.exists():
print('') print('')
print(string_concat('\033[93m\033[1m', print("\033[93m\033[1m{}\033[0m".format(_('Iniciando Textos Articulados...')))
_('Iniciando Textos Articulados...'),
'\033[0m'))
AppConfig.import_pattern() 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() @reversion.register()
class TextoArticulado(TimestampedMixin): 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')) 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( numero = models.CharField(
max_length=8, verbose_name=_('Número')) max_length=8,
verbose_name=_('Número')
)
ano = models.PositiveSmallIntegerField(verbose_name=_('Ano')) ano = models.PositiveSmallIntegerField(verbose_name=_('Ano'))
tipo_ta = models.ForeignKey( tipo_ta = models.ForeignKey(
TipoTextoArticulado, TipoTextoArticulado,
blank=True, null=True, default=None, blank=True,
verbose_name=_('Tipo de Texto Articulado')) null=True,
participacao_social = models.NullBooleanField(
default=None, 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, choices=PARTICIPACAO_SOCIAL_CHOICES,
verbose_name=_('Participação Social')) verbose_name=_('Participação Social')
)
content_type = models.ForeignKey( content_type = models.ForeignKey(
ContentType, ContentType,
blank=True, null=True, default=None) blank=True,
null=True,
default=None,
on_delete=models.PROTECT
)
object_id = models.PositiveIntegerField( object_id = models.PositiveIntegerField(
blank=True, null=True, default=None) blank=True,
null=True,
default=None)
content_object = GenericForeignKey('content_type', 'object_id') content_object = GenericForeignKey('content_type', 'object_id')
owners = models.ManyToManyField( owners = models.ManyToManyField(
get_settings_auth_user_model(), 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( editable_only_by_owners = models.BooleanField(
choices=YES_NO_CHOICES, choices=YES_NO_CHOICES,
default=True, 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( editing_locked = models.BooleanField(
choices=YES_NO_CHOICES, choices=YES_NO_CHOICES,
default=True, default=True,
verbose_name=_('Texto Articulado em Edição?')) verbose_name=_('Texto Articulado em Edição?')
)
privacidade = models.IntegerField( privacidade = models.IntegerField(
_('Privacidade'), _('Privacidade'),
choices=PRIVACIDADE_STATUS, choices=PRIVACIDADE_STATUS,
default=STATUS_TA_PRIVATE) default=STATUS_TA_PRIVATE
)
class Meta: class Meta:
verbose_name = _('Texto Articulado') verbose_name = _('Texto Articulado')
@ -775,23 +809,39 @@ class TipoDispositivo(BaseModel):
@reversion.register() @reversion.register()
class TipoDispositivoRelationship(BaseModel): 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( filho_permitido = models.ForeignKey(
TipoDispositivo, TipoDispositivo,
related_name='possiveis_pais') related_name='possiveis_pais',
perfil = models.ForeignKey(PerfilEstruturalTextoArticulado) on_delete=models.PROTECT
)
perfil = models.ForeignKey(
PerfilEstruturalTextoArticulado,
on_delete=models.PROTECT
)
filho_de_insercao_automatica = models.BooleanField( filho_de_insercao_automatica = models.BooleanField(
default=False, default=False,
choices=YES_NO_CHOICES, choices=YES_NO_CHOICES,
verbose_name=_('Filho de Inserção Automática')) verbose_name=_('Filho de Inserção Automática')
)
permitir_variacao = models.BooleanField( permitir_variacao = models.BooleanField(
default=True, default=True,
choices=YES_NO_CHOICES, choices=YES_NO_CHOICES,
verbose_name=_('Permitir Variação Numérica')) verbose_name=_('Permitir Variação Numérica')
)
quantidade_permitida = models.IntegerField( quantidade_permitida = models.IntegerField(
default=-1, default=-1,
verbose_name=_('Quantidade permitida nesta relação')) verbose_name=_('Quantidade permitida nesta relação')
)
class Meta: class Meta:
verbose_name = _('Relação Direta Permitida') verbose_name = _('Relação Direta Permitida')
@ -837,33 +887,66 @@ class VeiculoPublicacao(models.Model):
@reversion.register() @reversion.register()
class Publicacao(TimestampedMixin): class Publicacao(TimestampedMixin):
ta = models.ForeignKey( ta = models.ForeignKey(
TextoArticulado, verbose_name=_('Texto Articulado')) TextoArticulado,
verbose_name=_('Texto Articulado'),
on_delete=models.PROTECT
)
veiculo_publicacao = models.ForeignKey( 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( 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')) data = models.DateField(verbose_name=_('Data de Publicação'))
hora = models.TimeField( 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( numero = models.PositiveIntegerField(
blank=True, null=True, verbose_name=_('Número')) blank=True,
null=True,
verbose_name=_('Número')
)
ano = models.PositiveIntegerField( ano = models.PositiveIntegerField(
blank=True, null=True, verbose_name=_('Ano')) blank=True,
null=True,
verbose_name=_('Ano')
)
edicao = models.PositiveIntegerField( edicao = models.PositiveIntegerField(
blank=True, null=True, verbose_name=_('Edição')) blank=True,
null=True,
verbose_name=_('Edição')
)
url_externa = models.URLField( url_externa = models.URLField(
max_length=1024, max_length=1024,
blank=True, blank=True,
verbose_name=_('Link para Versão Eletrônica')) verbose_name=_('Link para Versão Eletrônica')
)
pagina_inicio = models.PositiveIntegerField( 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( pagina_fim = models.PositiveIntegerField(
blank=True, null=True, verbose_name=_('Pg. Fim')) blank=True,
null=True,
verbose_name=_('Pg. Fim')
)
class Meta: class Meta:
verbose_name = _('Publicação') verbose_name = _('Publicação')
@ -880,150 +963,222 @@ class Publicacao(TimestampedMixin):
class Dispositivo(BaseModel, TimestampedMixin): class Dispositivo(BaseModel, TimestampedMixin):
TEXTO_PADRAO_DISPOSITIVO_REVOGADO = force_text(_('(Revogado)')) TEXTO_PADRAO_DISPOSITIVO_REVOGADO = force_text(_('(Revogado)'))
INTERVALO_ORDEM = 1000 INTERVALO_ORDEM = 1000
ordem = models.PositiveIntegerField( ordem = models.PositiveIntegerField(
default=0, default=0,
verbose_name=_('Ordem de Renderização')) verbose_name=_('Ordem de Renderização')
)
ordem_bloco_atualizador = models.PositiveIntegerField( ordem_bloco_atualizador = models.PositiveIntegerField(
default=0, 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 # apenas articulacao recebe nivel zero
nivel = models.PositiveIntegerField( nivel = models.PositiveIntegerField(
default=0, default=0,
blank=True, blank=True,
null=True, null=True,
verbose_name=_('Nível Estrutural')) verbose_name=_('Nível Estrutural')
)
dispositivo0 = models.PositiveIntegerField( dispositivo0 = models.PositiveIntegerField(
default=0, default=0,
verbose_name=_('Número do Dispositivo')) verbose_name=_('Número do Dispositivo')
)
dispositivo1 = models.PositiveIntegerField( dispositivo1 = models.PositiveIntegerField(
default=0, default=0,
blank=True, blank=True,
null=True, null=True,
verbose_name=_('Primeiro Nível de Variação')) verbose_name=_('Primeiro Nível de Variação')
)
dispositivo2 = models.PositiveIntegerField( dispositivo2 = models.PositiveIntegerField(
default=0, default=0,
blank=True, blank=True,
null=True, null=True,
verbose_name=_('Segundo Nível de Variação')) verbose_name=_('Segundo Nível de Variação')
)
dispositivo3 = models.PositiveIntegerField( dispositivo3 = models.PositiveIntegerField(
default=0, default=0,
blank=True, blank=True,
null=True, null=True,
verbose_name=_('Terceiro Nível de Variação')) verbose_name=_('Terceiro Nível de Variação')
)
dispositivo4 = models.PositiveIntegerField( dispositivo4 = models.PositiveIntegerField(
default=0, default=0,
blank=True, blank=True,
null=True, null=True,
verbose_name=_('Quarto Nível de Variação')) verbose_name=_('Quarto Nível de Variação')
)
dispositivo5 = models.PositiveIntegerField( dispositivo5 = models.PositiveIntegerField(
default=0, default=0,
blank=True, blank=True,
null=True, null=True,
verbose_name=_('Quinto Nível de Variação')) verbose_name=_('Quinto Nível de Variação')
)
rotulo = models.CharField( rotulo = models.CharField(
max_length=50, max_length=50,
blank=True, blank=True,
default='', default='',
verbose_name=_('Rótulo')) verbose_name=_('Rótulo')
)
texto = models.TextField( texto = models.TextField(
blank=True, blank=True,
default='', default='',
verbose_name=_('Texto do Dispositivo')) verbose_name=_('Texto do Dispositivo')
)
texto_atualizador = models.TextField( texto_atualizador = models.TextField(
blank=True, blank=True,
default='', 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( 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( 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( inconstitucionalidade = models.BooleanField(
default=False, default=False,
choices=YES_NO_CHOICES, choices=YES_NO_CHOICES,
verbose_name=_('Declarado Inconstitucional')) verbose_name=_('Declarado Inconstitucional')
)
auto_inserido = models.BooleanField( auto_inserido = models.BooleanField(
default=False, default=False,
choices=YES_NO_CHOICES, choices=YES_NO_CHOICES,
verbose_name=_('Auto Inserido')) verbose_name=_('Auto Inserido')
)
visibilidade = models.BooleanField( visibilidade = models.BooleanField(
default=False, default=False,
choices=YES_NO_CHOICES, choices=YES_NO_CHOICES,
verbose_name=_('Visibilidade no Texto Articulado Publicado')) verbose_name=_('Visibilidade no Texto Articulado Publicado')
)
dispositivo_de_revogacao = models.BooleanField( dispositivo_de_revogacao = models.BooleanField(
default=False, default=False,
choices=YES_NO_CHOICES, choices=YES_NO_CHOICES,
verbose_name=_('Dispositivo de Revogação')) verbose_name=_('Dispositivo de Revogação')
)
tipo_dispositivo = models.ForeignKey( tipo_dispositivo = models.ForeignKey(
TipoDispositivo, TipoDispositivo,
on_delete=models.PROTECT, on_delete=models.PROTECT,
related_name='dispositivos_do_tipo_set', related_name='dispositivos_do_tipo_set',
verbose_name=_('Tipo do Dispositivo')) verbose_name=_('Tipo do Dispositivo')
)
publicacao = models.ForeignKey( publicacao = models.ForeignKey(
Publicacao, 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( ta = models.ForeignKey(
TextoArticulado, TextoArticulado,
on_delete=models.CASCADE, on_delete=models.CASCADE,
related_name='dispositivos_set', related_name='dispositivos_set',
verbose_name=_('Texto Articulado')) verbose_name=_('Texto Articulado'),
)
ta_publicado = models.ForeignKey( ta_publicado = models.ForeignKey(
TextoArticulado, TextoArticulado,
on_delete=models.PROTECT, on_delete=models.PROTECT,
blank=True, null=True, default=None, blank=True,
null=True,
default=None,
related_name='dispositivos_alterados_pelo_ta_set', related_name='dispositivos_alterados_pelo_ta_set',
verbose_name=_('Texto Articulado Publicado')) verbose_name=_('Texto Articulado Publicado')
)
dispositivo_subsequente = models.ForeignKey( dispositivo_subsequente = models.ForeignKey(
'self', 'self',
blank=True, null=True, default=None, blank=True,
null=True,
default=None,
related_name='dispositivo_subsequente_set', related_name='dispositivo_subsequente_set',
on_delete=models.SET_NULL, on_delete=models.SET_NULL,
verbose_name=_('Dispositivo Subsequente')) verbose_name=_('Dispositivo Subsequente')
)
dispositivo_substituido = models.ForeignKey( dispositivo_substituido = models.ForeignKey(
'self', 'self',
blank=True, null=True, default=None, blank=True,
null=True,
default=None,
related_name='dispositivo_substituido_set', related_name='dispositivo_substituido_set',
on_delete=models.SET_NULL, on_delete=models.SET_NULL,
verbose_name=_('Dispositivo Substituido')) verbose_name=_('Dispositivo Substituido')
)
dispositivo_pai = models.ForeignKey( dispositivo_pai = models.ForeignKey(
'self', 'self',
blank=True, null=True, default=None, blank=True,
null=True,
default=None,
related_name='dispositivos_filhos_set', related_name='dispositivos_filhos_set',
verbose_name=_('Dispositivo Pai')) verbose_name=_('Dispositivo Pai'),
on_delete=models.PROTECT
)
dispositivo_raiz = models.ForeignKey( dispositivo_raiz = models.ForeignKey(
'self', 'self',
blank=True, null=True, default=None, blank=True,
null=True,
default=None,
related_name='nodes', related_name='nodes',
verbose_name=_('Dispositivo Raiz')) verbose_name=_('Dispositivo Raiz'),
on_delete=models.PROTECT
)
dispositivo_vigencia = models.ForeignKey( dispositivo_vigencia = models.ForeignKey(
'self', 'self',
blank=True, null=True, default=None, blank=True,
null=True,
default=None,
on_delete=models.SET_NULL, on_delete=models.SET_NULL,
related_name='dispositivos_vigencias_set', related_name='dispositivos_vigencias_set',
verbose_name=_('Dispositivo de Vigência')) verbose_name=_('Dispositivo de Vigência')
)
dispositivo_atualizador = models.ForeignKey( dispositivo_atualizador = models.ForeignKey(
'self', 'self',
blank=True, null=True, default=None, blank=True,
null=True,
default=None,
related_name='dispositivos_alterados_set', related_name='dispositivos_alterados_set',
verbose_name=_('Dispositivo Atualizador')) verbose_name=_('Dispositivo Atualizador'),
on_delete=models.PROTECT
)
contagem_continua = models.BooleanField( contagem_continua = models.BooleanField(
default=False, default=False,
choices=YES_NO_CHOICES, verbose_name=_('Contagem contínua')) choices=YES_NO_CHOICES,
verbose_name=_('Contagem contínua')
)
class Meta: class Meta:
verbose_name = _('Dispositivo') verbose_name = _('Dispositivo')
@ -1740,16 +1895,25 @@ class Dispositivo(BaseModel, TimestampedMixin):
class Vide(TimestampedMixin): class Vide(TimestampedMixin):
texto = models.TextField(verbose_name=_('Texto do Vide')) 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_base = models.ForeignKey(
Dispositivo, Dispositivo,
verbose_name=_('Dispositivo Base'), verbose_name=_('Dispositivo Base'),
related_name='dispositivo_base_set') related_name='dispositivo_base_set',
on_delete=models.PROTECT
)
dispositivo_ref = models.ForeignKey( dispositivo_ref = models.ForeignKey(
Dispositivo, Dispositivo,
related_name='dispositivo_citado_set', related_name='dispositivo_citado_set',
verbose_name=_('Dispositivo Referido')) verbose_name=_('Dispositivo Referido'),
on_delete=models.PROTECT
)
class Meta: class Meta:
verbose_name = _('Vide') verbose_name = _('Vide')
@ -1784,24 +1948,40 @@ class Nota(TimestampedMixin):
verbose_name=_('Título'), verbose_name=_('Título'),
max_length=100, max_length=100,
default='', default='',
blank=True) blank=True
)
texto = models.TextField(verbose_name=_('Texto')) texto = models.TextField(verbose_name=_('Texto'))
url_externa = models.URLField( url_externa = models.URLField(
max_length=1024, max_length=1024,
blank=True, blank=True,
verbose_name=_('Url externa')) verbose_name=_('Url externa')
)
publicacao = models.DateTimeField(verbose_name=_('Data de Publicação')) publicacao = models.DateTimeField(verbose_name=_('Data de Publicação'))
efetividade = models.DateTimeField(verbose_name=_('Data de Efeito')) 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 = models.ForeignKey(
Dispositivo, Dispositivo,
verbose_name=_('Dispositivo da Nota'), verbose_name=_('Dispositivo da Nota'),
related_name='dispositivo_nota_set') related_name='dispositivo_nota_set',
on_delete=models.PROTECT
)
owner = models.ForeignKey( 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( publicidade = models.PositiveSmallIntegerField(
choices=NOTAS_PUBLICIDADE_CHOICES, choices=NOTAS_PUBLICIDADE_CHOICES,
verbose_name=_('Nível de Publicidade')) verbose_name=_('Nível de Publicidade'))

34
sapl/compilacao/views.py

@ -12,7 +12,7 @@ from django.contrib.auth.mixins import PermissionRequiredMixin
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError, PermissionDenied from django.core.exceptions import ValidationError, PermissionDenied
from django.core.signing import Signer 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 import transaction
from django.db.models import Q from django.db.models import Q
from django.db.models.query import QuerySet 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.shortcuts import get_object_or_404, redirect
from django.utils.dateparse import parse_date from django.utils.dateparse import parse_date
from django.utils.encoding import force_text from django.utils.encoding import force_text
from django.utils.translation import string_concat
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.views.generic.base import TemplateView from django.views.generic.base import TemplateView
from django.views.generic.detail import DetailView from django.views.generic.detail import DetailView
@ -121,11 +120,7 @@ class IntegracaoTaView(TemplateView):
tipo_ta.save() tipo_ta.save()
except Exception as e: except Exception as e:
print( print("{} {}".format(_('Ocorreu erro na importação do arquivo base dos Tipos de Dispositivos, entre outras informações iniciais.'), str(e)))
string_concat(
_('Ocorreu erro na importação do arquivo base dos Tipos de'
'Dispositivos, entre outras informações iniciais.'),
str(e)))
return self.get_redirect_deactivated() return self.get_redirect_deactivated()
assert hasattr(self, 'map_fields'), _( assert hasattr(self, 'map_fields'), _(
@ -187,7 +182,7 @@ class IntegracaoTaView(TemplateView):
if not ta_exists: if not ta_exists:
if ta.editable_only_by_owners and\ 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) ta.owners.add(self.request.user)
if not Dispositivo.objects.filter(ta_id=ta.pk).exists() and\ if not Dispositivo.objects.filter(ta_id=ta.pk).exists() and\
@ -1876,24 +1871,25 @@ class ActionDispositivoCreateMixin(ActionsCommonsMixin):
base = Dispositivo.objects.get( base = Dispositivo.objects.get(
pk=self.kwargs['dispositivo_id'] if not _base else _base) pk=self.kwargs['dispositivo_id'] if not _base else _base)
result = [{'tipo_insert': force_text(string_concat( result = [
_('Inserir Após'), {
' ', 'tipo_insert': force_text("{} {}".format(_('Inserir Após'), base.tipo_dispositivo.nome)),
base.tipo_dispositivo.nome)),
'icone': '&#8631;&nbsp;', 'icone': '&#8631;&nbsp;',
'action': 'json_add_next', 'action': 'json_add_next',
'itens': []}, 'itens': []},
{'tipo_insert': force_text(string_concat( {
_('Inserir em'), 'tipo_insert': force_text("{} {}".format(_('Inserir em'), base.tipo_dispositivo.nome)),
' ',
base.tipo_dispositivo.nome)),
'icone': '&#8690;&nbsp;', 'icone': '&#8690;&nbsp;',
'action': 'json_add_in', 'action': 'json_add_in',
'itens': []}, 'itens': []
{'tipo_insert': force_text(_('Inserir Antes')), },
{
'tipo_insert': force_text(_('Inserir Antes')),
'icone': '&#8630;&nbsp;', 'icone': '&#8630;&nbsp;',
'action': 'json_add_prior', 'action': 'json_add_prior',
'itens': []}] 'itens': []
}
]
perfil_pk = request.session['perfil_estrutural'] 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.helper import FormHelper
from crispy_forms.layout import HTML, Div, Fieldset, Layout, Submit from crispy_forms.layout import HTML, Div, Fieldset, Layout, Submit
from django import template 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 import formats
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
import rtyaml import rtyaml
@ -330,11 +330,18 @@ class CrispyLayoutFormMixin:
def read_yaml_from_file(yaml_layout): def read_yaml_from_file(yaml_layout):
from django.utils.safestring import SafeText
# TODO cache this at application level # TODO cache this at application level
t = template.loader.get_template(yaml_layout) t = template.loader.get_template(yaml_layout)
# aqui é importante converter para str pois, dependendo do ambiente, # aqui é importante converter para str pois, dependendo do ambiente,
# o rtyaml pode usar yaml.CSafeLoader, que exige str ou stream # o rtyaml pode usar yaml.CSafeLoader, que exige str ou stream
rendered = str(t.render()) rendered = str(t.render())
# Força conversão para string caso seja SafeText.
if isinstance(rendered, SafeText):
rendered = rendered.strip()
return rtyaml.load(rendered) 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 import messages
from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.auth.mixins import PermissionRequiredMixin
from django.core.exceptions import ObjectDoesNotExist 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 import models
from django.db.models.fields.related import ForeignKey, ManyToManyField from django.db.models.fields.related import ForeignKey, ManyToManyField
from django.http.response import Http404 from django.http.response import Http404
from django.shortcuts import redirect from django.shortcuts import redirect
from django.utils.decorators import classonlymethod from django.utils.decorators import classonlymethod
from django.utils.encoding import force_text from django.utils.encoding import force_text
from django.utils.translation import string_concat
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.views.generic import (CreateView, DeleteView, DetailView, ListView, from django.views.generic import (CreateView, DeleteView, DetailView, ListView,
UpdateView) UpdateView)
@ -613,7 +612,7 @@ class CrudListView(PermissionRequiredContainerCrudMixin, ListView):
) )
# print(queryset.query) # print(queryset.query)
if not self.request.user.is_authenticated(): if not self.request.user.is_authenticated:
return queryset return queryset
if self.container_field: if self.container_field:
@ -816,7 +815,7 @@ class CrudDetailView(PermissionRequiredContainerCrudMixin,
else: else:
queryset = super().get_queryset() queryset = super().get_queryset()
if not self.request.user.is_authenticated(): if not self.request.user.is_authenticated:
return queryset return queryset
if self.container_field_set: 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 django.core.urlresolvers import reverse
from model_bakery import baker from model_bakery import baker
from sapl.crud.base import (CrispyLayoutFormMixin, CrudListView, from_to,
get_field_display, make_pagination) get_field_display, make_pagination)
from sapl.crud.tests.stub_app.models import Continent, Country from sapl.crud.tests.stub_app.models import Continent, Country
from sapl.crud.tests.stub_app.views import CountryCrud from sapl.crud.tests.stub_app.views import CountryCrud

2
sapl/crud/tests/test_masterdetail.py

@ -1,5 +1,5 @@
import pytest import pytest
from django.core.urlresolvers import reverse from django.urls import reverse
@pytest.mark.parametrize('path_name', [ @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.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist, ValidationError from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.core.files.base import File 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 import models, transaction
from django.db.models import F, Max, Q from django.db.models import F, Max, Q
from django.forms import ModelChoiceField, ModelForm, widgets 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?'),
),
]

154
sapl/materia/models.py

@ -234,12 +234,20 @@ class MateriaLegislativa(models.Model):
verbose_name=_('Em Tramitação?'), verbose_name=_('Em Tramitação?'),
default=False, default=False,
choices=YES_NO_CHOICES) choices=YES_NO_CHOICES)
polemica = models.NullBooleanField( polemica = models.BooleanField(
blank=True, verbose_name=_('Matéria Polêmica?')) null=True,
blank=True,
default=False,
verbose_name=_('Matéria Polêmica?')
)
objeto = models.CharField( objeto = models.CharField(
max_length=150, blank=True, verbose_name=_('Objeto')) max_length=150, blank=True, verbose_name=_('Objeto'))
complementar = models.NullBooleanField( complementar = models.BooleanField(
blank=True, verbose_name=_('É Complementar?')) null=True,
blank=True,
default=False,
verbose_name=_('É Complementar?')
)
ementa = models.TextField(verbose_name=_('Ementa')) ementa = models.TextField(verbose_name=_('Ementa'))
indexacao = models.TextField( indexacao = models.TextField(
blank=True, verbose_name=_('Indexação')) blank=True, verbose_name=_('Indexação'))
@ -754,42 +762,73 @@ class Parecer(models.Model):
@reversion.register() @reversion.register()
class Proposicao(models.Model): class Proposicao(models.Model):
autor = models.ForeignKey(Autor, autor = models.ForeignKey(
Autor,
null=True, null=True,
blank=True, blank=True,
on_delete=models.PROTECT) on_delete=models.PROTECT
tipo = models.ForeignKey(TipoProposicao, on_delete=models.PROTECT, )
tipo = models.ForeignKey(
TipoProposicao,
on_delete=models.PROTECT,
blank=False, blank=False,
null=True, null=True,
verbose_name=_('Tipo')) verbose_name=_('Tipo')
)
# XXX data_envio was not null, but actual data said otherwise!!! # XXX data_envio was not null, but actual data said otherwise!!!
data_envio = models.DateTimeField( 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( 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( 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')) descricao = models.TextField(verbose_name=_('Ementa'))
justificativa_devolucao = models.CharField( justificativa_devolucao = models.CharField(
max_length=200, max_length=200,
blank=True, blank=True,
verbose_name=_('Justificativa da Devolução')) verbose_name=_('Justificativa da Devolução')
)
ano = models.PositiveSmallIntegerField(verbose_name=_('Ano'), ano = models.PositiveSmallIntegerField(
default=None, blank=True, null=True, verbose_name=_('Ano'),
choices=RANGE_ANOS) default=None,
blank=True,
null=True,
choices=RANGE_ANOS
)
numero_proposicao = models.PositiveIntegerField( 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( 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'), hash_code = models.CharField(
verbose_name=_('Código do Documento'),
max_length=200, max_length=200,
blank=True) blank=True
)
""" """
FIXME Campo não é necessário na modelagem e implementação atual para o 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. sua proposição ou resolva excluir.
""" """
# ind_enviado and ind_devolvido collapsed as char field (status) # ind_enviado and ind_devolvido collapsed as char field (status)
status = models.CharField(blank=True, status = models.CharField(
blank=True,
max_length=1, max_length=1,
choices=(('E', 'Enviada'), choices=(('E', 'Enviada'),
('R', 'Recebida'), ('R', 'Recebida'),
('I', 'Incorporada')), ('I', 'Incorporada')),
verbose_name=_('Status Proposição')) verbose_name=_('Status Proposição')
)
texto_original = models.FileField( texto_original = models.FileField(
max_length=300, max_length=300,
upload_to=materia_upload_path, upload_to=materia_upload_path,
@ -820,37 +862,70 @@ class Proposicao(models.Model):
null=True, null=True,
verbose_name=_('Texto Original'), verbose_name=_('Texto Original'),
storage=OverwriteStorage(), storage=OverwriteStorage(),
validators=[restringe_tipos_de_arquivo_txt]) validators=[restringe_tipos_de_arquivo_txt]
)
texto_articulado = GenericRelation( texto_articulado = GenericRelation(
TextoArticulado, related_query_name='texto_articulado') TextoArticulado,
related_query_name='texto_articulado'
)
materia_de_vinculo = models.ForeignKey( materia_de_vinculo = models.ForeignKey(
MateriaLegislativa, blank=True, null=True, MateriaLegislativa,
blank=True,
null=True,
on_delete=models.CASCADE, on_delete=models.CASCADE,
verbose_name=_('Matéria anexadora'), verbose_name=_('Matéria anexadora'),
related_name=_('proposicao_set')) related_name=_('proposicao_set')
)
content_type = models.ForeignKey( content_type = models.ForeignKey(
ContentType, default=None, blank=True, null=True, ContentType,
verbose_name=_('Tipo de Material Gerado')) default=None,
blank=True,
null=True,
verbose_name=_('Tipo de Material Gerado'),
on_delete=models.PROTECT
)
object_id = models.PositiveIntegerField( object_id = models.PositiveIntegerField(
blank=True, null=True, default=None) blank=True,
null=True,
default=None
)
conteudo_gerado_related = SaplGenericForeignKey( 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( observacao = models.TextField(
blank=True, verbose_name=_('Observação')) blank=True,
cancelado = models.BooleanField(verbose_name=_('Cancelada ?'), verbose_name=_('Observação')
)
cancelado = models.BooleanField(
verbose_name=_('Cancelada ?'),
choices=YES_NO_CHOICES, choices=YES_NO_CHOICES,
default=False) 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( materia_gerada = models.ForeignKey(
MateriaLegislativa, blank=True, null=True, MateriaLegislativa,
related_name=_('materia_gerada')) blank=True,
null=True,
related_name=_('materia_gerada')
)
documento_gerado = models.ForeignKey( documento_gerado = models.ForeignKey(
DocumentoAcessorio, blank=True, null=True)""" DocumentoAcessorio,
blank=True,
null=True
)
"""
user = models.ForeignKey( user = models.ForeignKey(
get_settings_auth_user_model(), get_settings_auth_user_model(),
@ -859,15 +934,18 @@ class Proposicao(models.Model):
null=True, null=True,
blank=True blank=True
) )
ip = models.CharField( ip = models.CharField(
verbose_name=_('IP'), verbose_name=_('IP'),
max_length=60, max_length=60,
blank=True, blank=True,
default='' default=''
) )
ultima_edicao = models.DateTimeField( ultima_edicao = models.DateTimeField(
verbose_name=_('Data e Hora da Edição'), verbose_name=_('Data e Hora da Edição'),
blank=True, null=True blank=True,
null=True
) )
@property @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.auth import get_user_model
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.files.uploadedfile import SimpleUploadedFile 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 django.db.models import Max
from model_bakery import baker from model_bakery import baker
import pytest 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['numero'] == [_('Este campo é obrigatório.')]
assert errors['ementa'] == [_('Este campo é obrigatório.')] assert errors['ementa'] == [_('Este campo é obrigatório.')]
assert errors['regime_tramitacao'] == [_('Este campo é obrigatório.')] assert errors['regime_tramitacao'] == [_('Este campo é obrigatório.')]
assert errors['em_tramitacao'] == [_('Este campo é obrigatório.')]
assert len(errors) == 6 assert len(errors) == 7
@pytest.mark.django_db(transaction=False) @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['nome'] == [_('Este campo é obrigatório.')]
assert errors['sigla'] == [_('Este campo é obrigatório.')] assert errors['sigla'] == [_('Este campo é obrigatório.')]
assert errors['unidade_deliberativa'] == [_('Este campo é obrigatório.')]
assert len(errors) == 2 assert len(errors) == 3
@pytest.mark.django_db(transaction=False) @pytest.mark.django_db(transaction=False)

2
sapl/materia/tests/test_materia_urls.py

@ -1,5 +1,5 @@
import pytest import pytest
from django.core.urlresolvers import reverse from django.urls import reverse
@pytest.mark.parametrize("test_input,kwargs,expected", [ @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.decorators import permission_required
from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.auth.mixins import PermissionRequiredMixin
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned, ValidationError 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.db.models import Max, Q
from django.http import HttpResponse, JsonResponse from django.http import HttpResponse, JsonResponse
from django.http.response import Http404, HttpResponseRedirect 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')) blank=True, verbose_name=_('Indexação'))
observacao = models.TextField( observacao = models.TextField(
blank=True, verbose_name=_('Observação')) blank=True, verbose_name=_('Observação'))
complemento = models.NullBooleanField( complemento = models.BooleanField(
blank=True, verbose_name=_('Complementar ?'), null=True,
choices=YES_NO_CHOICES) blank=True,
default=False,
verbose_name=_('Complementar ?'),
choices=YES_NO_CHOICES
)
# XXX was a CharField (attention on migrate) # XXX was a CharField (attention on migrate)
assuntos = models.ManyToManyField( assuntos = models.ManyToManyField(
AssuntoNorma, blank=True, 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 django.utils.translation import ugettext_lazy as _
from model_bakery import baker from model_bakery import baker
import pytest import pytest

2
sapl/norma/views.py

@ -4,7 +4,7 @@ import re
from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.auth.mixins import PermissionRequiredMixin
from django.core.exceptions import ObjectDoesNotExist 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.http import HttpResponse, JsonResponse
from django.template import RequestContext, loader from django.template import RequestContext, loader
from django.utils import timezone from django.utils import timezone

2
sapl/painel/views.py

@ -5,7 +5,7 @@ import logging
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.decorators import user_passes_test from django.contrib.auth.decorators import user_passes_test
from django.core.exceptions import ObjectDoesNotExist 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.db.models import Q
from django.http import HttpResponse, JsonResponse from django.http import HttpResponse, JsonResponse
from django.http.response import Http404, HttpResponseRedirect from django.http.response import Http404, HttpResponseRedirect

13
sapl/parlamentares/tests/test_parlamentares.py

@ -1,5 +1,5 @@
import pytest import pytest
from django.core.urlresolvers import reverse from django.urls import reverse
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from model_bakery import baker from model_bakery import baker
@ -33,7 +33,8 @@ def test_incluir_parlamentar_errors(admin_client):
url = reverse('sapl.parlamentares:parlamentar_create') url = reverse('sapl.parlamentares:parlamentar_create')
response = admin_client.post(url) response = admin_client.post(url)
erros_esperados = {campo: ['Este campo é obrigatório.'] erros_esperados = {campo: ['Este campo é obrigatório.']
for campo in ['nome_parlamentar', for campo in ['ativo',
'nome_parlamentar',
'nome_completo', 'nome_completo',
'sexo', 'sexo',
]} ]}
@ -113,7 +114,7 @@ def test_mandato_submit(admin_client):
baker.make(Parlamentar, pk=14) baker.make(Parlamentar, pk=14)
baker.make(Legislatura, pk=5) 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}), kwargs={'pk': 14}),
{ {
'parlamentar': 14, # hidden field 'parlamentar': 14, # hidden field
@ -124,10 +125,10 @@ def test_mandato_submit(admin_client):
Legislatura.objects.get(id=5).data_fim, Legislatura.objects.get(id=5).data_fim,
'data_expedicao_diploma': '2016-03-22', 'data_expedicao_diploma': '2016-03-22',
'observacao': 'Observação do mandato', 'observacao': 'Observação do mandato',
'salvar': 'salvar' 'salvar': 'salvar',
'titular': True
}, },
follow=True) follow=True)
mandato = Mandato.objects.first() mandato = Mandato.objects.first()
assert str(_('Observação do mandato')) == str(_(mandato.observacao)) assert str(_('Observação do mandato')) == str(_(mandato.observacao))
@ -192,6 +193,7 @@ def test_mandato_form_datas_invalidas():
form = MandatoForm(data={ form = MandatoForm(data={
'parlamentar': str(parlamentar.pk), 'parlamentar': str(parlamentar.pk),
'legislatura': str(legislatura.pk), 'legislatura': str(legislatura.pk),
'titular': True,
'data_expedicao_diploma': '2016-11-01', 'data_expedicao_diploma': '2016-11-01',
'data_inicio_mandato': '2016-12-12', 'data_inicio_mandato': '2016-12-12',
'data_fim_mandato': '2019-10-09' 'data_fim_mandato': '2019-10-09'
@ -204,6 +206,7 @@ def test_mandato_form_datas_invalidas():
form = MandatoForm(data={ form = MandatoForm(data={
'parlamentar': str(parlamentar.pk), 'parlamentar': str(parlamentar.pk),
'legislatura': str(legislatura.pk), 'legislatura': str(legislatura.pk),
'titular': True,
'data_expedicao_diploma': '2016-11-01', 'data_expedicao_diploma': '2016-11-01',
'data_inicio_mandato': '2017-02-02', 'data_inicio_mandato': '2017-02-02',
'data_fim_mandato': '2022-01-01' '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.auth.mixins import PermissionRequiredMixin
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist 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 import F, Q
from django.db.models.aggregates import Count from django.db.models.aggregates import Count
from django.http import JsonResponse 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): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('protocoloadm', '0032_auto_20200416_1538'), ('protocoloadm', '0033_auto_20200521_1534'),
] ]
operations = [ operations = [

8
sapl/protocoloadm/tests/test_protocoloadm.py

@ -1,6 +1,6 @@
from datetime import date, timedelta from datetime import date, timedelta
from django.core.urlresolvers import reverse from django.urls import reverse
from django.utils import timezone from django.utils import timezone
from django.utils.encoding import force_text from django.utils.encoding import force_text
from django.utils.translation import ugettext_lazy as _ 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['numero'] == [_('Este campo é obrigatório.')]
assert errors['data'] == [_('Este campo é obrigatório.')] assert errors['data'] == [_('Este campo é obrigatório.')]
assert errors['restrito'] == [_('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) @pytest.mark.django_db(transaction=False)
@ -402,7 +403,8 @@ def test_documento_administrativo_protocolo_inexistente():
'data': '2017-10-10', 'data': '2017-10-10',
'numero_protocolo': '11', 'numero_protocolo': '11',
'ano_protocolo': '2017', 'ano_protocolo': '2017',
'restrito': False 'restrito': False,
'tramitacao': False
}) })
assert not form.is_valid() 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.auth.mixins import PermissionRequiredMixin
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.core.urlresolvers import reverse from django.urls import reverse
from django.db import transaction from django.db import transaction
from django.db.models import Max, Q from django.db.models import Max, Q
from django.http import Http404, HttpResponse, JsonResponse from django.http import Http404, HttpResponse, JsonResponse
@ -95,7 +95,7 @@ def recuperar_materia_protocolo(request):
def doc_texto_integral(request, pk): def doc_texto_integral(request, pk):
can_see = True can_see = True
if not request.user.is_authenticated(): if not request.user.is_authenticated:
app_config = AppConfig.objects.last() app_config = AppConfig.objects.last()
if app_config and app_config.documentos_administrativos == 'R': if app_config and app_config.documentos_administrativos == 'R':
can_see = False can_see = False
@ -414,7 +414,7 @@ class DocumentoAdministrativoCrud(Crud):
def get(self, *args, **kwargs): def get(self, *args, **kwargs):
pk = self.kwargs['pk'] pk = self.kwargs['pk']
documento = DocumentoAdministrativo.objects.get(id=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 redirect('/')
return super(Crud.DetailView, self).get(args, kwargs) 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 # é usada essa verificação anônima para quando os documentos administrativos
# estão no modo ostensivo, mas podem existir documentos administrativos # estão no modo ostensivo, mas podem existir documentos administrativos
# restritos # restritos
if request.user.is_anonymous(): if request.user.is_anonymous:
length = self.object_list.filter(restrito=False).count() length = self.object_list.filter(restrito=False).count()
else: else:
length = self.object_list.count() 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 from django.test import TestCase
MovedPermanentlyHTTPStatusCode = 301 MovedPermanentlyHTTPStatusCode = 301

2
sapl/redireciona_urls/views.py

@ -1,6 +1,6 @@
import logging import logging
from django.core.urlresolvers import NoReverseMatch, reverse from django.urls import NoReverseMatch, reverse
from django.views.generic import RedirectView from django.views.generic import RedirectView
from sapl.audiencia.apps import AppConfig as audienciaConfig 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.core import exceptions
from django.db import models, router from django.db import models, router
from django.db.utils import DEFAULT_DB_ALIAS from django.db.utils import DEFAULT_DB_ALIAS
from django.utils.translation import string_concat
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
import reversion import reversion
@ -55,13 +54,9 @@ def create_proxy_permissions(
opts = klass._meta opts = klass._meta
permissions = ( permissions = (
("list_" + opts.model_name, ("list_" + opts.model_name,
string_concat( "{} {}".format(_('Visualizaçao da lista de'), opts.verbose_name_plural)),
_('Visualizaçao da lista de'), ' ',
opts.verbose_name_plural)),
("detail_" + opts.model_name, ("detail_" + opts.model_name,
string_concat( "{} {}".format(_('Visualização dos detalhes de'), opts.verbose_name_plural)),
_('Visualização dos detalhes de'), ' ',
opts.verbose_name_plural)),
) )
opts.permissions = tuple( opts.permissions = tuple(
set(list(permissions) + list(opts.permissions))) set(list(permissions) + list(opts.permissions)))
@ -216,9 +211,7 @@ def get_rules():
def update_groups(self): def update_groups(self):
print('') print('')
print(string_concat('\033[93m\033[1m', print("\033[93m\033[1m{}\033[0m".format(_('Atualizando grupos do SAPL:')))
_('Atualizando grupos do SAPL:'),
'\033[0m'))
for rules_group in self.rules_patterns: for rules_group in self.rules_patterns:
group_name = rules_group['group'] group_name = rules_group['group']
rules_list = rules_group['rules'] 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'),
),
]

40
sapl/sessao/models.py

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

2
sapl/sessao/tests/test_sessao_view.py

@ -1,5 +1,5 @@
import pytest import pytest
from django.core.urlresolvers import reverse from django.urls import reverse
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from model_bakery import baker 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.decorators import permission_required
from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.auth.mixins import PermissionRequiredMixin
from django.core.exceptions import ObjectDoesNotExist 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.db.models import Max, Q
from django.http import JsonResponse from django.http import JsonResponse
from django.http.response import Http404, HttpResponseRedirect from django.http.response import Http404, HttpResponseRedirect
@ -1364,7 +1364,7 @@ class PainelView(PermissionRequiredForAppCrudMixin, TemplateView):
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
if request.user.is_anonymous(): if request.user.is_anonymous:
self.template_name = 'painel/index.html' self.template_name = 'painel/index.html'
request.session['discurso'] = 'stop' request.session['discurso'] = 'stop'

5
sapl/settings.py

@ -136,10 +136,10 @@ MIDDLEWARE = [
'django.middleware.common.CommonMiddleware', 'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware', 'django.middleware.security.SecurityMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware',
] ]
if DEBUG: if DEBUG:
INSTALLED_APPS += ('debug_toolbar', ) INSTALLED_APPS += ('debug_toolbar', )
@ -311,6 +311,8 @@ CRISPY_ALLOWED_TEMPLATE_PACKS = 'bootstrap4'
CRISPY_FAIL_SILENTLY = not DEBUG CRISPY_FAIL_SILENTLY = not DEBUG
FLOPPY_FORMS_USE_GIS = False FLOPPY_FORMS_USE_GIS = False
FORM_RENDERER = 'django.forms.renderers.DjangoTemplates'
# suprime texto de ajuda default do django-filter # suprime texto de ajuda default do django-filter
FILTERS_HELP_TEXT_FILTER = False FILTERS_HELP_TEXT_FILTER = False
@ -367,5 +369,6 @@ def remove_warnings():
message='Unable to import floppyforms.gis' message='Unable to import floppyforms.gis'
) )
LOGOUT_REDIRECT_URL = '/login'
remove_warnings() 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.contrib.contenttypes.models import ContentType
from django.db import transaction from django.db import transaction
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.utils.translation import string_concat
from sapl.crud.base import PermissionRequiredForAppCrudMixin from sapl.crud.base import PermissionRequiredForAppCrudMixin
from sapl.rules.apps import AppConfig, update_groups from sapl.rules.apps import AppConfig, update_groups
@ -30,13 +29,9 @@ def create_perms_post_migrate(sapl_app_config):
opts = klass._meta opts = klass._meta
permissions = ( permissions = (
("list_" + opts.model_name, ("list_" + opts.model_name,
string_concat( "{} {}".format(_('Visualização da lista de'), opts.verbose_name_plural)),
_('Visualizaçao da lista de'), ' ',
opts.verbose_name_plural)),
("detail_" + opts.model_name, ("detail_" + opts.model_name,
string_concat( "{} {}".format(_('Visualização dos detalhes de'), opts.verbose_name_plural)),
_('Visualização dos detalhes de'), ' ',
opts.verbose_name_plural)),
) )
opts.permissions = tuple( opts.permissions = tuple(
set(list(permissions) + list(opts.permissions))) set(list(permissions) + list(opts.permissions)))

2
sapl/urls.py

@ -39,7 +39,7 @@ urlpatterns = [
url(r'^$', TemplateView.as_view(template_name='index.html'), url(r'^$', TemplateView.as_view(template_name='index.html'),
name='sapl_index'), name='sapl_index'),
url(r'^message$', TemplateView.as_view(template_name='base.html')), 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.comissoes.urls)),
url(r'', include(sapl.sessao.urls)), url(r'', include(sapl.sessao.urls)),

4
scripts/lista_urls.py

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

Loading…
Cancel
Save