Browse Source

Merge pull request #788 from interlegis/774-container-usuario-em-compilacao

774 container usuario em compilacao
pull/789/head
Leandro Roberto da Silva 8 years ago
committed by GitHub
parent
commit
adda444f18
  1. 46
      sapl/compilacao/migrations/0064_auto_20161104_1420.py
  2. 20
      sapl/compilacao/migrations/0065_auto_20161107_1024.py
  3. 20
      sapl/compilacao/migrations/0066_auto_20161107_1028.py
  4. 19
      sapl/compilacao/migrations/0067_auto_20161107_1351.py
  5. 19
      sapl/compilacao/migrations/0068_auto_20161107_1546.py
  6. 20
      sapl/compilacao/migrations/0069_auto_20161107_1932.py
  7. 147
      sapl/compilacao/models.py
  8. 4
      sapl/compilacao/templatetags/compilacao_filters.py
  9. 98
      sapl/compilacao/views.py
  10. 5
      sapl/crispy_layout_mixin.py
  11. 7
      sapl/crud/base.py
  12. 35
      sapl/materia/forms.py
  13. 86
      sapl/materia/views.py
  14. 35
      sapl/parlamentares/views.py
  15. 15
      sapl/rules/map_rules.py
  16. 2
      sapl/templates/compilacao/publicacao_detail.html
  17. 6
      sapl/templates/compilacao/text_edit.html
  18. 8
      sapl/templates/compilacao/text_list.html
  19. 27
      sapl/templates/compilacao/textoarticulado_detail.html
  20. 29
      sapl/templates/compilacao/tipotextoarticulado_detail.html
  21. 9
      sapl/templates/crud/detail.html
  22. 11
      sapl/templates/crud/detail_detail.html
  23. 1
      sapl/templates/materia/proposicao_detail.html

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'),
),
]

147
sapl/compilacao/models.py

@ -1,8 +1,10 @@
from django.contrib import messages
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.db.models import F, Q
from django.db.models.aggregates import Max
from django.http.response import Http404
from django.template import defaultfilters
from django.utils.translation import ugettext_lazy as _
@ -82,7 +84,7 @@ class TipoTextoArticulado(models.Model):
verbose_name=_('Participação Social'))
publicacao_func = models.NullBooleanField(
default=True,
default=False,
blank=True, null=True,
choices=YES_NO_CHOICES,
verbose_name=_('Histórico de Publicação'))
@ -101,6 +103,25 @@ PARTICIPACAO_SOCIAL_CHOICES = [
(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):
data = models.DateField(blank=True, null=True, verbose_name=_('Data'))
ementa = models.TextField(verbose_name=_('Ementa'))
@ -124,10 +145,35 @@ class TextoArticulado(TimestampedMixin):
blank=True, null=True, default=None)
content_object = GenericForeignKey('content_type', 'object_id')
owners = models.ManyToManyField(
get_settings_auth_user_model(),
blank=True, verbose_name=_('Donos do Texto Articulado'))
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:
verbose_name = _('Texto Articulado')
verbose_name_plural = _('Textos Articulados')
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):
if self.content_object:
@ -138,6 +184,102 @@ class TextoArticulado(TimestampedMixin):
'numero': self.numero,
'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):
dpts = Dispositivo.objects.filter(ta=self)
@ -703,6 +845,9 @@ class Dispositivo(BaseModel, TimestampedMixin):
('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.')),

4
sapl/compilacao/templatetags/compilacao_filters.py

@ -295,3 +295,7 @@ def urldetail_content_type(obj):
@register.filter
def list(obj):
return [obj, ]
@register.filter
def can_use_dynamic_editing(texto_articulado, user):
return texto_articulado.can_use_dynamic_editing(user)

98
sapl/compilacao/views.py

@ -7,7 +7,6 @@ from braces.views import FormMessagesMixin
from django import forms
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.decorators import login_required, permission_required
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.contrib.contenttypes.models import ContentType
from django.core.signing import Signer
@ -19,7 +18,6 @@ from django.http.response import (HttpResponse, HttpResponseRedirect,
JsonResponse)
from django.shortcuts import get_object_or_404, redirect
from django.utils.dateparse import parse_date
from django.utils.decorators import method_decorator
from django.utils.encoding import force_text
from django.utils.translation import string_concat
from django.utils.translation import ugettext_lazy as _
@ -44,7 +42,8 @@ from sapl.compilacao.models import (Dispositivo, Nota,
Publicacao, TextoArticulado,
TipoDispositivo, TipoNota, TipoPublicacao,
TipoTextoArticulado, TipoVide,
VeiculoPublicacao, Vide)
VeiculoPublicacao, Vide, STATUS_TA_EDITION,
STATUS_TA_PRIVATE, STATUS_TA_PUBLIC)
from sapl.compilacao.utils import (DISPOSITIVO_SELECT_RELATED,
DISPOSITIVO_SELECT_RELATED_EDIT)
from sapl.crud.base import Crud, CrudListView, make_pagination
@ -157,6 +156,7 @@ class IntegracaoTaView(TemplateView):
""")
map_fields = self.map_fields
ta_values = getattr(self, 'ta_values', {})
item = get_object_or_404(self.model, pk=kwargs['pk'])
related_object_type = ContentType.objects.get_for_model(item)
@ -168,18 +168,30 @@ class IntegracaoTaView(TemplateView):
tipo_ta = TipoTextoArticulado.objects.filter(
content_type=related_object_type)
if not ta.exists():
ta_exists = bool(ta.exists())
if not ta_exists:
ta = TextoArticulado()
tipo_ta = TipoTextoArticulado.objects.filter(
content_type=related_object_type)[:1]
if tipo_ta.exists():
ta.tipo_ta = tipo_ta[0]
ta.content_object = item
ta.privacidade = ta_values.get('privacidade', STATUS_TA_EDITION)
ta.editing_locked = ta_values.get('editing_locked', False)
ta.editable_only_by_owners = ta_values.get(
'editable_only_by_owners', False)
else:
ta = ta[0]
ta.data = getattr(item, map_fields['data']
if map_fields['data'] else 'xxx', datetime.now())
if not ta.data:
ta.data = getattr(item, map_fields['data']
if map_fields['data'] else 'xxx',
datetime.now())
if not ta.data:
ta.data = datetime.now()
ta.ementa = getattr(
item, map_fields['ementa']
@ -202,11 +214,17 @@ class IntegracaoTaView(TemplateView):
ta.save()
if Dispositivo.objects.filter(ta_id=ta.pk).exists():
return redirect(to=reverse_lazy('sapl.compilacao:ta_text',
if not ta_exists:
if ta.editable_only_by_owners and\
not self.request.user.is_anonymous():
ta.owners.add(self.request.user)
if not Dispositivo.objects.filter(ta_id=ta.pk).exists() and\
ta.can_use_dynamic_editing(self.request.user):
return redirect(to=reverse_lazy('sapl.compilacao:ta_text_edit',
kwargs={'ta_id': ta.pk}))
else:
return redirect(to=reverse_lazy('sapl.compilacao:ta_text_edit',
return redirect(to=reverse_lazy('sapl.compilacao:ta_text',
kwargs={'ta_id': ta.pk}))
def import_pattern(self):
@ -284,7 +302,8 @@ class CompMixin(PermissionRequiredMixin):
@property
def ta(self):
ta = TextoArticulado.objects.get(pk=self.kwargs['ta_id'])
ta = TextoArticulado.objects.get(
pk=self.kwargs.get('ta_id', self.kwargs.get('pk', 0)))
return ta
def get_context_data(self, **kwargs):
@ -407,10 +426,26 @@ class TaListView(CompMixin, ListView):
page_obj.number, paginator.num_pages)
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):
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
def title(self):
if self.get_object().content_object:
@ -736,9 +771,9 @@ class TextView(CompMixin, ListView):
fim_vigencia = None
ta_vigencia = None
def get(self, request, *args, **kwargs):
def has_permission(self):
self.object = self.ta
return super(TextView, self).get(request, *args, **kwargs)
return self.object.has_view_permission(self.request)
def get_context_data(self, **kwargs):
context = super(TextView, self).get_context_data(**kwargs)
@ -931,7 +966,42 @@ class DispositivoView(TextView):
class TextEditView(CompMixin, TemplateView):
template_name = 'compilacao/text_edit.html'
permission_required = 'compilacao.change_dispositivo_edicao_dinamica'
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):
dispositivo_id = int(self.kwargs['dispositivo_id']) \
@ -2421,8 +2491,6 @@ class DispositivoDinamicEditView(
template_name = 'compilacao/text_edit_bloco.html'
model = Dispositivo
form_class = DispositivoEdicaoBasicaForm
contador = -1
permission_required = 'compilacao.change_dispositivo_edicao_dinamica',
def get_initial(self):
initial = UpdateView.get_initial(self)

5
sapl/crispy_layout_mixin.py

@ -1,12 +1,12 @@
from math import ceil
import rtyaml
from crispy_forms.bootstrap import FormActions
from crispy_forms.helper import FormHelper
from crispy_forms.layout import HTML, Div, Fieldset, Layout, Submit
from django import template
from django.utils import formats
from django.utils.translation import ugettext as _
import rtyaml
def heads_and_tails(list_of_lists):
@ -91,7 +91,8 @@ def get_field_display(obj, fieldname):
else:
display = ''
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>'
for v in value.all():
display += '<li>%s</li>' % str(v)

7
sapl/crud/base.py

@ -16,8 +16,8 @@ from django.http.response import Http404
from django.shortcuts import redirect
from django.utils.decorators import classonlymethod
from django.utils.encoding import force_text
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import string_concat
from django.utils.translation import ugettext_lazy as _
from django.views.generic import (CreateView, DeleteView, DetailView, ListView,
UpdateView)
from django.views.generic.base import ContextMixin
@ -29,6 +29,7 @@ from sapl.rules.map_rules import (RP_ADD, RP_CHANGE, RP_DELETE, RP_DETAIL,
from sapl.settings import BASE_DIR
from sapl.utils import normalize
logger = logging.getLogger(BASE_DIR.name)
ACTION_LIST, ACTION_CREATE, ACTION_DETAIL, ACTION_UPDATE, ACTION_DELETE = \
@ -1401,10 +1402,6 @@ class CrudBaseForListAndDetailExternalAppView(MasterDetailCrud):
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):
obj = self.crud if hasattr(self, 'crud') else self

35
sapl/materia/forms.py

@ -1,8 +1,7 @@
import os
from datetime import date, datetime
import os
import django_filters
from crispy_forms.bootstrap import (Alert, FormActions, InlineCheckboxes,
InlineRadios)
from crispy_forms.helper import FormHelper
@ -18,10 +17,12 @@ from django.db.models import Max
from django.forms import ModelForm, widgets
from django.forms.forms import Form
from django.utils.translation import ugettext_lazy as _
import django_filters
import sapl
from sapl.base.models import Autor
from sapl.comissoes.models import Comissao
from sapl.compilacao.models import STATUS_TA_PRIVATE,\
STATUS_TA_IMMUTABLE_PUBLIC
from sapl.crispy_layout_mixin import (SaplFormLayout, form_actions, to_column,
to_row)
from sapl.materia.models import (MateriaLegislativa, RegimeTramitacao,
@ -35,6 +36,7 @@ from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES,
ChoiceWithoutValidationField,
MateriaPesquisaOrderingFilter, RangeWidgetOverride,
autor_label, autor_modal, models_with_gr_for_model)
import sapl
from .models import (AcompanhamentoMateria, Anexada, Autoria, DespachoInicial,
DocumentoAcessorio, MateriaLegislativa, Numeracao,
@ -1134,6 +1136,12 @@ class ConfirmarProposicaoForm(ProposicaoForm):
self.instance.data_envio = None
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 = {
'messages': {
'success': [_('Devolução efetuada com sucesso.'), ]
@ -1147,6 +1155,12 @@ class ConfirmarProposicaoForm(ProposicaoForm):
self.instance.data_devolucao = None
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()
"""
@ -1187,10 +1201,17 @@ class ConfirmarProposicaoForm(ProposicaoForm):
materia.data_apresentacao = datetime.now()
materia.em_tramitacao = True
materia.regime_tramitacao = cd['regime_tramitacao']
materia.texto_original = File(
proposicao.texto_original,
os.path.basename(proposicao.texto_original.path))
materia.texto_articulo = proposicao.texto_articulado
if proposicao.texto_original:
materia.texto_original = File(
proposicao.texto_original,
os.path.basename(proposicao.texto_original.path))
if proposicao.texto_articulado.exists():
pass
# FIXME - gerar texto_articulado da materia com base na prop.
# materia.texto_articulo = proposicao.texto_articulado
materia.save()
conteudo_gerado = materia

86
sapl/materia/views.py

@ -1,6 +1,7 @@
from datetime import datetime
from random import choice
from string import ascii_letters, digits
from crispy_forms.helper import FormHelper
from crispy_forms.layout import HTML
from django.contrib import messages
@ -11,7 +12,7 @@ from django.core.mail import send_mail
from django.core.urlresolvers import reverse
from django.http import JsonResponse
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.utils import formats
from django.utils.translation import ugettext_lazy as _
@ -21,6 +22,9 @@ from django.views.generic.edit import FormView
from django_filters.views import FilterView
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.crispy_layout_mixin import SaplFormLayout, form_actions
from sapl.crud.base import (ACTION_CREATE, ACTION_DELETE, ACTION_DETAIL,
@ -107,7 +111,11 @@ class MateriaTaView(IntegracaoTaView):
'ano': 'ano',
}
map_funcs = {
'publicacao_func': False
'publicacao_func': False,
}
ta_values = {
'editable_only_by_owners': False,
'editing_locked': False,
}
def get(self, request, *args, **kwargs):
@ -126,7 +134,7 @@ class ProposicaoTaView(IntegracaoTaView):
model = Proposicao
model_type_foreignkey = TipoProposicao
map_fields = {
'data': 'data_recebimento',
'data': 'data_envio',
'ementa': 'descricao',
'observacao': None,
'numero': 'numero_proposicao',
@ -135,6 +143,11 @@ class ProposicaoTaView(IntegracaoTaView):
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):
"""
@ -143,6 +156,13 @@ class ProposicaoTaView(IntegracaoTaView):
de usuário.
"""
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)
else:
return self.get_redirect_deactivated()
@ -313,10 +333,14 @@ class ReceberProposicao(PermissionRequiredForAppCrudMixin, FormView):
data_envio__isnull=False, data_recebimento__isnull=True)
for proposicao in proposicoes:
# FIXME implementar hash para texto eletrônico
hasher = gerar_hash_arquivo(
proposicao.texto_original.path,
str(proposicao.pk)) if proposicao.texto_original else None
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(
proposicao.texto_original.path,
str(proposicao.pk)) if proposicao.texto_original else None
if hasher == form.cleaned_data['cod_hash']:
return HttpResponseRedirect(
reverse('sapl.materia:proposicao-confirmar',
@ -343,9 +367,6 @@ class ConfirmarProposicao(PermissionRequiredForAppCrudMixin, UpdateView):
form_class = ConfirmarProposicaoForm
def get_success_url(self):
# FIXME redirecionamento trival,
# ainda por implementar se será para protocolo ou para doc resultante
msgs = self.object.results['messages']
for key, value in msgs.items():
@ -365,9 +386,15 @@ class ConfirmarProposicao(PermissionRequiredForAppCrudMixin, UpdateView):
data_recebimento__isnull=True)
self.object = None
# FIXME implementar hash para texto eletrônico
hasher = gerar_hash_arquivo(
proposicao.texto_original.path,
str(proposicao.pk)) if proposicao.texto_original else None
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(
proposicao.texto_original.path,
str(proposicao.pk)) if proposicao.texto_original else None
if hasher == 'P%s/%s' % (self.kwargs['hash'], proposicao.pk):
self.object = proposicao
@ -474,6 +501,13 @@ class ProposicaoCrud(Crud):
p.data_devolucao = None
p.data_envio = datetime.now()
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, _(
'Proposição enviada com sucesso.'))
@ -486,6 +520,11 @@ class ProposicaoCrud(Crud):
else:
p.data_envio = None
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, _(
'Proposição Retornada com sucesso.'))
@ -547,9 +586,8 @@ class ProposicaoCrud(Crud):
messages.info(self.request,
_('Sempre que uma Proposição é inclusa ou '
'alterada e a opção "Texto Articulado " for '
'marcada, você será redirecionado para o '
'Texto Eletrônico. Use a opção "Editar Texto" '
'para construir seu texto.'))
'marcada, você será redirecionado para a '
'edição do Texto Eletrônico.'))
return reverse('sapl.materia:proposicao_ta',
kwargs={'pk': self.object.pk})
else:
@ -618,10 +656,18 @@ class ReciboProposicaoView(TemplateView):
context = super(ReciboProposicaoView, self).get_context_data(
**kwargs)
proposicao = Proposicao.objects.get(pk=self.kwargs['pk'])
if proposicao.texto_original:
_hash = gerar_hash_arquivo(
proposicao.texto_original.path,
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': gerar_hash_arquivo(
proposicao.texto_original.path,
self.kwargs['pk'])})
'hash': _hash})
return context
def get(self, request, *args, **kwargs):
@ -631,7 +677,7 @@ class ReciboProposicaoView(TemplateView):
return TemplateView.get(self, request, *args, **kwargs)
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.'))
elif proposicao.data_devolucao:
messages.error(request, _('Não é possível gerar recibo.'))

35
sapl/parlamentares/views.py

@ -54,6 +54,12 @@ class RelatoriaParlamentarCrud(CrudBaseForListAndDetailExternalAppView):
help_path = 'relatoria_parlamentar'
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):
model = Proposicao
@ -61,10 +67,31 @@ class ProposicaoParlamentarCrud(CrudBaseForListAndDetailExternalAppView):
parent_field = 'autor__parlamentar_set'
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):
def get_queryset(self):
return super().get_queryset().filter(data_envio__isnull=False)
return super().get_queryset().filter(
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):
@ -74,6 +101,12 @@ class ParticipacaoParlamentarCrud(CrudBaseForListAndDetailExternalAppView):
list_field_names = ['composicao__comissao__nome', 'cargo__nome', (
'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):
ordering = ('-composicao__periodo')

15
sapl/rules/map_rules.py

@ -84,6 +84,7 @@ rules_group_protocolo = {
(materia.Autoria, __base__),
(materia.Proposicao, __listdetailchange__),
(compilacao.TextoArticulado, ['view_restricted_textoarticulado'])
]
}
@ -117,8 +118,8 @@ rules_group_materia = {
# 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
# oferecia um autografo eletrônico pronto ser convertido em Norma.
# uma matéria original. Fazer esse registro de compilação ofereceria
# um autografo eletrônico pronto para ser convertido em Norma.
])
]
}
@ -175,7 +176,7 @@ rules_group_autor = {
'rules': [
(materia.Proposicao, __base__),
(compilacao.Dispositivo, __base__ + [
'change_dispositivo_edicao_dinamica',
'change_your_dispositivo_edicao_dinamica',
])
]
}
@ -258,12 +259,18 @@ rules_group_geral = {
# 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__),
(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, []),
]
}

2
sapl/templates/compilacao/publicacao_detail.html

@ -8,7 +8,7 @@
{% block actions %}
<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_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>
{% endblock actions %}
</div>

6
sapl/templates/compilacao/text_edit.html

@ -11,7 +11,7 @@
{% endblock %}
{% 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 %}
{% block actions %}
@ -19,6 +19,10 @@
<div class="clearfix">
<div class="actions btn-toolbar pull-right" role="toolbar">
<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>
{% include 'compilacao/textoarticulado_menu_config.html' %}
</div>

8
sapl/templates/compilacao/text_list.html

@ -11,6 +11,13 @@
{% endblock %}
{% block extra_sections_nav %}{% endblock %}
{% block base_content %}
{% block actions %}
{{block.super}}
{% endblock %}
{% comment %}
{% if perms.compilacao.change_dispositivo_edicao_dinamica %}
{% block actions %}
<div class="clearfix">
@ -20,6 +27,7 @@
</div>
{% endblock actions %}
{% endif %}
{% endcomment %}
{% block detail_content %}
{{block.super}}

27
sapl/templates/compilacao/textoarticulado_detail.html

@ -7,7 +7,9 @@
<ul class="nav nav-pills navbar-right">
{%if object %}
<li>
{% if object.content_object%}
{% if request.GET.back_type == 'history' and object.content_object %}
<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%}
<a href="{% url 'sapl.compilacao:ta_detail' object.pk %}">{% trans 'Início' %}</a>
@ -30,13 +32,18 @@
{% block actions %}
<div class="clearfix">
<div class="actions btn-group pull-right" role="group">
{% if perms.compilacao.change_textoarticulado %}
{% 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>
{% endif %}
{% if perms.compilacao.change_dispositivo_edicao_dinamica %}
<a href="{% url 'sapl.compilacao:ta_text_edit' object.pk %}" class="btn btn-default">{% trans 'Editar Texto' %}</a>
{% 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>
{% endif %}
</div>
</div>
{% endblock actions %}
@ -82,14 +89,14 @@
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div id="div_id_ementa" class="holder">
<label>{% field_verbose_name object 'ementa' %}</label>
<p>{{ object.ementa|safe}}</p>
</div>
<div class="row">
<div class="col-md-12">
<div id="div_id_ementa" class="holder">
<label>{% field_verbose_name object 'ementa' %}</label>
<p>{{ object.ementa|safe}}</p>
</div>
</div>
</div>
</fieldset>
{% endblock detail_content %}
{% endblock base_content %}

29
sapl/templates/compilacao/tipotextoarticulado_detail.html

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

9
sapl/templates/crud/detail.html

@ -21,6 +21,15 @@
</a>
{% endif %}
</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 %}
<div class="editons pull-right">

11
sapl/templates/crud/detail_detail.html

@ -18,6 +18,17 @@
</a>
{% endif %}
</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 %}
<div class="actions btn-group pull-right " role="group">
{% if view.update_url %}

1
sapl/templates/materia/proposicao_detail.html

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

Loading…
Cancel
Save