Browse Source

Merge master

pull/792/head
Eduardo Calil 9 years ago
parent
commit
b14b11389c
  1. 2
      requirements/requirements.txt
  2. 2
      sapl/api/forms.py
  3. 1
      sapl/api/serializers.py
  4. 6
      sapl/api/urls.py
  5. 9
      sapl/api/views.py
  6. 108
      sapl/base/apps.py
  7. 15
      sapl/base/forms.py
  8. 8
      sapl/base/legacy.yaml
  9. 10
      sapl/base/models.py
  10. 26
      sapl/base/templatetags/common_tags.py
  11. 3
      sapl/base/templatetags/menus.py
  12. 2
      sapl/base/views.py
  13. 28
      sapl/compilacao/forms.py
  14. 19
      sapl/compilacao/migrations/0059_auto_20161027_1323.py
  15. 19
      sapl/compilacao/migrations/0060_auto_20161101_0913.py
  16. 21
      sapl/compilacao/migrations/0061_auto_20161101_1025.py
  17. 19
      sapl/compilacao/migrations/0062_auto_20161101_1221.py
  18. 20
      sapl/compilacao/migrations/0063_tipotextoarticulado_publicacao_func.py
  19. 46
      sapl/compilacao/migrations/0064_auto_20161104_1420.py
  20. 20
      sapl/compilacao/migrations/0065_auto_20161107_1024.py
  21. 20
      sapl/compilacao/migrations/0066_auto_20161107_1028.py
  22. 19
      sapl/compilacao/migrations/0067_auto_20161107_1351.py
  23. 19
      sapl/compilacao/migrations/0068_auto_20161107_1546.py
  24. 20
      sapl/compilacao/migrations/0069_auto_20161107_1932.py
  25. 169
      sapl/compilacao/models.py
  26. 6
      sapl/compilacao/templatetags/compilacao_filters.py
  27. 380
      sapl/compilacao/views.py
  28. 3
      sapl/crispy_layout_mixin.py
  29. 10
      sapl/crud/base.py
  30. 130
      sapl/legacy/migration.py
  31. 200
      sapl/materia/forms.py
  32. 23
      sapl/materia/legacy.yaml
  33. 13
      sapl/materia/models.py
  34. 6
      sapl/materia/tests/test_materia.py
  35. 7
      sapl/materia/urls.py
  36. 176
      sapl/materia/views.py
  37. 40
      sapl/norma/forms.py
  38. 23
      sapl/norma/migrations/0016_auto_20161027_1419.py
  39. 20
      sapl/norma/migrations/0017_auto_20161027_1432.py
  40. 26
      sapl/norma/migrations/0018_auto_20161027_1434.py
  41. 19
      sapl/norma/migrations/0019_auto_20161028_0232.py
  42. 39
      sapl/norma/migrations/0020_auto_20161028_1335.py
  43. 24
      sapl/norma/migrations/0021_auto_20161028_1335.py
  44. 14
      sapl/norma/models.py
  45. 6
      sapl/norma/urls.py
  46. 22
      sapl/norma/views.py
  47. 6
      sapl/painel/views.py
  48. 34
      sapl/parlamentares/views.py
  49. 79
      sapl/protocoloadm/forms.py
  50. 19
      sapl/protocoloadm/migrations/0005_auto_20161027_1741.py
  51. 20
      sapl/protocoloadm/migrations/0006_auto_20161103_1721.py
  52. 11
      sapl/protocoloadm/models.py
  53. 2
      sapl/protocoloadm/tests/test_protocoloadm.py
  54. 48
      sapl/protocoloadm/urls.py
  55. 192
      sapl/protocoloadm/views.py
  56. 2
      sapl/relatorios/views.py
  57. 38
      sapl/rules/__init__.py
  58. 234
      sapl/rules/apps.py
  59. 317
      sapl/rules/map_rules.py
  60. 0
      sapl/rules/migrations/__init__.py
  61. 0
      sapl/rules/models.py
  62. 255
      sapl/rules/tests/test_rules.py
  63. 21
      sapl/sessao/forms.py
  64. 27
      sapl/sessao/views.py
  65. 9
      sapl/settings.py
  66. 9
      sapl/static/styles/compilacao.scss
  67. 3
      sapl/templates/base.html
  68. 49
      sapl/templates/base/appconfig_list.html
  69. 16
      sapl/templates/compilacao/ajax_actions_dinamic_edit.html
  70. 8
      sapl/templates/compilacao/dispositivo_form.html
  71. 2
      sapl/templates/compilacao/publicacao_detail.html
  72. 2
      sapl/templates/compilacao/publicacao_list.html
  73. 8
      sapl/templates/compilacao/text_edit.html
  74. 16
      sapl/templates/compilacao/text_list.html
  75. 21
      sapl/templates/compilacao/text_list__print_version.html
  76. 21
      sapl/templates/compilacao/textoarticulado_detail.html
  77. 2
      sapl/templates/compilacao/textoarticulado_list.html
  78. 14
      sapl/templates/compilacao/textoarticulado_menu_config.html
  79. 20
      sapl/templates/compilacao/tipotextoarticulado_detail.html
  80. 2
      sapl/templates/compilacao/tipotextoarticulado_list.html
  81. 11
      sapl/templates/crud/detail.html
  82. 11
      sapl/templates/crud/detail_detail.html
  83. 4
      sapl/templates/materia/confirmar_proposicao.html
  84. 1
      sapl/templates/materia/proposicao_detail.html
  85. 5
      sapl/templates/materia/tipoproposicao_form.html
  86. 2
      sapl/templates/navbar.yaml
  87. 7
      sapl/templates/norma/layouts.yaml
  88. 1
      sapl/templates/norma/list_pesquisa.html
  89. 29
      sapl/templates/norma/normajuridica_form.html
  90. 2
      sapl/templates/norma/subnav.yaml
  91. 4
      sapl/templates/painel/controlador.html
  92. 29
      sapl/templates/protocoloadm/MateriaTemplate.html
  93. 10
      sapl/templates/protocoloadm/anular_protocoloadm.html
  94. 4
      sapl/templates/protocoloadm/comprovante.html
  95. 9
      sapl/templates/protocoloadm/protocolar_documento.html
  96. 8
      sapl/templates/protocoloadm/protocolar_materia.html
  97. 22
      sapl/templates/protocoloadm/protocolo_filter.html
  98. 25
      sapl/templates/protocoloadm/protocolo_mostrar.html
  99. 10
      sapl/templates/protocoloadm/protocoloadm_detail.html
  100. 2
      sapl/templates/sessao/painel.html

2
requirements/requirements.txt

@ -8,7 +8,7 @@ django-compressor==2.0
django-crispy-forms==1.6.0 django-crispy-forms==1.6.0
django-extensions==1.6.7 django-extensions==1.6.7
django-extra-views==0.8.0 django-extra-views==0.8.0
django-filter==0.13.0 django-filter==0.15.3
django-floppyforms==1.6.2 django-floppyforms==1.6.2
django-model-utils==2.5 django-model-utils==2.5
django-sass-processor==0.4.6 django-sass-processor==0.4.6

2
sapl/api/forms.py

@ -1,6 +1,7 @@
from django.db.models import Q from django.db.models import Q
from django_filters.filters import MethodFilter, ModelChoiceFilter from django_filters.filters import MethodFilter, ModelChoiceFilter
from rest_framework.filters import FilterSet from rest_framework.filters import FilterSet
from sapl.base.models import Autor, TipoAutor from sapl.base.models import Autor, TipoAutor
from sapl.utils import generic_relations_for_model from sapl.utils import generic_relations_for_model
@ -21,7 +22,6 @@ class SaplGenericRelationSearchFilterSet(FilterSet):
order_by = [] order_by = []
for gr in generic_relations_for_model(self._meta.model): for gr in generic_relations_for_model(self._meta.model):
model = gr[0]
sgr = gr[1] sgr = gr[1]
for item in sgr: for item in sgr:
if item.related_model != self._meta.model: if item.related_model != self._meta.model:

1
sapl/api/serializers.py

@ -1,4 +1,5 @@
from rest_framework import serializers from rest_framework import serializers
from sapl.base.models import Autor from sapl.base.models import Autor
from sapl.materia.models import MateriaLegislativa from sapl.materia.models import MateriaLegislativa

6
sapl/api/urls.py

@ -1,9 +1,9 @@
from django.conf import settings from django.conf import settings
from django.conf.urls import url, include from django.conf.urls import include, url
from rest_framework.routers import DefaultRouter from rest_framework.routers import DefaultRouter
from sapl.api.views import MateriaLegislativaViewSet, AutorListView,\ from sapl.api.views import (AutorListView, MateriaLegislativaViewSet,
ModelChoiceView ModelChoiceView)
from .apps import AppConfig from .apps import AppConfig

9
sapl/api/views.py

@ -5,13 +5,14 @@ from django.utils.translation import ugettext_lazy as _
from rest_framework.filters import DjangoFilterBackend from rest_framework.filters import DjangoFilterBackend
from rest_framework.generics import ListAPIView from rest_framework.generics import ListAPIView
from rest_framework.mixins import ListModelMixin, RetrieveModelMixin from rest_framework.mixins import ListModelMixin, RetrieveModelMixin
from rest_framework.permissions import IsAuthenticated, AllowAny from rest_framework.permissions import IsAuthenticated
from rest_framework.viewsets import GenericViewSet from rest_framework.viewsets import GenericViewSet
from sapl.api.forms import AutorChoiceFilterSet from sapl.api.forms import AutorChoiceFilterSet
from sapl.api.serializers import ChoiceSerializer, AutorSerializer,\ from sapl.api.serializers import (AutorChoiceSerializer, AutorSerializer,
AutorChoiceSerializer, ModelChoiceSerializer, MateriaLegislativaSerializer ChoiceSerializer,
MateriaLegislativaSerializer,
ModelChoiceSerializer)
from sapl.base.models import Autor, TipoAutor from sapl.base.models import Autor, TipoAutor
from sapl.materia.models import MateriaLegislativa from sapl.materia.models import MateriaLegislativa
from sapl.utils import SaplGenericRelation, sapl_logger from sapl.utils import SaplGenericRelation, sapl_logger

108
sapl/base/apps.py

@ -1,115 +1,9 @@
from builtins import LookupError
from django.apps import apps
from django.contrib.auth.management import _get_all_permissions
from django.core import exceptions
from django.db import router
from django.db.models.signals import pre_migrate, post_migrate
from django.db.utils import DEFAULT_DB_ALIAS
from django.utils.translation import string_concat
from django.utils.translation import ugettext_lazy as _
import django import django
from django.utils.translation import ugettext_lazy as _
def create_proxy_permissions(
app_config, verbosity=2, interactive=True,
using=DEFAULT_DB_ALIAS, **kwargs):
if not app_config.models_module:
return
# print(app_config)
try:
Permission = apps.get_model('auth', 'Permission')
except LookupError:
return
if not router.allow_migrate_model(using, Permission):
return
from django.contrib.contenttypes.models import ContentType
permission_name_max_length = Permission._meta.get_field('name').max_length
# This will hold the permissions we're looking for as
# (content_type, (codename, name))
searched_perms = list()
# The codenames and ctypes that should exist.
ctypes = set()
for klass in list(app_config.get_models()):
opts = klass._meta
permissions = (
("list_" + opts.model_name,
string_concat(
_('Visualizaçao da lista de'), ' ',
opts.verbose_name_plural)),
("detail_" + opts.model_name,
string_concat(
_('Visualização dos detalhes de'), ' ',
opts.verbose_name_plural)),
)
opts.permissions = tuple(
set(list(permissions) + list(opts.permissions)))
if opts.proxy:
# Force looking up the content types in the current database
# before creating foreign keys to them.
app_label, model = opts.app_label, opts.model_name
try:
ctype = ContentType.objects.db_manager(
using).get_by_natural_key(app_label, model)
except:
ctype = ContentType.objects.db_manager(
using).create(app_label=app_label, model=model)
else:
ctype = ContentType.objects.db_manager(using).get_for_model(klass)
ctypes.add(ctype)
for perm in _get_all_permissions(klass._meta, ctype):
searched_perms.append((ctype, perm))
# Find all the Permissions that have a content_type for a model we're
# looking for. We don't need to check for codenames since we already have
# a list of the ones we're going to create.
all_perms = set(Permission.objects.using(using).filter(
content_type__in=ctypes,
).values_list(
"content_type", "codename"
))
perms = [
Permission(codename=codename, name=name, content_type=ct)
for ct, (codename, name) in searched_perms
if (ct.pk, codename) not in all_perms
]
# Validate the permissions before bulk_creation to avoid cryptic database
# error when the name is longer than 255 characters
for perm in perms:
if len(perm.name) > permission_name_max_length:
raise exceptions.ValidationError(
'The permission name %s of %s.%s '
'is longer than %s characters' % (
perm.name,
perm.content_type.app_label,
perm.content_type.model,
permission_name_max_length,
)
)
Permission.objects.using(using).bulk_create(perms)
if verbosity >= 2:
for perm in perms:
print("Adding permission '%s'" % perm)
class AppConfig(django.apps.AppConfig): class AppConfig(django.apps.AppConfig):
name = 'sapl.base' name = 'sapl.base'
label = 'base' label = 'base'
verbose_name = _('Dados Básicos') verbose_name = _('Dados Básicos')
def ready(self):
#pre_migrate.connect(run_sql_organizers, self)
post_migrate.connect(
receiver=create_proxy_permissions,
dispatch_uid="django.contrib.auth.management.create_permissions")

15
sapl/base/forms.py

@ -1,22 +1,17 @@
import django_filters
from crispy_forms.bootstrap import FieldWithButtons, InlineRadios, StrictButton from crispy_forms.bootstrap import FieldWithButtons, InlineRadios, StrictButton
from crispy_forms.helper import FormHelper from crispy_forms.helper import FormHelper
from crispy_forms.layout import HTML, Button, Div, Field, Fieldset, Layout, Row from crispy_forms.layout import HTML, Button, Div, Field, Fieldset, Layout, Row
from crispy_forms.templatetags.crispy_forms_field import css_class
from django import forms from django import forms
from django.conf import settings from django.conf import settings
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.contrib.auth.forms import AuthenticationForm from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth.models import Group from django.contrib.auth.models import Group
from django.contrib.auth.password_validation import validate_password from django.contrib.auth.password_validation import validate_password
from django.contrib.contenttypes.fields import GenericRel
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db import models, transaction from django.db import models, transaction
from django.forms import ModelForm from django.forms import ModelForm
from django.utils.translation import ugettext_lazy as _, string_concat
import django_filters
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.utils.translation import string_concat from django.utils.translation import string_concat
@ -26,11 +21,9 @@ from sapl.crispy_layout_mixin import (SaplFormLayout, form_actions, to_column,
from sapl.materia.models import MateriaLegislativa from sapl.materia.models import MateriaLegislativa
from sapl.sessao.models import SessaoPlenaria from sapl.sessao.models import SessaoPlenaria
from sapl.settings import MAX_IMAGE_UPLOAD_SIZE from sapl.settings import MAX_IMAGE_UPLOAD_SIZE
from sapl.utils import (RANGE_ANOS, ImageThumbnailFileInput, from sapl.utils import (RANGE_ANOS, ChoiceWithoutValidationField,
RangeWidgetOverride, autor_label, autor_modal, ImageThumbnailFileInput, RangeWidgetOverride,
autor_label, autor_modal, models_with_gr_for_model)
SaplGenericRelation, models_with_gr_for_model,
ChoiceWithoutValidationField)
from .models import AppConfig, CasaLegislativa from .models import AppConfig, CasaLegislativa

8
sapl/base/legacy.yaml

@ -0,0 +1,8 @@
TipoAutor:
descricao: des_tipo_autor
Autor:
nome: nom_autor
cargo: des_cargo
tipo: tip_autor
username: col_username

10
sapl/base/models.py

@ -1,18 +1,10 @@
from builtins import LookupError
from django.apps import apps
from django.contrib.auth.management import _get_all_permissions
from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core import exceptions from django.db import models
from django.db import models, router
from django.db.utils import DEFAULT_DB_ALIAS
from django.utils.translation import string_concat
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from sapl.utils import UF, YES_NO_CHOICES, get_settings_auth_user_model from sapl.utils import UF, YES_NO_CHOICES, get_settings_auth_user_model
TIPO_DOCUMENTO_ADMINISTRATIVO = (('O', _('Ostensivo')), TIPO_DOCUMENTO_ADMINISTRATIVO = (('O', _('Ostensivo')),
('R', _('Restritivo'))) ('R', _('Restritivo')))

26
sapl/base/templatetags/common_tags.py

@ -3,7 +3,6 @@ from django import template
from sapl.base.models import AppConfig from sapl.base.models import AppConfig
from sapl.parlamentares.models import Filiacao from sapl.parlamentares.models import Filiacao
from sapl.utils import permissoes_adm
register = template.Library() register = template.Library()
@ -87,26 +86,6 @@ def get_delete_perm(value, arg):
return perm.__contains__(nome_app + can_delete) return perm.__contains__(nome_app + can_delete)
@register.filter
def get_doc_adm_template_perms(user):
app_config = AppConfig.objects.last()
if app_config:
if app_config.documentos_administrativos == 'O':
return True
return user.has_perms(permissoes_adm())
@register.filter
def ver_menu_sistema_perm(value):
u = value
if u.groups.filter(name='Operador Geral').exists() or u.is_superuser:
return True
else:
return False
@register.filter @register.filter
def ultima_filiacao(value): def ultima_filiacao(value):
parlamentar = value parlamentar = value
@ -120,11 +99,6 @@ def ultima_filiacao(value):
return None return None
@register.filter
def get_config_not_exists(user):
return not AppConfig.objects.exists()
@register.filter @register.filter
def get_config_attr(attribute): def get_config_attr(attribute):
return AppConfig.attr(attribute) return AppConfig.attr(attribute)

3
sapl/base/templatetags/menus.py

@ -1,11 +1,10 @@
import yaml
from django import template from django import template
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
import yaml
from sapl.utils import sapl_logger from sapl.utils import sapl_logger
register = template.Library() register = template.Library()

2
sapl/base/views.py

@ -13,8 +13,8 @@ from django.utils.http import urlsafe_base64_decode, urlsafe_base64_encode
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_filters.views import FilterView from django_filters.views import FilterView
from sapl.base.forms import AutorForm, TipoAutorForm, AutorFormForAdmin
from sapl.base.forms import AutorForm, AutorFormForAdmin, TipoAutorForm
from sapl.base.models import Autor, TipoAutor from sapl.base.models import Autor, TipoAutor
from sapl.crud.base import CrudAux from sapl.crud.base import CrudAux
from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa

28
sapl/compilacao/forms.py

@ -42,10 +42,18 @@ class TipoTaForm(ModelForm):
label=TipoTextoArticulado._meta.get_field( label=TipoTextoArticulado._meta.get_field(
'descricao').verbose_name) 'descricao').verbose_name)
participacao_social = forms.NullBooleanField( participacao_social = forms.ChoiceField(
label=TipoTextoArticulado._meta.get_field( label=TipoTextoArticulado._meta.get_field(
'participacao_social').verbose_name, 'participacao_social').verbose_name,
widget=forms.Select(choices=YES_NO_CHOICES), choices=YES_NO_CHOICES,
widget=forms.RadioSelect(),
required=True)
publicacao_func = forms.ChoiceField(
label=TipoTextoArticulado._meta.get_field(
'publicacao_func').verbose_name,
choices=YES_NO_CHOICES,
widget=forms.RadioSelect(),
required=True) required=True)
class Meta: class Meta:
@ -54,21 +62,27 @@ class TipoTaForm(ModelForm):
'descricao', 'descricao',
'content_type', 'content_type',
'participacao_social', 'participacao_social',
'publicacao_func'
] ]
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
row1 = to_row([ row1 = to_row([
('sigla', 2), ('sigla', 3),
('descricao', 4), ('descricao', 5),
('content_type', 3), ('content_type', 4),
('participacao_social', 3), ])
row2 = to_row([
(InlineRadios('participacao_social'), 3),
(InlineRadios('publicacao_func'), 3),
]) ])
self.helper = FormHelper() self.helper = FormHelper()
self.helper.layout = SaplFormLayout( self.helper.layout = SaplFormLayout(
Fieldset(_('Identificação Básica'), Fieldset(_('Identificação Básica'),
row1, css_class="col-md-12")) row1, css_class="col-md-12"),
Fieldset(_('Funcionalidades'),
row2, css_class="col-md-12"))
super(TipoTaForm, self).__init__(*args, **kwargs) super(TipoTaForm, self).__init__(*args, **kwargs)

19
sapl/compilacao/migrations/0059_auto_20161027_1323.py

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-10-27 13:23
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('compilacao', '0058_auto_20161008_0143'),
]
operations = [
migrations.AlterModelOptions(
name='dispositivo',
options={'ordering': ['ta', 'ordem'], 'permissions': (('change_dispositivo_edicao_dinamica', 'Permissão de edição de dispositivos originais via editor dinâmico.'), ('change_dispositivo_edicao_avancada', 'Permissão de edição de dispositivos originais via formulários de edição avançada.'), ('change_dispositivo_registros_compilacao', 'Permissão de registro de compilação via editor dinâmico.'), ('change_dispositivo_notificacoes', 'Permissão de acesso às notificações de pendências.')), 'verbose_name': 'Dispositivo', 'verbose_name_plural': 'Dispositivos'},
),
]

19
sapl/compilacao/migrations/0060_auto_20161101_0913.py

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-11-01 09:13
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('compilacao', '0059_auto_20161027_1323'),
]
operations = [
migrations.AlterModelOptions(
name='dispositivo',
options={'ordering': ['ta', 'ordem'], 'permissions': (('change_dispositivo_edicao_dinamica', 'Permissão de edição de dispositivos originais via editor dinâmico.'), ('change_dispositivo_edicao_avancada', 'Permissão de edição de dispositivos originais via formulários de edição avançada.'), ('change_dispositivo_registros_compilacao', 'Permissão de registro de compilação via editor dinâmico.'), ('view_dispositivo_notificacoes', 'Permissão de acesso às notificações de pendências.')), 'verbose_name': 'Dispositivo', 'verbose_name_plural': 'Dispositivos'},
),
]

21
sapl/compilacao/migrations/0061_auto_20161101_1025.py

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-11-01 10:25
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('compilacao', '0060_auto_20161101_0913'),
]
operations = [
migrations.AlterField(
model_name='dispositivo',
name='ta',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='dispositivos_set', to='compilacao.TextoArticulado', verbose_name='Texto Articulado'),
),
]

19
sapl/compilacao/migrations/0062_auto_20161101_1221.py

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-11-01 12:21
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('compilacao', '0061_auto_20161101_1025'),
]
operations = [
migrations.AlterModelOptions(
name='dispositivo',
options={'ordering': ['ta', 'ordem'], 'permissions': (('change_dispositivo_edicao_dinamica', 'Permissão de edição de dispositivos originais via editor dinâmico.'), ('change_dispositivo_edicao_avancada', 'Permissão de edição de dispositivos originais via formulários de edição avançada.'), ('change_dispositivo_registros_compilacao', 'Permissão de registro de compilação via editor dinâmico.'), ('view_dispositivo_notificacoes', 'Permissão de acesso às notificações de pendências.'), ('change_dispositivo_de_vigencia_global', 'Permissão alteração global do dispositivo de vigência')), 'verbose_name': 'Dispositivo', 'verbose_name_plural': 'Dispositivos'},
),
]

20
sapl/compilacao/migrations/0063_tipotextoarticulado_publicacao_func.py

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-11-03 11:06
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('compilacao', '0062_auto_20161101_1221'),
]
operations = [
migrations.AddField(
model_name='tipotextoarticulado',
name='publicacao_func',
field=models.NullBooleanField(choices=[(True, 'Sim'), (False, 'Não')], default=True, verbose_name='Histórico de Publicação'),
),
]

46
sapl/compilacao/migrations/0064_auto_20161104_1420.py

@ -0,0 +1,46 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-11-04 14:20
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('compilacao', '0063_tipotextoarticulado_publicacao_func'),
]
operations = [
migrations.AlterModelOptions(
name='textoarticulado',
options={'ordering': ['-data', '-numero'], 'permissions': (('view_restricted_textoarticulado', 'Pode ver qualquer Texto Articulado'),), 'verbose_name': 'Texto Articulado', 'verbose_name_plural': 'Textos Articulados'},
),
migrations.AddField(
model_name='textoarticulado',
name='editable_only_by_owners',
field=models.BooleanField(choices=[(True, 'Sim'), (False, 'Não')], default=True, verbose_name='Editável apenas pelos donos do Texto Articulado'),
),
migrations.AddField(
model_name='textoarticulado',
name='editing_locked',
field=models.BooleanField(choices=[(True, 'Sim'), (False, 'Não')], default=True, verbose_name='Texto Articulado em Edição'),
),
migrations.AddField(
model_name='textoarticulado',
name='owners',
field=models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL, verbose_name='Donos do Texto Articulado'),
),
migrations.AddField(
model_name='textoarticulado',
name='visibilidade',
field=models.IntegerField(choices=[(99, 'Privado'), (79, 'Restrito'), (89, 'Em Edição'), (0, 'Público')], default=99, verbose_name='Visibilidade'),
),
migrations.AlterField(
model_name='tipotextoarticulado',
name='publicacao_func',
field=models.NullBooleanField(choices=[(True, 'Sim'), (False, 'Não')], default=False, verbose_name='Histórico de Publicação'),
),
]

20
sapl/compilacao/migrations/0065_auto_20161107_1024.py

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-11-07 10:24
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('compilacao', '0064_auto_20161104_1420'),
]
operations = [
migrations.RenameField(
model_name='textoarticulado',
old_name='visibilidade',
new_name='privacidade'
),
]

20
sapl/compilacao/migrations/0066_auto_20161107_1028.py

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-11-07 10:28
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('compilacao', '0065_auto_20161107_1024'),
]
operations = [
migrations.AlterField(
model_name='textoarticulado',
name='privacidade',
field=models.IntegerField(choices=[(99, 'Privado'), (79, 'Restrito'), (89, 'Em Edição'), (0, 'Público')], default=99, verbose_name='Privacidade'),
),
]

19
sapl/compilacao/migrations/0067_auto_20161107_1351.py

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-11-07 13:51
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('compilacao', '0066_auto_20161107_1028'),
]
operations = [
migrations.AlterModelOptions(
name='textoarticulado',
options={'ordering': ['-data', '-numero'], 'permissions': (('view_restricted_textoarticulado', 'Pode ver qualquer Texto Articulado'), ('lock_unlock_textoarticulado', 'Pode bloquear/desbloquear edição de Texto Articulado')), 'verbose_name': 'Texto Articulado', 'verbose_name_plural': 'Textos Articulados'},
),
]

19
sapl/compilacao/migrations/0068_auto_20161107_1546.py

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-11-07 15:46
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('compilacao', '0067_auto_20161107_1351'),
]
operations = [
migrations.AlterModelOptions(
name='dispositivo',
options={'ordering': ['ta', 'ordem'], 'permissions': (('change_dispositivo_edicao_dinamica', 'Permissão de edição de dispositivos originais via editor dinâmico.'), ('change_your_dispositivo_edicao_dinamica', 'Permissão de edição de dispositivos originais via editor dinâmico desde que seja dono.'), ('change_dispositivo_edicao_avancada', 'Permissão de edição de dispositivos originais via formulários de edição avançada.'), ('change_dispositivo_registros_compilacao', 'Permissão de registro de compilação via editor dinâmico.'), ('view_dispositivo_notificacoes', 'Permissão de acesso às notificações de pendências.'), ('change_dispositivo_de_vigencia_global', 'Permissão alteração global do dispositivo de vigência')), 'verbose_name': 'Dispositivo', 'verbose_name_plural': 'Dispositivos'},
),
]

20
sapl/compilacao/migrations/0069_auto_20161107_1932.py

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-11-07 19:32
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('compilacao', '0068_auto_20161107_1546'),
]
operations = [
migrations.AlterField(
model_name='textoarticulado',
name='privacidade',
field=models.IntegerField(choices=[(99, 'Privado'), (79, 'Imotável Restrito'), (69, 'Imutável Público'), (89, 'Em Edição'), (0, 'Público')], default=99, verbose_name='Privacidade'),
),
]

169
sapl/compilacao/models.py

@ -1,8 +1,10 @@
from django.contrib import messages
from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.db import models from django.db import models
from django.db.models import F, Q from django.db.models import F, Q
from django.db.models.aggregates import Max from django.db.models.aggregates import Max
from django.http.response import Http404
from django.template import defaultfilters from django.template import defaultfilters
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
@ -81,6 +83,12 @@ class TipoTextoArticulado(models.Model):
choices=YES_NO_CHOICES, choices=YES_NO_CHOICES,
verbose_name=_('Participação Social')) verbose_name=_('Participação Social'))
publicacao_func = models.NullBooleanField(
default=False,
blank=True, null=True,
choices=YES_NO_CHOICES,
verbose_name=_('Histórico de Publicação'))
class Meta: class Meta:
verbose_name = _('Tipo de Texto Articulado') verbose_name = _('Tipo de Texto Articulado')
verbose_name_plural = _('Tipos de Texto Articulados') verbose_name_plural = _('Tipos de Texto Articulados')
@ -95,6 +103,25 @@ PARTICIPACAO_SOCIAL_CHOICES = [
(False, _('Não'))] (False, _('Não'))]
STATUS_TA_PRIVATE = 99 # Só os donos podem ver
STATUS_TA_EDITION = 89
STATUS_TA_IMMUTABLE_RESTRICT = 79
STATUS_TA_IMMUTABLE_PUBLIC = 69
STATUS_TA_PUBLIC = 0
PRIVACIDADE_STATUS = (
(STATUS_TA_PRIVATE, _('Privado')), # só dono ve e edita
# só quem tem permissão para ver
(STATUS_TA_IMMUTABLE_RESTRICT, _('Imotável Restrito')),
# só quem tem permissão para ver
(STATUS_TA_IMMUTABLE_PUBLIC, _('Imutável Público')),
(STATUS_TA_EDITION, _('Em Edição')), # só quem tem permissão para editar
(STATUS_TA_PUBLIC, _('Público')), # visualização pública
)
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'))
@ -118,10 +145,35 @@ class TextoArticulado(TimestampedMixin):
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(
get_settings_auth_user_model(),
blank=True, verbose_name=_('Donos do Texto Articulado'))
editable_only_by_owners = models.BooleanField(
choices=YES_NO_CHOICES,
default=True,
verbose_name=_('Editável apenas pelos donos do Texto Articulado'))
editing_locked = models.BooleanField(
choices=YES_NO_CHOICES,
default=True,
verbose_name=_('Texto Articulado em Edição'))
privacidade = models.IntegerField(
_('Privacidade'),
choices=PRIVACIDADE_STATUS,
default=STATUS_TA_PRIVATE)
class Meta: class Meta:
verbose_name = _('Texto Articulado') verbose_name = _('Texto Articulado')
verbose_name_plural = _('Textos Articulados') verbose_name_plural = _('Textos Articulados')
ordering = ['-data', '-numero'] ordering = ['-data', '-numero']
permissions = (
('view_restricted_textoarticulado',
_('Pode ver qualquer Texto Articulado')),
('lock_unlock_textoarticulado',
_('Pode bloquear/desbloquear edição de Texto Articulado')),
)
def __str__(self): def __str__(self):
if self.content_object: if self.content_object:
@ -132,6 +184,102 @@ class TextoArticulado(TimestampedMixin):
'numero': self.numero, 'numero': self.numero,
'data': defaultfilters.date(self.data, "d \d\e F \d\e Y")} 'data': defaultfilters.date(self.data, "d \d\e F \d\e Y")}
def hash(self):
from django.core import serializers
import hashlib
data = serializers.serialize(
"xml", Dispositivo.objects.filter(
Q(ta_id=self.id) | Q(ta_publicado_id=self.id)))
md5 = hashlib.md5()
md5.update(data.encode('utf-8'))
return md5.hexdigest()
def can_use_dynamic_editing(self, user):
return not self.editing_locked and\
(not self.editable_only_by_owners and
user.has_perm(
'compilacao.change_dispositivo_edicao_dinamica') or
self.editable_only_by_owners and user in self.owners.all() and
user.has_perm(
'compilacao.change_your_dispositivo_edicao_dinamica'))
def has_view_permission(self, request):
if self.privacidade in (STATUS_TA_IMMUTABLE_PUBLIC, STATUS_TA_PUBLIC):
return True
if request.user in self.owners.all():
return True
if self.privacidade == STATUS_TA_IMMUTABLE_RESTRICT and\
request.user.has_perm(
'compilacao.view_restricted_textoarticulado'):
return True
elif self.privacidade == STATUS_TA_EDITION:
if request.user.has_perm(
'compilacao.change_dispositivo_edicao_dinamica'):
return True
else:
messages.error(request, _(
'Este Texto Articulado está em edição.'))
elif self.privacidade == STATUS_TA_PRIVATE:
if request.user in self.owners.all():
return True
else:
raise Http404()
return False
def has_edit_permission(self, request):
if self.privacidade == STATUS_TA_PRIVATE:
if request.user not in self.owners.all():
raise Http404()
if not self.can_use_dynamic_editing(request.user):
messages.error(request, _(
'Usuário sem permissão para edição.'))
return False
else:
return True
if self.privacidade == STATUS_TA_IMMUTABLE_RESTRICT:
messages.error(request, _(
'A edição deste Texto Articulado está bloqueada. '
'Este documento é imutável e de acesso é restrito.'))
return False
if self.privacidade == STATUS_TA_IMMUTABLE_PUBLIC:
messages.error(request, _(
'A edição deste Texto Articulado está bloqueada. '
'Este documento é imutável.'))
return False
if self.editing_locked and\
self.privacidade in (STATUS_TA_PUBLIC, STATUS_TA_EDITION) and\
not request.user.has_perm(
'compilacao.lock_unlock_textoarticulado'):
messages.error(request, _(
'A edição deste Texto Articulado está bloqueada. '
'É necessário acessar com usuário que possui '
'permissão de desbloqueio.'))
return False
if not request.user.has_perm(
'compilacao.change_dispositivo_edicao_dinamica'):
messages.error(request, _(
'Usuário sem permissão para edição.'))
return False
if self.editable_only_by_owners and\
request.user not in self.owners.all():
messages.error(request, _(
'Apenas usuários donos do Texto Articulado podem editá-lo.'))
return False
return True
def reagrupar_ordem_de_dispositivos(self): def reagrupar_ordem_de_dispositivos(self):
dpts = Dispositivo.objects.filter(ta=self) dpts = Dispositivo.objects.filter(ta=self)
@ -635,7 +783,7 @@ class Dispositivo(BaseModel, TimestampedMixin):
ta = models.ForeignKey( ta = models.ForeignKey(
TextoArticulado, TextoArticulado,
on_delete=models.PROTECT, 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(
@ -693,6 +841,23 @@ class Dispositivo(BaseModel, TimestampedMixin):
'ta_publicado', 'ta_publicado',
'publicacao',), 'publicacao',),
) )
permissions = (
('change_dispositivo_edicao_dinamica', _(
'Permissão de edição de dispositivos originais '
'via editor dinâmico.')),
('change_your_dispositivo_edicao_dinamica', _(
'Permissão de edição de dispositivos originais '
'via editor dinâmico desde que seja dono.')),
('change_dispositivo_edicao_avancada', _(
'Permissão de edição de dispositivos originais '
'via formulários de edição avançada.')),
('change_dispositivo_registros_compilacao', _(
'Permissão de registro de compilação via editor dinâmico.')),
('view_dispositivo_notificacoes', _(
'Permissão de acesso às notificações de pendências.')),
('change_dispositivo_de_vigencia_global', _(
'Permissão alteração global do dispositivo de vigência')),
)
def __str__(self): def __str__(self):
return '%(rotulo)s' % { return '%(rotulo)s' % {
@ -1248,7 +1413,7 @@ class Dispositivo(BaseModel, TimestampedMixin):
disps[0].get_numero_completo()) disps[0].get_numero_completo())
# dispositivo.transform_in_next() # dispositivo.transform_in_next()
else: else:
dispositivo.set_numero_completo([1, 0, 0, 0, 0, 0, ]) dispositivo.set_numero_completo([0, 0, 0, 0, 0, 0, ])
else: else:
if ';' in tipo_base.rotulo_prefixo_texto: if ';' in tipo_base.rotulo_prefixo_texto:

6
sapl/compilacao/templatetags/compilacao_filters.py

@ -211,7 +211,7 @@ def heranca(request, d, ignore_ultimo=0, ignore_primeiro=0):
ta_id = str(d.ta_id) ta_id = str(d.ta_id)
d_pk = str(d.pk) d_pk = str(d.pk)
if ta_id not in ta_dpts_parents or d_pk not in ta_dpts_parents[ta_id]: if ta_id not in ta_dpts_parents or d_pk not in ta_dpts_parents[ta_id]:
print('recarregando estrutura temporaria de heranças') #print('recarregando estrutura temporaria de heranças')
dpts_parents = {} dpts_parents = {}
ta_dpts_parents[ta_id] = dpts_parents ta_dpts_parents[ta_id] = dpts_parents
update_dispositivos_parents(dpts_parents, ta_id) update_dispositivos_parents(dpts_parents, ta_id)
@ -295,3 +295,7 @@ def urldetail_content_type(obj):
@register.filter @register.filter
def list(obj): def list(obj):
return [obj, ] return [obj, ]
@register.filter
def can_use_dynamic_editing(texto_articulado, user):
return texto_articulado.can_use_dynamic_editing(user)

380
sapl/compilacao/views.py

@ -1,16 +1,16 @@
import logging
import sys
from collections import OrderedDict from collections import OrderedDict
from datetime import datetime, timedelta from datetime import datetime, timedelta
import logging
import sys
from braces.views import FormMessagesMixin from braces.views import FormMessagesMixin
from django import forms from django import forms
from django.conf import settings from django.conf import settings
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.decorators import login_required from django.contrib.auth.mixins import PermissionRequiredMixin
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.signing import Signer from django.core.signing import Signer
from django.core.urlresolvers import reverse_lazy from django.core.urlresolvers import reverse_lazy, reverse
from django.db import connection, transaction from django.db import connection, transaction
from django.db.models import Q from django.db.models import Q
from django.db.utils import IntegrityError from django.db.utils import IntegrityError
@ -18,10 +18,9 @@ from django.http.response import (HttpResponse, HttpResponseRedirect,
JsonResponse) JsonResponse)
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.decorators import method_decorator
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 string_concat from django.utils.translation import string_concat
from django.utils.translation import ugettext_lazy as _
from django.views.generic.base import TemplateView from django.views.generic.base import TemplateView
from django.views.generic.detail import DetailView from django.views.generic.detail import DetailView
from django.views.generic.edit import (CreateView, DeleteView, FormView, from django.views.generic.edit import (CreateView, DeleteView, FormView,
@ -43,12 +42,14 @@ from sapl.compilacao.models import (Dispositivo, Nota,
Publicacao, TextoArticulado, Publicacao, TextoArticulado,
TipoDispositivo, TipoNota, TipoPublicacao, TipoDispositivo, TipoNota, TipoPublicacao,
TipoTextoArticulado, TipoVide, TipoTextoArticulado, TipoVide,
VeiculoPublicacao, Vide) VeiculoPublicacao, Vide, STATUS_TA_EDITION,
STATUS_TA_PRIVATE, STATUS_TA_PUBLIC)
from sapl.compilacao.utils import (DISPOSITIVO_SELECT_RELATED, from sapl.compilacao.utils import (DISPOSITIVO_SELECT_RELATED,
DISPOSITIVO_SELECT_RELATED_EDIT) DISPOSITIVO_SELECT_RELATED_EDIT)
from sapl.crud.base import Crud, CrudListView, make_pagination from sapl.crud.base import Crud, CrudListView, make_pagination
from sapl.settings import BASE_DIR from sapl.settings import BASE_DIR
TipoNotaCrud = Crud.build(TipoNota, 'tipo_nota') TipoNotaCrud = Crud.build(TipoNota, 'tipo_nota')
TipoVideCrud = Crud.build(TipoVide, 'tipo_vide') TipoVideCrud = Crud.build(TipoVide, 'tipo_vide')
TipoPublicacaoCrud = Crud.build(TipoPublicacao, 'tipo_publicacao') TipoPublicacaoCrud = Crud.build(TipoPublicacao, 'tipo_publicacao')
@ -87,12 +88,31 @@ def choice_models_in_extenal_views():
return result return result
def choice_model_type_foreignkey_in_extenal_views(id_tipo_ta=None):
yield None, '-------------'
if not id_tipo_ta:
return
tipo_ta = TipoTextoArticulado.objects.get(pk=id_tipo_ta)
integrations_view_names = get_integrations_view_names()
for item in integrations_view_names:
if hasattr(item, 'model_type_foreignkey'):
if (tipo_ta.content_type.model == item.model.__name__.lower() and
tipo_ta.content_type.app_label ==
item.model._meta.app_label):
for i in item.model_type_foreignkey.objects.all():
yield i.pk, i
class IntegracaoTaView(TemplateView): class IntegracaoTaView(TemplateView):
def get_redirect_deactivated(self): def get_redirect_deactivated(self):
messages.error( messages.error(
self.request, self.request,
_('O modulo de Textos Articulados está desativado.')) _('O modulo de Textos Articulados para %s está desativado.'
) % self.model._meta.verbose_name_plural)
return redirect('/') return redirect('/')
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
@ -100,6 +120,16 @@ class IntegracaoTaView(TemplateView):
try: try:
if settings.DEBUG or not TipoDispositivo.objects.exists(): if settings.DEBUG or not TipoDispositivo.objects.exists():
self.import_pattern() self.import_pattern()
if hasattr(self, 'map_funcs'):
tipo_ta = TipoTextoArticulado.objects.get(
content_type=ContentType.objects.get_for_model(
self.model))
for key, value in self.map_funcs.items():
setattr(tipo_ta, key, value)
tipo_ta.save()
except Exception as e: except Exception as e:
logger.error( logger.error(
string_concat( string_concat(
@ -108,6 +138,26 @@ class IntegracaoTaView(TemplateView):
str(e))) str(e)))
return self.get_redirect_deactivated() return self.get_redirect_deactivated()
assert hasattr(self, 'map_fields'), _(
"""
O mapa dos campos não foi definido. Ele deve seguir a estrutura
de chaves abaixo:
map_fields = {
'data': 'data',
'ementa': 'ementa',
'observacao': 'observacao',
'numero': 'numero',
'ano': 'ano',
}
Caso o model de integração não possua um dos campos,
implemente, ou passe `None` para as chaves que são fixas.
""")
map_fields = self.map_fields
ta_values = getattr(self, 'ta_values', {})
item = get_object_or_404(self.model, pk=kwargs['pk']) item = get_object_or_404(self.model, pk=kwargs['pk'])
related_object_type = ContentType.objects.get_for_model(item) related_object_type = ContentType.objects.get_for_model(item)
@ -118,81 +168,71 @@ class IntegracaoTaView(TemplateView):
tipo_ta = TipoTextoArticulado.objects.filter( tipo_ta = TipoTextoArticulado.objects.filter(
content_type=related_object_type) content_type=related_object_type)
if not ta.exists(): ta_exists = bool(ta.exists())
if not ta_exists:
ta = TextoArticulado() ta = TextoArticulado()
tipo_ta = TipoTextoArticulado.objects.filter( tipo_ta = TipoTextoArticulado.objects.filter(
content_type=related_object_type)[:1] content_type=related_object_type)[:1]
if tipo_ta.exists(): if tipo_ta.exists():
ta.tipo_ta = tipo_ta[0] ta.tipo_ta = tipo_ta[0]
ta.content_object = item ta.content_object = item
else:
ta = ta[0]
if hasattr(item, 'ementa') and item.ementa: ta.privacidade = ta_values.get('privacidade', STATUS_TA_EDITION)
ta.ementa = item.ementa
else:
ta.ementa = _('Integração com %s sem ementa.') % item
if hasattr(item, 'observacao') and item.observacao: ta.editing_locked = ta_values.get('editing_locked', False)
ta.observacao = item.observacao ta.editable_only_by_owners = ta_values.get(
else: 'editable_only_by_owners', False)
ta.observacao = _('Integração com %s sem observacao.') % item
if hasattr(item, 'numero') and item.numero:
ta.numero = item.numero
else: else:
ta.numero = int('%s%s%s' % ( ta = ta[0]
int(datetime.now().year),
int(datetime.now().month),
int(datetime.now().day)))
if hasattr(item, 'ano') and item.ano:
ta.ano = item.ano
else:
ta.ano = datetime.now().year
if hasattr(item, 'data_apresentacao'): if not ta.data:
ta.data = item.data_apresentacao ta.data = getattr(item, map_fields['data']
elif hasattr(item, 'data'): if map_fields['data'] else 'xxx',
ta.data = item.data datetime.now())
else: if not ta.data:
ta.data = datetime.now() ta.data = datetime.now()
ta.save() ta.ementa = getattr(
item, map_fields['ementa']
if map_fields['ementa'] else 'xxx', _(
'Integração com %s sem ementa.') % item)
return redirect(to=reverse_lazy('sapl.compilacao:ta_text', ta.observacao = getattr(
kwargs={'ta_id': ta.pk})) item, map_fields['observacao']
if map_fields['observacao'] else 'xxx', '')
"""msg = messages.error if not request.user.is_anonymous( ta.numero = getattr(
) else messages.info item, map_fields['numero']
if map_fields['numero'] else 'xxx', int('%s%s%s' % (
int(datetime.now().year),
int(datetime.now().month),
int(datetime.now().day))))
msg(request, ta.ano = getattr(item, map_fields['ano']
_('A funcionalidade de Textos Articulados está desativada.')) if map_fields['ano'] else 'xxx', datetime.now().year)
if not request.user.is_anonymous(): ta.save()
msg(
request,
_('Para ativá-la, os Tipos de Textos devem ser criados.'))
msg(request, if not ta_exists:
_('Sua tela foi redirecionada para a tela de ' if ta.editable_only_by_owners and\
'cadastro de Textos Articulados.')) not self.request.user.is_anonymous():
ta.owners.add(self.request.user)
return redirect(to=reverse_lazy('sapl.compilacao:tipo_ta_list', if not Dispositivo.objects.filter(ta_id=ta.pk).exists() and\
kwargs={})) ta.can_use_dynamic_editing(self.request.user):
return redirect(to=reverse_lazy('sapl.compilacao:ta_text_edit',
kwargs={'ta_id': ta.pk}))
else: else:
return redirect(to=reverse_lazy('sapl.compilacao:ta_text',
return redirect(to=reverse_lazy( kwargs={'ta_id': ta.pk}))
'%s:%s_detail' % (
item._meta.app_config.name, item._meta.model_name),
kwargs={'pk': item.pk}))"""
def import_pattern(self): def import_pattern(self):
from unipath import Path from unipath import Path
compilacao_app = Path(__file__).ancestor(1) compilacao_app = Path(__file__).ancestor(1)
print(compilacao_app) # print(compilacao_app)
with open(compilacao_app + '/compilacao_data_tables.sql', 'r') as f: with open(compilacao_app + '/compilacao_data_tables.sql', 'r') as f:
lines = f.readlines() lines = f.readlines()
lines = [line.rstrip('\n') for line in lines] lines = [line.rstrip('\n') for line in lines]
@ -251,7 +291,20 @@ class IntegracaoTaView(TemplateView):
abstract = True abstract = True
class CompMixin: class CompMixin(PermissionRequiredMixin):
permission_required = []
def has_permission(self):
perms = self.get_permission_required()
# Torna a view pública se não possuir conteudo
# no atributo permission_required
return self.request.user.has_perms(perms) if len(perms) else True
@property
def ta(self):
ta = TextoArticulado.objects.get(
pk=self.kwargs.get('ta_id', self.kwargs.get('pk', 0)))
return ta
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(CompMixin, self).get_context_data(**kwargs) context = super(CompMixin, self).get_context_data(**kwargs)
@ -271,6 +324,7 @@ class TipoTaListView(CompMixin, ListView):
model = TipoTextoArticulado model = TipoTextoArticulado
paginate_by = 10 paginate_by = 10
verbose_name = model._meta.verbose_name verbose_name = model._meta.verbose_name
permission_required = 'compilacao.list_tipotextoarticulado'
@property @property
def title(self): def title(self):
@ -287,6 +341,7 @@ class TipoTaCreateView(CompMixin, FormMessagesMixin, CreateView):
template_name = "crud/form.html" template_name = "crud/form.html"
form_valid_message = _('Registro criado com sucesso!') form_valid_message = _('Registro criado com sucesso!')
form_invalid_message = _('O registro não foi criado.') form_invalid_message = _('O registro não foi criado.')
permission_required = 'compilacao.add_tipotextoarticulado'
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
self.object = None self.object = None
@ -308,12 +363,14 @@ class TipoTaCreateView(CompMixin, FormMessagesMixin, CreateView):
class TipoTaDetailView(CompMixin, DetailView): class TipoTaDetailView(CompMixin, DetailView):
model = TipoTextoArticulado model = TipoTextoArticulado
permission_required = 'compilacao.detail_tipotextoarticulado'
class TipoTaUpdateView(CompMixin, UpdateView): class TipoTaUpdateView(CompMixin, UpdateView):
model = TipoTextoArticulado model = TipoTextoArticulado
form_class = TipoTaForm form_class = TipoTaForm
template_name = "crud/form.html" template_name = "crud/form.html"
permission_required = 'compilacao.change_tipotextoarticulado'
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
self.object = self.get_object() self.object = self.get_object()
@ -336,6 +393,7 @@ class TipoTaUpdateView(CompMixin, UpdateView):
class TipoTaDeleteView(CompMixin, DeleteView): class TipoTaDeleteView(CompMixin, DeleteView):
model = TipoTextoArticulado model = TipoTextoArticulado
template_name = "crud/confirm_delete.html" template_name = "crud/confirm_delete.html"
permission_required = 'compilacao.delete_tipotextoarticulado'
@property @property
def detail_url(self): def detail_url(self):
@ -350,6 +408,7 @@ class TaListView(CompMixin, ListView):
model = TextoArticulado model = TextoArticulado
paginate_by = 10 paginate_by = 10
verbose_name = model._meta.verbose_name verbose_name = model._meta.verbose_name
permission_required = 'compilacao.list_textoarticulado'
@property @property
def title(self): def title(self):
@ -367,10 +426,26 @@ class TaListView(CompMixin, ListView):
page_obj.number, paginator.num_pages) page_obj.number, paginator.num_pages)
return context return context
def get_queryset(self):
qs = ListView.get_queryset(self)
qs = qs.exclude(
~Q(owners=self.request.user.id),
privacidade=STATUS_TA_PRIVATE)
return qs
class TaDetailView(CompMixin, DetailView): class TaDetailView(CompMixin, DetailView):
model = TextoArticulado model = TextoArticulado
def has_permission(self):
self.object = self.ta
if self.object.has_view_permission(self.request):
return CompMixin.has_permission(self)
else:
return False
@property @property
def title(self): def title(self):
if self.get_object().content_object: if self.get_object().content_object:
@ -389,6 +464,7 @@ class TaCreateView(CompMixin, FormMessagesMixin, CreateView):
template_name = "crud/form.html" template_name = "crud/form.html"
form_valid_message = _('Registro criado com sucesso!') form_valid_message = _('Registro criado com sucesso!')
form_invalid_message = _('O registro não foi criado.') form_invalid_message = _('O registro não foi criado.')
permission_required = 'compilacao.add_tipotextoarticulado'
def get_success_url(self): def get_success_url(self):
return reverse_lazy('sapl.compilacao:ta_detail', return reverse_lazy('sapl.compilacao:ta_detail',
@ -403,6 +479,7 @@ class TaUpdateView(CompMixin, UpdateView):
model = TextoArticulado model = TextoArticulado
form_class = TaForm form_class = TaForm
template_name = "crud/form.html" template_name = "crud/form.html"
permission_required = 'compilacao.change_textoarticulado'
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
self.object = self.get_object() self.object = self.get_object()
@ -425,6 +502,7 @@ class TaUpdateView(CompMixin, UpdateView):
class TaDeleteView(CompMixin, DeleteView): class TaDeleteView(CompMixin, DeleteView):
model = TextoArticulado model = TextoArticulado
template_name = "crud/confirm_delete.html" template_name = "crud/confirm_delete.html"
permission_required = 'compilacao.delete_textoarticulado'
@property @property
def detail_url(self): def detail_url(self):
@ -464,14 +542,11 @@ class NotaMixin(DispositivoSuccessUrlMixin):
return initial return initial
@method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(NotaMixin, self).dispatch(*args, **kwargs)
class NotasCreateView(NotaMixin, CreateView): class NotasCreateView(NotaMixin, CreateView):
template_name = 'compilacao/ajax_form.html' template_name = 'compilacao/ajax_form.html'
form_class = NotaForm form_class = NotaForm
permission_required = 'compilacao.add_nota'
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
flag_action, modelo_nota = self.get_modelo_nota(request) flag_action, modelo_nota = self.get_modelo_nota(request)
@ -506,6 +581,7 @@ class NotasEditView(NotaMixin, UpdateView):
model = Nota model = Nota
template_name = 'compilacao/ajax_form.html' template_name = 'compilacao/ajax_form.html'
form_class = NotaForm form_class = NotaForm
permission_required = 'compilacao.change_nota'
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
flag_action, modelo_nota = self.get_modelo_nota(request) flag_action, modelo_nota = self.get_modelo_nota(request)
@ -517,6 +593,8 @@ class NotasEditView(NotaMixin, UpdateView):
class NotasDeleteView(NotaMixin, TemplateView): class NotasDeleteView(NotaMixin, TemplateView):
permission_required = 'compilacao.delete_nota'
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
nt = Nota.objects.get(pk=self.kwargs['pk']) nt = Nota.objects.get(pk=self.kwargs['pk'])
nt.delete() nt.delete()
@ -535,59 +613,28 @@ class VideMixin(DispositivoSuccessUrlMixin):
return initial return initial
@method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(VideMixin, self).dispatch(*args, **kwargs)
def choice_model_type_foreignkey_in_extenal_views(id_tipo_ta=None):
yield None, '-------------'
if not id_tipo_ta:
return
tipo_ta = TipoTextoArticulado.objects.get(pk=id_tipo_ta)
integrations_view_names = get_integrations_view_names()
for item in integrations_view_names:
if hasattr(item, 'model_type_foreignkey'):
if (tipo_ta.content_type.model == item.model.__name__.lower() and
tipo_ta.content_type.app_label ==
item.model._meta.app_label):
for i in item.model_type_foreignkey.objects.all():
yield i.pk, i
class VideCreateView(VideMixin, CreateView): class VideCreateView(VideMixin, CreateView):
model = Vide model = Vide
template_name = 'compilacao/ajax_form.html' template_name = 'compilacao/ajax_form.html'
form_class = VideForm form_class = VideForm
permission_required = 'compilacao.add_vide'
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
self.object = None self.object = None
form = self.get_form() form = self.get_form()
return self.render_to_response(self.get_context_data(form=form)) return self.render_to_response(self.get_context_data(form=form))
"""
def get_form_kwargs(self):
kwargs = super(VideCreateView, self).get_form_kwargs()
if 'choice_model_type_foreignkey_in_extenal_views' not in kwargs:
kwargs.update({
'choice_model_type_foreignkey_in_extenal_views':
choice_model_type_foreignkey_in_extenal_views
})
return kwargs"""
class VideEditView(VideMixin, UpdateView): class VideEditView(VideMixin, UpdateView):
model = Vide model = Vide
template_name = 'compilacao/ajax_form.html' template_name = 'compilacao/ajax_form.html'
form_class = VideForm form_class = VideForm
permission_required = 'compilacao.change_vide'
class VideDeleteView(VideMixin, TemplateView): class VideDeleteView(VideMixin, TemplateView):
permission_required = 'compilacao.delete_vide'
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
vd = Vide.objects.get(pk=self.kwargs['pk']) vd = Vide.objects.get(pk=self.kwargs['pk'])
@ -595,9 +642,26 @@ class VideDeleteView(VideMixin, TemplateView):
return HttpResponseRedirect(self.get_success_url()) return HttpResponseRedirect(self.get_success_url())
class PublicacaoListView(CompMixin, ListView): class PublicacaoMixin(CompMixin):
def dispatch(self, request, *args, **kwargs):
ta = self.ta
if not ta.tipo_ta.publicacao_func:
messages.error(request, _(
'A funcionalidade de %s está desativada para %s.') % (
TipoTextoArticulado._meta.get_field(
'publicacao_func').verbose_name,
ta.tipo_ta.descricao))
return redirect(reverse('sapl.compilacao:ta_text',
kwargs={'ta_id': self.kwargs['ta_id']}))
return PermissionRequiredMixin.dispatch(self, request, *args, **kwargs)
class PublicacaoListView(PublicacaoMixin, ListView):
model = Publicacao model = Publicacao
verbose_name = model._meta.verbose_name verbose_name = model._meta.verbose_name
permission_required = []
@property @property
def title(self): def title(self):
@ -605,11 +669,6 @@ class PublicacaoListView(CompMixin, ListView):
self.model._meta.verbose_name_plural, self.model._meta.verbose_name_plural,
self.ta)) self.ta))
@property
def ta(self):
ta = TextoArticulado.objects.get(pk=self.kwargs['ta_id'])
return ta
@property @property
def create_url(self): def create_url(self):
return reverse_lazy( return reverse_lazy(
@ -626,12 +685,13 @@ class PublicacaoListView(CompMixin, ListView):
return context return context
class PublicacaoCreateView(CompMixin, FormMessagesMixin, CreateView): class PublicacaoCreateView(PublicacaoMixin, FormMessagesMixin, CreateView):
model = Publicacao model = Publicacao
form_class = PublicacaoForm form_class = PublicacaoForm
template_name = "crud/form.html" template_name = "crud/form.html"
form_valid_message = _('Registro criado com sucesso!') form_valid_message = _('Registro criado com sucesso!')
form_invalid_message = _('O registro não foi criado.') form_invalid_message = _('O registro não foi criado.')
permission_required = 'compilacao.add_publicacao'
def get_success_url(self): def get_success_url(self):
return reverse_lazy( return reverse_lazy(
@ -650,14 +710,16 @@ class PublicacaoCreateView(CompMixin, FormMessagesMixin, CreateView):
return {'ta': self.kwargs['ta_id']} return {'ta': self.kwargs['ta_id']}
class PublicacaoDetailView(CompMixin, DetailView): class PublicacaoDetailView(PublicacaoMixin, DetailView):
model = Publicacao model = Publicacao
permission_required = 'compilacao.detail_publicacao'
class PublicacaoUpdateView(CompMixin, UpdateView): class PublicacaoUpdateView(PublicacaoMixin, UpdateView):
model = Publicacao model = Publicacao
form_class = PublicacaoForm form_class = PublicacaoForm
template_name = "crud/form.html" template_name = "crud/form.html"
permission_required = 'compilacao.change_publicacao'
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
self.object = self.get_object() self.object = self.get_object()
@ -678,9 +740,10 @@ class PublicacaoUpdateView(CompMixin, UpdateView):
return self.get_success_url() return self.get_success_url()
class PublicacaoDeleteView(CompMixin, DeleteView): class PublicacaoDeleteView(PublicacaoMixin, DeleteView):
model = Publicacao model = Publicacao
template_name = "crud/confirm_delete.html" template_name = "crud/confirm_delete.html"
permission_required = 'compilacao.delete_publicacao'
@property @property
def detail_url(self): def detail_url(self):
@ -708,44 +771,14 @@ class TextView(CompMixin, ListView):
fim_vigencia = None fim_vigencia = None
ta_vigencia = None ta_vigencia = None
def get(self, request, *args, **kwargs): def has_permission(self):
ta = TextoArticulado.objects.get(pk=self.kwargs['ta_id']) self.object = self.ta
self.object = ta return self.object.has_view_permission(self.request)
if ta.content_object:
item = ta.content_object
self.object = item
if hasattr(item, 'ementa') and item.ementa:
ta.ementa = item.ementa
else:
ta.ementa = _('Integração com %s sem ementa.') % item
if hasattr(item, 'observacao') and item.observacao:
ta.observacao = item.observacao
else:
ta.observacao = _('Integração com %s sem observacao.') % item
if hasattr(item, 'numero') and item.numero:
ta.numero = item.numero
else:
ta.numero = int('%s%s%s' % (
int(datetime.now().year),
int(datetime.now().month),
int(datetime.now().day)))
if hasattr(item, 'ano') and item.ano: def get(self, request, *args, **kwargs):
ta.ano = item.ano if 'print' in request.GET:
else: self.template_name = 'compilacao/text_list__print_version.html'
ta.ano = datetime.now().year return ListView.get(self, request, *args, **kwargs)
if hasattr(item, 'data_apresentacao'):
ta.data = item.data_apresentacao
elif hasattr(item, 'data'):
ta.data = item.data
else:
ta.data = datetime.now()
ta.save()
return super(TextView, self).get(request, *args, **kwargs)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(TextView, self).get_context_data(**kwargs) context = super(TextView, self).get_context_data(**kwargs)
@ -936,9 +969,45 @@ class DispositivoView(TextView):
return itens return itens
class TextEditView(TemplateView): class TextEditView(CompMixin, TemplateView):
template_name = 'compilacao/text_edit.html' template_name = 'compilacao/text_edit.html'
def has_permission(self):
self.object = self.ta
return self.object.has_edit_permission(self.request)
def get(self, request, *args, **kwargs):
if self.object.editing_locked:
if 'unlock' not in request.GET:
messages.error(
request, _(
'A edição deste Texto Articulado está bloqueada.'))
return redirect(to=reverse_lazy(
'sapl.compilacao:ta_text', kwargs={
'ta_id': self.object.id}))
else:
# TODO - implementar logging de ação de usuário
self.object.editing_locked = False
self.object.privacidade = STATUS_TA_EDITION
self.object.save()
messages.success(request, _(
'Texto Articulado desbloqueado com sucesso.'))
else:
if 'lock' in request.GET:
# TODO - implementar logging de ação de usuário
self.object.editing_locked = True
self.object.privacidade = STATUS_TA_PUBLIC
self.object.save()
messages.success(request, _(
'Texto Articulado bloqueado com sucesso.'))
return redirect(to=reverse_lazy(
'sapl.compilacao:ta_text', kwargs={
'ta_id': self.object.id}))
return TemplateView.get(self, request, *args, **kwargs)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
dispositivo_id = int(self.kwargs['dispositivo_id']) \ dispositivo_id = int(self.kwargs['dispositivo_id']) \
if 'dispositivo_id' in self.kwargs else 0 if 'dispositivo_id' in self.kwargs else 0
@ -946,7 +1015,7 @@ class TextEditView(TemplateView):
if dispositivo_id: if dispositivo_id:
self.object = Dispositivo.objects.get(pk=dispositivo_id) self.object = Dispositivo.objects.get(pk=dispositivo_id)
context = super(TextEditView, self).get_context_data(**kwargs) context = super(TemplateView, self).get_context_data(**kwargs)
if not dispositivo_id: if not dispositivo_id:
ta = TextoArticulado.objects.get(pk=self.kwargs['ta_id']) ta = TextoArticulado.objects.get(pk=self.kwargs['ta_id'])
@ -1721,6 +1790,12 @@ class ActionDispositivoCreateMixin(ActionsCommonsMixin):
for td in otds: for td in otds:
if td.dispositivo_de_alteracao:
if not self.request.user.has_perm(
'compilacao.'
'change_dispositivo_registros_compilacao'):
continue
if paradentro and not td.permitido_inserir_in( if paradentro and not td.permitido_inserir_in(
tipb, tipb,
include_relative_autos=True, include_relative_autos=True,
@ -2417,11 +2492,10 @@ class ActionsEditMixin(ActionDragAndMoveDispositivoAlteradoMixin,
class DispositivoDinamicEditView( class DispositivoDinamicEditView(
CompMixin, ActionsEditMixin, TextEditView, UpdateView): ActionsEditMixin, TextEditView, UpdateView):
template_name = 'compilacao/text_edit_bloco.html' template_name = 'compilacao/text_edit_bloco.html'
model = Dispositivo model = Dispositivo
form_class = DispositivoEdicaoBasicaForm form_class = DispositivoEdicaoBasicaForm
contador = -1
def get_initial(self): def get_initial(self):
initial = UpdateView.get_initial(self) initial = UpdateView.get_initial(self)
@ -2615,7 +2689,6 @@ class DispositivoSearchFragmentFormView(ListView):
def get_queryset(self): def get_queryset(self):
try: try:
n = 10 n = 10
if 'max_results' in self.request.GET: if 'max_results' in self.request.GET:
n = int(self.request.GET['max_results']) n = int(self.request.GET['max_results'])
@ -2799,6 +2872,8 @@ class DispositivoEdicaoBasicaView(CompMixin, FormMessagesMixin, UpdateView):
form_invalid_message = _('Houve erro em registrar ' form_invalid_message = _('Houve erro em registrar '
'as alterações no Dispositivo') 'as alterações no Dispositivo')
permission_required = 'compilacao.change_dispositivo_edicao_avancada'
@property @property
def cancel_url(self): def cancel_url(self):
return reverse_lazy( return reverse_lazy(
@ -2871,6 +2946,8 @@ class DispositivoEdicaoVigenciaView(CompMixin, FormMessagesMixin, UpdateView):
form_invalid_message = _('Houve erro em registrar ' form_invalid_message = _('Houve erro em registrar '
'as alterações no Dispositivo') 'as alterações no Dispositivo')
permission_required = 'compilacao.change_dispositivo_edicao_avancada'
@property @property
def cancel_url(self): def cancel_url(self):
return reverse_lazy( return reverse_lazy(
@ -2894,6 +2971,9 @@ class DispositivoDefinidorVigenciaView(CompMixin, FormMessagesMixin, FormView):
form_invalid_message = _('Houve erro em registrar ' form_invalid_message = _('Houve erro em registrar '
'as alterações no Dispositivo') 'as alterações no Dispositivo')
permission_required = ('compilacao.change_dispositivo_edicao_avancada',
'compilacao.change_dispositivo_de_vigencia_global')
def get_form_kwargs(self): def get_form_kwargs(self):
kwargs = FormView.get_form_kwargs(self) kwargs = FormView.get_form_kwargs(self)
kwargs.update({ kwargs.update({
@ -2951,6 +3031,8 @@ class DispositivoEdicaoAlteracaoView(CompMixin, FormMessagesMixin, UpdateView):
form_invalid_message = _('Houve erro em registrar ' form_invalid_message = _('Houve erro em registrar '
'as alterações no Dispositivo') 'as alterações no Dispositivo')
permission_required = 'compilacao.change_dispositivo_registros_compilacao'
@property @property
def cancel_url(self): def cancel_url(self):
return reverse_lazy( return reverse_lazy(
@ -2983,6 +3065,8 @@ class TextNotificacoesView(CompMixin, ListView, FormView):
template_name = 'compilacao/text_notificacoes.html' template_name = 'compilacao/text_notificacoes.html'
form_class = TextNotificacoesForm form_class = TextNotificacoesForm
permission_required = 'compilacao.view_dispositivo_notificacoes'
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
self.object = TextoArticulado.objects.get(pk=self.kwargs['ta_id']) self.object = TextoArticulado.objects.get(pk=self.kwargs['ta_id'])
return super(TextNotificacoesView, self).get(request, *args, **kwargs) return super(TextNotificacoesView, self).get(request, *args, **kwargs)

3
sapl/crispy_layout_mixin.py

@ -91,7 +91,8 @@ def get_field_display(obj, fieldname):
else: else:
display = '' display = ''
elif 'ManyRelatedManager' in str(type(value))\ elif 'ManyRelatedManager' in str(type(value))\
or 'RelatedManager' in str(type(value)): or 'RelatedManager' in str(type(value))\
or 'GenericRelatedObjectManager' in str(type(value)):
display = '<ul>' display = '<ul>'
for v in value.all(): for v in value.all():
display += '<li>%s</li>' % str(v) display += '<li>%s</li>' % str(v)

10
sapl/crud/base.py

@ -24,6 +24,8 @@ from django.views.generic.base import ContextMixin
from django.views.generic.list import MultipleObjectMixin from django.views.generic.list import MultipleObjectMixin
from sapl.crispy_layout_mixin import CrispyLayoutFormMixin, get_field_display from sapl.crispy_layout_mixin import CrispyLayoutFormMixin, get_field_display
from sapl.rules.map_rules import (RP_ADD, RP_CHANGE, RP_DELETE, RP_DETAIL,
RP_LIST)
from sapl.settings import BASE_DIR from sapl.settings import BASE_DIR
from sapl.utils import normalize from sapl.utils import normalize
@ -33,10 +35,6 @@ logger = logging.getLogger(BASE_DIR.name)
ACTION_LIST, ACTION_CREATE, ACTION_DETAIL, ACTION_UPDATE, ACTION_DELETE = \ ACTION_LIST, ACTION_CREATE, ACTION_DETAIL, ACTION_UPDATE, ACTION_DELETE = \
'list', 'create', 'detail', 'update', 'delete' 'list', 'create', 'detail', 'update', 'delete'
# RP - Radical das permissões para "..."
RP_LIST, RP_DETAIL, RP_ADD, RP_CHANGE, RP_DELETE =\
'.list_', '.detail_', '.add_', '.change_', '.delete_',
def _form_invalid_message(msg): def _form_invalid_message(msg):
return '%s %s' % (_('Formulário inválido.'), msg) return '%s %s' % (_('Formulário inválido.'), msg)
@ -1404,10 +1402,6 @@ class CrudBaseForListAndDetailExternalAppView(MasterDetailCrud):
class BaseMixin(Crud.PublicMixin, MasterDetailCrud.BaseMixin): class BaseMixin(Crud.PublicMixin, MasterDetailCrud.BaseMixin):
@classmethod
def url_name(cls, suffix):
return '%s_parlamentar_%s' % (cls.model._meta.model_name, suffix)
def resolve_url(self, suffix, args=None): def resolve_url(self, suffix, args=None):
obj = self.crud if hasattr(self, 'crud') else self obj = self.crud if hasattr(self, 'crud') else self

130
sapl/legacy/migration.py

@ -4,26 +4,31 @@ import pkg_resources
import yaml import yaml
from django.apps import apps from django.apps import apps
from django.apps.config import AppConfig from django.apps.config import AppConfig
from django.contrib.auth.models import User
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.db import connections, models from django.db import connections, models
from django.db.models import CharField, TextField from django.db.models import CharField, TextField, ProtectedError
from django.db.models.base import ModelBase from django.db.models.base import ModelBase
from model_mommy import mommy from model_mommy import mommy
from model_mommy.mommy import foreign_key_required, make from model_mommy.mommy import foreign_key_required, make
from sapl.base.models import ProblemaMigracao from sapl.base.models import Autor, ProblemaMigracao, TipoAutor
from sapl.comissoes.models import Composicao, Participacao from sapl.comissoes.models import Composicao, Participacao
from sapl.materia.models import StatusTramitacao, TipoProposicao, Tramitacao from sapl.materia.models import (Proposicao, StatusTramitacao, TipoDocumento,
from sapl.norma.models import AssuntoNormaRelationship, NormaJuridica TipoMateriaLegislativa, TipoProposicao,
Tramitacao)
from sapl.norma.models import AssuntoNorma, NormaJuridica
from sapl.parlamentares.models import Parlamentar from sapl.parlamentares.models import Parlamentar
from sapl.protocoloadm.models import StatusTramitacaoAdministrativo from sapl.protocoloadm.models import StatusTramitacaoAdministrativo
from sapl.sessao.models import OrdemDia, SessaoPlenaria from sapl.sessao.models import ExpedienteMateria, OrdemDia, SessaoPlenaria
from sapl.utils import normalize
# BASE ###################################################################### # BASE ######################################################################
# apps to be migrated, in app dependency order (very important) # apps to be migrated, in app dependency order (very important)
appconfs = [apps.get_app_config(n) for n in [ appconfs = [apps.get_app_config(n) for n in [
'parlamentares', 'parlamentares',
'comissoes', 'comissoes',
'base',
'materia', 'materia',
'norma', 'norma',
'sessao', 'sessao',
@ -31,6 +36,7 @@ appconfs = [apps.get_app_config(n) for n in [
'protocoloadm', ]] 'protocoloadm', ]]
unique_constraints = [] unique_constraints = []
one_to_one_constraints = []
name_sets = [set(m.__name__ for m in ac.get_models()) for ac in appconfs] name_sets = [set(m.__name__ for m in ac.get_models()) for ac in appconfs]
@ -118,6 +124,9 @@ def get_fk_related(field, value, label=None):
else: else:
value = None value = None
else: else:
if field.model._meta.label == 'sessao.RegistroVotacao' and \
field.name == 'ordem':
return value
value = make_stub(field.related_model, value) value = make_stub(field.related_model, value)
descricao = 'stub criado para entrada orfã!' descricao = 'stub criado para entrada orfã!'
warn(msg + ' => ' + descricao) warn(msg + ' => ' + descricao)
@ -155,18 +164,33 @@ def delete_constraints(model):
cursor = exec_sql("SELECT conname FROM pg_constraint WHERE conrelid = " cursor = exec_sql("SELECT conname FROM pg_constraint WHERE conrelid = "
"(SELECT oid FROM pg_class WHERE relname LIKE " "(SELECT oid FROM pg_class WHERE relname LIKE "
"'%s') and contype = 'u';" % (table)) "'%s') and contype = 'u';" % (table))
result = cursor.fetchone() result = ()
result = cursor.fetchall()
# se existir um resultado, unique constraint será deletado # se existir um resultado, unique constraint será deletado
if result: for r in result:
warn('Excluindo unique constraint de nome %s' % result) if r[0].endswith('key'):
words_list = r[0].split('_')
one_to_one_constraints.append([table, r[0], words_list, model])
else:
args = None
args_list = []
if model._meta.unique_together:
args = model._meta.unique_together[0] args = model._meta.unique_together[0]
args_list = list(args) args_list = list(args)
unique_constraints.append([table, result[0], args_list, model]) unique_constraints.append([table, r[0], args_list, model])
warn('Excluindo unique constraint de nome %s' % r[0])
exec_sql("ALTER TABLE %s DROP CONSTRAINT %s;" % exec_sql("ALTER TABLE %s DROP CONSTRAINT %s;" %
(table, result[0])) (table, r[0]))
def recreate_constraints(): def recreate_constraints():
if one_to_one_constraints:
for constraint in one_to_one_constraints:
table, name, args, model = constraint
args_string = ''
args_string = "(" + "_".join(map(str, args[2:-1])) + ")"
exec_sql("ALTER TABLE %s ADD CONSTRAINT %s UNIQUE %s;" %
(table, name, args_string))
if unique_constraints: if unique_constraints:
for constraint in unique_constraints: for constraint in unique_constraints:
table, name, args, model = constraint table, name, args, model = constraint
@ -178,6 +202,7 @@ def recreate_constraints():
args_string += "(" + ', '.join(map(str, args)) + ")" args_string += "(" + ', '.join(map(str, args)) + ")"
exec_sql("ALTER TABLE %s ADD CONSTRAINT %s UNIQUE %s;" % exec_sql("ALTER TABLE %s ADD CONSTRAINT %s UNIQUE %s;" %
(table, name, args_string)) (table, name, args_string))
one_to_one_constraints.clear()
unique_constraints.clear() unique_constraints.clear()
@ -272,21 +297,58 @@ class DataMigrator:
self.data_mudada.setdefault('nome_campo', []).\ self.data_mudada.setdefault('nome_campo', []).\
append(field.name) append(field.name)
if field_type == 'CharField' or field_type == 'TextField': if field_type == 'CharField' or field_type == 'TextField':
if value is None: if value is None or value == 'None':
value = '' value = ''
if field.model._meta.label == 'sessao.RegistroVotacao' and \
field.name == 'ordem' and \
not isinstance(value, OrdemDia):
try:
new_value = ExpedienteMateria.objects.get(pk=value)
setattr(new, 'expediente', new_value)
setattr(new, field.name, None)
continue
except ObjectDoesNotExist:
msg = 'FK [%s] não encontrada para valor %s ' \
'(em %s %s)' % (
field.name, value,
field.model.__name__, label or '---')
value = make_stub(field.related_model, value)
descricao = 'stub criado para entrada orfã!'
warn(msg + ' => ' + descricao)
save_relation(value, [field.name], msg, descricao,
eh_stub=True)
setattr(new, field.name, value)
elif field.model.__name__ == 'TipoAutor' and \
field.name == 'content_type':
try:
value = field.related_model.objects.get(
model=normalize(new.descricao.lower()).replace(' ',
''))
except ObjectDoesNotExist:
value = None
setattr(new, field.name, value) setattr(new, field.name, value)
def migrate(self, obj=appconfs): def migrate(self, obj=appconfs):
# warning: model/app migration order is of utmost importance # warning: model/app migration order is of utmost importance
self.to_delete = [] self.to_delete = []
ProblemaMigracao.objects.all().delete() ProblemaMigracao.objects.all().delete()
User.objects.all().delete()
info('Começando migração: %s...' % obj) info('Começando migração: %s...' % obj)
self._do_migrate(obj) self._do_migrate(obj)
# exclude logically deleted in legacy base # exclude logically deleted in legacy base
info('Deletando models com ind_excluido...') info('Deletando models com ind_excluido...')
for obj in self.to_delete: for obj in self.to_delete:
try:
obj.delete() obj.delete()
except ProtectedError:
msg = 'A entrada de PK %s da model %s não pode ser excluida' %\
(obj.pk, obj._meta.model_name)
descricao = 'Um ou mais objetos protegidos '
warn(msg + ' => ' + descricao)
save_relation(obj=obj, problema=msg,
descricao=descricao, eh_stub=False)
info('Deletando stubs desnecessários...') info('Deletando stubs desnecessários...')
while self.delete_stubs(): while self.delete_stubs():
pass pass
@ -299,6 +361,12 @@ class DataMigrator:
if model in self.field_renames) if model in self.field_renames)
self._do_migrate(models_to_migrate) self._do_migrate(models_to_migrate)
elif isinstance(obj, ModelBase): elif isinstance(obj, ModelBase):
# A migração vai pular TipoProposicao e só vai migrar essa model
# antes de migrar Proposicao. Isso deve acontecer por causa da
# GenericRelation existente em TipoProposicao.
if not obj.__name__ == 'TipoProposicao':
if obj.__name__ == 'Proposicao':
self.migrate_model(TipoProposicao)
self.migrate_model(obj) self.migrate_model(obj)
elif hasattr(obj, '__iter__'): elif hasattr(obj, '__iter__'):
for item in obj: for item in obj:
@ -316,6 +384,10 @@ class DataMigrator:
# Clear all model entries # Clear all model entries
# They may have been created in a previous migration attempt # They may have been created in a previous migration attempt
try:
model.objects.all().delete()
except ProtectedError:
Proposicao.objects.all().delete()
model.objects.all().delete() model.objects.all().delete()
delete_constraints(model) delete_constraints(model)
@ -416,13 +488,12 @@ def adjust_sessaoplenaria(new, old):
def adjust_tipoproposicao(new, old): def adjust_tipoproposicao(new, old):
if new.materia_ou_documento == 'M': if old.ind_mat_ou_doc == 'M':
field = TipoProposicao.tipo_materia.field new.tipo_conteudo_related = TipoMateriaLegislativa.objects.get(
value = get_fk_related(field=field, value=old.tip_mat_ou_doc) pk=old.tip_mat_ou_doc)
elif new.materia_ou_documento == 'D': elif old.ind_mat_ou_doc == 'D':
field = TipoProposicao.tipo_documento.field new.tipo_conteudo_related = TipoDocumento.objects.get(
value = get_fk_related(field=field, value=old.tip_mat_ou_doc) pk=old.tip_mat_ou_doc)
setattr(new, field.name, value)
def adjust_statustramitacao(new, old): def adjust_statustramitacao(new, old):
@ -455,15 +526,24 @@ def adjust_normajuridica_antes_salvar(new, old):
def adjust_normajuridica_depois_salvar(new, old): def adjust_normajuridica_depois_salvar(new, old):
# Ajusta relação M2M # Ajusta relação M2M
lista_ids_assunto = old.cod_assunto.split(',') lista_pks_assunto = old.cod_assunto.split(',')
for id_assunto in lista_ids_assunto: for pk_assunto in lista_pks_assunto:
relacao = AssuntoNormaRelationship() new.assuntos.add(AssuntoNorma.objects.get(pk=pk_assunto))
relacao.assunto_id = int(id_assunto)
relacao.norma_id = new.pk
relacao.save() def adjust_autor(new, old):
new.autor_related = TipoAutor.objects.get(pk=old.tip_autor)
if old.col_username:
if not User.objects.filter(username=old.col_username).exists():
user = User(username=old.col_username, password=12345)
user.save()
new.user = user
else:
new.user = User.objects.filter(username=old.col_username)[0]
AJUSTE_ANTES_SALVAR = { AJUSTE_ANTES_SALVAR = {
Autor: adjust_autor,
NormaJuridica: adjust_normajuridica_antes_salvar, NormaJuridica: adjust_normajuridica_antes_salvar,
OrdemDia: adjust_ordemdia, OrdemDia: adjust_ordemdia,
Parlamentar: adjust_parlamentar, Parlamentar: adjust_parlamentar,

200
sapl/materia/forms.py

@ -1,12 +1,12 @@
from datetime import datetime, date from datetime import date, datetime
import os import os
from crispy_forms.bootstrap import Alert, InlineCheckboxes, FormActions,\ from crispy_forms.bootstrap import (Alert, FormActions, InlineCheckboxes,
InlineRadios InlineRadios)
from crispy_forms.helper import FormHelper from crispy_forms.helper import FormHelper
from crispy_forms.layout import HTML, Button, Column, Fieldset, Layout, Row,\ from crispy_forms.layout import (HTML, Button, Column, Field, Fieldset, Layout,
Field, Submit Submit)
from django import forms 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
@ -21,21 +21,25 @@ import django_filters
from sapl.base.models import Autor from sapl.base.models import Autor
from sapl.comissoes.models import Comissao from sapl.comissoes.models import Comissao
from sapl.crispy_layout_mixin import form_actions, to_row, to_column,\ from sapl.compilacao.models import STATUS_TA_PRIVATE,\
SaplFormLayout STATUS_TA_IMMUTABLE_PUBLIC, TextoArticulado
from sapl.materia.models import TipoProposicao, RegimeTramitacao, TipoDocumento from sapl.crispy_layout_mixin import (SaplFormLayout, form_actions, to_column,
to_row)
from sapl.materia.models import TipoProposicao, MateriaLegislativa,\
RegimeTramitacao, TipoDocumento
from sapl.norma.models import (LegislacaoCitada, NormaJuridica, from sapl.norma.models import (LegislacaoCitada, NormaJuridica,
TipoNormaJuridica) TipoNormaJuridica)
from sapl.parlamentares.models import Parlamentar from sapl.parlamentares.models import Parlamentar
from sapl.protocoloadm.models import Protocolo from sapl.protocoloadm.models import Protocolo
from sapl.settings import MAX_DOC_UPLOAD_SIZE from sapl.settings import MAX_DOC_UPLOAD_SIZE
from sapl.utils import (RANGE_ANOS, RangeWidgetOverride, autor_label, from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES,
autor_modal, models_with_gr_for_model, ChoiceWithoutValidationField,
ChoiceWithoutValidationField, YES_NO_CHOICES) MateriaPesquisaOrderingFilter, RangeWidgetOverride,
autor_label, autor_modal, models_with_gr_for_model)
import sapl import sapl
from .models import (AcompanhamentoMateria, Anexada, Autoria, DespachoInicial, from .models import (AcompanhamentoMateria, Anexada, Autoria, DespachoInicial,
DocumentoAcessorio, MateriaLegislativa, Numeracao, DocumentoAcessorio, Numeracao,
Proposicao, Relatoria, TipoMateriaLegislativa, Tramitacao, Proposicao, Relatoria, TipoMateriaLegislativa, Tramitacao,
UnidadeTramitacao) UnidadeTramitacao)
@ -65,6 +69,33 @@ class ReceberProposicaoForm(Form):
super(ReceberProposicaoForm, self).__init__(*args, **kwargs) super(ReceberProposicaoForm, self).__init__(*args, **kwargs)
class MateriaSimplificadaForm(ModelForm):
class Meta:
model = MateriaLegislativa
fields = ['tipo', 'numero', 'ano', 'data_apresentacao',
'numero_protocolo', 'regime_tramitacao',
'em_tramitacao', 'ementa', 'texto_original']
def __init__(self, *args, **kwargs):
row1 = to_row([('tipo', 6), ('numero', 3), ('ano', 3)])
row2 = to_row([('data_apresentacao', 6), ('numero_protocolo', 6)])
row3 = to_row([('regime_tramitacao', 6), ('em_tramitacao', 6)])
row4 = to_row([('ementa', 12)])
row5 = to_row([('texto_original', 12)])
self.helper = FormHelper()
self.helper.layout = Layout(
Fieldset(
_('Formulário Simplificado'),
row1, row2, row3, row4, row5,
form_actions(save_label='Salvar')
)
)
super(MateriaSimplificadaForm, self).__init__(*args, **kwargs)
class UnidadeTramitacaoForm(ModelForm): class UnidadeTramitacaoForm(ModelForm):
class Meta: class Meta:
@ -84,66 +115,6 @@ class UnidadeTramitacaoForm(ModelForm):
return cleaned_data return cleaned_data
class ProposicaoOldForm(ModelForm):
tipo_materia = forms.ModelChoiceField(
label=_('Matéria Vinculada'),
required=False,
queryset=TipoMateriaLegislativa.objects.all(),
empty_label='Selecione',
)
numero_materia = forms.CharField(
label='Número', required=False)
ano_materia = forms.CharField(
label='Ano', required=False)
def clean_texto_original(self):
texto_original = self.cleaned_data.get('texto_original', False)
if texto_original:
if texto_original.size > MAX_DOC_UPLOAD_SIZE:
raise ValidationError("Arquivo muito grande. ( > 5mb )")
return texto_original
def clean_data_envio(self):
data_envio = self.cleaned_data.get('data_envio') or None
if (not data_envio) and len(self.initial) > 1:
data_envio = datetime.now()
return data_envio
def clean(self):
cleaned_data = self.cleaned_data
if 'tipo' in cleaned_data:
if cleaned_data['tipo'].descricao == 'Parecer':
if self.instance.materia:
cleaned_data['materia'] = self.instance.materia
else:
try:
materia = MateriaLegislativa.objects.get(
tipo_id=cleaned_data['tipo_materia'],
ano=cleaned_data['ano_materia'],
numero=cleaned_data['numero_materia'])
except ObjectDoesNotExist:
msg = _('Matéria adicionada não existe!')
raise ValidationError(msg)
else:
cleaned_data['materia'] = materia
return cleaned_data
def save(self, commit=False):
proposicao = super(ProposicaoOldForm, self).save(commit)
if 'materia' in self.cleaned_data:
proposicao.materia = self.cleaned_data['materia']
proposicao.save()
return proposicao
class Meta:
model = Proposicao
fields = ['tipo', 'data_envio', 'descricao', 'texto_original', 'autor']
widgets = {'autor': forms.HiddenInput()}
class AcompanhamentoMateriaForm(ModelForm): class AcompanhamentoMateriaForm(ModelForm):
class Meta: class Meta:
@ -504,6 +475,8 @@ class MateriaLegislativaFilterSet(django_filters.FilterSet):
label=u'Ano da Matéria', label=u'Ano da Matéria',
choices=em_tramitacao) choices=em_tramitacao)
o = MateriaPesquisaOrderingFilter()
class Meta: class Meta:
model = MateriaLegislativa model = MateriaLegislativa
fields = ['numero', fields = ['numero',
@ -513,7 +486,7 @@ class MateriaLegislativaFilterSet(django_filters.FilterSet):
'data_apresentacao', 'data_apresentacao',
'data_publicacao', 'data_publicacao',
'autoria__autor__tipo', 'autoria__autor__tipo',
# 'autoria__autor__partido', # FIXME 'autoria__autor__partido',
'relatoria__parlamentar_id', 'relatoria__parlamentar_id',
'local_origem_externa', 'local_origem_externa',
'tramitacao__unidade_tramitacao_destino', 'tramitacao__unidade_tramitacao_destino',
@ -521,29 +494,6 @@ class MateriaLegislativaFilterSet(django_filters.FilterSet):
'em_tramitacao', 'em_tramitacao',
] ]
order_by = (
('', 'Selecione'),
('dataC', 'Data, Tipo, Ano, Numero - Ordem Crescente'),
('dataD', 'Data, Tipo, Ano, Numero - Ordem Decrescente'),
('tipoC', 'Tipo, Ano, Numero, Data - Ordem Crescente'),
('tipoD', 'Tipo, Ano, Numero, Data - Ordem Decrescente')
)
order_by_mapping = {
'': [],
'dataC': ['data_apresentacao', 'tipo__sigla', 'ano', 'numero'],
'dataD': ['-data_apresentacao', '-tipo__sigla', '-ano', '-numero'],
'tipoC': ['tipo__sigla', 'ano', 'numero', 'data_apresentacao'],
'tipoD': ['-tipo__sigla', '-ano', '-numero', '-data_apresentacao'],
}
def get_order_by(self, order_value):
if order_value in self.order_by_mapping:
return self.order_by_mapping[order_value]
else:
return super(MateriaLegislativaFilterSet,
self).get_order_by(order_value)
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(MateriaLegislativaFilterSet, self).__init__(*args, **kwargs) super(MateriaLegislativaFilterSet, self).__init__(*args, **kwargs)
@ -829,7 +779,8 @@ class TipoProposicaoForm(ModelForm):
content_type = cd['content_type'] content_type = cd['content_type']
if 'tipo_conteudo_related' not in cd or not cd['tipo_conteudo_related']: if 'tipo_conteudo_related' not in cd or not cd[
'tipo_conteudo_related']:
raise ValidationError( raise ValidationError(
_('Seleção de Tipo não definida')) _('Seleção de Tipo não definida'))
@ -951,11 +902,14 @@ class ProposicaoForm(forms.ModelForm):
if self.instance.materia_de_vinculo: if self.instance.materia_de_vinculo:
self.fields[ self.fields[
'tipo_materia'].initial = self.instance.materia_de_vinculo.tipo 'tipo_materia'
].initial = self.instance.materia_de_vinculo.tipo
self.fields[ self.fields[
'numero_materia'].initial = self.instance.materia_de_vinculo.numero 'numero_materia'
].initial = self.instance.materia_de_vinculo.numero
self.fields[ self.fields[
'ano_materia'].initial = self.instance.materia_de_vinculo.ano 'ano_materia'
].initial = self.instance.materia_de_vinculo.ano
def clean_texto_original(self): def clean_texto_original(self):
texto_original = self.cleaned_data.get('texto_original', False) texto_original = self.cleaned_data.get('texto_original', False)
@ -1128,11 +1082,14 @@ class ConfirmarProposicaoForm(ProposicaoForm):
if self.instance.materia_de_vinculo: if self.instance.materia_de_vinculo:
self.fields[ self.fields[
'tipo_materia'].initial = self.instance.materia_de_vinculo.tipo 'tipo_materia'
].initial = self.instance.materia_de_vinculo.tipo
self.fields[ self.fields[
'numero_materia'].initial = self.instance.materia_de_vinculo.numero 'numero_materia'
].initial = self.instance.materia_de_vinculo.numero
self.fields[ self.fields[
'ano_materia'].initial = self.instance.materia_de_vinculo.ano 'ano_materia'
].initial = self.instance.materia_de_vinculo.ano
if self.proposicao_incorporacao_obrigatoria == 'C': if self.proposicao_incorporacao_obrigatoria == 'C':
self.fields['gerar_protocolo'].initial = True self.fields['gerar_protocolo'].initial = True
@ -1148,8 +1105,8 @@ class ConfirmarProposicaoForm(ProposicaoForm):
raise ValidationError( raise ValidationError(
_('Regimente de Tramitação deve ser informado.')) _('Regimente de Tramitação deve ser informado.'))
elif self.instance.tipo.content_type.model_class() == TipoDocumento\ elif self.instance.tipo.content_type.model_class(
and not cd['materia_de_vinculo']: ) == TipoDocumento and not cd['materia_de_vinculo']:
raise ValidationError( raise ValidationError(
_('Documentos não podem ser incorporados sem definir ' _('Documentos não podem ser incorporados sem definir '
@ -1179,6 +1136,12 @@ class ConfirmarProposicaoForm(ProposicaoForm):
self.instance.data_envio = None self.instance.data_envio = None
self.instance.save() self.instance.save()
if self.instance.texto_articulado.exists():
ta = self.instance.texto_articulado.first()
ta.privacidade = STATUS_TA_PRIVATE
ta.editing_locked = False
ta.save()
self.instance.results = { self.instance.results = {
'messages': { 'messages': {
'success': [_('Devolução efetuada com sucesso.'), ] 'success': [_('Devolução efetuada com sucesso.'), ]
@ -1192,6 +1155,12 @@ class ConfirmarProposicaoForm(ProposicaoForm):
self.instance.data_devolucao = None self.instance.data_devolucao = None
self.instance.data_recebimento = datetime.now() self.instance.data_recebimento = datetime.now()
if self.instance.texto_articulado.exists():
ta = self.instance.texto_articulado.first()
ta.privacidade = STATUS_TA_IMMUTABLE_PUBLIC
ta.editing_locked = True
ta.save()
self.instance.save() self.instance.save()
""" """
@ -1216,7 +1185,8 @@ class ConfirmarProposicaoForm(ProposicaoForm):
proposicao = self.instance proposicao = self.instance
conteudo_gerado = None conteudo_gerado = None
if self.instance.tipo.content_type.model_class() == TipoMateriaLegislativa: if self.instance.tipo.content_type.model_class(
) == TipoMateriaLegislativa:
numero__max = MateriaLegislativa.objects.filter( numero__max = MateriaLegislativa.objects.filter(
tipo=proposicao.tipo.tipo_conteudo_related, tipo=proposicao.tipo.tipo_conteudo_related,
ano=datetime.now().year).aggregate(Max('numero')) ano=datetime.now().year).aggregate(Max('numero'))
@ -1231,13 +1201,26 @@ class ConfirmarProposicaoForm(ProposicaoForm):
materia.data_apresentacao = datetime.now() materia.data_apresentacao = datetime.now()
materia.em_tramitacao = True materia.em_tramitacao = True
materia.regime_tramitacao = cd['regime_tramitacao'] materia.regime_tramitacao = cd['regime_tramitacao']
if proposicao.texto_original:
materia.texto_original = File( materia.texto_original = File(
proposicao.texto_original, proposicao.texto_original,
os.path.basename(proposicao.texto_original.path)) os.path.basename(proposicao.texto_original.path))
materia.texto_articulo = proposicao.texto_articulado
materia.save() materia.save()
conteudo_gerado = materia conteudo_gerado = materia
if proposicao.texto_articulado.exists():
ta = proposicao.texto_articulado.first()
ta.id = None
ta.content_object = materia
ta.save()
pass
# FIXME - gerar texto_articulado da materia com base na prop.
# materia.texto_articulo = proposicao.texto_articulado
self.instance.results['messages']['success'].append(_( self.instance.results['messages']['success'].append(_(
'Matéria Legislativa registrada com sucesso (%s)' 'Matéria Legislativa registrada com sucesso (%s)'
) % str(materia)) ) % str(materia))
@ -1349,7 +1332,8 @@ class ConfirmarProposicaoForm(ProposicaoForm):
protocolo.numero_paginas = cd['numero_de_paginas'] protocolo.numero_paginas = cd['numero_de_paginas']
protocolo.anulado = False protocolo.anulado = False
if self.instance.tipo.content_type.model_class() == TipoMateriaLegislativa: if self.instance.tipo.content_type.model_class(
) == TipoMateriaLegislativa:
protocolo.tipo_materia = proposicao.tipo.tipo_conteudo_related protocolo.tipo_materia = proposicao.tipo.tipo_conteudo_related
elif self.instance.tipo.content_type.model_class() == TipoDocumento: elif self.instance.tipo.content_type.model_class() == TipoDocumento:
protocolo.tipo_documento = proposicao.tipo.tipo_conteudo_related protocolo.tipo_documento = proposicao.tipo.tipo_conteudo_related

23
sapl/materia/legacy.yaml

@ -37,6 +37,11 @@ MateriaLegislativa:
tipo: tip_id_basica tipo: tip_id_basica
tipo_origem_externa: tip_origem_externa tipo_origem_externa: tip_origem_externa
Autoria:
autor: cod_autor
materia: cod_materia
primeiro_autor: ind_primeiro_autor
AcompanhamentoMateria (AcompMateria): AcompanhamentoMateria (AcompMateria):
email: end_email email: end_email
hash: txt_hash hash: txt_hash
@ -52,24 +57,6 @@ AssuntoMateria:
assunto: des_assunto assunto: des_assunto
dispositivo: des_dispositivo dispositivo: des_dispositivo
TipoAutor:
descricao: des_tipo_autor
Autor:
cargo: des_cargo
comissao: cod_comissao
nome: nom_autor
parlamentar: cod_parlamentar
partido: cod_partido
tipo: tip_autor
username: col_username
user:
Autoria:
autor: cod_autor
materia: cod_materia
primeiro_autor: ind_primeiro_autor
DespachoInicial: DespachoInicial:
comissao: cod_comissao comissao: cod_comissao
materia: cod_materia materia: cod_materia

13
sapl/materia/models.py

@ -1,9 +1,5 @@
import datetime
import re
from django.contrib.auth.models import Group from django.contrib.auth.models import Group
from django.contrib.contenttypes.fields import GenericForeignKey,\ from django.contrib.contenttypes.fields import GenericRelation
GenericRelation
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.db import models from django.db import models
from django.db.models.deletion import PROTECT from django.db.models.deletion import PROTECT
@ -14,10 +10,9 @@ from sapl.base.models import Autor
from sapl.comissoes.models import Comissao from sapl.comissoes.models import Comissao
from sapl.compilacao.models import TextoArticulado from sapl.compilacao.models import TextoArticulado
from sapl.parlamentares.models import Parlamentar from sapl.parlamentares.models import Parlamentar
from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES, from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES, SaplGenericForeignKey,
restringe_tipos_de_arquivo_txt, SaplGenericRelation, SaplGenericRelation, restringe_tipos_de_arquivo_txt,
SaplGenericForeignKey, texto_upload_path) texto_upload_path)
EM_TRAMITACAO = [(1, 'Sim'), EM_TRAMITACAO = [(1, 'Sim'),
(0, 'Não')] (0, 'Não')]

6
sapl/materia/tests/test_materia.py

@ -1,9 +1,9 @@
import pytest
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.core.urlresolvers import reverse
from model_mommy import mommy from model_mommy import mommy
import pytest
from sapl.base.models import Autor, TipoAutor from sapl.base.models import Autor, TipoAutor
from sapl.comissoes.models import Comissao, TipoComissao from sapl.comissoes.models import Comissao, TipoComissao
@ -452,7 +452,7 @@ def test_proposicao_submit(admin_client):
*models_with_gr_for_model(TipoProposicao)) *models_with_gr_for_model(TipoProposicao))
for pk, mct in enumerate(mcts): for pk, mct in enumerate(mcts):
tipo_conteudo_related = mommy.make(mct, pk=pk) tipo_conteudo_related = mommy.make(mct, pk=pk + 1)
response = admin_client.post( response = admin_client.post(
reverse('sapl.materia:proposicao_create'), reverse('sapl.materia:proposicao_create'),
@ -475,7 +475,7 @@ def test_proposicao_submit(admin_client):
assert proposicao is not None assert proposicao is not None
assert proposicao.descricao == 'Teste proposição' assert proposicao.descricao == 'Teste proposição'
assert proposicao.tipo.pk == 3 assert proposicao.tipo.pk == 3
assert proposicao.tipo.tipo_conteudo_related.pk == pk assert proposicao.tipo.tipo_conteudo_related.pk == pk + 1
@pytest.mark.django_db(transaction=False) @pytest.mark.django_db(transaction=False)

7
sapl/materia/urls.py

@ -4,7 +4,8 @@ from sapl.materia.views import (AcompanhamentoConfirmarView,
AcompanhamentoExcluirView, AcompanhamentoExcluirView,
AcompanhamentoMateriaView, AnexadaCrud, AcompanhamentoMateriaView, AnexadaCrud,
AutoriaCrud, ConfirmarProposicao, AutoriaCrud, ConfirmarProposicao,
DespachoInicialCrud, DocumentoAcessorioCrud, CriarProtocoloMateriaView, DespachoInicialCrud,
DocumentoAcessorioCrud,
DocumentoAcessorioEmLoteView, DocumentoAcessorioEmLoteView,
LegislacaoCitadaCrud, MateriaLegislativaCrud, LegislacaoCitadaCrud, MateriaLegislativaCrud,
MateriaLegislativaPesquisaView, MateriaTaView, MateriaLegislativaPesquisaView, MateriaTaView,
@ -34,6 +35,10 @@ urlpatterns_materia = [
TramitacaoCrud.get_urls() + TramitacaoCrud.get_urls() +
RelatoriaCrud.get_urls() + RelatoriaCrud.get_urls() +
DocumentoAcessorioCrud.get_urls())), DocumentoAcessorioCrud.get_urls())),
url(r'^materia/(?P<pk>[0-9]+)/create_simplificado$',
CriarProtocoloMateriaView.as_view(),
name='materia_create_simplificado'),
url(r'^materia/recuperar-materia', recuperar_materia), url(r'^materia/recuperar-materia', recuperar_materia),
url(r'^materia/(?P<pk>[0-9]+)/ta$', url(r'^materia/(?P<pk>[0-9]+)/ta$',
MateriaTaView.as_view(), name='materia_ta'), MateriaTaView.as_view(), name='materia_ta'),

176
sapl/materia/views.py

@ -5,48 +5,45 @@ from string import ascii_letters, digits
from crispy_forms.helper import FormHelper from crispy_forms.helper import FormHelper
from crispy_forms.layout import HTML from crispy_forms.layout import HTML
from django.contrib import messages from django.contrib import messages
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 MultipleObjectsReturned, ObjectDoesNotExist,\ from django.core.exceptions import ObjectDoesNotExist
PermissionDenied
from django.core.mail import send_mail from django.core.mail import send_mail
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.db.models import Q
from django.http import JsonResponse from django.http import JsonResponse
from django.http.response import HttpResponseRedirect, Http404 from django.http.response import Http404, HttpResponseRedirect
from django.shortcuts import redirect from django.shortcuts import redirect, get_object_or_404
from django.template import Context, loader from django.template import Context, loader
from django.utils import dateformat, formats from django.utils import formats
from django.utils.http import urlsafe_base64_decode
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.views.generic import CreateView, ListView, TemplateView, UpdateView from django.views.generic import CreateView, ListView, TemplateView, UpdateView
from django.views.generic.base import RedirectView from django.views.generic.base import RedirectView
from django.views.generic.edit import FormView from django.views.generic.edit import FormView
from django_filters.views import FilterView from django_filters.views import FilterView
from sapl.base.models import Autor, CasaLegislativa, TipoAutor from sapl.base.models import Autor, CasaLegislativa
from sapl.compilacao.models import STATUS_TA_PRIVATE, STATUS_TA_EDITION,\
STATUS_TA_IMMUTABLE_RESTRICT
from sapl.compilacao.views import IntegracaoTaView from sapl.compilacao.views import IntegracaoTaView
from sapl.crispy_layout_mixin import SaplFormLayout, form_actions from sapl.crispy_layout_mixin import SaplFormLayout, form_actions
from sapl.crud.base import (ACTION_CREATE, ACTION_DELETE, ACTION_DETAIL, from sapl.crud.base import (ACTION_CREATE, ACTION_DELETE, ACTION_DETAIL,
ACTION_LIST, ACTION_UPDATE, RP_DETAIL, RP_LIST, ACTION_LIST, ACTION_UPDATE, RP_DETAIL, RP_LIST,
Crud, CrudAux, CrudDetailView, MasterDetailCrud, Crud, CrudAux, MasterDetailCrud,
make_pagination, PermissionRequiredForAppCrudMixin) PermissionRequiredForAppCrudMixin, make_pagination)
from sapl.materia import apps from sapl.materia.forms import (AnexadaForm, ConfirmarProposicaoForm,
from sapl.materia.forms import AnexadaForm, LegislacaoCitadaForm,\ LegislacaoCitadaForm, ProposicaoForm,
TipoProposicaoForm, ProposicaoForm, ConfirmarProposicaoForm TipoProposicaoForm)
from sapl.norma.models import LegislacaoCitada from sapl.norma.models import LegislacaoCitada
from sapl.protocoloadm.models import Protocolo
from sapl.utils import (TURNO_TRAMITACAO_CHOICES, YES_NO_CHOICES, autor_label, from sapl.utils import (TURNO_TRAMITACAO_CHOICES, YES_NO_CHOICES, autor_label,
autor_modal, gerar_hash_arquivo, get_base_url, autor_modal, gerar_hash_arquivo, get_base_url,
montar_row_autor, permission_required_for_app,
permissoes_autor, permissoes_materia,
permissoes_protocoloadm, permission_required_for_app,
montar_row_autor) montar_row_autor)
import sapl import sapl
from .forms import (AcessorioEmLoteFilterSet, AcompanhamentoMateriaForm, from .forms import (AcessorioEmLoteFilterSet, AcompanhamentoMateriaForm,
DocumentoAcessorioForm, DocumentoAcessorioForm, MateriaLegislativaFilterSet,
MateriaLegislativaFilterSet, MateriaSimplificadaForm, PrimeiraTramitacaoEmLoteFilterSet,
PrimeiraTramitacaoEmLoteFilterSet, ProposicaoOldForm,
ReceberProposicaoForm, TramitacaoEmLoteFilterSet, ReceberProposicaoForm, TramitacaoEmLoteFilterSet,
filtra_tramitacao_destino, filtra_tramitacao_destino,
filtra_tramitacao_destino_and_status, filtra_tramitacao_destino_and_status,
@ -74,9 +71,52 @@ TipoFimRelatoriaCrud = CrudAux.build(
TipoFimRelatoria, 'fim_relatoria') TipoFimRelatoria, 'fim_relatoria')
class CriarProtocoloMateriaView(CreateView):
template_name = "crud/form.html"
form_class = MateriaSimplificadaForm
form_valid_message = _('Matéria cadastrada com sucesso!')
def get_success_url(self, materia):
return reverse('sapl.materia:materialegislativa_detail', kwargs={
'pk': materia.pk})
def get_context_data(self, **kwargs):
context = super(
CriarProtocoloMateriaView, self).get_context_data(**kwargs)
protocolo = Protocolo.objects.get(pk=self.kwargs['pk'])
context['form'].fields['tipo'].initial = protocolo.tipo_materia
context['form'].fields['numero'].initial = protocolo.numero
context['form'].fields['ano'].initial = protocolo.ano
context['form'].fields['data_apresentacao'].initial = protocolo.data
context['form'].fields['numero_protocolo'].initial = protocolo.numero
context['form'].fields['ementa'].initial = protocolo.observacao
return context
def form_valid(self, form):
materia = form.save()
return redirect(self.get_success_url(materia))
class MateriaTaView(IntegracaoTaView): class MateriaTaView(IntegracaoTaView):
model = MateriaLegislativa model = MateriaLegislativa
model_type_foreignkey = TipoMateriaLegislativa model_type_foreignkey = TipoMateriaLegislativa
map_fields = {
'data': 'data_apresentacao',
'ementa': 'ementa',
'observacao': None,
'numero': 'numero',
'ano': 'ano',
}
map_funcs = {
'publicacao_func': False,
}
ta_values = {
'editable_only_by_owners': False,
'editing_locked': False,
}
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
""" """
@ -93,8 +133,20 @@ class MateriaTaView(IntegracaoTaView):
class ProposicaoTaView(IntegracaoTaView): class ProposicaoTaView(IntegracaoTaView):
model = Proposicao model = Proposicao
model_type_foreignkey = TipoProposicao model_type_foreignkey = TipoProposicao
# TODO implmentar o mapa de fields e utiliza-lo em IntegracaoTaView map_fields = {
fields = { 'data': 'data_envio',
'ementa': 'descricao',
'observacao': None,
'numero': 'numero_proposicao',
'ano': 'ano',
}
map_funcs = {
'publicacao_func': False
}
ta_values = {
'editable_only_by_owners': True,
'editing_locked': False,
'privacidade': STATUS_TA_PRIVATE
} }
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
@ -104,12 +156,19 @@ class ProposicaoTaView(IntegracaoTaView):
de usuário. de usuário.
""" """
if sapl.base.models.AppConfig.attr('texto_articulado_proposicao'): if sapl.base.models.AppConfig.attr('texto_articulado_proposicao'):
proposicao = get_object_or_404(self.model, pk=kwargs['pk'])
if not proposicao.data_envio and\
request.user != proposicao.autor.user:
raise Http404()
return IntegracaoTaView.get(self, request, *args, **kwargs) return IntegracaoTaView.get(self, request, *args, **kwargs)
else: else:
return self.get_redirect_deactivated() return self.get_redirect_deactivated()
@permission_required_for_app(app_label=apps.AppConfig.label) @permission_required('materia.detail_materialegislativa')
def recuperar_materia(request): def recuperar_materia(request):
tipo = TipoMateriaLegislativa.objects.get(pk=request.GET['tipo']) tipo = TipoMateriaLegislativa.objects.get(pk=request.GET['tipo'])
ano = request.GET.get('ano', '') ano = request.GET.get('ano', '')
@ -192,7 +251,7 @@ class ProposicaoDevolvida(PermissionRequiredMixin, ListView):
model = Proposicao model = Proposicao
ordering = ['data_envio'] ordering = ['data_envio']
paginate_by = 10 paginate_by = 10
permission_required = permissoes_protocoloadm() permission_required = ('materia.list_proposicao', )
def get_queryset(self): def get_queryset(self):
return Proposicao.objects.filter( return Proposicao.objects.filter(
@ -216,7 +275,7 @@ class ProposicaoPendente(PermissionRequiredMixin, ListView):
model = Proposicao model = Proposicao
ordering = ['data_envio', 'autor', 'tipo', 'descricao'] ordering = ['data_envio', 'autor', 'tipo', 'descricao']
paginate_by = 10 paginate_by = 10
permission_required = permissoes_protocoloadm() permission_required = ('materia.list_proposicao', )
def get_queryset(self): def get_queryset(self):
return Proposicao.objects.filter( return Proposicao.objects.filter(
@ -241,7 +300,7 @@ class ProposicaoRecebida(PermissionRequiredMixin, ListView):
model = Proposicao model = Proposicao
ordering = ['data_envio'] ordering = ['data_envio']
paginate_by = 10 paginate_by = 10
permission_required = permissoes_protocoloadm() permission_required = ('materia.list_proposicao', )
def get_queryset(self): def get_queryset(self):
return Proposicao.objects.filter( return Proposicao.objects.filter(
@ -274,7 +333,11 @@ class ReceberProposicao(PermissionRequiredForAppCrudMixin, FormView):
data_envio__isnull=False, data_recebimento__isnull=True) data_envio__isnull=False, data_recebimento__isnull=True)
for proposicao in proposicoes: for proposicao in proposicoes:
# FIXME implementar hash para texto eletrônico if proposicao.texto_articulado.exists():
ta = proposicao.texto_articulado.first()
# FIXME hash para textos articulados
hasher = 'P' + ta.hash() + '/' + str(proposicao.id)
else:
hasher = gerar_hash_arquivo( hasher = gerar_hash_arquivo(
proposicao.texto_original.path, proposicao.texto_original.path,
str(proposicao.pk)) if proposicao.texto_original else None str(proposicao.pk)) if proposicao.texto_original else None
@ -304,9 +367,6 @@ class ConfirmarProposicao(PermissionRequiredForAppCrudMixin, UpdateView):
form_class = ConfirmarProposicaoForm form_class = ConfirmarProposicaoForm
def get_success_url(self): def get_success_url(self):
# FIXME redirecionamento trival,
# ainda por implementar se será para protocolo ou para doc resultante
msgs = self.object.results['messages'] msgs = self.object.results['messages']
for key, value in msgs.items(): for key, value in msgs.items():
@ -326,6 +386,12 @@ class ConfirmarProposicao(PermissionRequiredForAppCrudMixin, UpdateView):
data_recebimento__isnull=True) data_recebimento__isnull=True)
self.object = None self.object = None
# FIXME implementar hash para texto eletrônico # FIXME implementar hash para texto eletrônico
if proposicao.texto_articulado.exists():
ta = proposicao.texto_articulado.first()
# FIXME hash para textos articulados
hasher = 'P' + ta.hash() + '/' + str(proposicao.id)
else:
hasher = gerar_hash_arquivo( hasher = gerar_hash_arquivo(
proposicao.texto_original.path, proposicao.texto_original.path,
str(proposicao.pk)) if proposicao.texto_original else None str(proposicao.pk)) if proposicao.texto_original else None
@ -435,6 +501,13 @@ class ProposicaoCrud(Crud):
p.data_devolucao = None p.data_devolucao = None
p.data_envio = datetime.now() p.data_envio = datetime.now()
p.save() p.save()
if p.texto_articulado.exists():
ta = p.texto_articulado.first()
ta.privacidade = STATUS_TA_IMMUTABLE_RESTRICT
ta.editing_locked = True
ta.save()
messages.success(request, _( messages.success(request, _(
'Proposição enviada com sucesso.')) 'Proposição enviada com sucesso.'))
@ -447,6 +520,11 @@ class ProposicaoCrud(Crud):
else: else:
p.data_envio = None p.data_envio = None
p.save() p.save()
if p.texto_articulado.exists():
ta = p.texto_articulado.first()
ta.privacidade = STATUS_TA_PRIVATE
ta.editing_locked = False
ta.save()
messages.success(request, _( messages.success(request, _(
'Proposição Retornada com sucesso.')) 'Proposição Retornada com sucesso.'))
@ -508,9 +586,8 @@ class ProposicaoCrud(Crud):
messages.info(self.request, messages.info(self.request,
_('Sempre que uma Proposição é inclusa ou ' _('Sempre que uma Proposição é inclusa ou '
'alterada e a opção "Texto Articulado " for ' 'alterada e a opção "Texto Articulado " for '
'marcada, você será redirecionado para o ' 'marcada, você será redirecionado para a '
'Texto Eletrônico. Use a opção "Editar Texto" ' 'edição do Texto Eletrônico.'))
'para construir seu texto.'))
return reverse('sapl.materia:proposicao_ta', return reverse('sapl.materia:proposicao_ta',
kwargs={'pk': self.object.pk}) kwargs={'pk': self.object.pk})
else: else:
@ -564,7 +641,7 @@ class ProposicaoCrud(Crud):
class ReciboProposicaoView(TemplateView): class ReciboProposicaoView(TemplateView):
template_name = "materia/recibo_proposicao.html" template_name = "materia/recibo_proposicao.html"
permission_required = permissoes_autor() permission_required = ('materia.detail_proposicao', )
def has_permission(self): def has_permission(self):
perms = self.get_permission_required() perms = self.get_permission_required()
@ -579,10 +656,18 @@ class ReciboProposicaoView(TemplateView):
context = super(ReciboProposicaoView, self).get_context_data( context = super(ReciboProposicaoView, self).get_context_data(
**kwargs) **kwargs)
proposicao = Proposicao.objects.get(pk=self.kwargs['pk']) proposicao = Proposicao.objects.get(pk=self.kwargs['pk'])
context.update({'proposicao': proposicao,
'hash': gerar_hash_arquivo( if proposicao.texto_original:
_hash = gerar_hash_arquivo(
proposicao.texto_original.path, proposicao.texto_original.path,
self.kwargs['pk'])}) self.kwargs['pk'])
elif proposicao.texto_articulado.exists():
ta = proposicao.texto_articulado.first()
# FIXME hash para textos articulados
_hash = 'P' + ta.hash() + '/' + str(proposicao.id)
context.update({'proposicao': proposicao,
'hash': _hash})
return context return context
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
@ -592,7 +677,7 @@ class ReciboProposicaoView(TemplateView):
return TemplateView.get(self, request, *args, **kwargs) return TemplateView.get(self, request, *args, **kwargs)
if not proposicao.data_envio and not proposicao.data_devolucao: if not proposicao.data_envio and not proposicao.data_devolucao:
messages.error(request, _('Não é possível gerar recebo para uma ' messages.error(request, _('Não é possível gerar recibo para uma '
'Proposição ainda não enviada.')) 'Proposição ainda não enviada.'))
elif proposicao.data_devolucao: elif proposicao.data_devolucao:
messages.error(request, _('Não é possível gerar recibo.')) messages.error(request, _('Não é possível gerar recibo.'))
@ -916,7 +1001,7 @@ class MateriaLegislativaCrud(Crud):
class DocumentoAcessorioView(PermissionRequiredMixin, CreateView): class DocumentoAcessorioView(PermissionRequiredMixin, CreateView):
template_name = "materia/documento_acessorio.html" template_name = "materia/documento_acessorio.html"
form_class = DocumentoAcessorioForm form_class = DocumentoAcessorioForm
permission_required = permissoes_materia() permission_required = ('materia.add_documentoacessorio', )
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
materia = MateriaLegislativa.objects.get(id=kwargs['pk']) materia = MateriaLegislativa.objects.get(id=kwargs['pk'])
@ -950,8 +1035,7 @@ class DocumentoAcessorioView(PermissionRequiredMixin, CreateView):
return reverse('sapl.materia:documento_acessorio', kwargs={'pk': pk}) return reverse('sapl.materia:documento_acessorio', kwargs={'pk': pk})
class AcompanhamentoConfirmarView(PermissionRequiredMixin, TemplateView): class AcompanhamentoConfirmarView(TemplateView):
permission_required = permissoes_materia()
def get_redirect_url(self): def get_redirect_url(self):
return reverse('sapl.sessao:list_pauta_sessao') return reverse('sapl.sessao:list_pauta_sessao')
@ -968,8 +1052,7 @@ class AcompanhamentoConfirmarView(PermissionRequiredMixin, TemplateView):
return HttpResponseRedirect(self.get_redirect_url()) return HttpResponseRedirect(self.get_redirect_url())
class AcompanhamentoExcluirView(PermissionRequiredMixin, TemplateView): class AcompanhamentoExcluirView(TemplateView):
permission_required = permissoes_materia()
def get_redirect_url(self): def get_redirect_url(self):
return reverse('sapl.sessao:list_pauta_sessao') return reverse('sapl.sessao:list_pauta_sessao')
@ -1045,9 +1128,8 @@ class MateriaLegislativaPesquisaView(FilterView):
return context return context
class AcompanhamentoMateriaView(PermissionRequiredMixin, CreateView): class AcompanhamentoMateriaView(CreateView):
template_name = "materia/acompanhamento_materia.html" template_name = "materia/acompanhamento_materia.html"
permission_required = permissoes_materia()
def get_random_chars(self): def get_random_chars(self):
s = ascii_letters + digits s = ascii_letters + digits
@ -1303,7 +1385,7 @@ def do_envia_email_tramitacao(request, materia):
class DocumentoAcessorioEmLoteView(PermissionRequiredMixin, FilterView): class DocumentoAcessorioEmLoteView(PermissionRequiredMixin, FilterView):
filterset_class = AcessorioEmLoteFilterSet filterset_class = AcessorioEmLoteFilterSet
template_name = 'materia/em_lote/acessorio.html' template_name = 'materia/em_lote/acessorio.html'
permission_required = permissoes_materia() permission_required = ('materia.add_documentoacessorio',)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(DocumentoAcessorioEmLoteView, context = super(DocumentoAcessorioEmLoteView,
@ -1347,7 +1429,7 @@ class DocumentoAcessorioEmLoteView(PermissionRequiredMixin, FilterView):
class PrimeiraTramitacaoEmLoteView(PermissionRequiredMixin, FilterView): class PrimeiraTramitacaoEmLoteView(PermissionRequiredMixin, FilterView):
filterset_class = PrimeiraTramitacaoEmLoteFilterSet filterset_class = PrimeiraTramitacaoEmLoteFilterSet
template_name = 'materia/em_lote/tramitacao.html' template_name = 'materia/em_lote/tramitacao.html'
permission_required = permissoes_materia() permission_required = ('materia.add_tramitacao', )
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(PrimeiraTramitacaoEmLoteView, context = super(PrimeiraTramitacaoEmLoteView,

40
sapl/norma/forms.py

@ -4,7 +4,7 @@ from crispy_forms.helper import FormHelper
from crispy_forms.layout import Fieldset, Layout from crispy_forms.layout import Fieldset, Layout
from django import forms from django import forms
from django.core.exceptions import ObjectDoesNotExist, ValidationError from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.forms import ModelForm from django.forms import ModelForm, widgets
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from sapl.crispy_layout_mixin import form_actions, to_row from sapl.crispy_layout_mixin import form_actions, to_row
@ -12,7 +12,7 @@ from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa
from sapl.settings import MAX_DOC_UPLOAD_SIZE from sapl.settings import MAX_DOC_UPLOAD_SIZE
from sapl.utils import RANGE_ANOS from sapl.utils import RANGE_ANOS
from .models import NormaJuridica from .models import AssuntoNorma, NormaJuridica
def get_esferas(): def get_esferas():
@ -64,7 +64,7 @@ class NormaJuridicaPesquisaForm(ModelForm):
ano = forms.ModelChoiceField( ano = forms.ModelChoiceField(
label='Ano', label='Ano',
required=False, required=False,
queryset=NormaJuridica.objects.order_by('ano').values_list( queryset=NormaJuridica.objects.order_by('-ano').values_list(
'ano', flat=True).distinct(), 'ano', flat=True).distinct(),
empty_label='Selecione' empty_label='Selecione'
) )
@ -81,6 +81,13 @@ class NormaJuridicaPesquisaForm(ModelForm):
numero = forms.IntegerField(required=False) numero = forms.IntegerField(required=False)
assunto = forms.ModelChoiceField(
label='Assunto',
required=False,
queryset=AssuntoNorma.objects.all(),
empty_label='Selecione'
)
class Meta: class Meta:
model = NormaJuridica model = NormaJuridica
fields = ['tipo', fields = ['tipo',
@ -89,25 +96,20 @@ class NormaJuridicaPesquisaForm(ModelForm):
'periodo_inicial', 'periodo_inicial',
'periodo_final', 'periodo_final',
'publicacao_inicial', 'publicacao_inicial',
'publicacao_final'] 'publicacao_final',
'assunto']
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
row1 = to_row( row1 = to_row([('tipo', 12)])
[('tipo', 12)])
row2 = to_row( row2 = to_row([('numero', 6), ('ano', 6)])
[('numero', 6), ('ano', 6)])
row3 = to_row( row3 = to_row([('periodo_inicial', 6), ('periodo_final', 6)])
[('periodo_inicial', 6), ('periodo_final', 6)])
row4 = to_row( row4 = to_row([('publicacao_inicial', 6), ('publicacao_final', 6)])
[('publicacao_inicial', 6), ('publicacao_final', 6)])
row5 = to_row( row5 = to_row([('em_vigencia', 4), ('ordenacao', 4), ('assunto', 4)])
[('em_vigencia', 6),
('ordenacao', 6)])
self.helper = FormHelper() self.helper = FormHelper()
self.helper.layout = Layout( self.helper.layout = Layout(
@ -155,7 +157,9 @@ class NormaJuridicaForm(ModelForm):
'ementa', 'ementa',
'indexacao', 'indexacao',
'observacao', 'observacao',
'texto_integral'] 'texto_integral',
'assuntos']
widgets = {'assuntos': widgets.CheckboxSelectMultiple}
def clean(self): def clean(self):
cleaned_data = self.cleaned_data cleaned_data = self.cleaned_data
@ -186,8 +190,8 @@ class NormaJuridicaForm(ModelForm):
return texto_integral return texto_integral
def save(self, commit=False): def save(self, commit=False):
norma = super(NormaJuridicaForm, self).save(commit) norma = self.instance
norma.timestamp = datetime.now() norma.timestamp = datetime.now()
norma.materia = self.cleaned_data['materia'] norma.materia = self.cleaned_data['materia']
norma.save() norma = super(NormaJuridicaForm, self).save(commit=True)
return norma return norma

23
sapl/norma/migrations/0016_auto_20161027_1419.py

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-10-27 14:19
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('norma', '0015_auto_20160929_1635'),
]
operations = [
migrations.AlterModelOptions(
name='assuntonormarelationship',
options={'verbose_name': 'Assunto', 'verbose_name_plural': 'Assuntos'},
),
migrations.AlterUniqueTogether(
name='assuntonormarelationship',
unique_together=set([]),
),
]

20
sapl/norma/migrations/0017_auto_20161027_1432.py

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-10-27 14:32
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('norma', '0016_auto_20161027_1419'),
]
operations = [
migrations.AlterField(
model_name='normajuridica',
name='assuntos',
field=models.ManyToManyField(through='norma.AssuntoNormaRelationship', to='norma.AssuntoNorma', verbose_name='Assuntos'),
),
]

26
sapl/norma/migrations/0018_auto_20161027_1434.py

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-10-27 14:34
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('norma', '0017_auto_20161027_1432'),
]
operations = [
migrations.AlterField(
model_name='assuntonormarelationship',
name='assunto',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='norma.AssuntoNorma', verbose_name='Assunto'),
),
migrations.AlterField(
model_name='assuntonormarelationship',
name='norma',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='norma.NormaJuridica', verbose_name='Norma'),
),
]

19
sapl/norma/migrations/0019_auto_20161028_0232.py

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-10-28 02:32
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('norma', '0018_auto_20161027_1434'),
]
operations = [
migrations.AlterUniqueTogether(
name='assuntonormarelationship',
unique_together=set([('assunto', 'norma')]),
),
]

39
sapl/norma/migrations/0020_auto_20161028_1335.py

@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-10-28 13:35
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('norma', '0019_auto_20161028_0232'),
]
operations = [
migrations.AlterUniqueTogether(
name='assuntonormarelationship',
unique_together=set([]),
),
migrations.RemoveField(
model_name='assuntonormarelationship',
name='assunto',
),
migrations.RemoveField(
model_name='assuntonormarelationship',
name='norma',
),
migrations.RemoveField(
model_name='normajuridica',
name='assuntos',
),
migrations.AddField(
model_name='normajuridica',
name='assuntos',
field=models.TextField(blank=True, null=True),
),
migrations.DeleteModel(
name='AssuntoNormaRelationship',
),
]

24
sapl/norma/migrations/0021_auto_20161028_1335.py

@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-10-28 13:35
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('norma', '0020_auto_20161028_1335'),
]
operations = [
migrations.RemoveField(
model_name='normajuridica',
name='assuntos',
),
migrations.AddField(
model_name='normajuridica',
name='assuntos',
field=models.ManyToManyField(blank=True, to='norma.AssuntoNorma', verbose_name='Assuntos'),
),
]

14
sapl/norma/models.py

@ -97,8 +97,8 @@ class NormaJuridica(models.Model):
choices=YES_NO_CHOICES) 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, AssuntoNorma, blank=True,
through='AssuntoNormaRelationship') verbose_name=_('Assuntos'))
data_vigencia = models.DateField(blank=True, null=True) data_vigencia = models.DateField(blank=True, null=True)
timestamp = models.DateTimeField() timestamp = models.DateTimeField()
@ -134,16 +134,6 @@ class NormaJuridica(models.Model):
update_fields=update_fields) update_fields=update_fields)
class AssuntoNormaRelationship(models.Model):
assunto = models.ForeignKey(AssuntoNorma)
norma = models.ForeignKey(NormaJuridica)
class Meta:
unique_together = (
('assunto', 'norma'),
)
class LegislacaoCitada(models.Model): class LegislacaoCitada(models.Model):
materia = models.ForeignKey(MateriaLegislativa) materia = models.ForeignKey(MateriaLegislativa)
norma = models.ForeignKey(NormaJuridica) norma = models.ForeignKey(NormaJuridica)

6
sapl/norma/urls.py

@ -1,8 +1,8 @@
from django.conf.urls import include, url from django.conf.urls import include, url
from sapl.norma.views import (AssuntoNormaCrud, NormaCrud, NormaPesquisaView, from sapl.norma.views import (AssuntoNormaCrud,
NormaTaView, PesquisaNormaListView, NormaCrud, NormaPesquisaView, NormaTaView,
TipoNormaCrud) PesquisaNormaListView, TipoNormaCrud)
from .apps import AppConfig from .apps import AppConfig

22
sapl/norma/views.py

@ -7,11 +7,13 @@ from django.views.generic.base import RedirectView
from sapl.base.models import AppConfig from sapl.base.models import AppConfig
from sapl.compilacao.views import IntegracaoTaView from sapl.compilacao.views import IntegracaoTaView
from sapl.crud.base import RP_DETAIL, RP_LIST, Crud, CrudAux, make_pagination from sapl.crud.base import (RP_DETAIL, RP_LIST, Crud, CrudAux,
MasterDetailCrud, make_pagination)
from sapl.norma.forms import NormaJuridicaForm from sapl.norma.forms import NormaJuridicaForm
from .forms import NormaJuridicaPesquisaForm from .forms import NormaJuridicaPesquisaForm
from .models import AssuntoNorma, NormaJuridica, TipoNormaJuridica from .models import (AssuntoNorma, NormaJuridica,
TipoNormaJuridica)
# LegislacaoCitadaCrud = Crud.build(LegislacaoCitada, '') # LegislacaoCitadaCrud = Crud.build(LegislacaoCitada, '')
AssuntoNormaCrud = CrudAux.build(AssuntoNorma, 'assunto_norma_juridica', AssuntoNormaCrud = CrudAux.build(AssuntoNorma, 'assunto_norma_juridica',
@ -26,6 +28,17 @@ TipoNormaCrud = CrudAux.build(
class NormaTaView(IntegracaoTaView): class NormaTaView(IntegracaoTaView):
model = NormaJuridica model = NormaJuridica
model_type_foreignkey = TipoNormaJuridica model_type_foreignkey = TipoNormaJuridica
map_fields = {
'data': 'data',
'ementa': 'ementa',
'observacao': 'observacao',
'numero': 'numero',
'ano': 'ano',
}
map_funcs = {
'publicacao_func': True
}
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
""" """
@ -116,6 +129,8 @@ class NormaPesquisaView(FormView):
kwargs['ordenacao'] = form.data['ordenacao'] kwargs['ordenacao'] = form.data['ordenacao']
if form.data['em_vigencia']: if form.data['em_vigencia']:
kwargs['em_vigencia'] = form.data['em_vigencia'] kwargs['em_vigencia'] = form.data['em_vigencia']
if form.data['assunto']:
kwargs['assunto'] = form.data['assunto']
request.session['kwargs'] = kwargs request.session['kwargs'] = kwargs
return redirect('sapl.norma:list_pesquisa_norma') return redirect('sapl.norma:list_pesquisa_norma')
@ -183,6 +198,9 @@ class PesquisaNormaListView(ListView):
if 'ano' in kwargs: if 'ano' in kwargs:
normas = normas.filter(ano=kwargs['ano']) normas = normas.filter(ano=kwargs['ano'])
if 'assunto' in kwargs:
normas = normas.filter(assuntos=kwargs['assunto'])
return normas return normas
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):

6
sapl/painel/views.py

@ -7,20 +7,22 @@ from django.shortcuts import render
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from sapl.crud.base import Crud from sapl.crud.base import Crud
from sapl.painel.apps import AppConfig
from sapl.painel.models import Painel from sapl.painel.models import Painel
from sapl.parlamentares.models import Filiacao from sapl.parlamentares.models import Filiacao
from sapl.sessao.models import (ExpedienteMateria, OrdemDia, PresencaOrdemDia, from sapl.sessao.models import (ExpedienteMateria, OrdemDia, PresencaOrdemDia,
RegistroVotacao, SessaoPlenaria, RegistroVotacao, SessaoPlenaria,
SessaoPlenariaPresenca, VotoParlamentar) SessaoPlenariaPresenca, VotoParlamentar)
from sapl.utils import permissoes_painel
from .models import Cronometro from .models import Cronometro
CronometroPainelCrud = Crud.build(Cronometro, '') CronometroPainelCrud = Crud.build(Cronometro, '')
# FIXME mudar lógica
def check_permission(user): def check_permission(user):
return user.has_perms(permissoes_painel()) return user.has_module_perms(AppConfig.label)
@user_passes_test(check_permission) @user_passes_test(check_permission)

34
sapl/parlamentares/views.py

@ -54,6 +54,12 @@ class RelatoriaParlamentarCrud(CrudBaseForListAndDetailExternalAppView):
help_path = 'relatoria_parlamentar' help_path = 'relatoria_parlamentar'
namespace = AppConfig.name namespace = AppConfig.name
class BaseMixin(CrudBaseForListAndDetailExternalAppView.BaseMixin):
@classmethod
def url_name(cls, suffix):
return '%s_parlamentar_%s' % (cls.model._meta.model_name, suffix)
class ProposicaoParlamentarCrud(CrudBaseForListAndDetailExternalAppView): class ProposicaoParlamentarCrud(CrudBaseForListAndDetailExternalAppView):
model = Proposicao model = Proposicao
@ -61,11 +67,31 @@ class ProposicaoParlamentarCrud(CrudBaseForListAndDetailExternalAppView):
parent_field = 'autor__parlamentar_set' parent_field = 'autor__parlamentar_set'
namespace = AppConfig.name namespace = AppConfig.name
class BaseMixin(CrudBaseForListAndDetailExternalAppView.BaseMixin):
@classmethod
def url_name(cls, suffix):
return '%s_parlamentar_%s' % (cls.model._meta.model_name, suffix)
class ListView(CrudBaseForListAndDetailExternalAppView.ListView): class ListView(CrudBaseForListAndDetailExternalAppView.ListView):
def get_queryset(self): def get_queryset(self):
return super().get_queryset().filter( return super().get_queryset().filter(
data_envio__isnull=False) data_envio__isnull=False,
data_recebimento__isnull=False)
class DetailView(CrudBaseForListAndDetailExternalAppView.DetailView):
@property
def extras_url(self):
if self.object.texto_articulado.exists():
ta = self.object.texto_articulado.first()
yield (str(reverse_lazy(
'sapl.compilacao:ta_text',
kwargs={'ta_id': ta.pk})) + '?back_type=history',
'btn-success',
_('Texto Eletrônico'))
class ParticipacaoParlamentarCrud(CrudBaseForListAndDetailExternalAppView): class ParticipacaoParlamentarCrud(CrudBaseForListAndDetailExternalAppView):
@ -75,6 +101,12 @@ class ParticipacaoParlamentarCrud(CrudBaseForListAndDetailExternalAppView):
list_field_names = ['composicao__comissao__nome', 'cargo__nome', ( list_field_names = ['composicao__comissao__nome', 'cargo__nome', (
'composicao__periodo__data_inicio', 'composicao__periodo__data_fim')] 'composicao__periodo__data_inicio', 'composicao__periodo__data_fim')]
class BaseMixin(CrudBaseForListAndDetailExternalAppView.BaseMixin):
@classmethod
def url_name(cls, suffix):
return '%s_parlamentar_%s' % (cls.model._meta.model_name, suffix)
class ListView(CrudBaseForListAndDetailExternalAppView.ListView): class ListView(CrudBaseForListAndDetailExternalAppView.ListView):
ordering = ('-composicao__periodo') ordering = ('-composicao__periodo')

79
sapl/protocoloadm/forms.py

@ -13,14 +13,15 @@ from django.utils.translation import ugettext_lazy as _
from sapl.base.models import Autor from sapl.base.models import Autor
from sapl.crispy_layout_mixin import form_actions, to_row from sapl.crispy_layout_mixin import form_actions, to_row
from sapl.materia.models import UnidadeTramitacao from sapl.materia.models import UnidadeTramitacao
from sapl.utils import (RANGE_ANOS, RangeWidgetOverride, autor_label, from sapl.utils import (RANGE_ANOS, AnoNumeroOrderingFilter,
autor_modal) RangeWidgetOverride, autor_label, autor_modal)
from .models import (DocumentoAcessorioAdministrativo, DocumentoAdministrativo, from .models import (DocumentoAcessorioAdministrativo, DocumentoAdministrativo,
Protocolo, TipoDocumentoAdministrativo, Protocolo, TipoDocumentoAdministrativo,
TramitacaoAdministrativo) TramitacaoAdministrativo)
TIPOS_PROTOCOLO = [('0', 'Enviado'), ('1', 'Recebido'), ('', 'Ambos')] TIPOS_PROTOCOLO = [('0', 'Enviado'), ('1', 'Recebido'), ('', 'Ambos')]
TIPOS_PROTOCOLO_CREATE = [('0', 'Enviado'), ('1', 'Recebido')]
NATUREZA_PROCESSO = [('', 'Ambos'), NATUREZA_PROCESSO = [('', 'Ambos'),
('0', 'Administrativo'), ('0', 'Administrativo'),
@ -68,6 +69,8 @@ class ProtocoloFilterSet(django_filters.FilterSet):
widget=forms.Select( widget=forms.Select(
attrs={'class': 'selector'})) attrs={'class': 'selector'}))
o = AnoNumeroOrderingFilter()
class Meta: class Meta:
model = Protocolo model = Protocolo
fields = ['numero', fields = ['numero',
@ -76,25 +79,6 @@ class ProtocoloFilterSet(django_filters.FilterSet):
'tipo_materia', 'tipo_materia',
] ]
order_by = (
('', 'Selecione'),
('CRE', 'Ordem Crescente'),
('DEC', 'Ordem Decrescente'),
)
order_by_mapping = {
'': [],
'CRE': ['ano', 'numero'],
'DEC': ['-ano', '-numero'],
}
def get_order_by(self, order_value):
if order_value in self.order_by_mapping:
return self.order_by_mapping[order_value]
else:
return super(ProtocoloFilterSet,
self).get_order_by(order_value)
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(ProtocoloFilterSet, self).__init__(*args, **kwargs) super(ProtocoloFilterSet, self).__init__(*args, **kwargs)
@ -131,7 +115,7 @@ class ProtocoloFilterSet(django_filters.FilterSet):
self.form.helper = FormHelper() self.form.helper = FormHelper()
self.form.helper.form_method = 'GET' self.form.helper.form_method = 'GET'
self.form.helper.layout = Layout( self.form.helper.layout = Layout(
Fieldset(_('Pesquisar Protocolo'), Fieldset('',
row1, row2, row1, row2,
row3, row3,
HTML(autor_label), HTML(autor_label),
@ -162,6 +146,8 @@ class DocumentoAdministrativoFilterSet(django_filters.FilterSet):
interessado = django_filters.CharFilter(lookup_expr='icontains') interessado = django_filters.CharFilter(lookup_expr='icontains')
o = AnoNumeroOrderingFilter()
class Meta: class Meta:
model = DocumentoAdministrativo model = DocumentoAdministrativo
fields = ['tipo', fields = ['tipo',
@ -171,25 +157,6 @@ class DocumentoAdministrativoFilterSet(django_filters.FilterSet):
'tramitacaoadministrativo__unidade_tramitacao_destino', 'tramitacaoadministrativo__unidade_tramitacao_destino',
'tramitacaoadministrativo__status'] 'tramitacaoadministrativo__status']
order_by = (
('', 'Selecione'),
('CRE', 'Ordem Crescente'),
('DEC', 'Ordem Decrescente'),
)
order_by_mapping = {
'': [],
'CRE': ['ano', 'numero'],
'DEC': ['-ano', '-numero'],
}
def get_order_by(self, order_value):
if order_value in self.order_by_mapping:
return self.order_by_mapping[order_value]
else:
return super(DocumentoAdministrativoFilterSet,
self).get_order_by(order_value)
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(DocumentoAdministrativoFilterSet, self).__init__(*args, **kwargs) super(DocumentoAdministrativoFilterSet, self).__init__(*args, **kwargs)
@ -304,7 +271,7 @@ class ProtocoloDocumentForm(ModelForm):
tipo_protocolo = forms.ChoiceField(required=True, tipo_protocolo = forms.ChoiceField(required=True,
label=_('Tipo de Protocolo'), label=_('Tipo de Protocolo'),
choices=TIPOS_PROTOCOLO,) choices=TIPOS_PROTOCOLO_CREATE,)
tipo_documento = forms.ModelChoiceField( tipo_documento = forms.ModelChoiceField(
label=_('Tipo de Documento'), label=_('Tipo de Documento'),
@ -313,7 +280,7 @@ class ProtocoloDocumentForm(ModelForm):
empty_label='Selecione', empty_label='Selecione',
) )
num_paginas = forms.CharField(label=_('Núm. Páginas'), required=True) numero_paginas = forms.CharField(label=_('Núm. Páginas'), required=True)
assunto = forms.CharField( assunto = forms.CharField(
widget=forms.Textarea, label='Assunto', required=True) widget=forms.Textarea, label='Assunto', required=True)
@ -327,7 +294,7 @@ class ProtocoloDocumentForm(ModelForm):
model = Protocolo model = Protocolo
fields = ['tipo_protocolo', fields = ['tipo_protocolo',
'tipo_documento', 'tipo_documento',
'num_paginas', 'numero_paginas',
'assunto', 'assunto',
'interessado', 'interessado',
'observacao', 'observacao',
@ -339,7 +306,7 @@ class ProtocoloDocumentForm(ModelForm):
[(InlineRadios('tipo_protocolo'), 12)]) [(InlineRadios('tipo_protocolo'), 12)])
row2 = to_row( row2 = to_row(
[('tipo_documento', 6), [('tipo_documento', 6),
('num_paginas', 6)]) ('numero_paginas', 6)])
row3 = to_row( row3 = to_row(
[('assunto', 12)]) [('assunto', 12)])
row4 = to_row( row4 = to_row(
@ -364,11 +331,6 @@ class ProtocoloDocumentForm(ModelForm):
class ProtocoloMateriaForm(ModelForm): class ProtocoloMateriaForm(ModelForm):
tipo_protocolo = forms.ChoiceField(required=True,
label='Tipo de Protocolo',
choices=TIPOS_PROTOCOLO,)
autor = forms.IntegerField(widget=forms.HiddenInput(), required=False) autor = forms.IntegerField(widget=forms.HiddenInput(), required=False)
def clean_autor(self): def clean_autor(self):
@ -383,8 +345,7 @@ class ProtocoloMateriaForm(ModelForm):
class Meta: class Meta:
model = Protocolo model = Protocolo
fields = ['tipo_protocolo', fields = ['tipo_materia',
'tipo_materia',
'numero_paginas', 'numero_paginas',
'autor', 'autor',
'observacao'] 'observacao']
@ -392,11 +353,9 @@ class ProtocoloMateriaForm(ModelForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
row1 = to_row( row1 = to_row(
[(InlineRadios('tipo_protocolo'), 12)])
row2 = to_row(
[('tipo_materia', 4), [('tipo_materia', 4),
('numero_paginas', 4)]) ('numero_paginas', 4)])
row3 = to_row( row2 = to_row(
[('autor', 0), [('autor', 0),
(Button('pesquisar', (Button('pesquisar',
'Pesquisar Autor', 'Pesquisar Autor',
@ -404,25 +363,23 @@ class ProtocoloMateriaForm(ModelForm):
(Button('limpar', (Button('limpar',
'limpar Autor', 'limpar Autor',
css_class='btn btn-primary btn-sm'), 10)]) css_class='btn btn-primary btn-sm'), 10)])
row4 = to_row( row3 = to_row(
[('observacao', 12)]) [('observacao', 12)])
self.helper = FormHelper() self.helper = FormHelper()
self.helper.layout = Layout( self.helper.layout = Layout(
Fieldset(_('Identificação da Matéria'), Fieldset(_('Identificação da Matéria'),
row1, row1,
row2,
HTML(autor_label), HTML(autor_label),
HTML(autor_modal), HTML(autor_modal),
row2,
row3, row3,
row4,
form_actions(save_label='Protocolar Matéria') form_actions(save_label='Protocolar Matéria')
) )
) )
super(ProtocoloMateriaForm, self).__init__( super(ProtocoloMateriaForm, self).__init__(
*args, **kwargs) *args, **kwargs)
self.fields['tipo_protocolo'].inline_class = True
class DocumentoAcessorioAdministrativoForm(ModelForm): class DocumentoAcessorioAdministrativoForm(ModelForm):
@ -587,6 +544,10 @@ class DocumentoAdministrativoForm(ModelForm):
'texto_integral', 'texto_integral',
] ]
def save(self, commit=True):
documento = super(DocumentoAdministrativoForm, self).save(commit)
return documento
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
row1 = to_row( row1 = to_row(

19
sapl/protocoloadm/migrations/0005_auto_20161027_1741.py

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-10-27 17:41
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('protocoloadm', '0004_auto_20161023_1444'),
]
operations = [
migrations.AlterModelOptions(
name='protocolo',
options={'permissions': (('action_anular_protocolo', 'Permissão para Anular Protocolo'),), 'verbose_name': 'Protocolo', 'verbose_name_plural': 'Protocolos'},
),
]

20
sapl/protocoloadm/migrations/0006_auto_20161103_1721.py

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-11-03 17:21
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('protocoloadm', '0005_auto_20161027_1741'),
]
operations = [
migrations.AlterField(
model_name='protocolo',
name='tipo_protocolo',
field=models.PositiveIntegerField(blank=True, null=True, verbose_name='Tipo de Protocolo'),
),
]

11
sapl/protocoloadm/models.py

@ -1,13 +1,9 @@
from uuid import uuid4
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from model_utils import Choices from model_utils import Choices
from sapl.base.models import Autor from sapl.base.models import Autor
from sapl.materia.models import TipoMateriaLegislativa, UnidadeTramitacao
from sapl.materia.models import (TipoMateriaLegislativa,
UnidadeTramitacao)
from sapl.utils import RANGE_ANOS, YES_NO_CHOICES, texto_upload_path from sapl.utils import RANGE_ANOS, YES_NO_CHOICES, texto_upload_path
@ -123,7 +119,7 @@ class Protocolo(models.Model):
# TODO transformar campo timestamp em auto_now_add # TODO transformar campo timestamp em auto_now_add
timestamp = models.DateTimeField() timestamp = models.DateTimeField()
tipo_protocolo = models.PositiveIntegerField( tipo_protocolo = models.PositiveIntegerField(
verbose_name=_('Tipo de Protocolo')) blank=True, null=True, verbose_name=_('Tipo de Protocolo'))
tipo_processo = models.PositiveIntegerField() tipo_processo = models.PositiveIntegerField()
interessado = models.CharField( interessado = models.CharField(
max_length=60, blank=True, verbose_name=_('Interessado')) max_length=60, blank=True, verbose_name=_('Interessado'))
@ -153,6 +149,9 @@ class Protocolo(models.Model):
class Meta: class Meta:
verbose_name = _('Protocolo') verbose_name = _('Protocolo')
verbose_name_plural = _('Protocolos') verbose_name_plural = _('Protocolos')
permissions = (
('action_anular_protocolo', _('Permissão para Anular Protocolo')),
)
class StatusTramitacaoAdministrativo(models.Model): class StatusTramitacaoAdministrativo(models.Model):

2
sapl/protocoloadm/tests/test_protocoloadm.py

@ -1,10 +1,10 @@
import datetime import datetime
import pytest
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
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 _
from model_mommy import mommy from model_mommy import mommy
import pytest
from sapl.materia.models import UnidadeTramitacao from sapl.materia.models import UnidadeTramitacao
from sapl.protocoloadm.forms import AnularProcoloAdmForm from sapl.protocoloadm.forms import AnularProcoloAdmForm

48
sapl/protocoloadm/urls.py

@ -9,9 +9,8 @@ from sapl.protocoloadm.views import (AnularProtocoloAdmView,
DocumentoAcessorioAdministrativoView, DocumentoAcessorioAdministrativoView,
DocumentoAdministrativoCrud, DocumentoAdministrativoCrud,
PesquisarDocumentoAdministrativoView, PesquisarDocumentoAdministrativoView,
ProtocoloDocumentoCrud, ProtocoloDocumentoView,
ProtocoloDocumentoView, ProtocoloListView, ProtocoloMateriaTemplateView,
ProtocoloMateriaCrud,
ProtocoloMateriaView, ProtocoloMateriaView,
ProtocoloMostrarView, ProtocoloMostrarView,
ProtocoloPesquisaView, ProtocoloPesquisaView,
@ -42,31 +41,40 @@ urlpatterns_documento_administrativo = [
] ]
urlpatterns_protocolo = [ urlpatterns_protocolo = [
url(r'^protocoloadm/protocolo-doc/',
include(ProtocoloDocumentoCrud.get_urls())), # url(r'^protocoloadm/protocolo-doc/',
url(r'^protocoloadm/protocolo-mat/', # include(ProtocoloDocumentoCrud.get_urls())),
include(ProtocoloMateriaCrud.get_urls()), name='protocolomat'), # url(r'^protocoloadm/protocolo-mat/',
url(r'^protocoloadm/protocolo$', # include(ProtocoloMateriaCrud.get_urls()), name='protocolomat'),
# url(r'^protocoloadm/protocolo-list$',
# ProtocoloListView.as_view(), name='protocolo_list'),
url(r'^protocoloadm/$',
ProtocoloPesquisaView.as_view(), name='protocolo'), ProtocoloPesquisaView.as_view(), name='protocolo'),
url(r'^protocoloadm/protocolo-list$',
ProtocoloListView.as_view(), name='protocolo_list'),
url(r'^protocoloadm/anular-protocolo',
AnularProtocoloAdmView.as_view(), name='anular_protocolo'),
url(r'^protocoloadm/protocolar-doc', url(r'^protocoloadm/protocolar-doc',
ProtocoloDocumentoView.as_view(), name='protocolar_doc'), ProtocoloDocumentoView.as_view(), name='protocolar_doc'),
url(r'^protocoloadm/(?P<pk>\d+)/protocolo-mostrar$',
ProtocoloMostrarView.as_view(), name='protocolo_mostrar'),
url(r'^protocoloadm/(?P<pk>\d+)/continuar$',
ProtocoloMateriaTemplateView.as_view(), name='materia_continuar'),
url(r'^protocoloadm/anular-protocolo',
AnularProtocoloAdmView.as_view(), name='anular_protocolo'),
url(r'^protocoloadm/protocolar-mat', url(r'^protocoloadm/protocolar-mat',
ProtocoloMateriaView.as_view(), name='protocolar_mat'), ProtocoloMateriaView.as_view(), name='protocolar_mat'),
# FIXME estas urls com pk e ano não fazem sentido url(r'^protocoloadm/(?P<pk>\d+)/comprovante$',
# se vai buscar por pk não precisa de nenhuma outra informação
# mas veja, apesar de chamar de pk aqui nas urls
# usou-se dentro da view como paramentro para ano.
url(r'^protocoloadm/(?P<pk>\d+)/(?P<ano>\d+)/protocolo-mostrar$',
ProtocoloMostrarView.as_view(), name='protocolo_mostrar'),
url(r'^protocoloadm/(?P<pk>\d+)/(?P<ano>\d+)/comprovante$',
ComprovanteProtocoloView.as_view(), name='comprovante_protocolo'), ComprovanteProtocoloView.as_view(), name='comprovante_protocolo'),
url(r'^protocoloadm/(?P<pk>\d+)/(?P<ano>\d+)/criar-documento$', url(r'^protocoloadm/(?P<pk>\d+)/criar-documento$',
CriarDocumentoProtocolo.as_view(), name='criar_documento'), CriarDocumentoProtocolo.as_view(), name='criar_documento'),
] ]
urlpatterns_sistema = [ urlpatterns_sistema = [

192
sapl/protocoloadm/views.py

@ -3,6 +3,7 @@ from datetime import date, datetime
from braces.views import FormValidMessageMixin from braces.views import FormValidMessageMixin
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.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.db.models import Max from django.db.models import Max
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
@ -12,12 +13,10 @@ from django.views.generic import CreateView, DetailView, FormView, ListView
from django.views.generic.base import TemplateView from django.views.generic.base import TemplateView
from django_filters.views import FilterView from django_filters.views import FilterView
from sapl.base.apps import AppConfig as AppsAppConfig import sapl
from sapl.base.models import AppConfig
from sapl.crud.base import Crud, CrudAux, MasterDetailCrud, make_pagination from sapl.crud.base import Crud, CrudAux, MasterDetailCrud, make_pagination
from sapl.materia.models import TipoMateriaLegislativa from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa
from sapl.utils import (create_barcode, get_client_ip, permissoes_adm, from sapl.utils import create_barcode, get_client_ip
permissoes_protocoloadm)
from .forms import (AnularProcoloAdmForm, DocumentoAcessorioAdministrativoForm, from .forms import (AnularProcoloAdmForm, DocumentoAcessorioAdministrativoForm,
DocumentoAdministrativoFilterSet, DocumentoAdministrativoFilterSet,
@ -32,9 +31,9 @@ TipoDocumentoAdministrativoCrud = CrudAux.build(
TipoDocumentoAdministrativo, '') TipoDocumentoAdministrativo, '')
ProtocoloDocumentoCrud = Crud.build(Protocolo, '') # ProtocoloDocumentoCrud = Crud.build(Protocolo, '')
# FIXME precisa de uma chave diferente para o layout # FIXME precisa de uma chave diferente para o layout
ProtocoloMateriaCrud = Crud.build(Protocolo, '') # ProtocoloMateriaCrud = Crud.build(Protocolo, '')
DocumentoAcessorioAdministrativoCrud = Crud.build( DocumentoAcessorioAdministrativoCrud = Crud.build(
@ -44,11 +43,11 @@ DocumentoAcessorioAdministrativoCrud = Crud.build(
class DocumentoAdministrativoMixin: class DocumentoAdministrativoMixin:
def has_permission(self): def has_permission(self):
app_config = AppConfig.objects.last() app_config = sapl.base.models.AppConfig.objects.last()
if app_config and app_config.documentos_administrativos == 'O': if app_config and app_config.documentos_administrativos == 'O':
return True return True
return self.request.user.has_module_perms(AppsAppConfig.label) return super().has_permission()
class DocumentoAdministrativoCrud(Crud): class DocumentoAdministrativoCrud(Crud):
@ -60,10 +59,10 @@ class DocumentoAdministrativoCrud(Crud):
'numero_protocolo', 'assunto', 'numero_protocolo', 'assunto',
'interessado', 'tramitacao', 'texto_integral'] 'interessado', 'tramitacao', 'texto_integral']
class ListView(Crud.ListView, DocumentoAdministrativoMixin): class ListView(DocumentoAdministrativoMixin, Crud.ListView):
pass pass
class DetailView(Crud.DetailView, DocumentoAdministrativoMixin): class DetailView(DocumentoAdministrativoMixin, Crud.DetailView):
pass pass
@ -82,7 +81,7 @@ class ProtocoloPesquisaView(PermissionRequiredMixin, FilterView):
model = Protocolo model = Protocolo
filterset_class = ProtocoloFilterSet filterset_class = ProtocoloFilterSet
paginate_by = 10 paginate_by = 10
permission_required = permissoes_protocoloadm() permission_required = ('protocoloadm.list_protocolo',)
def get_filterset_kwargs(self, filterset_class): def get_filterset_kwargs(self, filterset_class):
super(ProtocoloPesquisaView, super(ProtocoloPesquisaView,
@ -109,6 +108,8 @@ class ProtocoloPesquisaView(PermissionRequiredMixin, FilterView):
context['page_range'] = make_pagination( context['page_range'] = make_pagination(
page_obj.number, paginator.num_pages) page_obj.number, paginator.num_pages)
context['title'] = _('Pesquisa de Protocolos')
return context return context
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
@ -143,7 +144,7 @@ class ProtocoloListView(PermissionRequiredMixin, ListView):
context_object_name = 'protocolos' context_object_name = 'protocolos'
model = Protocolo model = Protocolo
paginate_by = 10 paginate_by = 10
permission_required = permissoes_protocoloadm() permission_required = ('protocoloadm.list_protocolo',)
def get_queryset(self): def get_queryset(self):
kwargs = self.request.session['kwargs'] kwargs = self.request.session['kwargs']
@ -166,7 +167,7 @@ class AnularProtocoloAdmView(PermissionRequiredMixin, CreateView):
template_name = 'protocoloadm/anular_protocoloadm.html' template_name = 'protocoloadm/anular_protocoloadm.html'
form_class = AnularProcoloAdmForm form_class = AnularProcoloAdmForm
form_valid_message = _('Protocolo anulado com sucesso!') form_valid_message = _('Protocolo anulado com sucesso!')
permission_required = permissoes_protocoloadm() permission_required = ('protocoloadm.action_anular_protocolo', )
def get_success_url(self): def get_success_url(self):
return reverse('sapl.protocoloadm:protocolo') return reverse('sapl.protocoloadm:protocolo')
@ -195,16 +196,18 @@ class ProtocoloDocumentoView(PermissionRequiredMixin,
template_name = "protocoloadm/protocolar_documento.html" template_name = "protocoloadm/protocolar_documento.html"
form_class = ProtocoloDocumentForm form_class = ProtocoloDocumentForm
form_valid_message = _('Protocolo cadastrado com sucesso!') form_valid_message = _('Protocolo cadastrado com sucesso!')
permission_required = permissoes_protocoloadm() permission_required = ('protocoloadm.add_protocolo', )
def get_success_url(self): def get_success_url(self):
return reverse('sapl.protocoloadm:protocolo') return reverse('sapl.protocoloadm:protocolo_mostrar',
kwargs={'pk': self.object.id})
def form_valid(self, form): def form_valid(self, form):
f = form.save(commit=False) f = form.save(commit=False)
try: try:
numeracao = AppConfig.objects.last().sequencia_numeracao numeracao = sapl.base.models.AppConfig.objects.last(
).sequencia_numeracao
except AttributeError: except AttributeError:
msg = _('É preciso definir a sequencia de ' + msg = _('É preciso definir a sequencia de ' +
'numeração na tabelas auxiliares!') 'numeração na tabelas auxiliares!')
@ -230,31 +233,28 @@ class ProtocoloDocumentoView(PermissionRequiredMixin,
f.assunto_ementa = self.request.POST['assunto'] f.assunto_ementa = self.request.POST['assunto']
f.save() f.save()
self.object = f
return redirect(self.get_success_url()) return redirect(self.get_success_url())
class CriarDocumentoProtocolo(PermissionRequiredMixin, CreateView): class CriarDocumentoProtocolo(PermissionRequiredMixin, CreateView):
template_name = "protocoloadm/criar_documento.html" template_name = "protocoloadm/criar_documento.html"
form_class = DocumentoAdministrativoForm form_class = DocumentoAdministrativoForm
permission_required = permissoes_protocoloadm() permission_required = ('protocoloadm.add_documentoadministrativo',)
def get_initial(self): def get_initial(self):
numero = self.kwargs['pk'] protocolo = Protocolo.objects.get(pk=self.kwargs['pk'])
ano = self.kwargs['ano']
protocolo = Protocolo.objects.get(ano=ano, numero=numero)
return self.criar_documento(protocolo) return self.criar_documento(protocolo)
def get_success_url(self): def get_success_url(self):
return reverse('sapl.protocoloadm:protocolo_mostrar', return reverse('sapl.protocoloadm:protocolo_mostrar',
kwargs={'pk': self.kwargs['pk'], kwargs={'pk': self.kwargs['pk']})
'ano': self.kwargs['ano']})
def criar_documento(self, protocolo): def criar_documento(self, protocolo):
numero = Protocolo.objects.filter( numero_max = DocumentoAdministrativo.objects.filter(
tipo_documento=protocolo.tipo_documento, tipo=protocolo.tipo_documento
ano=protocolo.ano, ).aggregate(Max('numero'))['numero__max']
anulado=False).aggregate(Max('numero'))
doc = {} doc = {}
doc['tipo'] = protocolo.tipo_documento doc['tipo'] = protocolo.tipo_documento
@ -263,24 +263,36 @@ class CriarDocumentoProtocolo(PermissionRequiredMixin, CreateView):
doc['numero_protocolo'] = protocolo.numero doc['numero_protocolo'] = protocolo.numero
doc['assunto'] = protocolo.assunto_ementa doc['assunto'] = protocolo.assunto_ementa
doc['interessado'] = protocolo.interessado doc['interessado'] = protocolo.interessado
doc['numero'] = numero['numero__max'] doc['numero'] = numero_max + 1 if numero_max else 1
if doc['numero'] is None:
doc['numero'] = 1
else:
doc['numero'] = doc['numero'] + 1
return doc return doc
class ProtocoloMostrarView(PermissionRequiredMixin, TemplateView): class ProtocoloMostrarView(PermissionRequiredMixin, TemplateView):
template_name = "protocoloadm/protocolo_mostrar.html" template_name = "protocoloadm/protocolo_mostrar.html"
permission_required = permissoes_protocoloadm() permission_required = ('protocoloadm.detail_protocolo', )
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(ProtocoloMostrarView, self).get_context_data(**kwargs) context = super(ProtocoloMostrarView, self).get_context_data(**kwargs)
numero = self.kwargs['pk'] protocolo = Protocolo.objects.get(pk=self.kwargs['pk'])
ano = self.kwargs['ano']
protocolo = Protocolo.objects.get(ano=ano, numero=numero) if protocolo.tipo_materia:
try:
materia = MateriaLegislativa.objects.get(
numero_protocolo=protocolo.numero, ano=protocolo.ano)
except ObjectDoesNotExist:
context['materia'] = None
else:
context['materia'] = materia
elif protocolo.tipo_documento:
try:
documento = DocumentoAdministrativo.objects.get(
numero_protocolo=protocolo.numero, ano=protocolo.ano)
except ObjectDoesNotExist:
context['documento'] = None
else:
context['documento'] = documento
context['protocolo'] = protocolo context['protocolo'] = protocolo
return context return context
@ -288,16 +300,14 @@ class ProtocoloMostrarView(PermissionRequiredMixin, TemplateView):
class ComprovanteProtocoloView(PermissionRequiredMixin, TemplateView): class ComprovanteProtocoloView(PermissionRequiredMixin, TemplateView):
template_name = "protocoloadm/comprovante.html" template_name = "protocoloadm/comprovante.html"
permission_required = permissoes_protocoloadm() permission_required = ('protocoloadm.detail_protocolo', )
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(ComprovanteProtocoloView, self).get_context_data( context = super(ComprovanteProtocoloView, self).get_context_data(
**kwargs) **kwargs)
numero = self.kwargs['pk'] protocolo = Protocolo.objects.get(pk=self.kwargs['pk'])
ano = self.kwargs['ano']
protocolo = Protocolo.objects.get(ano=ano, numero=numero)
# numero is string, padd with zeros left via .zfill() # numero is string, padd with zeros left via .zfill()
base64_data = create_barcode(numero.zfill(6)) base64_data = create_barcode(str(protocolo.numero).zfill(6))
barcode = 'data:image/png;base64,{0}'.format(base64_data) barcode = 'data:image/png;base64,{0}'.format(base64_data)
autenticacao = _("** NULO **") autenticacao = _("** NULO **")
@ -319,14 +329,16 @@ class ProtocoloMateriaView(PermissionRequiredMixin, CreateView):
template_name = "protocoloadm/protocolar_materia.html" template_name = "protocoloadm/protocolar_materia.html"
form_class = ProtocoloMateriaForm form_class = ProtocoloMateriaForm
form_valid_message = _('Matéria cadastrada com sucesso!') form_valid_message = _('Matéria cadastrada com sucesso!')
permission_required = permissoes_protocoloadm() permission_required = ('protocoloadm.add_protocolo',)
def get_success_url(self): def get_success_url(self, protocolo):
return reverse('sapl.protocoloadm:protocolo') return reverse('sapl.protocoloadm:materia_continuar', kwargs={
'pk': protocolo.pk})
def form_valid(self, form): def form_valid(self, form):
try: try:
numeracao = AppConfig.objects.last().sequencia_numeracao numeracao = sapl.base.models.AppConfig.objects.last(
).sequencia_numeracao
except AttributeError: except AttributeError:
msg = _('É preciso definir a sequencia de ' + msg = _('É preciso definir a sequencia de ' +
'numeração na tabelas auxiliares!') 'numeração na tabelas auxiliares!')
@ -349,7 +361,6 @@ class ProtocoloMateriaView(PermissionRequiredMixin, CreateView):
protocolo.data = datetime.now().strftime("%Y-%m-%d") protocolo.data = datetime.now().strftime("%Y-%m-%d")
protocolo.hora = datetime.now().strftime("%H:%M") protocolo.hora = datetime.now().strftime("%H:%M")
protocolo.timestamp = datetime.now().strftime("%Y-%m-%d %H:%M") protocolo.timestamp = datetime.now().strftime("%Y-%m-%d %H:%M")
protocolo.tipo_protocolo = self.request.POST['tipo_protocolo']
protocolo.tipo_processo = '0' # TODO validar o significado protocolo.tipo_processo = '0' # TODO validar o significado
if form.cleaned_data['autor']: if form.cleaned_data['autor']:
protocolo.autor = form.cleaned_data['autor'] protocolo.autor = form.cleaned_data['autor']
@ -359,16 +370,29 @@ class ProtocoloMateriaView(PermissionRequiredMixin, CreateView):
protocolo.numero_paginas = self.request.POST['numero_paginas'] protocolo.numero_paginas = self.request.POST['numero_paginas']
protocolo.observacao = self.request.POST['observacao'] protocolo.observacao = self.request.POST['observacao']
protocolo.save() protocolo.save()
return redirect(self.get_success_url()) return redirect(self.get_success_url(protocolo))
class ProtocoloMateriaTemplateView(PermissionRequiredMixin, TemplateView):
class PesquisarDocumentoAdministrativoView(PermissionRequiredMixin, template_name = "protocoloadm/MateriaTemplate.html"
FilterView, permission_required = ('protocoloadm.detail_protocolo', )
DocumentoAdministrativoMixin):
def get_context_data(self, **kwargs):
context = super(ProtocoloMateriaTemplateView, self).get_context_data(
**kwargs)
protocolo = Protocolo.objects.get(pk=self.kwargs['pk'])
context.update({'protocolo': protocolo})
return context
class PesquisarDocumentoAdministrativoView(DocumentoAdministrativoMixin,
PermissionRequiredMixin,
FilterView):
model = DocumentoAdministrativo model = DocumentoAdministrativo
filterset_class = DocumentoAdministrativoFilterSet filterset_class = DocumentoAdministrativoFilterSet
paginate_by = 10 paginate_by = 10
permission_required = permissoes_adm() permission_required = ('protocoloadm.list_documentoadministrativo', )
def get_filterset_kwargs(self, filterset_class): def get_filterset_kwargs(self, filterset_class):
super(PesquisarDocumentoAdministrativoView, super(PesquisarDocumentoAdministrativoView,
@ -426,7 +450,7 @@ class PesquisarDocumentoAdministrativoView(PermissionRequiredMixin,
class DetailDocumentoAdministrativo(PermissionRequiredMixin, DetailView): class DetailDocumentoAdministrativo(PermissionRequiredMixin, DetailView):
template_name = "protocoloadm/detail_doc_adm.html" template_name = "protocoloadm/detail_doc_adm.html"
permission_required = permissoes_adm() permission_required = ('protocoloadm.detail_documentoadministrativo', )
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
documento = DocumentoAdministrativo.objects.get( documento = DocumentoAdministrativo.objects.get(
@ -468,7 +492,8 @@ class DetailDocumentoAdministrativo(PermissionRequiredMixin, DetailView):
class DocumentoAcessorioAdministrativoEditView(PermissionRequiredMixin, class DocumentoAcessorioAdministrativoEditView(PermissionRequiredMixin,
FormView): FormView):
template_name = "protocoloadm/documento_acessorio_administrativo_edit.html" template_name = "protocoloadm/documento_acessorio_administrativo_edit.html"
permission_required = permissoes_adm() permission_required = (
'protocoloadm.change_documentoacessorioadministrativo', )
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
doc = DocumentoAdministrativo.objects.get( doc = DocumentoAdministrativo.objects.get(
@ -516,7 +541,8 @@ class DocumentoAcessorioAdministrativoEditView(PermissionRequiredMixin,
class DocumentoAcessorioAdministrativoView(PermissionRequiredMixin, FormView): class DocumentoAcessorioAdministrativoView(PermissionRequiredMixin, FormView):
template_name = "protocoloadm/documento_acessorio_administrativo.html" template_name = "protocoloadm/documento_acessorio_administrativo.html"
permission_required = permissoes_adm() permission_required = (
'protocoloadm.add_documentoacessorioadministrativo', )
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
form = DocumentoAcessorioAdministrativoForm() form = DocumentoAcessorioAdministrativoForm()
@ -569,65 +595,13 @@ class TramitacaoAdmCrud(MasterDetailCrud):
class UpdateView(MasterDetailCrud.UpdateView): class UpdateView(MasterDetailCrud.UpdateView):
form_class = TramitacaoAdmEditForm form_class = TramitacaoAdmEditForm
class ListView(MasterDetailCrud.ListView, DocumentoAdministrativoMixin): class ListView(DocumentoAdministrativoMixin, MasterDetailCrud.ListView):
def get_queryset(self): def get_queryset(self):
qs = super(MasterDetailCrud.ListView, self).get_queryset() qs = super(MasterDetailCrud.ListView, self).get_queryset()
kwargs = {self.crud.parent_field: self.kwargs['pk']} kwargs = {self.crud.parent_field: self.kwargs['pk']}
return qs.filter(**kwargs).order_by('-data_tramitacao', '-id') return qs.filter(**kwargs).order_by('-data_tramitacao', '-id')
class DetailView(MasterDetailCrud.DetailView, class DetailView(DocumentoAdministrativoMixin,
DocumentoAdministrativoMixin): MasterDetailCrud.DetailView):
pass
"""
def get_nome_autor(request):
nome_autor = ''
if request.method == 'GET':
id = request.GET.get('id', '')
try:
autor = Autor.objects.get(pk=id)
if autor.parlamentar:
nome_autor = autor.parlamentar.nome_parlamentar
elif autor.comissao:
nome_autor = autor.comissao.nome
except ObjectDoesNotExist:
pass pass
return HttpResponse("{\"nome\":\"" + nome_autor + "\"}",
content_type="application/json; charset=utf-8")"""
"""
def pesquisa_autores(request):
q = ''
if request.method == 'GET':
q = request.GET.get('q', '')
autor = Autor.objects.filter(
Q(nome__icontains=q) |
Q(parlamentar__nome_parlamentar__icontains=q) |
Q(comissao__nome__icontains=q)
)
autor = Autor.objects.filter(nome__icontains=q)
autores = []
for a in autor:
nome = ''
if a.nome:
nome = a.nome
elif a.parlamentar:
nome = a.parlamentar.nome_parlamentar
elif a.comissao:
nome = a.comissao.nome
autores.append((a.id, nome))
autores = sorted(autores, key=lambda x: x[1])
return HttpResponse(json.dumps(autores,
sort_keys=True,
ensure_ascii=False),
content_type="application/json; charset=utf-8")
"""

2
sapl/relatorios/views.py

@ -19,7 +19,6 @@ from sapl.sessao.models import (ExpedienteMateria, ExpedienteSessao, Orador,
SessaoPlenariaPresenca, TipoExpediente) SessaoPlenariaPresenca, TipoExpediente)
from sapl.settings import STATIC_ROOT from sapl.settings import STATIC_ROOT
from sapl.utils import UF from sapl.utils import UF
import sapl
from .templates import (pdf_capa_processo_gerar, from .templates import (pdf_capa_processo_gerar,
pdf_documento_administrativo_gerar, pdf_espelho_gerar, pdf_documento_administrativo_gerar, pdf_espelho_gerar,
@ -27,7 +26,6 @@ from .templates import (pdf_capa_processo_gerar,
pdf_ordem_dia_gerar, pdf_pauta_sessao_gerar, pdf_ordem_dia_gerar, pdf_pauta_sessao_gerar,
pdf_protocolo_gerar, pdf_sessao_plenaria_gerar) pdf_protocolo_gerar, pdf_sessao_plenaria_gerar)
uf_dic = dict(UF) uf_dic = dict(UF)

38
sapl/rules/__init__.py

@ -0,0 +1,38 @@
from django.utils.translation import ugettext_lazy as _
default_app_config = 'sapl.rules.apps.AppConfig'
SAPL_GROUP_ADMINISTRATIVO = _("Operador Administrativo")
SAPL_GROUP_PROTOCOLO = _("Operador de Protocolo Administrativo")
SAPL_GROUP_COMISSOES = _("Operador de Comissões")
SAPL_GROUP_MATERIA = _("Operador de Matéria")
SAPL_GROUP_NORMA = _("Operador de Norma Jurídica")
SAPL_GROUP_SESSAO = _("Operador de Sessão Plenária")
SAPL_GROUP_PAINEL = _("Operador de Painel Eletrônico")
SAPL_GROUP_GERAL = _("Operador Geral")
SAPL_GROUP_AUTOR = _("Autor")
SAPL_GROUP_PARLAMENTAR = _("Parlamentar")
# TODO - funcionalidade ainda não existe mas está aqui para efeito de anotação
SAPL_GROUP_LOGIN_SOCIAL = _("Usuários com Login Social")
# ANONYMOUS não é um grupo mas é uma variável usadas nas rules para anotar
# explicitamente models que podem ter ação de usuários anônimos
# como por exemplo AcompanhamentoMateria
SAPL_GROUP_ANONYMOUS = ''
SAPL_GROUPS = [
SAPL_GROUP_ADMINISTRATIVO,
SAPL_GROUP_PROTOCOLO,
SAPL_GROUP_COMISSOES,
SAPL_GROUP_MATERIA,
SAPL_GROUP_NORMA,
SAPL_GROUP_SESSAO,
SAPL_GROUP_PAINEL,
SAPL_GROUP_GERAL,
SAPL_GROUP_AUTOR,
SAPL_GROUP_PARLAMENTAR,
SAPL_GROUP_LOGIN_SOCIAL,
SAPL_GROUP_ANONYMOUS,
]

234
sapl/rules/apps.py

@ -0,0 +1,234 @@
from builtins import LookupError
import django
from django.apps import apps
from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.management import _get_all_permissions
from django.core import exceptions
from django.db import models, router
from django.db.utils import DEFAULT_DB_ALIAS
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import string_concat
from sapl.rules import (SAPL_GROUP_ADMINISTRATIVO, SAPL_GROUP_COMISSOES,
SAPL_GROUP_GERAL, SAPL_GROUP_MATERIA, SAPL_GROUP_NORMA,
SAPL_GROUP_PAINEL, SAPL_GROUP_PROTOCOLO,
SAPL_GROUP_SESSAO)
class AppConfig(django.apps.AppConfig):
name = 'sapl.rules'
label = 'rules'
verbose_name = _('Regras de Acesso')
def create_proxy_permissions(
app_config, verbosity=2, interactive=True,
using=DEFAULT_DB_ALIAS, **kwargs):
if not app_config.models_module:
return
# print(app_config)
try:
Permission = apps.get_model('auth', 'Permission')
except LookupError:
return
if not router.allow_migrate_model(using, Permission):
return
from django.contrib.contenttypes.models import ContentType
permission_name_max_length = Permission._meta.get_field('name').max_length
# This will hold the permissions we're looking for as
# (content_type, (codename, name))
searched_perms = list()
# The codenames and ctypes that should exist.
ctypes = set()
for klass in list(app_config.get_models()):
opts = klass._meta
permissions = (
("list_" + opts.model_name,
string_concat(
_('Visualizaçao da lista de'), ' ',
opts.verbose_name_plural)),
("detail_" + opts.model_name,
string_concat(
_('Visualização dos detalhes de'), ' ',
opts.verbose_name_plural)),
)
opts.permissions = tuple(
set(list(permissions) + list(opts.permissions)))
if opts.proxy:
# Force looking up the content types in the current database
# before creating foreign keys to them.
app_label, model = opts.app_label, opts.model_name
try:
ctype = ContentType.objects.db_manager(
using).get_by_natural_key(app_label, model)
except:
ctype = ContentType.objects.db_manager(
using).create(app_label=app_label, model=model)
else:
ctype = ContentType.objects.db_manager(using).get_for_model(klass)
ctypes.add(ctype)
for perm in _get_all_permissions(klass._meta, ctype):
searched_perms.append((ctype, perm))
# Find all the Permissions that have a content_type for a model we're
# looking for. We don't need to check for codenames since we already have
# a list of the ones we're going to create.
all_perms = set(Permission.objects.using(using).filter(
content_type__in=ctypes,
).values_list(
"content_type", "codename"
))
perms = [
Permission(codename=codename, name=name, content_type=ct)
for ct, (codename, name) in searched_perms
if (ct.pk, codename) not in all_perms
]
# Validate the permissions before bulk_creation to avoid cryptic database
# error when the name is longer than 255 characters
for perm in perms:
if len(perm.name) > permission_name_max_length:
raise exceptions.ValidationError(
'The permission name %s of %s.%s '
'is longer than %s characters' % (
perm.name,
perm.content_type.app_label,
perm.content_type.model,
permission_name_max_length,
)
)
Permission.objects.using(using).bulk_create(perms)
if verbosity >= 2:
for perm in perms:
print("Adding permission '%s'" % perm)
def update_groups(app_config, verbosity=2, interactive=True,
using=DEFAULT_DB_ALIAS, **kwargs):
if app_config != AppConfig and not isinstance(app_config, AppConfig):
return
from sapl.rules.map_rules import rules_patterns
from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType
class Rules:
def __init__(self, rules_patterns):
self.rules_patterns = rules_patterns
def associar(self, g, model, tipo):
for t in tipo:
content_type = ContentType.objects.get_by_natural_key(
app_label=model._meta.app_label,
model=model._meta.model_name)
codename = (t[1:] + model._meta.model_name)\
if t[0] == '.' and t[-1] == '_' else t
p = Permission.objects.get(
content_type=content_type,
codename=codename)
g.permissions.add(p)
g.save()
def _config_group(self, group_name, rules_list):
if not group_name:
return
g = Group.objects.get_or_create(name=group_name)
if not isinstance(g, Group):
g = g[0]
g.permissions.clear()
try:
print(' ', group_name)
for model, perms in rules_list:
self.associar(g, model, perms)
except Exception as e:
print(group_name, e)
if settings.DEBUG:
user = ''
if group_name == SAPL_GROUP_ADMINISTRATIVO:
user = 'operador_administrativo'
elif group_name == SAPL_GROUP_PROTOCOLO:
user = 'operador_protocoloadm'
elif group_name == SAPL_GROUP_COMISSOES:
user = 'operador_comissoes'
elif group_name == SAPL_GROUP_MATERIA:
user = 'operador_materia'
elif group_name == SAPL_GROUP_NORMA:
user = 'operador_norma'
elif group_name == SAPL_GROUP_SESSAO:
user = 'operador_sessao'
elif group_name == SAPL_GROUP_PAINEL:
user = 'operador_painel'
elif group_name == SAPL_GROUP_GERAL:
user = 'operador_geral'
if user:
self.cria_usuario(user, g)
def groups_add_user(self, user, groups_name):
if not isinstance(groups_name, list):
groups_name = [groups_name, ]
for group_name in groups_name:
if not group_name or user.groups.filter(
name=group_name).exists():
continue
g = Group.objects.get_or_create(name=group_name)[0]
user.groups.add(g)
def groups_remove_user(self, user, groups_name):
if not isinstance(groups_name, list):
groups_name = [groups_name, ]
for group_name in groups_name:
if not group_name or not user.groups.filter(
name=group_name).exists():
continue
g = Group.objects.get_or_create(name=group_name)[0]
user.groups.remove(g)
def cria_usuario(self, nome, grupo):
nome_usuario = nome
usuario = get_user_model().objects.get_or_create(
username=nome_usuario)[0]
usuario.set_password('interlegis')
usuario.save()
grupo.user_set.add(usuario)
def update_groups(self):
print('')
print(string_concat('\033[93m\033[1m',
_('Atualizando grupos:'),
'\033[0m'))
for rules_group in self.rules_patterns:
group_name = rules_group['group']
rules_list = rules_group['rules']
self._config_group(group_name, rules_list)
rules = Rules(rules_patterns)
rules.update_groups()
models.signals.post_migrate.connect(
receiver=update_groups)
models.signals.post_migrate.connect(
receiver=create_proxy_permissions,
dispatch_uid="django.contrib.auth.management.create_permissions")

317
sapl/rules/map_rules.py

@ -0,0 +1,317 @@
from sapl.base import models as base
from sapl.comissoes import models as comissoes
from sapl.compilacao import models as compilacao
from sapl.lexml import models as lexml
from sapl.materia import models as materia
from sapl.norma import models as norma
from sapl.painel import models as painel
from sapl.parlamentares import models as parlamentares
from sapl.protocoloadm import models as protocoloadm
from sapl.rules import (SAPL_GROUP_ADMINISTRATIVO, SAPL_GROUP_ANONYMOUS,
SAPL_GROUP_AUTOR, SAPL_GROUP_COMISSOES,
SAPL_GROUP_GERAL, SAPL_GROUP_LOGIN_SOCIAL,
SAPL_GROUP_MATERIA, SAPL_GROUP_NORMA,
SAPL_GROUP_PAINEL, SAPL_GROUP_PARLAMENTAR,
SAPL_GROUP_PROTOCOLO, SAPL_GROUP_SESSAO)
from sapl.sessao import models as sessao
"""
Todas as permissões do django framework seguem o padrão
[app_label].[radical_de_permissao]_[model]
ou seja, em sapl.norma.NormaJuridica, por exemplo, o django framework cria
três permissões registadas na classe Permission:
definição uso
- add_normajuridica norma.add_normajuridica
- change_normajuridica norma.change_normajuridica
- delete_normajuridica norma.delete_normajuridica
No SAPL foram acrescidas em todos os models as duas regras abaixo, adicionadas
com o Signal post_migrate `create_proxy_permissions`
localizado em sapl.rules.apps.py.
- list_normajuridica norma.list_normajuridica
- detail_normajuridica norma.detail_normajuridica
Tanto o Crud implementado em sapl.crud.base.py quanto o Signal post_migrate
`update_groups` que é responsável por ler o mapa deste
arquivo (sapl.rules.map_rules.py) e criar os grupos definidos na regra de
negócio trabalham com os cinco radiais de permissão
e com qualquer outro tipo de permissão customizada, nesta ordem de precedência.
Os cinco radicais de permissão completa são:
RP_LIST, RP_DETAIL, RP_ADD, RP_CHANGE, RP_DELETE =\
'.list_', '.detail_', '.add_', '.change_', '.delete_',
Tanto a app crud quanto a app rules estão sempre ligadas a um model. Ao lidar
com permissões, sempre é analisado se é apenas um radical ou permissão
completa, sendo apenas um radical, a permissão completa é montada com base
no model associado.
"""
RP_LIST, RP_DETAIL, RP_ADD, RP_CHANGE, RP_DELETE =\
'.list_', '.detail_', '.add_', '.change_', '.delete_',
__base__ = [RP_LIST, RP_DETAIL, RP_ADD, RP_CHANGE, RP_DELETE]
__listdetailchange__ = [RP_LIST, RP_DETAIL, RP_CHANGE]
rules_group_administrativo = {
'group': SAPL_GROUP_ADMINISTRATIVO,
'rules': [
(protocoloadm.DocumentoAdministrativo, __base__),
(protocoloadm.DocumentoAcessorioAdministrativo, __base__),
(protocoloadm.TramitacaoAdministrativo, __base__),
]
}
rules_group_protocolo = {
'group': SAPL_GROUP_PROTOCOLO,
'rules': [
(protocoloadm.Protocolo, __base__ + [
'action_anular_protocolo']),
(protocoloadm.DocumentoAdministrativo,
[RP_ADD] + __listdetailchange__),
(protocoloadm.DocumentoAcessorioAdministrativo, __listdetailchange__),
(materia.MateriaLegislativa, __listdetailchange__),
(materia.DocumentoAcessorio, __listdetailchange__),
(materia.Anexada, __base__),
(materia.Autoria, __base__),
(materia.Proposicao, __listdetailchange__),
(compilacao.TextoArticulado, ['view_restricted_textoarticulado'])
]
}
rules_group_comissoes = {
'group': SAPL_GROUP_COMISSOES,
'rules': [
(comissoes.Comissao, __base__),
(comissoes.Composicao, __base__),
(comissoes.Participacao, __base__),
]
}
rules_group_materia = {
'group': SAPL_GROUP_MATERIA,
'rules': [
(materia.Anexada, __base__),
(materia.Autoria, __base__),
(materia.DespachoInicial, __base__),
(materia.DocumentoAcessorio, __base__),
(materia.MateriaLegislativa, __base__),
(materia.Numeracao, __base__),
(materia.Relatoria, __base__),
(materia.Tramitacao, __base__),
(materia.Relatoria, __base__),
(norma.LegislacaoCitada, __base__),
(compilacao.Dispositivo, __base__ + [
'change_dispositivo_edicao_dinamica',
# TODO: adicionar 'change_dispositivo_registros_compilacao'
# quando testes forem feitos para permtir que matérias possam
# ser vinculadas a outras matérias via registro de compilação.
# Normalmente emendas e/ou projetos substitutivos podem alterar
# uma matéria original. Fazer esse registro de compilação ofereceria
# um autografo eletrônico pronto para ser convertido em Norma.
])
]
}
rules_group_norma = {
'group': SAPL_GROUP_NORMA,
'rules': [
(norma.NormaJuridica, __base__),
(norma.VinculoNormaJuridica, __base__),
# Publicacao está com permissão apenas para norma e não para matéria
# e proposições apenas por análise do contexto, não é uma limitação
# da ferramenta.
(compilacao.Publicacao, __base__),
(compilacao.Vide, __base__),
(compilacao.Nota, __base__),
(compilacao.Dispositivo, __base__ + [
'view_dispositivo_notificacoes',
'change_dispositivo_edicao_dinamica',
'change_dispositivo_edicao_avancada',
'change_dispositivo_registros_compilacao',
'change_dispositivo_de_vigencia_global'
])
]
}
rules_group_sessao = {
'group': SAPL_GROUP_SESSAO,
'rules': [
(sessao.SessaoPlenaria, __base__),
(sessao.SessaoPlenariaPresenca, __base__),
(sessao.ExpedienteMateria, __base__),
(sessao.IntegranteMesa, __base__),
(sessao.ExpedienteSessao, __base__),
(sessao.Orador, __base__),
(sessao.OradorExpediente, __base__),
(sessao.OrdemDia, __base__),
(sessao.PresencaOrdemDia, __base__),
(sessao.RegistroVotacao, __base__),
(sessao.VotoParlamentar, __base__),
]
}
rules_group_painel = {
'group': SAPL_GROUP_PAINEL,
'rules': [
(painel.Painel, __base__),
(painel.Cronometro, __base__),
]
}
rules_group_autor = {
'group': SAPL_GROUP_AUTOR,
'rules': [
(materia.Proposicao, __base__),
(compilacao.Dispositivo, __base__ + [
'change_your_dispositivo_edicao_dinamica',
])
]
}
rules_group_parlamentar = {
'group': SAPL_GROUP_PARLAMENTAR,
'rules': []
}
rules_group_geral = {
'group': SAPL_GROUP_GERAL,
'rules': [
(base.AppConfig, __base__ + [
'menu_sistemas',
'view_tabelas_auxiliares'
]),
(base.CasaLegislativa, __listdetailchange__),
(base.ProblemaMigracao, []),
(base.TipoAutor, __base__),
(base.Autor, __base__),
(protocoloadm.StatusTramitacaoAdministrativo, __base__),
(protocoloadm.TipoDocumentoAdministrativo, __base__),
(comissoes.CargoComissao, __base__),
(comissoes.TipoComissao, __base__),
(comissoes.Periodo, __base__),
(materia.AssuntoMateria, __base__), # não há implementação
(materia.MateriaAssunto, __base__), # não há implementação
(materia.TipoProposicao, __base__),
(materia.TipoMateriaLegislativa, __base__),
(materia.RegimeTramitacao, __base__),
(materia.Origem, __base__),
(materia.TipoDocumento, __base__),
(materia.Orgao, __base__),
(materia.TipoFimRelatoria, __base__),
(materia.Parecer, __base__),
(materia.StatusTramitacao, __base__),
(materia.UnidadeTramitacao, __base__),
(norma.AssuntoNorma, __base__),
(norma.TipoNormaJuridica, __base__),
(parlamentares.Legislatura, __base__),
(parlamentares.SessaoLegislativa, __base__),
(parlamentares.Coligacao, __base__),
(parlamentares.ComposicaoColigacao, __base__),
(parlamentares.Partido, __base__),
(parlamentares.Municipio, __base__),
(parlamentares.NivelInstrucao, __base__),
(parlamentares.SituacaoMilitar, __base__),
(parlamentares.Parlamentar, __base__),
(parlamentares.TipoDependente, __base__),
(parlamentares.Dependente, __base__),
(parlamentares.Filiacao, __base__),
(parlamentares.TipoAfastamento, __base__),
(parlamentares.Mandato, __base__),
(parlamentares.CargoMesa, __base__),
(parlamentares.ComposicaoMesa, __base__),
(parlamentares.Frente, __base__),
(sessao.CargoBancada, __base__),
(sessao.Bancada, __base__),
(sessao.TipoSessaoPlenaria, __base__),
(sessao.TipoResultadoVotacao, __base__),
(sessao.TipoExpediente, __base__),
(sessao.Bloco, __base__),
(lexml.LexmlProvedor, __base__),
(lexml.LexmlPublicador, __base__),
(compilacao.VeiculoPublicacao, __base__),
(compilacao.TipoTextoArticulado, __base__),
(compilacao.TipoNota, __base__),
(compilacao.TipoVide, __base__),
(compilacao.TipoPublicacao, __base__),
# este model é um espelho do model integrado e sua edição pode
# confundir Autores, operadores de matéria e/ou norma.
# Por isso está adicionado apenas para o operador geral
(compilacao.TextoArticulado, __base__ + ['lock_unlock_textoarticulado']),
# estes tres models são complexos e a principio apenas o admin tem perm
(compilacao.TipoDispositivo, []),
(compilacao.TipoDispositivoRelationship, []),
(compilacao.PerfilEstruturalTextoArticulado, []),
]
}
# não possui efeito e é usada nos testes que verificam se todos os models estão
# neste arquivo rules.py
rules_group_anonymous = {
'group': SAPL_GROUP_ANONYMOUS,
'rules': [
(materia.AcompanhamentoMateria, [RP_ADD, RP_DELETE]),
]
}
rules_group_login_social = {
'group': SAPL_GROUP_LOGIN_SOCIAL,
'rules': []
}
rules_group_geral['rules'] = (rules_group_geral['rules'] +
rules_group_administrativo['rules'] +
rules_group_protocolo['rules'] +
rules_group_comissoes['rules'] +
rules_group_materia['rules'] +
rules_group_norma['rules'] +
rules_group_sessao['rules'] +
rules_group_painel['rules'] +
rules_group_login_social['rules'])
rules_patterns = [
rules_group_administrativo,
rules_group_protocolo,
rules_group_comissoes,
rules_group_materia,
rules_group_norma,
rules_group_sessao,
rules_group_painel,
rules_group_geral,
rules_group_autor,
rules_group_parlamentar,
rules_group_anonymous, # anotação para validação do teste de rules
rules_group_login_social # TODO não implementado
]

0
sapl/rules/migrations/__init__.py

0
sapl/rules/models.py

255
sapl/rules/tests/test_rules.py

@ -0,0 +1,255 @@
import pytest
from django.apps import apps
from django.conf import settings
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.utils import six
from django.utils.translation import ugettext_lazy as _
from sapl.base.models import CasaLegislativa, ProblemaMigracao
from sapl.compilacao.models import (PerfilEstruturalTextoArticulado,
TipoDispositivo,
TipoDispositivoRelationship)
from sapl.materia.models import AcompanhamentoMateria
from sapl.rules import SAPL_GROUPS, map_rules
from sapl.test_urls import create_perms_post_migrate
from scripts.lista_permissions_in_decorators import \
lista_permissions_in_decorators
from scripts.lista_urls import lista_urls
sapl_appconfs = [apps.get_app_config(n[5:]) for n in settings.SAPL_APPS]
sapl_models = []
for app in sapl_appconfs:
sapl_models.extend(app.get_models())
sapl_models.reverse()
@pytest.mark.parametrize('group_item', SAPL_GROUPS)
def test_groups_in_rules_patterns(group_item):
test = False
for rules_group in map_rules.rules_patterns:
if rules_group['group'] == group_item:
test = True
assert test, _('O grupo (%s) não foi a rules_patterns.') % (group_item)
@pytest.mark.parametrize('model_item', sapl_models)
def test_models_in_rules_patterns(model_item):
test = False
for rules_group in map_rules.rules_patterns:
rules_model = rules_group['rules']
for rm in rules_model:
if rm[0] == model_item:
test = True
break
assert test, _('O model %s (%s) não foi adicionado em nenhum '
'grupo padrão para regras de acesso.') % (
str(model_item),
model_item._meta.verbose_name)
# __falsos_positivos__
__fp__in__test_permission_of_models_in_rules_patterns = {
map_rules.RP_ADD: [CasaLegislativa,
ProblemaMigracao,
TipoDispositivo,
TipoDispositivoRelationship,
PerfilEstruturalTextoArticulado],
map_rules.RP_CHANGE: [ProblemaMigracao,
AcompanhamentoMateria,
TipoDispositivo,
TipoDispositivoRelationship,
PerfilEstruturalTextoArticulado],
map_rules.RP_DELETE: [CasaLegislativa,
ProblemaMigracao,
TipoDispositivo,
TipoDispositivoRelationship,
PerfilEstruturalTextoArticulado],
map_rules.RP_LIST: [ProblemaMigracao,
AcompanhamentoMateria,
TipoDispositivo,
TipoDispositivoRelationship,
PerfilEstruturalTextoArticulado],
map_rules.RP_DETAIL: [ProblemaMigracao,
AcompanhamentoMateria,
TipoDispositivo,
TipoDispositivoRelationship,
PerfilEstruturalTextoArticulado]
}
@pytest.mark.django_db(transaction=False)
@pytest.mark.parametrize('model_item', sapl_models)
def test_permission_of_models_in_rules_patterns(model_item):
create_perms_post_migrate(model_item._meta.app_config)
permissions = map_rules.__base__ + list(
filter(
lambda perm: not perm.startswith(
'detail_') and not perm.startswith('list_'),
map(lambda x: x[0],
model_item._meta.permissions))
)
__fp__ = __fp__in__test_permission_of_models_in_rules_patterns
for perm in permissions:
if perm in __fp__ and model_item in __fp__[perm]:
continue
test = False
for rules_group in map_rules.rules_patterns:
rules_model = rules_group['rules']
for rm in rules_model:
model = rm[0]
rules = rm[1]
if model == model_item:
if perm in rules:
test = True
break
assert test, _('A permissão (%s) do model (%s) não foi adicionado em '
'nenhum grupo padrão para regras de acesso.') % (
perm,
str(model_item))
@pytest.mark.django_db(transaction=False)
@pytest.mark.parametrize('model_item', sapl_models)
def test_permission_of_rules_exists(model_item):
print(model_item)
create_perms_post_migrate(model_item._meta.app_config)
for rules_group in map_rules.rules_patterns:
rules_model = rules_group['rules']
for rm in rules_model:
model = rm[0]
rules = rm[1]
if model != model_item:
continue
for r in rules:
content_type = ContentType.objects.get_by_natural_key(
app_label=model._meta.app_label,
model=model._meta.model_name)
codename = (r[1:] + model._meta.model_name)\
if r[0] == '.' and r[-1] == '_' else r
p = Permission.objects.filter(
content_type=content_type,
codename=codename).exists()
assert p, _('Permissão (%s) associada ao model (%s) '
'não está em _meta.permissions.') % (
codename,
model_item)
_lista_urls = lista_urls()
@pytest.mark.django_db(transaction=False)
@pytest.mark.parametrize('url_item', _lista_urls)
def test_permission_required_of_views_exists(url_item):
"""
testa se, nas views que possuem atributo permission_required,
as permissões fixas escritas manualmente realmente exitem em Permission
Obs: isso não testa permissões escritas em anotações de método ou classe
"""
for app in sapl_appconfs:
# readequa permissões dos models adicionando
# list e detail permissions
create_perms_post_migrate(app)
key, url, var, app_name = url_item
url = '/' + (url % {v: 1 for v in var})
assert '\n' not in url, """
A url (%s) da app (%s) está mal formada.
""" % (app_name, url)
view = None
if hasattr(key, 'view_class'):
view = key.view_class
if hasattr(view, 'permission_required'):
if isinstance(view.permission_required, six.string_types):
perms = (view.permission_required, )
else:
perms = view.permission_required
if not perms:
return
for perm in perms:
if perm[0] == '.' and perm[-1] == '_':
model = None
if hasattr(view, 'model') and view.model:
model = view.model
elif hasattr(view, 'filterset_class'):
model = view.fielterset_class._meta.model
elif hasattr(view, 'form_class'):
model = view.form_class._meta.model
assert model, _('model %s não localizado em %s'
) % (model, view)
codename = perm[1:] + view.model._meta.model_name
else:
codename = perm
codename = codename.split('.')
if len(codename) == 1:
content_type = ContentType.objects.get_by_natural_key(
app_label=model._meta.app_label,
model=model._meta.model_name)
p = Permission.objects.filter(
content_type=content_type,
codename=codename[0]).exists()
elif len(codename) == 2:
p = Permission.objects.filter(
content_type__app_label=codename[0],
codename=codename[1]).exists()
assert p, _('Permissão (%s) na view (%s) não existe.') % (
codename,
view)
_lista_permissions_in_decorators = lista_permissions_in_decorators()
@pytest.mark.django_db(transaction=False)
@pytest.mark.parametrize('permission', _lista_permissions_in_decorators)
def test_permission_required_of_decorators(permission):
"""
testa se, nos decorators permission_required com ou sem method_decorator
as permissões fixas escritas manualmente realmente exitem em Permission
"""
for app in sapl_appconfs:
# readequa permissões dos models adicionando
# list e detail permissions
create_perms_post_migrate(app)
codename = permission[0].split('.')
p = Permission.objects.filter(
content_type__app_label=codename[0],
codename=codename[1]).exists()
assert p, _('Permissão (%s) na view (%s) não existe.') % (
permission[0],
permission[1])

21
sapl/sessao/forms.py

@ -12,7 +12,9 @@ from sapl.crispy_layout_mixin import form_actions, to_row
from sapl.materia.forms import MateriaLegislativaFilterSet from sapl.materia.forms import MateriaLegislativaFilterSet
from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa
from sapl.parlamentares.models import Parlamentar from sapl.parlamentares.models import Parlamentar
from sapl.utils import RANGE_DIAS_MES, RANGE_MESES, autor_label, autor_modal from sapl.utils import (RANGE_DIAS_MES, RANGE_MESES,
MateriaPesquisaOrderingFilter, autor_label,
autor_modal)
from .models import (Bancada, ExpedienteMateria, Orador, OradorExpediente, from .models import (Bancada, ExpedienteMateria, Orador, OradorExpediente,
OrdemDia, SessaoPlenaria, SessaoPlenariaPresenca) OrdemDia, SessaoPlenaria, SessaoPlenariaPresenca)
@ -21,7 +23,10 @@ from .models import (Bancada, ExpedienteMateria, Orador, OradorExpediente,
def recupera_anos(): def recupera_anos():
try: try:
anos_list = SessaoPlenaria.objects.all().dates('data_inicio', 'year') anos_list = SessaoPlenaria.objects.all().dates('data_inicio', 'year')
anos = [(k.year, k.year) for k in anos_list] # a listagem deve ser em ordem descrescente, mas por algum motivo
# a adicao de .order_by acima depois do all() nao surte efeito
# apos a adicao do .dates(), por isso o reversed() abaixo
anos = [(k.year, k.year) for k in reversed(anos_list)]
return anos return anos
except: except:
return [] return []
@ -203,6 +208,8 @@ class SessaoPlenariaFilterSet(django_filters.FilterSet):
class AdicionarVariasMateriasFilterSet(MateriaLegislativaFilterSet): class AdicionarVariasMateriasFilterSet(MateriaLegislativaFilterSet):
o = MateriaPesquisaOrderingFilter()
class Meta: class Meta:
model = MateriaLegislativa model = MateriaLegislativa
fields = ['numero', fields = ['numero',
@ -212,20 +219,12 @@ class AdicionarVariasMateriasFilterSet(MateriaLegislativaFilterSet):
'data_apresentacao', 'data_apresentacao',
'data_publicacao', 'data_publicacao',
'autoria__autor__tipo', 'autoria__autor__tipo',
# 'autoria__autor__partido', # FIXME 'autoria__autor__partido',
'relatoria__parlamentar_id', 'relatoria__parlamentar_id',
'local_origem_externa', 'local_origem_externa',
'em_tramitacao', 'em_tramitacao',
] ]
order_by = (
('', 'Selecione'),
('dataC', 'Data, Tipo, Ano, Numero - Ordem Crescente'),
('dataD', 'Data, Tipo, Ano, Numero - Ordem Decrescente'),
('tipoC', 'Tipo, Ano, Numero, Data - Ordem Crescente'),
('tipoD', 'Tipo, Ano, Numero, Data - Ordem Decrescente')
)
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(MateriaLegislativaFilterSet, self).__init__(*args, **kwargs) super(MateriaLegislativaFilterSet, self).__init__(*args, **kwargs)

27
sapl/sessao/views.py

@ -2,6 +2,7 @@ from datetime import datetime
from re import sub from re import sub
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.decorators import permission_required
from django.core.exceptions import (ObjectDoesNotExist, MultipleObjectsReturned, from django.core.exceptions import (ObjectDoesNotExist, MultipleObjectsReturned,
ValidationError) ValidationError)
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
@ -61,11 +62,9 @@ TipoResultadoVotacaoCrud = CrudAux.build(
def reordernar_materias_expediente(request, pk): def reordernar_materias_expediente(request, pk):
expedientes = ExpedienteMateria.objects.filter( expedientes = ExpedienteMateria.objects.filter(
sessao_plenaria_id=pk) sessao_plenaria_id=pk)
exp_num = 1 for exp_num, e in enumerate(expedientes, 1):
for e in expedientes:
e.numero_ordem = exp_num e.numero_ordem = exp_num
e.save() e.save()
exp_num += 1
return HttpResponseRedirect( return HttpResponseRedirect(
reverse('sapl.sessao:expedientemateria_list', kwargs={'pk': pk})) reverse('sapl.sessao:expedientemateria_list', kwargs={'pk': pk}))
@ -74,17 +73,15 @@ def reordernar_materias_expediente(request, pk):
def reordernar_materias_ordem(request, pk): def reordernar_materias_ordem(request, pk):
ordens = OrdemDia.objects.filter( ordens = OrdemDia.objects.filter(
sessao_plenaria_id=pk) sessao_plenaria_id=pk)
ordem_num = 1 for ordem_num, o in enumerate(ordens, 1):
for o in ordens:
o.numero_ordem = ordem_num o.numero_ordem = ordem_num
o.save() o.save()
ordem_num += 1
return HttpResponseRedirect( return HttpResponseRedirect(
reverse('sapl.sessao:ordemdia_list', kwargs={'pk': pk})) reverse('sapl.sessao:ordemdia_list', kwargs={'pk': pk}))
@permission_required_for_app(app_label=apps.AppConfig.label) @permission_required('sessao.change_expedientemateria')
def abrir_votacao_expediente_view(request, pk, spk): def abrir_votacao_expediente_view(request, pk, spk):
existe_votacao_aberta = ExpedienteMateria.objects.filter( existe_votacao_aberta = ExpedienteMateria.objects.filter(
sessao_plenaria_id=spk, votacao_aberta=True sessao_plenaria_id=spk, votacao_aberta=True
@ -101,7 +98,7 @@ def abrir_votacao_expediente_view(request, pk, spk):
reverse('sapl.sessao:expedientemateria_list', kwargs={'pk': spk})) reverse('sapl.sessao:expedientemateria_list', kwargs={'pk': spk}))
@permission_required_for_app(app_label=apps.AppConfig.label) @permission_required('sessao.change_ordemdia')
def abrir_votacao_ordem_view(request, pk, spk): def abrir_votacao_ordem_view(request, pk, spk):
existe_votacao_aberta = OrdemDia.objects.filter( existe_votacao_aberta = OrdemDia.objects.filter(
sessao_plenaria_id=spk, votacao_aberta=True sessao_plenaria_id=spk, votacao_aberta=True
@ -524,14 +521,12 @@ class PresencaView(FormMixin, PresencaMixin, DetailView):
_('Presença'), self.object) _('Presença'), self.object)
return context return context
@method_decorator(permission_required_for_app(AppConfig.label)) @method_decorator(permission_required(
'sessao.add_sessaoplenariapresenca'))
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
self.object = self.get_object() self.object = self.get_object()
form = self.get_form() form = self.get_form()
if not self.request.user.has_module_perms(AppConfig.label):
return self.form_invalid(form)
if form.is_valid(): if form.is_valid():
# Pegar os presentes salvos no banco # Pegar os presentes salvos no banco
presentes_banco = SessaoPlenariaPresenca.objects.filter( presentes_banco = SessaoPlenariaPresenca.objects.filter(
@ -604,7 +599,7 @@ class PresencaOrdemDiaView(FormMixin, PresencaMixin, DetailView):
_('Presença Ordem do Dia'), self.object) _('Presença Ordem do Dia'), self.object)
return context return context
@method_decorator(permission_required_for_app(AppConfig.label)) @method_decorator(permission_required('sessao.add_presencaordemdia'))
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
self.object = self.get_object() self.object = self.get_object()
@ -684,7 +679,7 @@ class ListMateriaOrdemDiaView(FormMixin, DetailView):
return self.render_to_response(context) return self.render_to_response(context)
@method_decorator(permission_required_for_app(AppConfig.label)) @method_decorator(permission_required('sessao.change_ordemdia'))
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
self.object = self.get_object() self.object = self.get_object()
context = self.get_context_data(object=self.object) context = self.get_context_data(object=self.object)
@ -786,7 +781,7 @@ class MesaView(FormMixin, DetailView):
_('Mesa Diretora'), self.object) _('Mesa Diretora'), self.object)
return context return context
@method_decorator(permission_required_for_app(AppConfig.label)) @method_decorator(permission_required('sessao.change_integrantemesa'))
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
self.object = self.get_object() self.object = self.get_object()
form = MesaForm(request.POST) form = MesaForm(request.POST)
@ -1041,7 +1036,7 @@ class ExpedienteView(FormMixin, DetailView):
_('Expediente Diversos'), self.object) _('Expediente Diversos'), self.object)
return context return context
@method_decorator(permission_required_for_app(AppConfig.label)) @method_decorator(permission_required('sessao.add_expedientesessao'))
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
self.object = self.get_object() self.object = self.get_object()
form = ExpedienteForm(request.POST) form = ExpedienteForm(request.POST)

9
sapl/settings.py

@ -53,7 +53,10 @@ SAPL_APPS = (
'sapl.painel', 'sapl.painel',
'sapl.protocoloadm', 'sapl.protocoloadm',
'sapl.compilacao', 'sapl.compilacao',
'sapl.api' 'sapl.api',
'sapl.rules'
) )
INSTALLED_APPS = ( INSTALLED_APPS = (
@ -129,8 +132,8 @@ TEMPLATES = [
'django.template.context_processors.debug', 'django.template.context_processors.debug',
'django.template.context_processors.request', 'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth', 'django.contrib.auth.context_processors.auth',
"django.core.context_processors.media", "django.template.context_processors.media",
"django.core.context_processors.static", "django.template.context_processors.static",
'django.contrib.messages.context_processors.messages', 'django.contrib.messages.context_processors.messages',
'sapl.context_processors.parliament_info', 'sapl.context_processors.parliament_info',
], ],

9
sapl/static/styles/compilacao.scss

@ -1513,8 +1513,13 @@ a:link:after, a:visited:after {
} }
@media print { @media print {
.cp .vigencias, .toggle-topbar, .menu-icon, .button { .cp .vigencias, .toggle-topbar, .menu-icon, .button, .tipo-vigencias, .dne {
display:none; display:none;
} }
#btn_font_menos, #btn_font_mais {
display:none;
}
.container {
width: 100%;
}
} }

3
sapl/templates/base.html

@ -146,6 +146,8 @@
{% endblock content_container %} {% endblock content_container %}
{% block footer_container %}
<footer id="footer" class="footer page__row"> <footer id="footer" class="footer page__row">
<div class="container"> <div class="container">
@ -196,6 +198,7 @@
</div> </div>
</footer> </footer>
</div> </div>
{% endblock footer_container %}
{% block foot_js %} {% block foot_js %}
<!-- Bootstrap core JavaScript ================================================== --> <!-- Bootstrap core JavaScript ================================================== -->

49
sapl/templates/base/appconfig_list.html

@ -1,49 +0,0 @@
{% extends "crud/list.html" %}
{% load i18n %}
{% load common_tags %}
{% block base_content %}
<div class="actions btn-group pull-right" role="group">
{% if user|get_config_not_exists %}
<a href="{{ view.create_url }}" class="btn btn-default">
{% blocktrans with verbose_name=view.verbose_name %} Adicionar {{ verbose_name }} {% endblocktrans %}
</a>
{% endif %}
{% block more_buttons %}{% endblock more_buttons %}
</div>
<br/><br/>
{% block extra_content %} {% endblock %}
{% if not rows %}
<p>{{ NO_ENTRIES_MSG }}</p>
{% else %}
<table class="table table-striped table-hover">
<thead>
<tr>
{% for name in headers %}
<th>{{ name }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for value_list in rows %}
<tr>
{% for value, href in value_list %}
<td>
{% if href %}
<a href="{{ href }}">{{ value }}</a>
{% else %}
{{ value|safe }}
{% endif %}
</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
{% include "paginacao.html" %}
{% endblock %}

16
sapl/templates/compilacao/ajax_actions_dinamic_edit.html

@ -1,21 +1,23 @@
{% load i18n %} {% load i18n %}
<div class="btn-toolbar pull-right" role="toolbar" > <div class="btn-toolbar pull-right" role="toolbar" >
{% if perms.compilacao.change_dispositivo_registros_compilacao %}
{% if object.tipo_dispositivo.dispositivo_de_articulacao and object.tipo_dispositivo.dispositivo_de_alteracao %} {% if object.tipo_dispositivo.dispositivo_de_articulacao and object.tipo_dispositivo.dispositivo_de_alteracao %}
<div class="btn-group " role="group"> <div class="btn-group " role="group">
<button type="button" class="btn btn-default btn-sm btn-compila" pk="{{object.pk}}" action="get_form_revogacao" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true"> <button type="button" class="btn btn-default btn-sm btn-compila" pk="{{object.pk}}" action="get_form_revogacao" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
Registrar Revogação {% trans "Registrar Revogação" %}
</button> </button>
<button type="button" class="btn btn-default btn-sm btn-compila" pk="{{object.pk}}" action="get_form_alteracao" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true"> <button type="button" class="btn btn-default btn-sm btn-compila" pk="{{object.pk}}" action="get_form_alteracao" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
Registrar Alteração {% trans "Registrar Alteração" %}
</button> </button>
<button type="button" class="btn btn-default btn-sm btn-compila" pk="{{object.pk}}" action="get_form_inclusao" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true"> <button type="button" class="btn btn-default btn-sm btn-compila" pk="{{object.pk}}" action="get_form_inclusao" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
Registrar Inclusão {% trans "Registrar Inclusão" %}
</button> </button>
</div> </div>
{%endif%} {%endif%}
{% endif %}
<div class="btn-group " role="group"> <div class="btn-group " role="group">
<button type="button" class="btn btn-default btn-sm radius-right" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true"> <button type="button" class="btn btn-default btn-sm radius-right" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
<i class="fa fa-edit fa-lg"></i> <i class="fa fa-edit fa-lg"></i>
@ -25,7 +27,9 @@
<li><a class="btn-editor-type" editortype="construct">Construtor</a></li> <li><a class="btn-editor-type" editortype="construct">Construtor</a></li>
<li><a class="btn-editor-type" editortype="textarea">Editor Simples</a></li> <li><a class="btn-editor-type" editortype="textarea">Editor Simples</a></li>
<li><a class="btn-editor-type" editortype="tinymce">Editor Tinymce</a></li> <li><a class="btn-editor-type" editortype="tinymce">Editor Tinymce</a></li>
{% if perms.compilacao.change_dispositivo_edicao_avancada %}
<li><a class="btn-editor-avancado" href="{% url 'sapl.compilacao:dispositivo_edit' object.ta_id object.pk %}" >Editor Avançado</a></li> <li><a class="btn-editor-avancado" href="{% url 'sapl.compilacao:dispositivo_edit' object.ta_id object.pk %}" >Editor Avançado</a></li>
{% endif %}
</ul> </ul>
</div> </div>
</div> </div>
@ -61,11 +65,13 @@
{% endfor %} {% endfor %}
{%endif%} {%endif%}
</div> </div>
{% if perms.compilacao.change_dispositivo_de_vigencia_global %}
{% if not object.ta_publicado and not object.dispositivo_subsequente and not object.tipo_dispositivo.dispositivo_de_alteracao%} {% if not object.ta_publicado and not object.dispositivo_subsequente and not object.tipo_dispositivo.dispositivo_de_alteracao%}
<div class="btn-group " role="group"> <div class="btn-group " role="group">
<button type="button" class="btn-action btn btn-xs radius-right {% if object.pk == object.dispositivo_vigencia_id %}btn-primary{%else%}btn-default{%endif%}" pk="{{object.pk}}" action="json_set_dvt" title="{% if object.pk == object.dispositivo_vigencia_id %}{% trans 'Dispositivo de Vigência Atual'%}{%else%}{% trans 'Tornar este o Dispositivo de Vigência de todo o Texto Articulado.'%}{%endif%}"> <button type="button" class="btn-action btn btn-xs radius-right {% if object.pk == object.dispositivo_vigencia_id %}btn-primary{%else%}btn-default{%endif%}" pk="{{object.pk}}" action="json_set_dvt" title="{% if object.pk == object.dispositivo_vigencia_id %}{% trans 'Dispositivo de Vigência Atual'%}{%else%}{% trans 'Tornar este o Dispositivo de Vigência de todo o Texto Articulado.'%}{%endif%}">
DVt DVt
</button> </button>
</div> </div>
{%endif%} {% endif %}
{% endif %}
</div> </div>

8
sapl/templates/compilacao/dispositivo_form.html

@ -10,10 +10,10 @@
{% url 'sapl.compilacao:dispositivo_edit_vigencia' object.ta_id object.pk as edit_vigencia_url %} {% url 'sapl.compilacao:dispositivo_edit_vigencia' object.ta_id object.pk as edit_vigencia_url %}
{% url 'sapl.compilacao:dispositivo_edit_alteracao' object.ta_id object.pk as edit_alteracao_url %} {% url 'sapl.compilacao:dispositivo_edit_alteracao' object.ta_id object.pk as edit_alteracao_url %}
{% url 'sapl.compilacao:dispositivo_edit_definidor_vigencia' object.ta_id object.pk as edit_definidor_vigencia_url %} {% url 'sapl.compilacao:dispositivo_edit_definidor_vigencia' object.ta_id object.pk as edit_definidor_vigencia_url %}
<li {% if request.get_full_path == edit_url %}class="active"{%endif%}><a class="btn-warning" href="{{ edit_url }}">{% trans 'Dados Básicos' %}</a></li> {% if perms.compilacao.change_dispositivo_edicao_avancada %}<li {% if request.get_full_path == edit_url %}class="active"{%endif%}><a class="btn-warning" href="{{ edit_url }}">{% trans 'Dados Básicos' %}</a></li>{% endif %}
<li {% if request.get_full_path == edit_vigencia_url %}class="active"{%endif%}><a class="btn-warning" href="{{ edit_vigencia_url }}" >{% trans 'Vigência' %}</a></li> {% if perms.compilacao.change_dispositivo_edicao_avancada %}<li {% if request.get_full_path == edit_vigencia_url %}class="active"{%endif%}><a class="btn-warning" href="{{ edit_vigencia_url }}" >{% trans 'Vigência' %}</a></li>{% endif %}
<li {% if request.get_full_path == edit_definidor_vigencia_url %}class="active"{%endif%}><a class="btn-danger" href="{{ edit_definidor_vigencia_url }}" >{% trans 'Definidor de Vigência' %}</a></li> {% if perms.compilacao.change_dispositivo_de_vigencia_global %}<li {% if request.get_full_path == edit_definidor_vigencia_url %}class="active"{%endif%}><a class="btn-danger" href="{{ edit_definidor_vigencia_url }}" >{% trans 'Definidor de Vigência' %}</a></li>{% endif %}
<li {% if request.get_full_path == edit_alteracao_url %}class="active"{%endif%}><a class="btn-danger" href="{{ edit_alteracao_url }}" >{% trans 'Alteração' %}</a></li> {% if perms.compilacao.change_dispositivo_registros_compilacao %}<li {% if request.get_full_path == edit_alteracao_url %}class="active"{%endif%}><a class="btn-danger" href="{{ edit_alteracao_url }}" >{% trans 'Alteração' %}</a></li>{% endif %}
</ul> </ul>
{% endblock sections_nav %}{% trans '' %} {% endblock sections_nav %}{% trans '' %}

2
sapl/templates/compilacao/publicacao_detail.html

@ -8,7 +8,7 @@
{% block actions %} {% block actions %}
<div class="actions btn-group pull-right" role="group"> <div class="actions btn-group pull-right" role="group">
<a href="{% url 'sapl.compilacao:ta_pub_edit' object.ta.pk object.pk %}" class="btn btn-default">{% trans 'Editar' %}</a> <a href="{% url 'sapl.compilacao:ta_pub_edit' object.ta.pk object.pk %}" class="btn btn-default">{% trans 'Editar' %}</a>
<a href="{% url 'sapl.compilacao:ta_pub_delete' object.ta.pk object.pk %}" class="btn btn-default">{% trans 'Excluir' %}</a> <a href="{% url 'sapl.compilacao:ta_pub_delete' object.ta.pk object.pk %}" class="btn btn-default btn-excluir">{% trans 'Excluir' %}</a>
</div> </div>
{% endblock actions %} {% endblock actions %}
</div> </div>

2
sapl/templates/compilacao/publicacao_list.html

@ -5,11 +5,13 @@
{% block base_content %} {% block base_content %}
{% if perms.compilacao.add_publicacao %}
<div class="actions btn-group pull-right" role="group"> <div class="actions btn-group pull-right" role="group">
<a href="{{ view.create_url }}" class="btn btn-default"> <a href="{{ view.create_url }}" class="btn btn-default">
{% trans 'Adicionar'%} {%model_verbose_name 'sapl.compilacao.models.Publicacao'%} {% trans 'Adicionar'%} {%model_verbose_name 'sapl.compilacao.models.Publicacao'%}
</a> </a>
</div> </div>
{% endif %}
{% if not object_list %} {% if not object_list %}
<p>{{ NO_ENTRIES_MSG }}</p> <p>{{ NO_ENTRIES_MSG }}</p>

8
sapl/templates/compilacao/text_edit.html

@ -11,18 +11,24 @@
{% endblock %} {% endblock %}
{% block title%} {% block title%}
<h1>{{object }}. <small><i>{% trans 'Texto Multivigente em Edição' %}</i></small></h1> <h1 class="page-header">{{object }}. <small><i>{% trans 'Texto Multivigente em Edição' %}</i></small></h1>
{% endblock %} {% endblock %}
{% block actions %} {% block actions %}
{% if perms.compilacao.change_textoarticulado %}
<div class="clearfix"> <div class="clearfix">
<div class="actions btn-toolbar pull-right" role="toolbar"> <div class="actions btn-toolbar pull-right" role="toolbar">
<div class="actions btn-group" role="group"> <div class="actions btn-group" role="group">
{% if perms.compilacao.lock_unlock_textoarticulado and not object.editable_only_by_owners%}
<a href="{% url 'sapl.compilacao:ta_text_edit' object.pk %}?{% if object.editing_locked %}unlock{%else%}lock{% endif %}" class="btn btn-default btn-excluir">{% if object.editing_locked %}{% trans 'Desbloquear Edição' %}{%else%}{% trans 'Bloquear Edição' %}{% endif %}</a>
{% endif %}
<a href="{% url 'sapl.compilacao:ta_edit' object.pk %}" class="btn btn-default">{% trans 'Editar Metadados do Texto Articulado' %}</a> <a href="{% url 'sapl.compilacao:ta_edit' object.pk %}" class="btn btn-default">{% trans 'Editar Metadados do Texto Articulado' %}</a>
{% include 'compilacao/textoarticulado_menu_config.html' %} {% include 'compilacao/textoarticulado_menu_config.html' %}
</div> </div>
</div> </div>
</div> </div>
{% endif %}
{% endblock actions %} {% endblock actions %}
{% block base_content %}{{block.super}} {% block base_content %}{{block.super}}

16
sapl/templates/compilacao/text_list.html

@ -9,17 +9,27 @@
{{block.super}} {{block.super}}
<link rel="stylesheet" href="{% sass_src 'styles/compilacao.scss' %}" type="text/css"> <link rel="stylesheet" href="{% sass_src 'styles/compilacao.scss' %}" type="text/css">
{% endblock %} {% endblock %}
{% block extra_sections_nav %}
<li><a href="{% url 'sapl.compilacao:ta_text' object.pk %}?print">{% trans 'Versão para Impressão' %}</a></li>
{% endblock %}
{% block base_content %} {% block base_content %}
{% block actions %}
{{block.super}}
{% endblock %}
{% comment %}
{% if perms.compilacao.change_dispositivo_edicao_dinamica %}
{% block actions %} {% block actions %}
<div class="clearfix"> <div class="clearfix">
<div class="actions btn-group pull-right" role="group"> <div class="actions btn-group pull-right" role="group">
{% if user.is_authenticated %}
<a href="{% url 'sapl.compilacao:ta_text_edit' object.pk %}" class="btn btn-default">{% trans 'Editar Texto' %}</a> <a href="{% url 'sapl.compilacao:ta_text_edit' object.pk %}" class="btn btn-default">{% trans 'Editar Texto' %}</a>
{% endif %}
</div> </div>
</div> </div>
{% endblock actions %} {% endblock actions %}
{% endif %}
{% endcomment %}
{% block detail_content %} {% block detail_content %}
{{block.super}} {{block.super}}

21
sapl/templates/compilacao/text_list__print_version.html

@ -0,0 +1,21 @@
{% extends "compilacao/text_list.html" %}
{% load i18n %}
{% load compilacao_filters %}
{% load common_tags %}
{% load staticfiles %}
{% load sass_tags %}
{% block navigation %}{% endblock %}
{% block sections_nav %}{% endblock %}
{% block extra_sections_nav %}{% endblock %}
{% block actions %}{% endblock %}
{% block dsp_actions %}{% endblock %}
{% block detail_content %}{% endblock %}
{% block footer_container %}{% endblock %}
{% block foot_js %}{{block.super}}
<script type="text/javascript" src="{% static 'js/compilacao.js' %}"></script>
<script type="text/javascript" src="{% static 'js/compilacao_view.js' %}"></script>
{% endblock %}

21
sapl/templates/compilacao/textoarticulado_detail.html

@ -7,15 +7,23 @@
<ul class="nav nav-pills navbar-right"> <ul class="nav nav-pills navbar-right">
{%if object %} {%if object %}
<li> <li>
{% if object.content_object%} {% if request.GET.back_type == 'history' and object.content_object %}
<a href="{% url object|urldetail_content_type object.content_object.pk %}" title="{% trans 'Ir para '%}{{object.content_object}}">Início</a> <a href="javascript:window.history.back()" title="{% trans 'Voltar para '%}{{object.content_object}}">{% trans 'Voltar para '%}{{object.content_object}}</a>
{% elif object.content_object%}
<a href="{% url object|urldetail_content_type object.content_object.pk %}" title="{% trans 'Voltar para '%}{{object.content_object}}">{% trans 'Voltar para '%}{{object.content_object}}</a>
{%else%} {%else%}
<a href="{% url 'sapl.compilacao:ta_detail' object.pk %}">{% trans 'Início' %}</a> <a href="{% url 'sapl.compilacao:ta_detail' object.pk %}">{% trans 'Início' %}</a>
{%endif%} {%endif%}
</li> </li>
{% if object.tipo_ta.publicacao_func %}
<li><a href="{% url 'sapl.compilacao:ta_pub_list' object.pk %}">{% model_verbose_name_plural 'sapl.compilacao.models.Publicacao' %}</a></li> <li><a href="{% url 'sapl.compilacao:ta_pub_list' object.pk %}">{% model_verbose_name_plural 'sapl.compilacao.models.Publicacao' %}</a></li>
{% endif %}
{% if perms.compilacao.view_dispositivo_notificacoes %}
<li><a href="{% url 'sapl.compilacao:ta_text_notificacoes' object.pk %}">{% trans 'Notificações' %}</a></li> <li><a href="{% url 'sapl.compilacao:ta_text_notificacoes' object.pk %}">{% trans 'Notificações' %}</a></li>
{% endif %}
{% block extra_sections_nav %}
<li><a href="{% url 'sapl.compilacao:ta_text' object.pk %}">{% trans 'Texto' %}</a></li> <li><a href="{% url 'sapl.compilacao:ta_text' object.pk %}">{% trans 'Texto' %}</a></li>
{% endblock %}
{% endif %} {% endif %}
</ul> </ul>
{% endblock %} {% endblock %}
@ -24,11 +32,18 @@
{% block actions %} {% block actions %}
<div class="clearfix"> <div class="clearfix">
<div class="actions btn-group pull-right" role="group"> <div class="actions btn-group pull-right" role="group">
{% if user.is_authenticated %} {% if perms.compilacao.lock_unlock_textoarticulado and not object.editable_only_by_owners%}
<a href="{% url 'sapl.compilacao:ta_text_edit' object.pk %}?{% if object.editing_locked %}unlock{%else%}lock{% endif %}" class="btn btn-default btn-excluir">{% if object.editing_locked %}{% trans 'Desbloquear Edição' %}{%else%}{% trans 'Bloquear Edição' %}{% endif %}</a>
{% endif %}
{% if perms.compilacao.change_textoarticulado and object|can_use_dynamic_editing:user %}
<a href="{% url 'sapl.compilacao:ta_edit' object.pk %}" class="btn btn-default">{% trans 'Editar Metadados do Texto Articulado' %}</a> <a href="{% url 'sapl.compilacao:ta_edit' object.pk %}" class="btn btn-default">{% trans 'Editar Metadados do Texto Articulado' %}</a>
{% endif %}
{% if object|can_use_dynamic_editing:user %}
<a href="{% url 'sapl.compilacao:ta_text_edit' object.pk %}" class="btn btn-default">{% trans 'Editar Texto' %}</a> <a href="{% url 'sapl.compilacao:ta_text_edit' object.pk %}" class="btn btn-default">{% trans 'Editar Texto' %}</a>
{% endif %} {% endif %}
</div> </div>
</div> </div>
{% endblock actions %} {% endblock actions %}

2
sapl/templates/compilacao/textoarticulado_list.html

@ -8,12 +8,14 @@
{% endblock detail_content %} {% endblock detail_content %}
{% block actions %} {% block actions %}
{% if perms.compilacao.add_textoarticulado %}
<div class="actions btn-group pull-right clearfix" role="group"> <div class="actions btn-group pull-right clearfix" role="group">
<a href="{{ view.create_url }}" class="btn btn-default"> <a href="{{ view.create_url }}" class="btn btn-default">
{% trans 'Adicionar'%} {%model_verbose_name 'sapl.compilacao.models.TextoArticulado'%} {% trans 'Adicionar'%} {%model_verbose_name 'sapl.compilacao.models.TextoArticulado'%}
</a> </a>
{% include 'compilacao/textoarticulado_menu_config.html' %} {% include 'compilacao/textoarticulado_menu_config.html' %}
</div> </div>
{% endif %}
{% endblock actions %} {% endblock actions %}
<table class="table table-striped table-hover"> <table class="table table-striped table-hover">

14
sapl/templates/compilacao/textoarticulado_menu_config.html

@ -4,11 +4,15 @@
<i class="fa fa-cog fa-1x fa-fw"></i> <i class="fa fa-cog fa-1x fa-fw"></i>
</button> </button>
<ul class="dropdown-menu" role="menu"> <ul class="dropdown-menu" role="menu">
<li><a href="{% url 'sapl.compilacao:tipo_ta_list' %}">{%model_verbose_name_plural 'sapl.compilacao.models.TipoTextoArticulado'%}</a></li>
<li><a href="{% url 'sapl.compilacao:tipopublicacao_list' %}">{%model_verbose_name_plural 'sapl.compilacao.models.TipoPublicacao'%}</a></li> {% if perms.compilacao.list_tipotextoarticulado %}<li><a href="{% url 'sapl.compilacao:tipo_ta_list' %}">{%model_verbose_name_plural 'sapl.compilacao.models.TipoTextoArticulado'%}</a></li>{% endif %}
<li><a href="{% url 'sapl.compilacao:veiculopublicacao_list' %}">{%model_verbose_name_plural 'sapl.compilacao.models.VeiculoPublicacao'%}</a></li> {% if perms.compilacao.list_tipo_publicacao %}<li><a href="{% url 'sapl.compilacao:tipopublicacao_list' %}">{%model_verbose_name_plural 'sapl.compilacao.models.TipoPublicacao'%}</a></li>{% endif %}
<li><a href="{% url 'sapl.compilacao:tiponota_list' %}">{%model_verbose_name_plural 'sapl.compilacao.models.TipoNota'%}</a></li> {% if perms.compilacao.list_veiculopublicacao %}<li><a href="{% url 'sapl.compilacao:veiculopublicacao_list' %}">{%model_verbose_name_plural 'sapl.compilacao.models.VeiculoPublicacao'%}</a></li>{% endif %}
<li><a href="{% url 'sapl.compilacao:tipovide_list' %}">{%model_verbose_name_plural 'sapl.compilacao.models.TipoVide'%}</a></li> {% if perms.compilacao.list_tiponota %}<li><a href="{% url 'sapl.compilacao:tiponota_list' %}">{%model_verbose_name_plural 'sapl.compilacao.models.TipoNota'%}</a></li>{% endif %}
{% if perms.compilacao.list_tipovide %}<li><a href="{% url 'sapl.compilacao:tipovide_list' %}">{%model_verbose_name_plural 'sapl.compilacao.models.TipoVide'%}</a></li>{% endif %}
{% if user.is_superuser %}
<li><a href="{% url 'sapl.compilacao:tipodispositivo_list' %}">{%model_verbose_name_plural 'sapl.compilacao.models.TipoDispositivo'%}</a></li> <li><a href="{% url 'sapl.compilacao:tipodispositivo_list' %}">{%model_verbose_name_plural 'sapl.compilacao.models.TipoDispositivo'%}</a></li>
<li><a href="#">TODO: Perfil Estrutural de Textos Articulados</a></li> <li><a href="#">TODO: Perfil Estrutural de Textos Articulados</a></li>
{% endif %}
</ul> </ul>

20
sapl/templates/compilacao/tipotextoarticulado_detail.html

@ -9,8 +9,8 @@
<div class="clearfix"> <div class="clearfix">
{% block actions %} {% block actions %}
<div class="actions btn-group pull-right" role="group"> <div class="actions btn-group pull-right" role="group">
<a class="btn btn-default" href="{% url 'sapl.compilacao:tipo_ta_edit' object.pk %}">{% trans 'Editar' %}</a> {% if perms.compilacao.change_tipotextoarticulado %}<a class="btn btn-default" href="{% url 'sapl.compilacao:tipo_ta_edit' object.pk %}">{% trans 'Editar' %}</a>{% endif %}
<a class="btn btn-default" href="{% url 'sapl.compilacao:tipo_ta_delete' object.pk %}">{% trans 'Excluir' %}</a> {% if perms.compilacao.delete_tipotextoarticulado %}<a class="btn btn-default btn-excluir" href="{% url 'sapl.compilacao:tipo_ta_delete' object.pk %}">{% trans 'Excluir' %}</a>{% endif %}
</div> </div>
{% endblock actions %} {% endblock actions %}
</div> </div>
@ -19,32 +19,42 @@
<fieldset> <fieldset>
<legend>{%trans 'Identificação Básica'%}</legend> <legend>{%trans 'Identificação Básica'%}</legend>
<div class="row"> <div class="row">
<div class="col-md-2"> <div class="col-md-3">
<div id="div_id_tipo" class="holder"> <div id="div_id_tipo" class="holder">
<label>{% field_verbose_name object 'sigla' %}</label> <label>{% field_verbose_name object 'sigla' %}</label>
<p>{{ object.sigla}}</p> <p>{{ object.sigla}}</p>
</div> </div>
</div> </div>
<div class="col-md-4"> <div class="col-md-5">
<div id="div_id_numero" class="holder"> <div id="div_id_numero" class="holder">
<label>{% field_verbose_name object 'descricao' %}</label> <label>{% field_verbose_name object 'descricao' %}</label>
<p>{{ object.descricao}}</p> <p>{{ object.descricao}}</p>
</div> </div>
</div> </div>
<div class="col-md-3"> <div class="col-md-4">
<div id="div_id_ano" class="holder"> <div id="div_id_ano" class="holder">
<label>{% field_verbose_name object 'content_type' %}</label> <label>{% field_verbose_name object 'content_type' %}</label>
<p>{{ object.content_type|default:""}}</p> <p>{{ object.content_type|default:""}}</p>
</div> </div>
</div> </div>
</fieldset>
<fieldset>
<legend>{%trans 'Funcionalidades'%}</legend>
<div class="row">
<div class="col-md-3"> <div class="col-md-3">
<div id="div_id_ano" class="holder"> <div id="div_id_ano" class="holder">
<label>{% field_verbose_name object 'participacao_social' %}</label> <label>{% field_verbose_name object 'participacao_social' %}</label>
<p>{{ object.get_participacao_social_display}}</p> <p>{{ object.get_participacao_social_display}}</p>
</div> </div>
</div> </div>
<div class="col-md-3">
<div id="div_id_ano" class="holder">
<label>{% field_verbose_name object 'publicacao_func' %}</label>
<p>{{ object.get_publicacao_func_display}}</p>
</div>
</div>
</div> </div>
</fieldset> </fieldset>
{% endblock detail_content %} {% endblock detail_content %}

2
sapl/templates/compilacao/tipotextoarticulado_list.html

@ -5,11 +5,13 @@
{% block base_content %} {% block base_content %}
{% block actions %} {% block actions %}
{% if perms.compilacao.add_tipotextoarticulado %}
<div class="actions btn-group pull-right" role="group"> <div class="actions btn-group pull-right" role="group">
<a href="{{ view.create_url }}" class="btn btn-default"> <a href="{{ view.create_url }}" class="btn btn-default">
{% trans 'Adicionar'%} {%model_verbose_name 'sapl.compilacao.models.TipoTextoArticulado'%} {% trans 'Adicionar'%} {%model_verbose_name 'sapl.compilacao.models.TipoTextoArticulado'%}
</a> </a>
</div> </div>
{% endif %}
{% endblock actions %} {% endblock actions %}
{% if not object_list %} {% if not object_list %}

11
sapl/templates/crud/detail.html

@ -21,6 +21,15 @@
</a> </a>
{% endif %} {% endif %}
</div> </div>
{% if view.extras_url %}
<div class="actions btn-group btn-group-sm" role="group">
{% for url, css_class, text in view.extras_url %}
<a href="{{url}}" class="btn btn-default {{css_class}}">
{{text}}
</a>
{% endfor %}
</div>
{% endif %}
{% endblock sub_actions %} {% endblock sub_actions %}
<div class="editons pull-right"> <div class="editons pull-right">
@ -41,6 +50,7 @@
{% endblock actions %} {% endblock actions %}
</div> </div>
<div class="container-detail clearfix">
{% block detail_content %} {% block detail_content %}
{% for fieldset in view.layout_display %} {% for fieldset in view.layout_display %}
<h2 class="legend">{{ fieldset.legend }}</h2> <h2 class="legend">{{ fieldset.legend }}</h2>
@ -65,6 +75,7 @@
{% endfor %} {% endfor %}
{% endfor %} {% endfor %}
{% endblock detail_content %} {% endblock detail_content %}
</div>
{% block table_content %} {% block table_content %}
<div class="container-table"> <div class="container-table">

11
sapl/templates/crud/detail_detail.html

@ -18,6 +18,17 @@
</a> </a>
{% endif %} {% endif %}
</div> </div>
{% if view.extras_url %}
<div class="actions btn-group btn-group-sm" role="group">
{% for url, css_class, text in view.extras_url %}
<a href="{{url}}" class="btn btn-default {{css_class}}">
{{text}}
</a>
{% endfor %}
</div>
{% endif %}
{% if view.update_url or view.delete_url %} {% if view.update_url or view.delete_url %}
<div class="actions btn-group pull-right " role="group"> <div class="actions btn-group pull-right " role="group">
{% if view.update_url %} {% if view.update_url %}

4
sapl/templates/materia/confirmar_proposicao.html

@ -7,7 +7,9 @@
{% block actions %}{{block.super}} {% block actions %}{{block.super}}
<div class="actions btn-group btn-group-sm pull-right" role="group"> <div class="actions btn-group btn-group-sm pull-right" role="group">
{% if object.texto_articulado.exists %} {% if object.texto_articulado.exists %}
<a class="btn btn-default" href="{% url 'sapl.materia:proposicao_ta' object.pk%}">{% trans "Texto Eletrônico da Proposição" %}</a>
<a class="btn btn-default" href="{% url 'sapl.compilacao:ta_text' object.texto_articulado.first.pk%}?back_type=history">{% trans "Texto Eletrônico da Proposição" %}</a>
{% endif %} {% endif %}
{% if object.texto_original %} {% if object.texto_original %}
<a class="btn btn-default" href="{{ object.texto_original.url }}">{% trans "Texto Original da Proposição" %}</a> <a class="btn btn-default" href="{{ object.texto_original.url }}">{% trans "Texto Original da Proposição" %}</a>

1
sapl/templates/materia/proposicao_detail.html

@ -123,5 +123,4 @@
</div> </div>
</div> </div>
{% endif %} {% endif %}
{% endblock detail_content %} {% endblock detail_content %}

5
sapl/templates/materia/tipoproposicao_form.html

@ -22,13 +22,10 @@ $(document).ready(function(){
initial_select=''; initial_select='';
radios.append(html_radio); radios.append(html_radio);
}); });
}); });
}); });
$('#id_content_type').trigger('change');
$("#div_id_tipo_conteudo_related_radio .controls").addClass('controls-radio-checkbox'); $("#div_id_tipo_conteudo_related_radio .controls").addClass('controls-radio-checkbox');
$('#id_content_type').trigger('change');
}); });

2
sapl/templates/navbar.yaml

@ -13,7 +13,7 @@
url: sapl.parlamentares:parlamentar_list url: sapl.parlamentares:parlamentar_list
- title: {% trans 'Protocolo' %} - title: {% trans 'Protocolo' %}
check_permission: protolcoloadm.list_protocolo check_permission: protocoloadm.list_protocolo
children: children:
- title: {% trans 'Pesquisar Protocolo' %} - title: {% trans 'Pesquisar Protocolo' %}
url: sapl.protocoloadm:protocolo url: sapl.protocoloadm:protocolo

7
sapl/templates/norma/layouts.yaml

@ -1,4 +1,9 @@
{% load i18n %} {% load i18n %}
AssuntoNormaRelationship:
{% trans 'Assunto Norma Jurídica' %}:
- assunto
AssuntoNorma: AssuntoNorma:
{% trans 'Assunto Norma Jurídica' %}: {% trans 'Assunto Norma Jurídica' %}:
- assunto descricao - assunto descricao
@ -17,6 +22,7 @@ NormaJuridica:
- ementa - ementa
- indexacao - indexacao
- observacao - observacao
- assuntos
NormaJuridicaCreate: NormaJuridicaCreate:
{% trans 'Identificação Básica' %}: {% trans 'Identificação Básica' %}:
@ -28,6 +34,7 @@ NormaJuridicaCreate:
- ementa - ementa
- indexacao - indexacao
- observacao - observacao
- assuntos
LegislacaoCitada: LegislacaoCitada:
{% trans 'Legislação Citada' %}: {% trans 'Legislação Citada' %}:

1
sapl/templates/norma/list_pesquisa.html

@ -32,7 +32,6 @@
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>
{% include "paginacao.html" %}
{% else %} {% else %}
<h2>Nenhum Registro recuperado</h2> <h2>Nenhum Registro recuperado</h2>
{% endif %} {% endif %}

29
sapl/templates/norma/normajuridica_form.html

@ -0,0 +1,29 @@
{% extends "crud/form.html" %}
{% load i18n %}
{% load crispy_forms_tags %}
{% load common_tags %}
{% block extra_js %}
<script language="Javascript">
function recuperar_materia() {
var tipo_materia = $("#id_tipo_materia").val()
var numero_materia = $("#id_numero_materia").val()
var ano_materia = $("#id_ano_materia").val()
if (tipo_materia && numero_materia && ano_materia) {
$.get("/sessao/recuperar-materia",{tipo_materia: tipo_materia,
numero_materia: numero_materia,
ano_materia: ano_materia},
function(data, status) {
$("#id_ementa").val(data.ementa);
});
}
}
var fields = ["#id_tipo_materia", "#id_numero_materia", "#id_ano_materia"]
for (i = 0; i < fields.length; i++) {
$(fields[i]).change(recuperar_materia);
}
</script>
{% endblock %}

2
sapl/templates/norma/subnav.yaml

@ -1,4 +1,5 @@
{% load i18n common_tags%} {% load i18n common_tags%}
- title: {% trans 'Início' %} - title: {% trans 'Início' %}
url: normajuridica_detail url: normajuridica_detail
@ -6,6 +7,7 @@
# para integração foram necessárias apenas criar a url norma_ta em urls.py # para integração foram necessárias apenas criar a url norma_ta em urls.py
# e a view NormaTaView(IntegracaoTaView) em views.py # e a view NormaTaView(IntegracaoTaView) em views.py
# Em nada mais a integração interfere em NormaJuridica # Em nada mais a integração interfere em NormaJuridica
{% if 'texto_articulado_norma'|get_config_attr %} {% if 'texto_articulado_norma'|get_config_attr %}
- title: {% trans 'Texto' %} - title: {% trans 'Texto' %}
url: norma_ta url: norma_ta

4
sapl/templates/painel/controlador.html

@ -21,4 +21,8 @@ FECHADO
<input type="submit" name="stop-painel" value="Fechar Painel" class="button primary"> <input type="submit" name="stop-painel" value="Fechar Painel" class="button primary">
<input type="submit" name="save-painel" value="Salvar" class="button primary"> <input type="submit" name="save-painel" value="Salvar" class="button primary">
</form> </form>
</br>
</br>
<a class="btn btn-primary" href="{{request.META.HTTP_REFERER}}">Voltar</a>
{% endblock %} {% endblock %}

29
sapl/templates/protocoloadm/MateriaTemplate.html

@ -0,0 +1,29 @@
{% extends "base.html" %}
{% load i18n common_tags%}
{% block base_content %}
<div class="alert alert-success alert-dismissible fade in" role="alert">
<p align="center"><b><font color="green">Matéria procololada com sucesso!</font></b></p>
</div>
<div align="center">
<div class="row" style="width:50%;">
<div class="col-md-6">
<a onclick="window.open('{% url 'sapl.relatorios:relatorio_etiqueta_protocolo' protocolo.numero protocolo.ano %}','Comprovante','width=400, height=200')"class="btn btn-secondary">Imprimir Etiqueta</a>
</div>
<div class="col-md-6">
<a target="popup" class="btn btn-secondary" onclick="window.open('{% url 'sapl.protocoloadm:comprovante_protocolo' protocolo.pk %}','Comprovante','width=800, height=700')">Imprimir Comprovante</a>
</div>
</div>
<div class="row" style="width:50%;">
<div class="col-md-6">
<a href="{% url 'sapl.materia:materia_create_simplificado' protocolo.pk %}" class="btn btn-warning">Criar Matéria</a>
</div>
<div class="col-md-6">
<a href="{% url 'sapl.protocoloadm:protocolo_mostrar' protocolo.pk %}" class="btn btn-primary">Continuar</a>
</div>
</div>
</div>
{% endblock base_content %}

10
sapl/templates/protocoloadm/anular_protocoloadm.html

@ -1,6 +1,16 @@
{% extends "protocoloadm/protocoloadm_detail.html" %} {% extends "protocoloadm/protocoloadm_detail.html" %}
{% load i18n %} {% load i18n %}
{% load crispy_forms_tags %} {% load crispy_forms_tags %}
{% block actions %}
{{ block.super }}
<div class="actions btn-group pull-right grid-gutter-width-right " role="group">
<a href="{% url 'protocoloadm:protocolo' %}" class="btn btn-default">{% trans 'Fazer nova pesquisa' %}</a>
</div>
{% endblock %}
{% block detail_content %} {% block detail_content %}
<div>{{ message }} </div> <div>{{ message }} </div>
{% crispy form %} {% crispy form %}

4
sapl/templates/protocoloadm/comprovante.html

@ -83,9 +83,5 @@
<th>Número Páginas</th> <th>Número Páginas</th>
<td>{{ protocolo.numero_paginas }}</td> <td>{{ protocolo.numero_paginas }}</td>
</tr> </tr>
<tr>
<th>Número Páginas</th>
<td>{{ protocolo.numero_paginas }}</td>
</tr>
</table> </table>
{% endblock detail_content %} {% endblock detail_content %}

9
sapl/templates/protocoloadm/protocolar_documento.html

@ -2,6 +2,15 @@
{% load i18n %} {% load i18n %}
{% load crispy_forms_tags %} {% load crispy_forms_tags %}
{% block actions %}
{{ block.super }}
<div class="actions btn-group pull-right grid-gutter-width-right " role="group">
<a href="{% url 'protocoloadm:protocolo' %}" class="btn btn-default">{% trans 'Fazer nova pesquisa' %}</a>
</div>
{% endblock %}
{% block detail_content %} {% block detail_content %}
{% crispy form %} {% crispy form %}
{% endblock detail_content %} {% endblock detail_content %}

8
sapl/templates/protocoloadm/protocolar_materia.html

@ -2,6 +2,14 @@
{% load i18n %} {% load i18n %}
{% load crispy_forms_tags %} {% load crispy_forms_tags %}
{% block actions %}
{{ block.super }}
<div class="actions btn-group pull-right grid-gutter-width-right " role="group">
<a href="{% url 'protocoloadm:protocolo' %}" class="btn btn-default">{% trans 'Fazer nova pesquisa' %}</a>
</div>
{% endblock %}
{% block detail_content %} {% block detail_content %}
{% crispy form %} {% crispy form %}
{% endblock detail_content %} {% endblock detail_content %}

22
sapl/templates/protocoloadm/protocolo_filter.html

@ -3,17 +3,22 @@
{% load crispy_forms_tags %} {% load crispy_forms_tags %}
{% load static %} {% load static %}
{% block sections_nav %} {% endblock %}
{% block base_content %} {% block actions %}
<h1><b>Pesquisa de Protocolo</b></h1>
<br></br> {{ block.super }}
{% if filter_url %} {% if filter_url %}
<div class="actions btn-group pull-right" role="group"> <div class="actions btn-group pull-right grid-gutter-width-right " role="group">
<a href="{% url 'protocoloadm:protocolo' %}" class="btn btn-default">{% trans 'Fazer nova pesquisa' %}</a> <a href="{% url 'protocoloadm:protocolo' %}" class="btn btn-default">{% trans 'Fazer nova pesquisa' %}</a>
</div> </div>
{% endif %} {% endif %}
{% endblock %}
{% block detail_content %}
<br>
{% if not filter_url %} {% if not filter_url %}
{% crispy filter.form %} {% crispy filter.form %}
{% endif %} {% endif %}
@ -33,7 +38,7 @@
<tr> <tr>
<td> <td>
<strong>Protocolo: <strong>Protocolo:
<a href="{% url 'protocoloadm:protocolo_mostrar' p.numero p.ano %}">{{ p.numero|stringformat:'06d' }}/{{ p.ano }}</a></strong>&nbsp;&nbsp;<strong>-</strong>&nbsp;&nbsp; <a href="{% url 'protocoloadm:protocolo_mostrar' p.pk %}">{{ p.numero|stringformat:'06d' }}/{{ p.ano }}</a></strong>&nbsp;&nbsp;<strong>-</strong>&nbsp;&nbsp;
<a href="{% url 'relatorios:relatorio_etiqueta_protocolo' p.numero p.ano %}"><img src="{% static 'img/etiqueta.png' %}" alt="Etiqueta Individual"></a></br> <a href="{% url 'relatorios:relatorio_etiqueta_protocolo' p.numero p.ano %}"><img src="{% static 'img/etiqueta.png' %}" alt="Etiqueta Individual"></a></br>
<strong>Assunto:</strong> {{ p.assunto_ementa|default_if_none:"Não Informado"}}</br> <strong>Assunto:</strong> {{ p.assunto_ementa|default_if_none:"Não Informado"}}</br>
<strong>Data Protocolo:</strong> {{ p.data|date:"d/m/Y"|default_if_none:"Não Informado" }} - Horário: {{ p.hora|date:"G:i:s" }}</br> <strong>Data Protocolo:</strong> {{ p.data|date:"d/m/Y"|default_if_none:"Não Informado" }} - Horário: {{ p.hora|date:"G:i:s" }}</br>
@ -50,4 +55,7 @@
</table> </table>
{% include "paginacao.html" %} {% include "paginacao.html" %}
{% endif %} {% endif %}
{% endblock base_content %} {% endblock detail_content %}
{% block table_content %}
{% endblock table_content %}

25
sapl/templates/protocoloadm/protocolo_mostrar.html

@ -15,9 +15,28 @@
<br /> <br />
<strong>Documento Vinculado:</strong></br> <strong>Documento Vinculado:</strong>
<a href="{% url 'sapl.protocoloadm:criar_documento' protocolo.numero protocolo.ano %}" class="btn btn-primary">Criar Documento</a>
{% if protocolo.tipo_documento %}
{% if documento %}
<a href="{% url 'sapl.protocoloadm:documentoadministrativo_detail' documento.pk %}"> {{documento}} </a>
</br>
{% else %}
<br />
<a href="{% url 'sapl.protocoloadm:criar_documento' protocolo.pk %}" class="btn btn-primary">Criar Documento</a>
{% endif %}
{% elif protocolo.tipo_materia %}
{% if materia %}
<a href="{% url 'sapl.materia:materialegislativa_detail' materia.pk %}"> {{materia}} </a>
</br>
{% else %}
<br />
<a href="{% url 'sapl.materia:materia_create_simplificado' protocolo.pk %}" class="btn btn-primary">Criar Matéria</a>
{% endif %}
{% endif %}
&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;
<a href="{% url 'sapl.protocoloadm:comprovante_protocolo' protocolo.numero protocolo.ano %}" target="popup" class="btn btn-primary" onclick="window.open('{% url 'sapl.protocoloadm:comprovante_protocolo' protocolo.numero protocolo.ano %}','Comprovante','width=800, height=600')">Comprovante <a target="popup" class="btn btn-primary" onclick="window.open('{% url 'sapl.protocoloadm:comprovante_protocolo' protocolo.pk%}','Comprovante','width=800, height=600')">Comprovante
</a> </a>
{% endblock detail_content %} {% endblock detail_content %}

10
sapl/templates/protocoloadm/protocoloadm_detail.html

@ -1,9 +1,9 @@
{% extends "crud/detail.html" %} {% extends "crud/detail.html" %}
{% load i18n %} {% load i18n %}
{% block actions %} {% block editions %}
<div class="actions btn-group pull-right" role="group"> <div class="actions btn-group pull-right" role="group">
<a href="{% url 'protocoloadm:protocolar_doc' %}" class="btn btn-default">{% trans 'Protocolar Documento' %}</a> <a href="{% url 'protocoloadm:protocolar_doc' %}" class="btn btn-default">{% trans 'Protocolar Documento' %}</a>
<a href="{% url 'protocoloadm:protocolar_mat' %}" class="btn btn-default">{% trans 'Protocolar Matéria' %}</a> <a href="{% url 'protocoloadm:protocolar_mat' %}" class="btn btn-default">{% trans 'Protocolar Matéria' %}</a>
<a href="{% url 'protocoloadm:anular_protocolo' %}" class="btn btn-default">{% trans 'Anular Protocolo' %}</a> <a href="{% url 'protocoloadm:anular_protocolo' %}" class="btn btn-default btn-excluir">{% trans 'Anular Protocolo' %}</a>
</div> </div>
{% endblock actions %} {% endblock editions %}

2
sapl/templates/sessao/painel.html

@ -18,7 +18,7 @@
<!--<div class="col-md-6"><a href="{% url 'sapl.painel:painel_mensagem' %}" class="btn btn-primary btn-sm active">Iniciar painel mensagem</a></div> <!--<div class="col-md-6"><a href="{% url 'sapl.painel:painel_mensagem' %}" class="btn btn-primary btn-sm active">Iniciar painel mensagem</a></div>
<div class="col-md-6"><a href="{% url 'sapl.painel:painel_parlamentar' %}" class="btn btn-primary btn-sm active">Iniciar painel parlamentares</a></div> <div class="col-md-6"><a href="{% url 'sapl.painel:painel_parlamentar' %}" class="btn btn-primary btn-sm active">Iniciar painel parlamentares</a></div>
<div class="col-md-6"><a href="{% url 'sapl.painel:painel_votacao' %}" class="btn btn-primary btn-sm active">Iniciar painel votação</a></div> --> <div class="col-md-6"><a href="{% url 'sapl.painel:painel_votacao' %}" class="btn btn-primary btn-sm active">Iniciar painel votação</a></div> -->
<div class="col-md-6"><a href="{% url 'sapl.painel:painel_controlador' %}" target="_blank" class="btn btn-primary btn-sm active">Controlador Painel</a></div> <div class="col-md-6"><a href="{% url 'sapl.painel:painel_controlador' %}" class="btn btn-primary btn-sm active">Controlador Painel</a></div>
</div> </div>
<br /> <br />
<h1>Operação do Painel Eletrônico</h1> <h1>Operação do Painel Eletrônico</h1>

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

Loading…
Cancel
Save