diff --git a/docker-compose.yml b/docker-compose.yml
index fa0abdd5d..df1b25454 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -11,7 +11,7 @@ sapldb:
ports:
- "5432:5432"
sapl:
- image: interlegis/sapl:3.1.102
+ image: interlegis/sapl:3.1.107
restart: always
environment:
ADMIN_PASSWORD: interlegis
diff --git a/sapl/audiencia/forms.py b/sapl/audiencia/forms.py
index 3ad74cca9..9312a237b 100644
--- a/sapl/audiencia/forms.py
+++ b/sapl/audiencia/forms.py
@@ -16,18 +16,21 @@ class AudienciaForm(forms.ModelForm):
tipo_materia = forms.ModelChoiceField(
label=_('Tipo Matéria'),
- required=True,
+ required=False,
queryset=TipoMateriaLegislativa.objects.all(),
empty_label='Selecione',
)
numero_materia = forms.CharField(
- label='Número Matéria', required=True)
+ label='Número Matéria', required=False)
ano_materia = forms.CharField(
label='Ano Matéria',
- initial=int(data_atual.year),
- required=True)
+ required=False)
+
+ materia = forms.ModelChoiceField(required=False,
+ widget=forms.HiddenInput(),
+ queryset=MateriaLegislativa.objects.all())
class Meta:
model = AudienciaPublica
@@ -36,7 +39,7 @@ class AudienciaForm(forms.ModelForm):
'observacao', 'audiencia_cancelada', 'url_audio',
'url_video', 'upload_pauta', 'upload_ata',
'upload_anexo', 'tipo_materia', 'numero_materia',
- 'ano_materia']
+ 'ano_materia', 'materia']
def __init__(self, **kwargs):
@@ -59,17 +62,38 @@ class AudienciaForm(forms.ModelForm):
if not self.is_valid():
return cleaned_data
- try:
- materia = MateriaLegislativa.objects.get(
- numero=self.cleaned_data['numero_materia'],
- ano=self.cleaned_data['ano_materia'],
- tipo=self.cleaned_data['tipo_materia'])
- except ObjectDoesNotExist:
- msg = _('A matéria a ser inclusa não existe no cadastro'
- ' de matérias legislativas.')
- raise ValidationError(msg)
+ materia = cleaned_data['numero_materia']
+ ano_materia = cleaned_data['ano_materia']
+ tipo_materia = cleaned_data['tipo_materia']
+
+ if materia and ano_materia and tipo_materia:
+ try:
+ materia = MateriaLegislativa.objects.get(
+ numero=materia,
+ ano=ano_materia,
+ tipo=tipo_materia)
+ except ObjectDoesNotExist:
+ msg = _('A matéria %s nº %s/%s não existe no cadastro'
+ ' de matérias legislativas.' % (tipo_materia, materia, ano_materia))
+ raise ValidationError(msg)
+ else:
+ cleaned_data['materia'] = materia
+
else:
- cleaned_data['materia'] = materia
+ campos = [materia, tipo_materia, ano_materia]
+ if campos.count(None) + campos.count('') < len(campos):
+ msg = _('Preencha todos os campos relacionados à Matéria Legislativa')
+ raise ValidationError(msg)
+
+ if not cleaned_data['numero']:
+
+ ultima_audiencia = AudienciaPublica.objects.all().order_by('numero').last()
+ if ultima_audiencia:
+ cleaned_data['numero'] = ultima_audiencia.numero + 1
+ else:
+ cleaned_data['numero'] = 1
+
+
if self.cleaned_data['hora_inicio'] and self.cleaned_data['hora_fim']:
if (self.cleaned_data['hora_fim'] <
@@ -77,9 +101,4 @@ class AudienciaForm(forms.ModelForm):
msg = _('A hora de fim não pode ser anterior a hora de ínicio')
raise ValidationError(msg)
- return self.cleaned_data
-
- @transaction.atomic()
- def save(self, commit=True):
- audiencia = super(AudienciaForm, self).save(commit)
- return audiencia
\ No newline at end of file
+ return cleaned_data
diff --git a/sapl/audiencia/migrations/0005_auto_20180806_1236.py b/sapl/audiencia/migrations/0005_auto_20180806_1236.py
new file mode 100644
index 000000000..9601a233d
--- /dev/null
+++ b/sapl/audiencia/migrations/0005_auto_20180806_1236.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.13 on 2018-08-06 15:36
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('audiencia', '0004_auto_20180305_1006'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='audienciapublica',
+ name='hora_fim',
+ field=models.CharField(blank=True, max_length=5, null=True, verbose_name='Horário Fim(hh:mm)'),
+ ),
+ ]
diff --git a/sapl/audiencia/migrations/0006_auto_20180808_0856.py b/sapl/audiencia/migrations/0006_auto_20180808_0856.py
new file mode 100644
index 000000000..3809f66cf
--- /dev/null
+++ b/sapl/audiencia/migrations/0006_auto_20180808_0856.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.13 on 2018-08-08 11:56
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('audiencia', '0005_auto_20180806_1236'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='audienciapublica',
+ name='hora_fim',
+ field=models.CharField(blank=True, max_length=5, verbose_name='Horário Fim(hh:mm)'),
+ ),
+ ]
diff --git a/sapl/audiencia/models.py b/sapl/audiencia/models.py
index 96ad8272a..477a442d9 100644
--- a/sapl/audiencia/models.py
+++ b/sapl/audiencia/models.py
@@ -71,7 +71,7 @@ class AudienciaPublica(models.Model):
hora_inicio = models.CharField(
max_length=5, verbose_name=_('Horário Início(hh:mm)'))
hora_fim = models.CharField(
- max_length=5, verbose_name=_('Horário Fim(hh:mm)'))
+ max_length=5, blank=True, verbose_name=_('Horário Fim(hh:mm)'))
observacao = models.TextField(
max_length=500, blank=True, verbose_name=_('Observação'))
audiencia_cancelada = models.BooleanField(
diff --git a/sapl/audiencia/tests/test_audiencia.py b/sapl/audiencia/tests/test_audiencia.py
index 710d70dff..5640b3d0d 100644
--- a/sapl/audiencia/tests/test_audiencia.py
+++ b/sapl/audiencia/tests/test_audiencia.py
@@ -3,6 +3,8 @@ from django.utils.translation import ugettext as _
from model_mommy import mommy
from sapl.audiencia import forms
+from sapl.audiencia.models import TipoAudienciaPublica
+from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa
@pytest.mark.django_db(transaction=False)
def test_valida_campos_obrigatorios_audiencia_form():
@@ -15,11 +17,25 @@ def test_valida_campos_obrigatorios_audiencia_form():
assert errors['nome'] == [_('Este campo é obrigatório.')]
assert errors['tema'] == [_('Este campo é obrigatório.')]
assert errors['tipo'] == [_('Este campo é obrigatório.')]
- assert errors['tipo_materia'] == [_('Este campo é obrigatório.')]
- assert errors['numero_materia'] == [_('Este campo é obrigatório.')]
- assert errors['ano_materia'] == [_('Este campo é obrigatório.')]
assert errors['data'] == [_('Este campo é obrigatório.')]
assert errors['hora_inicio'] == [_('Este campo é obrigatório.')]
- assert errors['hora_fim'] == [_('Este campo é obrigatório.')]
- assert len(errors) == 9
+ assert len(errors) == 5
+
+
+@pytest.mark.django_db(transaction=False)
+def test_audiencia_form_hora_invalida():
+ tipo_materia = mommy.make(TipoMateriaLegislativa)
+
+ tipo = mommy.make(TipoAudienciaPublica)
+
+ form = forms.AudienciaForm(data={'nome': 'Nome da Audiencia',
+ 'tema': 'Tema da Audiencia',
+ 'tipo': tipo,
+ 'data': '2016-10-01',
+ 'hora_inicio': '10:00',
+ 'hora_fim': '9:00',
+ })
+ assert not form.is_valid()
+
+
\ No newline at end of file
diff --git a/sapl/audiencia/views.py b/sapl/audiencia/views.py
index 2c16b0919..bdfc6993c 100644
--- a/sapl/audiencia/views.py
+++ b/sapl/audiencia/views.py
@@ -6,15 +6,17 @@ from sapl.crud.base import RP_DETAIL, RP_LIST, Crud
from .forms import AudienciaForm
from .models import AudienciaPublica
+
def index(request):
return HttpResponse("Audiência Pública")
+
class AudienciaCrud(Crud):
model = AudienciaPublica
public = [RP_LIST, RP_DETAIL, ]
class BaseMixin(Crud.BaseMixin):
- list_field_names = ['materia', 'tipo', 'numero', 'nome',
+ list_field_names = ['numero', 'nome', 'tipo', 'materia',
'data']
ordering = 'nome', 'numero', 'tipo', 'data'
diff --git a/sapl/base/forms.py b/sapl/base/forms.py
index de6b50403..b71d161da 100644
--- a/sapl/base/forms.py
+++ b/sapl/base/forms.py
@@ -45,7 +45,7 @@ STATUS_USER_CHOICE = [
def get_roles():
roles = [(g.id, g.name) for g in Group.objects.all().order_by('name')
- if g.name != 'Votante' and g.name != 'Autor']
+ if g.name != 'Votante']
return roles
@@ -62,8 +62,6 @@ class UsuarioCreateForm(ModelForm):
user_active = forms.ChoiceField(required=False, choices=YES_NO_CHOICES,
label="Usuário ativo?", initial='True')
- #ROLES = [(g.id, g.name) for g in Group.objects.all().order_by('name')]
-
roles = forms.MultipleChoiceField(
required=True, widget=forms.CheckboxSelectMultiple(), choices=get_roles)
@@ -673,6 +671,11 @@ class RelatorioMateriasTramitacaoilterSet(django_filters.FilterSet):
label='Ano da Matéria',
choices=RANGE_ANOS)
+ @property
+ def qs(self):
+ parent = super(RelatorioMateriasTramitacaoilterSet, self).qs
+ return parent.distinct().order_by('-ano', 'tipo', '-numero')
+
class Meta:
model = MateriaLegislativa
fields = ['ano', 'tipo', 'tramitacao__unidade_tramitacao_local',
@@ -738,7 +741,7 @@ class RelatorioMateriasPorAutorFilterSet(django_filters.FilterSet):
@property
def qs(self):
parent = super(RelatorioMateriasPorAutorFilterSet, self).qs
- return parent.distinct().order_by('-ano', '-numero')
+ return parent.distinct().filter(autoria__primeiro_autor=True).order_by('autoria__autor', '-autoria__primeiro_autor', 'tipo', '-ano', '-numero')
class Meta:
model = MateriaLegislativa
diff --git a/sapl/base/migrations/0018_auto_20180801_1652.py b/sapl/base/migrations/0018_auto_20180801_1652.py
new file mode 100644
index 000000000..d2f758105
--- /dev/null
+++ b/sapl/base/migrations/0018_auto_20180801_1652.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.13 on 2018-08-01 19:52
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('base', '0017_appconfig_cronometro_consideracoes'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='autor',
+ name='nome',
+ field=models.CharField(blank=True, max_length=120, verbose_name='Nome do Autor'),
+ ),
+ ]
diff --git a/sapl/base/migrations/0019_auto_20180815_1025.py b/sapl/base/migrations/0019_auto_20180815_1025.py
new file mode 100644
index 000000000..e96759e52
--- /dev/null
+++ b/sapl/base/migrations/0019_auto_20180815_1025.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.13 on 2018-08-15 13:25
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('base', '0018_auto_20180801_1652'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='autor',
+ name='tipo',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='base.TipoAutor', verbose_name='Tipo do Autor'),
+ ),
+ ]
diff --git a/sapl/base/models.py b/sapl/base/models.py
index bbc03b6c7..df8ae7c3f 100644
--- a/sapl/base/models.py
+++ b/sapl/base/models.py
@@ -5,6 +5,7 @@ from django.db import models
from django.db.models.signals import post_migrate
from django.db.utils import DEFAULT_DB_ALIAS
from django.utils.translation import ugettext_lazy as _
+
from sapl.utils import (LISTA_DE_UFS, YES_NO_CHOICES,
get_settings_auth_user_model, models_with_gr_for_model)
@@ -178,7 +179,8 @@ class Autor(models.Model):
on_delete=models.SET_NULL,
null=True)
- tipo = models.ForeignKey(TipoAutor, verbose_name=_('Tipo do Autor'))
+ tipo = models.ForeignKey(TipoAutor, verbose_name=_('Tipo do Autor'),
+ on_delete=models.PROTECT)
content_type = models.ForeignKey(
ContentType,
@@ -188,7 +190,7 @@ class Autor(models.Model):
autor_related = GenericForeignKey('content_type', 'object_id')
nome = models.CharField(
- max_length=60, blank=True, verbose_name=_('Nome do Autor'))
+ max_length=120, blank=True, verbose_name=_('Nome do Autor'))
cargo = models.CharField(max_length=50, blank=True)
@@ -199,23 +201,17 @@ class Autor(models.Model):
ordering = ('nome',)
def __str__(self):
-
if self.autor_related:
return str(self.autor_related)
else:
- if str(self.cargo):
- return _('%(nome)s - %(cargo)s') % {
- 'nome': self.nome, 'cargo': self.cargo}
- else:
- return str(self.nome)
- """if str(self.tipo) == 'Parlamentar' and self.parlamentar:
- return self.parlamentar.nome_parlamentar
- elif str(self.tipo) == 'Comissao' and self.comissao:
- return str(self.comissao)
- elif str(self.tipo) == 'Partido' and self.partido:
- return str(self.partido)
- else:
- """
+ if self.nome:
+ if self.cargo:
+ return '{} - {}'.format(self.nome, self.cargo)
+ else:
+ return str(self.nome)
+ if self.user:
+ return str(self.user.username)
+ return '?'
def cria_models_tipo_autor(app_config=None, verbosity=2, interactive=True,
diff --git a/sapl/base/templatetags/common_tags.py b/sapl/base/templatetags/common_tags.py
index 73d71faa8..71f63e130 100644
--- a/sapl/base/templatetags/common_tags.py
+++ b/sapl/base/templatetags/common_tags.py
@@ -44,6 +44,15 @@ def split(value, arg):
return value.split(arg)
+@register.filter
+def to_str(arg):
+ return str(arg)
+
+@register.filter
+def get_last_item_from_list(list,arg):
+ return list[arg]
+
+
@register.filter
def sort_by_keys(value, key):
transformed = []
@@ -63,6 +72,16 @@ def sort_by_keys(value, key):
return transformed
+@register.filter
+def paginacao_limite_inferior(pagina):
+ return (int(pagina) - 1) * 10
+
+
+@register.filter
+def paginacao_limite_superior(pagina):
+ return int(pagina) * 10
+
+
@register.filter
def lookup(d, key):
return d[key] if key in d else []
diff --git a/sapl/base/views.py b/sapl/base/views.py
index b41492f4e..870fbbaa5 100644
--- a/sapl/base/views.py
+++ b/sapl/base/views.py
@@ -393,7 +393,7 @@ class RelatorioMateriasTramitacaoView(FilterView):
context = super(RelatorioMateriasTramitacaoView,
self).get_context_data(**kwargs)
- context['title'] = _('Matérias por Ano, Autor e Tipo')
+ context['title'] = _('Matérias em Tramitação')
qs = context['object_list']
qs = qs.filter(em_tramitacao=True)
diff --git a/sapl/comissoes/forms.py b/sapl/comissoes/forms.py
index db1822e4a..fc81b3510 100644
--- a/sapl/comissoes/forms.py
+++ b/sapl/comissoes/forms.py
@@ -242,7 +242,7 @@ class ComissaoForm(forms.ModelForm):
if not self.is_valid():
return self.cleaned_data
- if len(self.cleaned_data['nome']) > 50:
+ if len(self.cleaned_data['nome']) > 100:
msg = _('Nome da Comissão deve ter no máximo 50 caracteres.')
raise ValidationError(msg)
if self.cleaned_data['data_extincao']:
diff --git a/sapl/compilacao/models.py b/sapl/compilacao/models.py
index f677cf97a..28b963038 100644
--- a/sapl/compilacao/models.py
+++ b/sapl/compilacao/models.py
@@ -69,8 +69,15 @@ class BaseModel(models.Model):
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None, clean=True):
- if clean:
+ # método clean não pode ser chamado no caso do save que está sendo
+ # executado é o save de revision_pre_delete_signal
+ import inspect
+ funcs = list(filter(lambda x: x == 'revision_pre_delete_signal',
+ map(lambda x: x[3], inspect.stack())))
+
+ if clean and not funcs:
self.clean()
+
return models.Model.save(
self,
force_insert=force_insert,
@@ -1458,7 +1465,7 @@ class Dispositivo(BaseModel, TimestampedMixin):
tipo_dispositivo_id=self.tipo_dispositivo.pk))
else: # contagem continua restrita a articulacao
- proxima_articulacao = self.get_proximo_nivel_zero()
+ proxima_articulacao = self.select_next_root()
if proxima_articulacao is None:
irmaos = list(Dispositivo.objects.filter(
@@ -1550,25 +1557,15 @@ class Dispositivo(BaseModel, TimestampedMixin):
irmao.clean()
irmao.save()
- def get_proximo_nivel_zero(self):
- proxima_articulacao = Dispositivo.objects.order_by('ordem').filter(
- ordem__gt=self.ordem,
- nivel=0,
- ta_id=self.ta_id).first()
- return proxima_articulacao
-
- def get_nivel_zero_anterior(self):
- anterior_articulacao = Dispositivo.objects.order_by('ordem').filter(
- ordem__lt=self.ordem,
- nivel=0,
- ta_id=self.ta_id).last()
- return anterior_articulacao
-
- def get_niveis_zero(self):
- niveis_zero = Dispositivo.objects.order_by('ordem').filter(
- nivel=0,
- ta_id=self.ta_id)
- return niveis_zero
+ def select_roots(self):
+ return Dispositivo.objects.order_by(
+ 'ordem').filter(nivel=0, ta_id=self.ta_id)
+
+ def select_next_root(self):
+ return self.select_roots().filter(ordem__gt=self.ordem).first()
+
+ def select_prev_root(self):
+ return self.select_roots().filter(ordem__lt=self.ordem).last()
# metodo obsoleto, foi acrescentado o campo auto_inserido no modelo
def is_relative_auto_insert__obsoleto(self, perfil_pk=None):
diff --git a/sapl/compilacao/views.py b/sapl/compilacao/views.py
index a68c456a1..dac910cc2 100644
--- a/sapl/compilacao/views.py
+++ b/sapl/compilacao/views.py
@@ -15,7 +15,7 @@ from django.db import connection, transaction
from django.db.models import Q
from django.db.utils import IntegrityError
from django.http.response import (HttpResponse, HttpResponseRedirect,
- JsonResponse)
+ JsonResponse, Http404)
from django.shortcuts import get_object_or_404, redirect
from django.utils.dateparse import parse_date
from django.utils.encoding import force_text
@@ -51,7 +51,6 @@ from sapl.compilacao.utils import (DISPOSITIVO_SELECT_RELATED,
from sapl.crud.base import Crud, CrudAux, CrudListView, make_pagination
from sapl.settings import BASE_DIR
-
TipoNotaCrud = CrudAux.build(TipoNota, 'tipo_nota')
TipoVideCrud = CrudAux.build(TipoVide, 'tipo_vide')
TipoPublicacaoCrud = CrudAux.build(TipoPublicacao, 'tipo_publicacao')
@@ -104,7 +103,7 @@ class IntegracaoTaView(TemplateView):
) % self.model._meta.verbose_name_plural)
return redirect('/')
- def get(self, request, *args, **kwargs):
+ def get(self, request, *args, **kwargs):
try:
if settings.DEBUG or not TipoDispositivo.objects.exists():
@@ -211,8 +210,12 @@ class CompMixin(PermissionRequiredMixin):
@property
def ta(self):
- ta = TextoArticulado.objects.get(
- pk=self.kwargs.get('ta_id', self.kwargs.get('pk', 0)))
+ try:
+ ta = TextoArticulado.objects.get(
+ pk=self.kwargs.get('ta_id', self.kwargs.get('pk', 0)))
+ except TextoArticulado.DoesNotExist:
+ raise Http404()
+
return ta
def get_context_data(self, **kwargs):
@@ -1237,9 +1240,9 @@ class TextEditView(CompMixin, TemplateView):
'filhos': [],
'alts': [],
'pai': None,
- 'st': None, # dispositivo substituido
- 'sq': None, # dispositivo subsequente
- 'da': None, # dispositivo atualizador
+ 'st': None, # dispositivo substituido
+ 'sq': None, # dispositivo subsequente
+ 'da': None, # dispositivo atualizador
'td': tds[d.tipo_dispositivo_id], # tipo do dispositivo
'na': self.nota_alteracao(d, lista_ta_publicado)\
if d.ta_id == ta_id else None
@@ -1388,7 +1391,7 @@ class ActionsCommonsMixin:
pkfilho = dp.pk
dp = dp.dispositivo_pai
- proxima_articulacao = dp.get_proximo_nivel_zero()
+ proxima_articulacao = dp.select_next_root()
if proxima_articulacao is not None:
parents = Dispositivo.objects.filter(
@@ -1480,30 +1483,36 @@ class ActionDeleteDispositivoMixin(ActionsCommonsMixin):
).first()
data = {}
- if base_anterior:
- data = self.get_json_for_refresh(base_anterior)
+ if not base_anterior or base == base.get_raiz():
+ base_anterior = base.select_prev_root()
+ if not base_anterior:
+ base_anterior = base
+ data = self.get_json_for_refresh(base_anterior)
+
+ if base == base_anterior:
+ data['pk'] = base.pk
+ self.set_message(data, 'danger', _(
+ 'Base Inicial não pode ser removida!'), modal=True)
else:
- base_anterior = base.get_nivel_zero_anterior()
- data = self.get_json_for_refresh(base_anterior)
-
- data['pai'] = [base.get_raiz().pk]
+ if base != base.get_raiz():
+ data['pai'] = [base.get_raiz().pk]
- if ta_base.id != int(self.kwargs['ta_id']):
- data['pai'] = [base.dispositivo_atualizador.pk]
- data['pk'] = base.dispositivo_atualizador.pk
+ if ta_base.id != int(self.kwargs['ta_id']):
+ data['pai'] = [base.dispositivo_atualizador.pk]
+ data['pk'] = base.dispositivo_atualizador.pk
- try:
- with transaction.atomic():
- message = str(self.remover_dispositivo(base, bloco))
- if message:
- self.set_message(data, 'warning', message, modal=True)
- else:
- self.set_message(data, 'success', _(
- 'Exclusão efetuada com sucesso!'), modal=True)
- ta_base.reagrupar_ordem_de_dispositivos()
- except Exception as e:
- data['pk'] = self.kwargs['dispositivo_id']
- self.set_message(data, 'danger', str(e), modal=True)
+ try:
+ with transaction.atomic():
+ message = str(self.remover_dispositivo(base, bloco))
+ if message:
+ self.set_message(data, 'warning', message, modal=True)
+ else:
+ self.set_message(data, 'success', _(
+ 'Exclusão efetuada com sucesso!'), modal=True)
+ ta_base.reagrupar_ordem_de_dispositivos()
+ except Exception as e:
+ data['pk'] = self.kwargs['dispositivo_id']
+ self.set_message(data, 'danger', str(e), modal=True)
return data
@@ -1536,7 +1545,7 @@ class ActionDeleteDispositivoMixin(ActionsCommonsMixin):
print(e)
base.delete()
else:
- proxima_articulacao = base.get_proximo_nivel_zero()
+ proxima_articulacao = base.select_next_root()
if not bloco:
# tranferir filhos para primeiro pai possível acima da base
# de exclusão
@@ -1694,7 +1703,7 @@ class ActionDeleteDispositivoMixin(ActionsCommonsMixin):
base_adicao = {}
- nivel_zero_anterior = base.get_nivel_zero_anterior()
+ nivel_zero_anterior = base.select_prev_root()
if nivel_zero_anterior:
nivel_zero_anterior = nivel_zero_anterior.ordem
else:
@@ -2374,7 +2383,7 @@ class ActionDispositivoCreateMixin(ActionsCommonsMixin):
if dp.nivel == 0:
- proxima_articulacao = dp.get_proximo_nivel_zero()
+ proxima_articulacao = dp.select_next_root()
if not proxima_articulacao:
filhos_continuos = list(Dispositivo.objects.filter(
diff --git a/sapl/crud/base.py b/sapl/crud/base.py
index 8bb8c3794..739fc4f37 100644
--- a/sapl/crud/base.py
+++ b/sapl/crud/base.py
@@ -851,7 +851,9 @@ class CrudDeleteView(PermissionRequiredContainerCrudMixin,
é referenciado por outros registros:
\
'
for i in err.protected_objects:
- error_msg += '- ' + i.__str__() + '
'
+ error_msg += '- {} - {}
'.format(
+ i._meta.verbose_name, i
+ )
error_msg += '
'
messages.add_message(request,
diff --git a/sapl/legacy/management/commands/migracao_25_31.py b/sapl/legacy/management/commands/migracao_25_31.py
index e3864ad0f..ba9a80224 100644
--- a/sapl/legacy/management/commands/migracao_25_31.py
+++ b/sapl/legacy/management/commands/migracao_25_31.py
@@ -7,5 +7,14 @@ class Command(BaseCommand):
help = 'Migração de dados do SAPL 2.5 para o SAPL 3.1'
+ def add_arguments(self, parser):
+ parser.add_argument(
+ '-a',
+ action='store_true',
+ default=False,
+ dest='apagar_do_legado',
+ help='Apagar entradas migradas do legado',
+ )
+
def handle(self, *args, **options):
- migrar(interativo=False)
+ migrar(apagar_do_legado=options['apagar_do_legado'])
diff --git a/sapl/legacy/migracao.py b/sapl/legacy/migracao.py
index dd54a103e..c4c183b84 100644
--- a/sapl/legacy/migracao.py
+++ b/sapl/legacy/migracao.py
@@ -18,7 +18,7 @@ def adornar_msg(msg):
return '\n{1}\n{0}\n{1}'.format(msg, '#' * len(msg))
-def migrar(interativo=False):
+def migrar(apagar_do_legado=False):
if TAG_MARCO in REPO.tags:
info('A migração já está feita.')
return
@@ -26,7 +26,7 @@ def migrar(interativo=False):
'Antes de migrar '
'é necessário fazer a exportação de documentos do zope')
management.call_command('migrate')
- migrar_dados()
+ migrar_dados(apagar_do_legado)
migrar_usuarios(REPO.working_dir)
migrar_documentos(REPO)
gravar_marco()
diff --git a/sapl/legacy/migracao_dados.py b/sapl/legacy/migracao_dados.py
index 4c953d5f5..24bc9948d 100644
--- a/sapl/legacy/migracao_dados.py
+++ b/sapl/legacy/migracao_dados.py
@@ -8,7 +8,6 @@ from datetime import date
from functools import lru_cache, partial
from itertools import groupby
from operator import xor
-from subprocess import PIPE, call
import git
import pkg_resources
@@ -31,12 +30,10 @@ from unipath import Path
from sapl.base.models import AppConfig as AppConf
from sapl.base.models import Autor, TipoAutor, cria_models_tipo_autor
from sapl.comissoes.models import Comissao, Composicao, Participacao, Reuniao
-from sapl.legacy import scripts
from sapl.legacy.models import NormaJuridica as OldNormaJuridica
from sapl.legacy.models import TipoNumeracaoProtocolo
-from sapl.legacy_migration_settings import (DATABASES, DIR_DADOS_MIGRACAO,
- DIR_REPO, NOME_BANCO_LEGADO,
- PROJECT_DIR)
+from sapl.legacy_migration_settings import (DIR_DADOS_MIGRACAO, DIR_REPO,
+ NOME_BANCO_LEGADO)
from sapl.materia.models import (AcompanhamentoMateria, MateriaLegislativa,
Proposicao, StatusTramitacao, TipoDocumento,
TipoMateriaLegislativa, TipoProposicao,
@@ -141,10 +138,6 @@ models_novos_para_antigos = {
for model in field_renames}
models_novos_para_antigos[Composicao] = models_novos_para_antigos[Participacao]
-content_types = {model: ContentType.objects.get(
- app_label=model._meta.app_label, model=model._meta.model_name)
- for model in field_renames}
-
campos_novos_para_antigos = {
model._meta.get_field(nome_novo): nome_antigo
for model, renames in field_renames.items()
@@ -226,10 +219,6 @@ class ForeignKeyFaltando(ObjectDoesNotExist):
'Uma FK aponta para um registro inexistente'
def __init__(self, field, valor, old):
- if (field.related_model.__name__ == 'Comissao'
- and old.__class__.__name__ == 'ReuniaoComissao'
- and valor == 1):
- __import__('pdb').set_trace()
self.field = field
self.valor = valor
self.old = old
@@ -546,7 +535,6 @@ PROPAGACOES_DE_EXCLUSAO = [
('sessao_plenaria', 'ordem_dia', 'cod_sessao_plen'),
('sessao_plenaria', 'expediente_materia', 'cod_sessao_plen'),
('sessao_plenaria', 'expediente_sessao_plenaria', 'cod_sessao_plen'),
- ('registro_votacao', 'registro_votacao_parlamentar', 'cod_votacao'),
# as consultas no código do sapl 2.5
# votacao_ordem_dia_obter_zsql e votacao_expediente_materia_obter_zsql
# indicam que os registros de votação de matérias excluídas não são
@@ -562,30 +550,38 @@ PROPAGACOES_DE_EXCLUSAO = [
('materia_legislativa', 'anexada', 'cod_materia_anexada'),
('materia_legislativa', 'documento_acessorio', 'cod_materia'),
('materia_legislativa', 'numeracao', 'cod_materia'),
+ ('materia_legislativa', 'expediente_materia', 'cod_materia'),
# norma
('norma_juridica', 'vinculo_norma_juridica', 'cod_norma_referente'),
('norma_juridica', 'vinculo_norma_juridica', 'cod_norma_referida'),
+ ('norma_juridica', 'legislacao_citada', 'cod_norma'),
# documento administrativo
('documento_administrativo', 'tramitacao_administrativo', 'cod_documento'),
]
+PROPAGACOES_DE_EXCLUSAO_REGISTROS_VOTACAO = [
+ ('registro_votacao', 'registro_votacao_parlamentar', 'cod_votacao'),
+]
-def propaga_exclusoes():
- for tabela_pai, tabela_filha, fk in PROPAGACOES_DE_EXCLUSAO:
+
+def propaga_exclusoes(propagacoes):
+ for tabela_pai, tabela_filha, fk in propagacoes:
[pk_pai] = get_pk_legado(tabela_pai)
- exec_legado('''
+ sql = '''
update {} set ind_excluido = 1 where {} not in (
select {} from {} where ind_excluido != 1)
- '''.format(tabela_filha, fk, pk_pai, tabela_pai))
+ '''.format(tabela_filha, fk, pk_pai, tabela_pai)
+ exec_legado(sql)
def uniformiza_banco():
exec_legado('SET SESSION sql_mode = "";') # desliga checagens do mysql
- propaga_exclusoes()
+ propaga_exclusoes(PROPAGACOES_DE_EXCLUSAO)
checa_registros_votacao_ambiguos_e_remove_nao_usados()
+ propaga_exclusoes(PROPAGACOES_DE_EXCLUSAO_REGISTROS_VOTACAO)
garante_coluna_no_legado('proposicao',
'num_proposicao int(11) NULL')
@@ -808,7 +804,7 @@ def roda_comando_shell(cmd):
assert res == 0, 'O comando falhou: {}'.format(cmd)
-def migrar_dados():
+def migrar_dados(apagar_do_legado=False):
try:
ocorrencias.clear()
ocorrencias.default_factory = list
@@ -840,7 +836,7 @@ def migrar_dados():
fill_vinculo_norma_juridica()
fill_dados_basicos()
info('Começando migração: ...')
- migrar_todos_os_models()
+ migrar_todos_os_models(apagar_do_legado)
except Exception as e:
ocorrencias['traceback'] = str(traceback.format_exc())
raise e
@@ -874,7 +870,6 @@ def get_models_a_migrar():
if model in field_renames]
# retira reuniões quando não existe na base legada
# (só existe no sapl 3.0)
- tabelas_legado = [t for (t,) in exec_legado('show tables')]
if not EXISTE_REUNIAO_NO_LEGADO:
models.remove(Reuniao)
# Devido à referência TipoProposicao.tipo_conteudo_related
@@ -890,12 +885,12 @@ def get_models_a_migrar():
return models
-def migrar_todos_os_models():
+def migrar_todos_os_models(apagar_do_legado):
for model in get_models_a_migrar():
- migrar_model(model)
+ migrar_model(model, apagar_do_legado)
-def migrar_model(model):
+def migrar_model(model, apagar_do_legado):
print('Migrando %s...' % model.__name__)
model_legado, tabela_legado, campos_pk_legado = \
@@ -949,12 +944,13 @@ def migrar_model(model):
novos.append(new) # guarda para salvar
# acumula deleção do registro no legado
- sql_delete_legado += 'delete from {} where {};\n'.format(
- tabela_legado,
- ' and '.join(
- '{} = "{}"'.format(campo,
- getattr(old, campo))
- for campo in campos_pk_legado))
+ if apagar_do_legado:
+ sql_delete_legado += 'delete from {} where {};\n'.format(
+ tabela_legado,
+ ' and '.join(
+ '{} = "{}"'.format(campo,
+ getattr(old, campo))
+ for campo in campos_pk_legado))
# salva novos registros
with reversion.create_revision():
@@ -973,7 +969,7 @@ def migrar_model(model):
reinicia_sequence(model, ultima_pk_legado + 1)
# apaga registros migrados do legado
- if sql_delete_legado:
+ if apagar_do_legado and sql_delete_legado:
exec_legado(sql_delete_legado)
@@ -1156,7 +1152,9 @@ def adjust_tipoafastamento(new, old):
def set_generic_fk(new, campo_virtual, old):
- new.content_type = content_types[campo_virtual.related_model]
+ model = campo_virtual.related_model
+ new.content_type = ContentType.objects.get(
+ app_label=model._meta.app_label, model=model._meta.model_name)
new.object_id = get_fk_related(campo_virtual, old)
diff --git a/sapl/legacy/migracao_usuarios.py b/sapl/legacy/migracao_usuarios.py
index 51b3348c8..d767343a3 100644
--- a/sapl/legacy/migracao_usuarios.py
+++ b/sapl/legacy/migracao_usuarios.py
@@ -16,6 +16,7 @@ PERFIL_LEGADO_PARA_NOVO = {legado: Group.objects.get(name=novo)
('Operador Protocolo', 'Operador de Protocolo Administrativo'),
('Operador Sessao Plenaria', 'Operador de Sessão Plenária'),
('Parlamentar', 'Votante'),
+ ('Operador Painel', 'Operador de Painel Eletrônico'),
]
}
diff --git a/sapl/legacy/scripts/ressucita_dependencias.py b/sapl/legacy/scripts/ressucita_dependencias.py
new file mode 100644
index 000000000..793e51483
--- /dev/null
+++ b/sapl/legacy/scripts/ressucita_dependencias.py
@@ -0,0 +1,59 @@
+import yaml
+from unipath import Path
+
+from sapl.legacy.migracao_dados import DIR_REPO, exec_legado
+
+fks_legado = '''
+ autor cod_parlamentar parlamentar
+ autor tip_autor tipo_autor
+ autoria cod_autor autor
+ expediente_materia cod_materia materia_legislativa
+ ordem_dia cod_materia materia_legislativa
+ legislacao_citada cod_norma norma_juridica
+ oradores cod_parlamentar parlamentar
+ oradores_expediente cod_parlamentar parlamentar
+ ordem_dia_presenca cod_parlamentar parlamentar
+ protocolo cod_autor autor
+ registro_votacao tip_resultado_votacao tipo_resultado_votacao
+ registro_votacao_parlamentar cod_parlamentar parlamentar
+ registro_votacao_parlamentar cod_votacao registro_votacao
+ sessao_legislativa num_legislatura legislatura
+ sessao_plenaria_presenca cod_parlamentar parlamentar
+'''
+fks_legado = [l.split() for l in fks_legado.strip().splitlines()]
+fks_legado = {(o, c): t for (o, c, t) in fks_legado}
+
+
+def get_excluido(fk):
+ campo, valor, tabela_origem = [fk[k] for k in ('campo', 'valor', 'tabela')]
+ tabela_alvo = fks_legado[(tabela_origem, campo)]
+ sql = 'select ind_excluido, t.* from {} t where {} = {}'.format(
+ tabela_alvo, campo, valor)
+ res = list(exec_legado(sql))
+ return tabela_origem, campo, valor, tabela_alvo, res
+
+
+def get_dependencias_a_ressucitar():
+ ocorrencias = yaml.load(
+ Path(DIR_REPO.child('ocorrencias.yaml').read_file()))
+ fks = ocorrencias['fk']
+ excluidos = [get_excluido(fk) for fk in fks]
+ desexcluir, criar = [
+ set([(tabela_alvo, campo, valor)
+ for tabela_origem, campo, valor, tabela_alvo, res in excluidos
+ if condicao(res)])
+ for condicao in (
+ # o registro existe e ind_excluido == 1
+ lambda res: res and res[0][0] == 1,
+ # o registro não existe
+ lambda res: not res
+ )]
+ return desexcluir, criar
+
+
+def get_sqls_desexcluir_criar(desexcluir, criar):
+ sqls_desexcluir = [
+ 'update {} set ind_excluido = 0 where {} = {};'.format(
+ tabela_alvo, campo, valor)
+ for tabela_alvo, campo, valor in desexcluir]
+ return '\n'.join(sqls_desexcluir)
diff --git a/sapl/materia/forms.py b/sapl/materia/forms.py
index 04ee8d0e0..88db58f8b 100644
--- a/sapl/materia/forms.py
+++ b/sapl/materia/forms.py
@@ -675,6 +675,7 @@ class MateriaLegislativaFilterSet(django_filters.FilterSet):
widget=forms.HiddenInput())
ementa = django_filters.CharFilter(lookup_expr='icontains')
+ indexacao = django_filters.CharFilter(lookup_expr='icontains')
em_tramitacao = django_filters.ChoiceFilter(required=False,
label='Em tramitação',
@@ -751,7 +752,7 @@ class MateriaLegislativaFilterSet(django_filters.FilterSet):
[('em_tramitacao', 6),
('o', 6)])
row9 = to_row(
- [('materiaassunto__assunto', 12)])
+ [('materiaassunto__assunto', 6), ('indexacao', 6)])
row10 = to_row(
[('ementa', 12)])
@@ -1294,12 +1295,11 @@ class ProposicaoForm(forms.ModelForm):
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:
- max_size = str(MAX_DOC_UPLOAD_SIZE / (1024 * 1024))
- raise ValidationError(
+ if texto_original and texto_original.size > MAX_DOC_UPLOAD_SIZE:
+ max_size = str(MAX_DOC_UPLOAD_SIZE / (1024 * 1024))
+ raise ValidationError(
"Arquivo muito grande. ( > {0}MB )".format(max_size))
- return texto_original
+ return texto_original
def gerar_hash(self, inst, receber_recibo):
diff --git a/sapl/materia/views.py b/sapl/materia/views.py
index f2e3e76db..5f1b81ccf 100644
--- a/sapl/materia/views.py
+++ b/sapl/materia/views.py
@@ -426,6 +426,10 @@ class ProposicaoPendente(PermissionRequiredMixin, ListView):
def get_context_data(self, **kwargs):
context = super(ProposicaoPendente, self).get_context_data(**kwargs)
+ context['object_list'] = Proposicao.objects.filter(
+ data_envio__isnull=False,
+ data_recebimento__isnull=True,
+ data_devolucao__isnull=True)
paginator = context['paginator']
page_obj = context['page_obj']
context['AppConfig'] = sapl.base.models.AppConfig.objects.all().last()
@@ -434,6 +438,8 @@ class ProposicaoPendente(PermissionRequiredMixin, ListView):
context['NO_ENTRIES_MSG'] = 'Nenhuma proposição pendente.'
context['subnav_template_name'] = 'materia/subnav_prop.yaml'
+ qr = self.request.GET.copy()
+ context['filter_url'] = ('&o=' + qr['o']) if 'o' in qr.keys() else ''
return context
@@ -701,12 +707,12 @@ class ProposicaoCrud(Crud):
messages.success(request, _(
'Proposição enviada com sucesso.'))
try:
- Numero = MateriaLegislativa.objects.filter(tipo=p.tipo.tipo_conteudo_related,
+ numero = MateriaLegislativa.objects.filter(tipo=p.tipo.tipo_conteudo_related,
ano=p.ano).last().numero + 1
messages.success(request, _(
'%s : nº %s de %s
Atenção! Este número é apenas um provável '
'número que pode não corresponder com a realidade'
- % (p.tipo, Numero, p.ano)))
+ % (p.tipo, numero, p.ano)))
except ValueError:
pass
diff --git a/sapl/norma/forms.py b/sapl/norma/forms.py
index 610895e09..7d791207a 100644
--- a/sapl/norma/forms.py
+++ b/sapl/norma/forms.py
@@ -14,7 +14,7 @@ from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa
from sapl.settings import MAX_DOC_UPLOAD_SIZE
from sapl.utils import RANGE_ANOS, RangeWidgetOverride
-from .models import (AssuntoNorma, NormaJuridica, NormaRelacionada,
+from .models import (AnexoNormaJuridica, AssuntoNorma, NormaJuridica, NormaRelacionada,
TipoNormaJuridica)
@@ -120,6 +120,7 @@ class NormaJuridicaForm(ModelForm):
'assuntos']
widgets = {'assuntos': widgets.CheckboxSelectMultiple}
+
def clean(self):
cleaned_data = super(NormaJuridicaForm, self).clean()
@@ -163,11 +164,10 @@ class NormaJuridicaForm(ModelForm):
def clean_texto_integral(self):
texto_integral = self.cleaned_data.get('texto_integral', False)
- if texto_integral:
- if texto_integral.size > MAX_DOC_UPLOAD_SIZE:
- max_size = str(MAX_DOC_UPLOAD_SIZE / (1024 * 1024))
- raise ValidationError(
- "Arquivo muito grande. ( > {0}MB )".format(max_size))
+ if texto_integral and texto_integral.size > MAX_DOC_UPLOAD_SIZE:
+ max_size = str(MAX_DOC_UPLOAD_SIZE / (1024 * 1024))
+ raise ValidationError(
+ "Arquivo muito grande. ( > {0}MB )".format(max_size))
return texto_integral
def save(self, commit=False):
@@ -175,9 +175,40 @@ class NormaJuridicaForm(ModelForm):
norma.timestamp = timezone.now()
norma.materia = self.cleaned_data['materia']
norma = super(NormaJuridicaForm, self).save(commit=True)
+
return norma
+class AnexoNormaJuridicaForm(ModelForm):
+ class Meta:
+ model = AnexoNormaJuridica
+ fields = ['norma', 'anexo_arquivo']
+ widgets = {
+ 'norma': forms.HiddenInput(),
+ }
+
+ def clean(self):
+ cleaned_data = super(AnexoNormaJuridicaForm, self).clean()
+ if not self.is_valid():
+ return cleaned_data
+ anexo_arquivo = self.cleaned_data.get('anexo_arquivo', False)
+ if anexo_arquivo and anexo_arquivo.size > MAX_DOC_UPLOAD_SIZE:
+ max_size = str(MAX_DOC_UPLOAD_SIZE / (1024 * 1024))
+ raise ValidationError(
+ "Arquivo muito grande. ( > {0}MB )".format(max_size))
+ return cleaned_data
+
+ def save(self, commit=False):
+ anexo = self.instance
+ anexo.ano = self.cleaned_data['norma'].ano
+ anexo = super(AnexoNormaJuridicaForm, self).save(commit=True)
+ anexo.norma = self.cleaned_data['norma']
+ anexo.anexo_arquivo = self.cleaned_data['anexo_arquivo']
+ anexo.save()
+ return anexo
+
+
+
class NormaRelacionadaForm(ModelForm):
tipo = forms.ModelChoiceField(
diff --git a/sapl/norma/migrations/0012_anexonormajuridica.py b/sapl/norma/migrations/0012_anexonormajuridica.py
new file mode 100644
index 000000000..2ba9ebdca
--- /dev/null
+++ b/sapl/norma/migrations/0012_anexonormajuridica.py
@@ -0,0 +1,31 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.13 on 2018-08-06 19:48
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+import sapl.norma.models
+import sapl.utils
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('norma', '0011_auto_20180220_1859'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='AnexoNormaJuridica',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('anexo_arquivo', models.FileField(blank=True, null=True, upload_to=sapl.norma.models.norma_upload_path, validators=[sapl.utils.restringe_tipos_de_arquivo_txt], verbose_name='Arquivo Anexo')),
+ ('ano', models.PositiveSmallIntegerField(choices=[(2018, 2018), (2017, 2017), (2016, 2016), (2015, 2015), (2014, 2014), (2013, 2013), (2012, 2012), (2011, 2011), (2010, 2010), (2009, 2009), (2008, 2008), (2007, 2007), (2006, 2006), (2005, 2005), (2004, 2004), (2003, 2003), (2002, 2002), (2001, 2001), (2000, 2000), (1999, 1999), (1998, 1998), (1997, 1997), (1996, 1996), (1995, 1995), (1994, 1994), (1993, 1993), (1992, 1992), (1991, 1991), (1990, 1990), (1989, 1989), (1988, 1988), (1987, 1987), (1986, 1986), (1985, 1985), (1984, 1984), (1983, 1983), (1982, 1982), (1981, 1981), (1980, 1980), (1979, 1979), (1978, 1978), (1977, 1977), (1976, 1976), (1975, 1975), (1974, 1974), (1973, 1973), (1972, 1972), (1971, 1971), (1970, 1970), (1969, 1969), (1968, 1968), (1967, 1967), (1966, 1966), (1965, 1965), (1964, 1964), (1963, 1963), (1962, 1962), (1961, 1961), (1960, 1960), (1959, 1959), (1958, 1958), (1957, 1957), (1956, 1956), (1955, 1955), (1954, 1954), (1953, 1953), (1952, 1952), (1951, 1951), (1950, 1950), (1949, 1949), (1948, 1948), (1947, 1947), (1946, 1946), (1945, 1945), (1944, 1944), (1943, 1943), (1942, 1942), (1941, 1941), (1940, 1940), (1939, 1939), (1938, 1938), (1937, 1937), (1936, 1936), (1935, 1935), (1934, 1934), (1933, 1933), (1932, 1932), (1931, 1931), (1930, 1930), (1929, 1929), (1928, 1928), (1927, 1927), (1926, 1926), (1925, 1925), (1924, 1924), (1923, 1923), (1922, 1922), (1921, 1921), (1920, 1920), (1919, 1919), (1918, 1918), (1917, 1917), (1916, 1916), (1915, 1915), (1914, 1914), (1913, 1913), (1912, 1912), (1911, 1911), (1910, 1910), (1909, 1909), (1908, 1908), (1907, 1907), (1906, 1906), (1905, 1905), (1904, 1904), (1903, 1903), (1902, 1902), (1901, 1901), (1900, 1900), (1899, 1899), (1898, 1898), (1897, 1897), (1896, 1896), (1895, 1895), (1894, 1894), (1893, 1893), (1892, 1892), (1891, 1891), (1890, 1890)], verbose_name='Ano')),
+ ('norma', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='norma', to='norma.NormaJuridica', verbose_name='Norma Juridica')),
+ ],
+ options={
+ 'verbose_name_plural': 'Anexos da Norma Juridica',
+ 'verbose_name': 'Anexo da Norma Juridica',
+ },
+ ),
+ ]
diff --git a/sapl/norma/models.py b/sapl/norma/models.py
index b045ea1f9..ff7b44dca 100644
--- a/sapl/norma/models.py
+++ b/sapl/norma/models.py
@@ -141,9 +141,14 @@ class NormaJuridica(models.Model):
norma_relacionada=self.id)
return (principais, relacionadas)
+ def get_anexos_norma_juridica(self):
+ anexos = AnexoNormaJuridica.objects.filter(
+ norma=self.id)
+ return anexos
+
+
def __str__(self):
- return _('%(tipo)s nº %(numero)s de %(data)s') % {
- 'tipo': self.tipo,
+ return _('nº %(numero)s de %(data)s') % {
'numero': self.numero,
'data': defaultfilters.date(self.data, "d \d\e F \d\e Y")}
@@ -252,3 +257,28 @@ class NormaRelacionada(models.Model):
' - Relacionada: %(norma_relacionada)s') % {
'norma_principal': self.norma_principal,
'norma_relacionada': self.norma_relacionada}
+
+
+@reversion.register()
+class AnexoNormaJuridica(models.Model):
+ norma = models.ForeignKey(
+ NormaJuridica,
+ related_name='norma',
+ on_delete=models.PROTECT,
+ verbose_name=_('Norma Juridica'))
+ anexo_arquivo = models.FileField(
+ blank=True,
+ null=True,
+ upload_to=norma_upload_path,
+ verbose_name=_('Arquivo Anexo'),
+ validators=[restringe_tipos_de_arquivo_txt])
+ ano = models.PositiveSmallIntegerField(verbose_name=_('Ano'),
+ choices=RANGE_ANOS)
+
+ class Meta:
+ verbose_name = _('Anexo da Norma Juridica')
+ verbose_name_plural = _('Anexos da Norma Juridica')
+
+ def __str__(self):
+ return _('Anexo: %(anexo)s da norma %(norma)s') % {
+ 'anexo': self.anexo_arquivo, 'norma': self.norma}
diff --git a/sapl/norma/urls.py b/sapl/norma/urls.py
index 93081c4fc..d943f71e8 100644
--- a/sapl/norma/urls.py
+++ b/sapl/norma/urls.py
@@ -1,6 +1,6 @@
from django.conf.urls import include, url
-from sapl.norma.views import (AssuntoNormaCrud, NormaCrud, NormaPesquisaView,
+from sapl.norma.views import (AnexoNormaJuridicaCrud,AssuntoNormaCrud, NormaCrud, NormaPesquisaView,
NormaRelacionadaCrud, NormaTaView, TipoNormaCrud,
TipoVinculoNormaJuridicaCrud, recuperar_norma,
recuperar_numero_norma)
@@ -12,11 +12,11 @@ app_name = AppConfig.name
urlpatterns = [
url(r'^norma/', include(NormaCrud.get_urls() +
- NormaRelacionadaCrud.get_urls())),
+ NormaRelacionadaCrud.get_urls() +
+ AnexoNormaJuridicaCrud.get_urls())),
# Integração com Compilação
url(r'^norma/(?P[0-9]+)/ta$', NormaTaView.as_view(), name='norma_ta'),
-
url(r'^sistema/norma/tipo/', include(TipoNormaCrud.get_urls())),
url(r'^sistema/norma/assunto/', include(AssuntoNormaCrud.get_urls())),
url(r'^sistema/norma/vinculo/', include(
diff --git a/sapl/norma/views.py b/sapl/norma/views.py
index 9c98e783e..87b842802 100644
--- a/sapl/norma/views.py
+++ b/sapl/norma/views.py
@@ -1,4 +1,5 @@
+import re
import weasyprint
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.core.exceptions import ObjectDoesNotExist
@@ -18,9 +19,9 @@ from sapl.crud.base import (RP_DETAIL, RP_LIST, Crud, CrudAux,
MasterDetailCrud, make_pagination)
from sapl.utils import show_results_filter_set
-from .forms import (NormaFilterSet, NormaJuridicaForm,
+from .forms import (AnexoNormaJuridicaForm, NormaFilterSet, NormaJuridicaForm,
NormaPesquisaSimplesForm, NormaRelacionadaForm)
-from .models import (AssuntoNorma, NormaJuridica, NormaRelacionada,
+from .models import (AnexoNormaJuridica, AssuntoNorma, NormaJuridica, NormaRelacionada,
TipoNormaJuridica, TipoVinculoNormaJuridica)
# LegislacaoCitadaCrud = Crud.build(LegislacaoCitada, '')
@@ -71,7 +72,7 @@ class NormaPesquisaView(FilterView):
def get_queryset(self):
qs = super().get_queryset()
- qs.select_related('tipo', 'materia')
+ qs = qs.extra({'norma_i': "CAST(regexp_replace(numero,'[^0-9]','', 'g') AS INTEGER)", 'norma_letra': "regexp_replace(numero,'[^a-zA-Z]','', 'g')"}).order_by('-data', '-norma_i', '-norma_letra')
return qs
@@ -97,6 +98,39 @@ class NormaPesquisaView(FilterView):
return context
+class AnexoNormaJuridicaCrud(MasterDetailCrud):
+ model = AnexoNormaJuridica
+ parent_field = 'norma'
+ help_topic = 'anexonormajuridica'
+ public = [RP_LIST, RP_DETAIL]
+
+ class BaseMixin(MasterDetailCrud.BaseMixin):
+ list_field_names = ['id','anexo_arquivo']
+
+ class CreateView(MasterDetailCrud.CreateView):
+ form_class = AnexoNormaJuridicaForm
+ layout_key = 'AnexoNormaJuridica'
+
+ def get_initial(self):
+ initial = super(MasterDetailCrud.CreateView, self).get_initial()
+ initial['norma'] = NormaJuridica.objects.get(id=self.kwargs['pk'])
+ return initial
+
+ class UpdateView(MasterDetailCrud.UpdateView):
+ form_class = AnexoNormaJuridicaForm
+ layout_key = 'AnexoNormaJuridica'
+
+ def get_initial(self):
+ initial = super(UpdateView, self).get_initial()
+ initial['norma'] = self.object.norma
+ initial['anexo_arquivo'] = self.object.anexo_arquivo
+ initial['ano'] = self.object.ano
+ return initial
+
+ class DetailView(MasterDetailCrud.DetailView):
+ form_class = AnexoNormaJuridicaForm
+ layout_key = 'AnexoNormaJuridica'
+
class NormaTaView(IntegracaoTaView):
model = NormaJuridica
@@ -201,14 +235,12 @@ def recuperar_norma(request):
def recuperar_numero_norma(request):
tipo = TipoNormaJuridica.objects.get(pk=request.GET['tipo'])
ano = request.GET.get('ano', '')
-
param = {'tipo': tipo}
param['ano'] = ano if ano else timezone.now().year
- norma = NormaJuridica.objects.filter(**param).extra(
- {'numero_id': "CAST(numero as INTEGER)"}).order_by(
- 'tipo', 'ano','numero_id').values_list('numero', 'ano').last()
+ norma = NormaJuridica.objects.filter(**param).order_by(
+ 'tipo', 'ano', 'numero').values_list('numero', 'ano').last()
if norma:
- response = JsonResponse({'numero': int(norma[0]) + 1,
+ response = JsonResponse({'numero': int(re.sub("[^0-9].*", '', norma[0])) + 1,
'ano': norma[1]})
else:
response = JsonResponse(
diff --git a/sapl/painel/views.py b/sapl/painel/views.py
index 847518898..88d0aa785 100644
--- a/sapl/painel/views.py
+++ b/sapl/painel/views.py
@@ -82,10 +82,114 @@ def votacao_aberta(request):
return votacoes_abertas.first(), None
+def votacao(context,context_vars):
+
+ parlamentar = context_vars['votante'].parlamentar
+ parlamentar_presente = False
+ if parlamentar.id in context_vars['presentes']:
+ parlamentar_presente = True
+ context_vars.update({'parlamentar': parlamentar})
+ else:
+ context.update({'error_message':
+ 'Não há presentes na Sessão com a '
+ 'matéria em votação.'})
+
+ if parlamentar_presente:
+ voto = []
+ if context_vars['ordem_dia']:
+ voto = VotoParlamentar.objects.filter(
+ ordem=context_vars['ordem_dia'])
+ elif context_vars['expediente']:
+ voto = VotoParlamentar.objects.filter(
+ expediente=context_vars['expediente'])
+
+ if voto:
+ try:
+ voto = voto.get(parlamentar=context_vars['parlamentar'])
+ context.update({'voto_parlamentar': voto.voto})
+ except ObjectDoesNotExist:
+ context.update(
+ {'voto_parlamentar': 'Voto não '
+ 'computado.'})
+ else:
+ context.update({'error_message':
+ 'Você não está presente na '
+ 'Ordem do Dia/Expediente em votação.'})
+ return context, context_vars
+
+def sessao_votacao(context,context_vars):
+ pk = context_vars['sessao'].pk
+ context.update({'sessao_id': pk})
+ context.update({'sessao': context_vars['sessao'],
+ 'data': context_vars['sessao'].data_inicio,
+ 'hora': context_vars['sessao'].hora_inicio})
+
+ # Inicializa presentes
+ presentes = []
+ ordem_dia = get_materia_aberta(pk)
+ expediente = get_materia_expediente_aberta(pk)
+ errors_msgs = {'materia':'Não há nenhuma matéria aberta.',
+ 'registro':'A votação para esta matéria já encerrou.',
+ 'tipo':'A matéria aberta não é do tipo votação nominal.'}
+
+ materia_aberta = None
+ if ordem_dia:
+ materia_aberta = ordem_dia
+ presentes = PresencaOrdemDia.objects.filter(
+ sessao_plenaria_id=pk).values_list(
+ 'parlamentar_id', flat=True).distinct()
+ elif expediente:
+ materia_aberta = expediente
+ presentes = SessaoPlenariaPresenca.objects.filter(
+ sessao_plenaria_id=pk).values_list(
+ 'parlamentar_id', flat=True).distinct()
+
+ context_vars.update({'ordem_dia': ordem_dia,
+ 'expediente':expediente,
+ 'presentes': presentes})
+
+ # Verifica votação aberta
+ # Se aberta, verifica se é nominal. ID nominal == 2
+ erro = None
+ if not materia_aberta:
+ erro = 'materia'
+ elif materia_aberta.registro_aberto:
+ erro = 'registro'
+ elif materia_aberta.tipo_votacao != VOTACAO_NOMINAL:
+ erro = 'tipo'
+
+ if not erro:
+ context.update({'materia': materia_aberta.materia,
+ 'ementa': materia_aberta.materia.ementa})
+ context, context_vars = votacao(context, context_vars)
+ else:
+ context.update({'error_message': errors_msgs[erro]})
+
+ return context, context_vars
+
+
+def can_vote(context, context_vars, request):
+ context.update({'permissao': True})
+
+ # Pega sessão
+ sessao, msg = votacao_aberta(request)
+ context_vars.update({'sessao':sessao})
+ if sessao and not msg:
+ context, context_vars = sessao_votacao(context, context_vars)
+ elif not sessao and msg:
+ return HttpResponseRedirect('/')
+ else:
+ context.update(
+ {'error_message': 'Não há nenhuma sessão com matéria aberta.'})
+ return context, context_vars
+
+
def votante_view(request):
# Pega o votante relacionado ao usuário
template_name = 'painel/voto_nominal.html'
context = {}
+ context_vars = {}
+
try:
votante = Votante.objects.get(user=request.user)
except ObjectDoesNotExist:
@@ -95,96 +199,12 @@ def votante_view(request):
})
return render(request, template_name, context)
-
+ context_vars = {'votante': votante}
context = {'head_title': str(_('Votação Individual'))}
# Verifica se usuário possui permissão para votar
if 'parlamentares.can_vote' in request.user.get_all_permissions():
- context.update({'permissao': True})
-
- # Pega sessão
- sessao, msg = votacao_aberta(request)
-
- if sessao and not msg:
- pk = sessao.pk
- context.update({'sessao_id': pk})
- context.update({'sessao': sessao,
- 'data': sessao.data_inicio,
- 'hora': sessao.hora_inicio})
-
- # Inicializa presentes
- presentes = []
-
- # Verifica votação aberta
- # Se aberta, verifica se é nominal. ID nominal == 2
- ordem_dia = get_materia_aberta(pk)
- expediente = get_materia_expediente_aberta(pk)
-
- materia_aberta = None
- if ordem_dia:
- materia_aberta = ordem_dia
- presentes = PresencaOrdemDia.objects.filter(
- sessao_plenaria_id=pk).values_list(
- 'parlamentar_id', flat=True).distinct()
- elif expediente:
- materia_aberta = expediente
- presentes = SessaoPlenariaPresenca.objects.filter(
- sessao_plenaria_id=pk).values_list(
- 'parlamentar_id', flat=True).distinct()
-
- if materia_aberta:
- if not materia_aberta.registro_aberto:
- if materia_aberta.tipo_votacao == VOTACAO_NOMINAL:
- context.update({'materia': materia_aberta.materia,
- 'ementa': materia_aberta.materia.ementa})
-
- parlamentar = votante.parlamentar
- parlamentar_presente = False
- if parlamentar.id in presentes:
- parlamentar_presente = True
- else:
- context.update({'error_message':
- 'Não há presentes na Sessão com a '
- 'matéria em votação.'})
-
- if parlamentar_presente:
- voto = []
- if ordem_dia:
- voto = VotoParlamentar.objects.filter(
- ordem=ordem_dia)
- elif expediente:
- voto = VotoParlamentar.objects.filter(
- expediente=expediente)
-
- if voto:
- try:
- voto = voto.get(parlamentar=parlamentar)
- context.update({'voto_parlamentar': voto.voto})
- except ObjectDoesNotExist:
- context.update(
- {'voto_parlamentar': 'Voto não '
- 'computado.'})
- else:
- context.update({'error_message':
- 'Você não está presente na '
- 'Ordem do Dia/Expediente em votação.'})
- else:
- context.update(
- {'error_message': 'A matéria aberta não é do tipo '
- 'votação nominal.'})
- else:
- context.update(
- {'error_message': 'A votação para esta matéria já encerrou.'})
- else:
- context.update(
- {'error_message': 'Não há nenhuma matéria aberta.'})
-
- elif not sessao and msg:
- return HttpResponseRedirect('/')
-
- else:
- context.update(
- {'error_message': 'Não há nenhuma sessão com matéria aberta.'})
+ context, context_vars = can_vote(context, context_vars, request)
else:
context.update({'permissao': False,
@@ -192,36 +212,36 @@ def votante_view(request):
# Salva o voto
if request.method == 'POST':
- if ordem_dia:
+ if context_vars['ordem_dia']:
try:
voto = VotoParlamentar.objects.get(
- parlamentar=parlamentar,
- ordem=ordem_dia)
+ parlamentar=context_vars['parlamentar'],
+ ordem=context_vars['ordem_dia'])
except ObjectDoesNotExist:
voto = VotoParlamentar.objects.create(
- parlamentar=parlamentar,
+ parlamentar=context_vars['parlamentar'],
voto=request.POST['voto'],
user=request.user,
ip=get_client_ip(request),
- ordem=ordem_dia)
+ ordem=context_vars['ordem_dia'])
else:
voto.voto = request.POST['voto']
voto.ip = get_client_ip(request)
voto.user = request.user
voto.save()
- elif expediente:
+ elif context_vars['expediente']:
try:
voto = VotoParlamentar.objects.get(
- parlamentar=parlamentar,
- expediente=expediente)
+ parlamentar=context_vars['parlamentar'],
+ expediente=context_vars['expediente'])
except ObjectDoesNotExist:
voto = VotoParlamentar.objects.create(
- parlamentar=parlamentar,
+ parlamentar=context_vars['parlamentar'],
voto=request.POST['voto'],
user=request.user,
ip=get_client_ip(request),
- expediente=expediente)
+ expediente=context_vars['expediente'])
else:
voto.voto = request.POST['voto']
voto.ip = get_client_ip(request)
diff --git a/sapl/parlamentares/forms.py b/sapl/parlamentares/forms.py
index 36ce94c3a..326d41b3a 100644
--- a/sapl/parlamentares/forms.py
+++ b/sapl/parlamentares/forms.py
@@ -99,6 +99,19 @@ class MandatoForm(ModelForm):
raise ValidationError(_("Data fim mandato fora do intervalo de"
" legislatura informada"))
+ data_expedicao_diploma = data['data_expedicao_diploma']
+ if (data_expedicao_diploma and
+ data_expedicao_diploma > data_inicio_mandato):
+ raise ValidationError(_("A data da expedição do diploma deve ser anterior "
+ "a data de início do mandato"))
+
+ coligacao = data['coligacao']
+ if coligacao and not coligacao.legislatura == legislatura:
+ raise ValidationError(_("A coligação selecionada não está cadastrada "
+ "na mesma legislatura que o presente mandato, "
+ "favor verificar a coligação ou fazer o cadastro "
+ "de uma nova coligação na legislatura correspondente"))
+
existe_mandato = Mandato.objects.filter(
parlamentar=data['parlamentar'],
legislatura=data['legislatura']).exists()
@@ -318,18 +331,32 @@ class FrenteForm(ModelForm):
model = Frente
fields = '__all__'
+ def clean(self):
+ frente = super(FrenteForm, self).clean()
+ cd = self.cleaned_data
+ if not self.is_valid():
+ return self.cleaned_data
+
+ if cd['data_criacao'] >= cd['data_extincao']:
+ raise ValidationError(_("Data Dissolução não pode ser anterior a Data Criação"))
+
+ return cd
+
@transaction.atomic
def save(self, commit=True):
frente = super(FrenteForm, self).save(commit)
- content_type = ContentType.objects.get_for_model(Frente)
- object_id = frente.pk
- tipo = TipoAutor.objects.get(descricao__icontains='Frente')
- Autor.objects.create(
- content_type=content_type,
- object_id=object_id,
- tipo=tipo,
- nome=frente.nome
- )
+
+ if not self.instance.pk:
+ frente = super(FrenteForm, self).save(commit)
+ content_type = ContentType.objects.get_for_model(Frente)
+ object_id = frente.pk
+ tipo = TipoAutor.objects.get(descricao__icontains='Frente')
+ Autor.objects.create(
+ content_type=content_type,
+ object_id=object_id,
+ tipo=tipo,
+ nome=frente.nome
+ )
return frente
diff --git a/sapl/parlamentares/migrations/0024_auto_20180814_1237.py b/sapl/parlamentares/migrations/0024_auto_20180814_1237.py
new file mode 100644
index 000000000..43873cc68
--- /dev/null
+++ b/sapl/parlamentares/migrations/0024_auto_20180814_1237.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.13 on 2018-08-14 15:37
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('parlamentares', '0023_auto_20180626_1524'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='mandato',
+ name='titular',
+ field=models.BooleanField(choices=[(True, 'Sim'), (False, 'Não')], db_index=True, default=True, verbose_name='Parlamentar Titular'),
+ ),
+ ]
diff --git a/sapl/parlamentares/models.py b/sapl/parlamentares/models.py
index 19d4fa57c..43c4e2215 100644
--- a/sapl/parlamentares/models.py
+++ b/sapl/parlamentares/models.py
@@ -443,7 +443,7 @@ class Mandato(models.Model):
db_index=True,
default=True,
choices=YES_NO_CHOICES,
- verbose_name=_('Vereador Titular'))
+ verbose_name=_('Parlamentar Titular'))
observacao = models.TextField(
blank=True, verbose_name=_('Observação'))
diff --git a/sapl/parlamentares/views.py b/sapl/parlamentares/views.py
index 78678c3fc..ad4a8f5e5 100644
--- a/sapl/parlamentares/views.py
+++ b/sapl/parlamentares/views.py
@@ -87,8 +87,7 @@ class FrenteList(MasterDetailCrud):
CreateView, UpdateView, DeleteView = None, None, None
class BaseMixin(Crud.PublicMixin, MasterDetailCrud.BaseMixin):
- list_field_names = ['nome', 'data_criacao']
-
+ list_field_names = ['nome', 'data_criacao', 'data_extincao']
@classmethod
def url_name(cls, suffix):
return '%s_parlamentar_%s' % (cls.model._meta.model_name, suffix)
@@ -282,7 +281,8 @@ class FrenteCrud(CrudAux):
model = Frente
help_topic = 'tipo_situa_militar'
public = [RP_DETAIL, RP_LIST]
- list_field_names = ['nome', 'data_criacao', 'parlamentares']
+ list_field_names = ['nome', 'data_criacao', 'data_extincao', 'parlamentares']
+
class CreateView(Crud.CreateView):
form_class = FrenteForm
@@ -290,6 +290,10 @@ class FrenteCrud(CrudAux):
def form_valid(self, form):
return super(Crud.CreateView, self).form_valid(form)
+ class UpdateView(Crud.UpdateView):
+ form_class = FrenteForm
+
+
class MandatoCrud(MasterDetailCrud):
model = Mandato
diff --git a/sapl/protocoloadm/views.py b/sapl/protocoloadm/views.py
index 3350c81cf..67d43fe51 100644
--- a/sapl/protocoloadm/views.py
+++ b/sapl/protocoloadm/views.py
@@ -550,9 +550,16 @@ class PesquisarDocumentoAdministrativoView(DocumentoAdministrativoMixin,
qs = self.get_queryset()
+ qs = qs.prefetch_related("documentoacessorioadministrativo_set",
+ "tramitacaoadministrativo_set",
+ "tipo",
+ "tramitacaoadministrativo_set__status",
+ "tramitacaoadministrativo_set__unidade_tramitacao_local",
+ "tramitacaoadministrativo_set__unidade_tramitacao_destino")
+
if status_tramitacao and unidade_destino:
lista = filtra_tramitacao_adm_destino_and_status(status_tramitacao,
- unidade_destino)
+ unidade_destino)
qs = qs.filter(id__in=lista).distinct()
elif status_tramitacao:
@@ -566,11 +573,6 @@ class PesquisarDocumentoAdministrativoView(DocumentoAdministrativoMixin,
if 'o' in self.request.GET and not self.request.GET['o']:
qs = qs.order_by('-ano', '-numero')
- qs = qs.prefetch_related("documentoacessorioadministrativo_set",
- "tramitacaoadministrativo_set",
- "tramitacaoadministrativo_set__status",
- "tramitacaoadministrativo_set__unidade_tramitacao_local",
- "tramitacaoadministrativo_set__unidade_tramitacao_destino")
kwargs.update({
'queryset': qs,
@@ -607,10 +609,10 @@ class PesquisarDocumentoAdministrativoView(DocumentoAdministrativoMixin,
self.filterset.form.fields['o'].label = _('Ordenação')
+ length = self.object_list.count()
context = self.get_context_data(filter=self.filterset,
- object_list=self.object_list,
filter_url=url,
- numero_res=len(self.object_list)
+ numero_res=length
)
context['show_results'] = show_results_filter_set(
diff --git a/sapl/redireciona_urls/urls.py b/sapl/redireciona_urls/urls.py
index 63afd8b6b..f8a9aa685 100644
--- a/sapl/redireciona_urls/urls.py
+++ b/sapl/redireciona_urls/urls.py
@@ -2,20 +2,22 @@ from django.conf.urls import url
from .apps import AppConfig
from .views import (RedirecionaAtasList, RedirecionaComissao,
+ RedirecionaComposicaoComissao,
RedirecionaHistoricoTramitacoesList,
RedirecionaMateriaLegislativaDetail,
RedirecionaMateriaLegislativaList,
RedirecionaMateriasPorAnoAutorTipo,
RedirecionaMateriasPorAutor, RedirecionaMesaDiretoraView,
RedirecionaNormasJuridicasDetail,
- RedirecionaNormasJuridicasList, RedirecionaParlamentar,
- RedirecionaPautaSessao, RedirecionaPresencaParlamentares,
+ RedirecionaNormasJuridicasList,
+ RedirecionaNormasJuridicasTextoIntegral,
+ RedirecionaParlamentar, RedirecionaPautaSessao,
+ RedirecionaPresencaParlamentares,
RedirecionaRelatoriosList,
RedirecionaRelatoriosMateriasEmTramitacaoList,
RedirecionaSAPLIndex, RedirecionaSessaoPlenaria)
app_name = AppConfig.name
-
urlpatterns = [
url(r'^default_index_html$',
RedirecionaSAPLIndex.as_view(),
@@ -26,6 +28,9 @@ urlpatterns = [
url(r'^consultas/comissao/comissao_',
RedirecionaComissao.as_view(),
name='redireciona_comissao'),
+ url(r'^consultas/comissao/composicao/composicao_index_html',
+ RedirecionaComposicaoComissao.as_view(),
+ name='redireciona_composicaio_comissao'),
url(r'^consultas/pauta_sessao/pauta_sessao_',
RedirecionaPautaSessao.as_view(),
name='redireciona_pauta_sessao_'),
@@ -44,6 +49,9 @@ urlpatterns = [
url(r'^consultas/norma_juridica/norma_juridica_mostrar_proc',
RedirecionaNormasJuridicasDetail.as_view(),
name='redireciona_norma_juridica_detail'),
+ url(r'^sapl_documentos/norma_juridica/(?P[0-9]+)_texto_integral',
+ RedirecionaNormasJuridicasTextoIntegral.as_view(),
+ name='redireciona_norma_juridica_texto_integral'),
url(r'^relatorios_administrativos/relatorios_administrativos_index_html$',
RedirecionaRelatoriosList.as_view(),
name='redireciona_relatorios_list'),
@@ -51,6 +59,9 @@ urlpatterns = [
RedirecionaRelatoriosMateriasEmTramitacaoList.as_view(),
name='redireciona_relatorio_materia_por_tramitacao'),
url(r'tramitacaoMaterias/materia_mostrar_proc$',
+ RedirecionaMateriaLegislativaDetail.as_view(),
+ name='redireciona_materialegislativa_detail_tramitacao'),
+ url(r'consultas/materia/materia_mostrar_proc$',
RedirecionaMateriaLegislativaDetail.as_view(),
name='redireciona_materialegislativa_detail'),
url(r'^generico/materia_pesquisar_',
@@ -71,4 +82,4 @@ urlpatterns = [
url(r'propositurasAnoAutorTipo',
RedirecionaMateriasPorAnoAutorTipo.as_view(),
name='redireciona_materia_por_ano_autor_tipo_list'),
-]
+]
\ No newline at end of file
diff --git a/sapl/redireciona_urls/views.py b/sapl/redireciona_urls/views.py
index 369ff7518..ade47c9de 100644
--- a/sapl/redireciona_urls/views.py
+++ b/sapl/redireciona_urls/views.py
@@ -1,14 +1,14 @@
from django.core.urlresolvers import NoReverseMatch, reverse
from django.views.generic import RedirectView
+from sapl.audiencia.apps import AppConfig as audienciaConfig
from sapl.base.apps import AppConfig as atasConfig
from sapl.comissoes.apps import AppConfig as comissoesConfig
from sapl.materia.apps import AppConfig as materiaConfig
from sapl.norma.apps import AppConfig as normaConfig
+from sapl.norma.models import NormaJuridica
from sapl.parlamentares.apps import AppConfig as parlamentaresConfig
from sapl.sessao.apps import AppConfig as sessaoConfig
-from sapl.audiencia.apps import AppConfig as audienciaConfig
-
from .exceptions import UnknownUrlNameError
EMPTY_STRING = ''
@@ -142,6 +142,33 @@ class RedirecionaComissao(RedirectView):
return url
+class RedirecionaComposicaoComissao(RedirectView):
+ permanent = True
+
+ def get_redirect_url(self):
+ url = EMPTY_STRING
+ pk_composicao = self.request.GET.get(
+ 'cod_periodo_comp_sel', EMPTY_STRING)
+ pk_comissao = self.request.GET.get('cod_comissao', EMPTY_STRING)
+
+ if pk_comissao:
+ kwargs = {'pk': pk_comissao}
+
+ try:
+ url = reverse(comissao_detail, kwargs=kwargs)
+ except NoReverseMatch:
+ raise UnknownUrlNameError(comissao_detail)
+ else:
+ try:
+ url = reverse(comissao_list)
+ except NoReverseMatch:
+ raise UnknownUrlNameError(comissao_list)
+
+ url = has_iframe(url, self.request)
+
+ return url
+
+
class RedirecionaPautaSessao(RedirectView):
permanent = True
@@ -418,6 +445,23 @@ class RedirecionaNormasJuridicasDetail(RedirectView):
return url
+class RedirecionaNormasJuridicasTextoIntegral(RedirectView):
+ permanent = False
+
+ def get_redirect_url(self, **kwargs):
+ url = EMPTY_STRING
+ try:
+ norma = NormaJuridica.objects.get(pk=kwargs['norma_id'])
+ if norma:
+ url = norma.texto_integral.url
+ except Exception as e:
+ raise e
+
+ url = has_iframe(url, self.request)
+
+ return url
+
+
class RedirecionaNormasJuridicasList(RedirectView):
permanent = True
diff --git a/sapl/rules/apps.py b/sapl/rules/apps.py
index 606190110..dbdfce8ce 100644
--- a/sapl/rules/apps.py
+++ b/sapl/rules/apps.py
@@ -1,15 +1,15 @@
from builtins import LookupError
import django
-import reversion
from django.apps import apps
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 django.utils.translation import ugettext_lazy as _
+import reversion
from sapl.rules import (SAPL_GROUP_ADMINISTRATIVO, SAPL_GROUP_COMISSOES,
SAPL_GROUP_GERAL, SAPL_GROUP_MATERIA, SAPL_GROUP_NORMA,
diff --git a/sapl/rules/map_rules.py b/sapl/rules/map_rules.py
index f98da8c0e..bba114149 100644
--- a/sapl/rules/map_rules.py
+++ b/sapl/rules/map_rules.py
@@ -135,6 +135,7 @@ rules_group_norma = {
'rules': [
(norma.NormaJuridica, __base__),
(norma.NormaRelacionada, __base__),
+ (norma.AnexoNormaJuridica, __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
diff --git a/sapl/sessao/forms.py b/sapl/sessao/forms.py
index 06dd40830..cc68df531 100644
--- a/sapl/sessao/forms.py
+++ b/sapl/sessao/forms.py
@@ -112,6 +112,24 @@ class BancadaForm(ModelForm):
if not self.is_valid():
return self.cleaned_data
+ data = self.cleaned_data
+
+ legislatura = data['legislatura']
+
+ data_criacao = data['data_criacao']
+ if data_criacao:
+ if (data_criacao < legislatura.data_inicio or
+ data_criacao > legislatura.data_fim):
+ raise ValidationError(_("Data de criação da bancada fora do intervalo"
+ " de legislatura informada"))
+
+ data_extincao = data['data_extincao']
+ if data_extincao:
+ if (data_extincao < legislatura.data_inicio or
+ data_extincao > legislatura.data_fim):
+ raise ValidationError(_("Data fim da bancada fora do intervalo de"
+ " legislatura informada"))
+
if self.cleaned_data['data_extincao']:
if (self.cleaned_data['data_extincao'] <
self.cleaned_data['data_criacao']):
diff --git a/sapl/sessao/tests/test_sessao.py b/sapl/sessao/tests/test_sessao.py
index d83c56e86..336c62ad0 100644
--- a/sapl/sessao/tests/test_sessao.py
+++ b/sapl/sessao/tests/test_sessao.py
@@ -1,4 +1,5 @@
import pytest
+from datetime import datetime
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _
from model_mommy import mommy
@@ -87,9 +88,16 @@ def test_valida_campos_obrigatorios_bancada_form():
assert len(errors) == 3
+def data(valor):
+ return datetime.strptime(valor, '%Y-%m-%d').date()
+
+
@pytest.mark.django_db(transaction=False)
def test_bancada_form_valido():
- legislatura = mommy.make(Legislatura)
+ legislatura = mommy.make(Legislatura,
+ data_inicio=data('2017-11-10'),
+ data_fim=data('2017-12-31'),
+ )
partido = mommy.make(Partido)
form = forms.BancadaForm(data={'legislatura': str(legislatura.pk),
@@ -105,7 +113,10 @@ def test_bancada_form_valido():
@pytest.mark.django_db(transaction=False)
def test_bancada_form_datas_invalidas():
- legislatura = mommy.make(Legislatura)
+ legislatura = mommy.make(Legislatura,
+ data_inicio=data('2017-11-10'),
+ data_fim=data('2017-12-31'),
+ )
partido = mommy.make(Partido)
form = forms.BancadaForm(data={'legislatura': str(legislatura.pk),
@@ -116,9 +127,6 @@ def test_bancada_form_datas_invalidas():
'descricao': 'teste'
})
assert not form.is_valid()
- assert form.errors['__all__'] == [_('Data de extinção não pode ser menor '
- 'que a de criação')]
-
@pytest.mark.django_db(transaction=False)
def test_expediente_materia_form_valido():
diff --git a/sapl/settings.py b/sapl/settings.py
index 72cf086cc..f29011464 100644
--- a/sapl/settings.py
+++ b/sapl/settings.py
@@ -94,7 +94,10 @@ INSTALLED_APPS = (
) + SAPL_APPS
# FTS = Full Text Search
-HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
+# Desabilita a indexação textual até encontramos uma solução para a issue
+# https://github.com/interlegis/sapl/issues/2055
+#HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
+HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.BaseSignalProcessor'
SEARCH_BACKEND = 'haystack.backends.whoosh_backend.WhooshEngine'
SEARCH_URL = ('PATH', PROJECT_DIR.child('whoosh'))
@@ -218,7 +221,7 @@ EMAIL_SEND_USER = config('EMAIL_SEND_USER', cast=str, default='')
DEFAULT_FROM_EMAIL = config('DEFAULT_FROM_EMAIL', cast=str, default='')
SERVER_EMAIL = config('SERVER_EMAIL', cast=str, default='')
-MAX_DOC_UPLOAD_SIZE = 20 * 1024 * 1024 # 20MB
+MAX_DOC_UPLOAD_SIZE = 50 * 1024 * 1024 # 50MB
MAX_IMAGE_UPLOAD_SIZE = 2 * 1024 * 1024 # 2MB
# Internationalization
diff --git a/sapl/templates/base.html b/sapl/templates/base.html
index c6a5d223b..3e1dc3a39 100644
--- a/sapl/templates/base.html
+++ b/sapl/templates/base.html
@@ -203,7 +203,7 @@
{{ endereco }}
CEP: {{ cep }} | Telefone: {{ telefone }}
- {% trans 'Site da Câmara' %} |
+ {% trans 'Site' %} |
{% trans 'Fale Conosco' %}
diff --git a/sapl/templates/base/RelatorioMateriasPorAutor_filter.html b/sapl/templates/base/RelatorioMateriasPorAutor_filter.html
index ec26ecd63..bf0b7702f 100644
--- a/sapl/templates/base/RelatorioMateriasPorAutor_filter.html
+++ b/sapl/templates/base/RelatorioMateriasPorAutor_filter.html
@@ -15,56 +15,55 @@
- | QUADRO GERAL |
+ | QUADRO GERAL |
- | Tipo Matéria |
+ Tipo Matéria |
Quantidade |
{% for key, value in qtdes.items %}
- | {{key.sigla}} - {{key}} |
+ {{key.sigla}} - {{key}} |
{{value}} |
{% endfor %}
-
-
-
-
- | Matéria |
- Ementa |
- Autor |
- Coautor(es) |
-
-
-
- {% for materia in object_list %}
-
- |
- {{materia.tipo.sigla}} {{materia.numero}}/{{materia.ano}}
- |
- {{materia.ementa}} |
-
- {% for autor in materia.autoria_set.all %}
- {% if autor.primeiro_autor %}
- {{autor.autor}}
- {% endif %}
- {% endfor %}
- |
-
- {% for autor in materia.autoria_set.all %}
- {% if not autor.primeiro_autor %}
- {{autor.autor}}
- {% endif %}
- {% endfor %}
- |
-
- {% endfor %}
-
+ {% for materia in object_list %}
+ {% ifchanged materia.autoria_set.first.autor %}
+
+
+ |
+ | Autor: {{ materia.autoria_set.first.autor }} |
+
+ | Matéria |
+ Ementa |
+ Coautor(es) |
+
+
+ {% endifchanged %}
+
+
+ |
+ {{materia.tipo.sigla}} {{materia.numero}}/{{materia.ano}}
+ |
+ {% autoescape off %}{{materia.ementa}}{% endautoescape %} |
+
+ {% if materia.autoria_set.first != materia.autoria_set.last %}
+ {% for autor in materia.autoria_set.all %}
+ {% if not autor.primeiro_autor %}
+ {{ autor.autor }}
+ {% endif %}
+ {% endfor %}
+ {% endif %}
+ |
+
+
+
+ {% endfor %}
+
{% endif %}
{% endblock base_content %}
diff --git a/sapl/templates/compilacao/dispositivo_form.html b/sapl/templates/compilacao/dispositivo_form.html
index 0e1d1f0f0..15d383600 100644
--- a/sapl/templates/compilacao/dispositivo_form.html
+++ b/sapl/templates/compilacao/dispositivo_form.html
@@ -29,7 +29,7 @@