Browse Source

Merge

pull/1367/head
Eduardo Calil 8 years ago
parent
commit
1ad9140389
  1. 7
      .gitignore
  2. 0
      media/.gitkeep
  3. 126
      sapl/api/forms.py
  4. 8
      sapl/api/urls.py
  5. 60
      sapl/api/views.py
  6. 7
      sapl/base/forms.py
  7. 19
      sapl/base/migrations/0007_auto_20170808_0850.py
  8. 5
      sapl/base/models.py
  9. 8
      sapl/base/views.py
  10. 20
      sapl/comissoes/migrations/0002_auto_20170809_1236.py
  11. 2
      sapl/comissoes/models.py
  12. 5
      sapl/crispy_layout_mixin.py
  13. 16
      sapl/legacy/migration.py
  14. 8
      sapl/legacy/scripts/fix_tables.sql
  15. 129
      sapl/materia/forms.py
  16. 19
      sapl/materia/migrations/0010_auto_20170808_0850.py
  17. 19
      sapl/materia/migrations/0011_auto_20170808_1034.py
  18. 2
      sapl/materia/models.py
  19. 22
      sapl/materia/tests/test_materia.py
  20. 14
      sapl/materia/urls.py
  21. 142
      sapl/materia/views.py
  22. 40
      sapl/protocoloadm/forms.py
  23. 7
      sapl/protocoloadm/views.py
  24. 66
      sapl/redireciona_urls/views.py
  25. 25
      sapl/sessao/migrations/0010_auto_20170810_1033.py
  26. 11
      sapl/sessao/models.py
  27. 2
      sapl/sessao/views.py
  28. 10
      sapl/settings.py
  29. 17
      sapl/static/js/app.js
  30. 4
      sapl/static/styles/app.scss
  31. 5
      sapl/templates/base/autor_form.html
  32. 15
      sapl/templates/materia/autoria_form.html
  33. 4
      sapl/templates/materia/autoria_list.html
  34. 51
      sapl/templates/materia/autoria_multicreate_form.html
  35. 7
      sapl/templates/materia/layouts.yaml
  36. 24
      sapl/templates/materia/materialegislativa_filter.html
  37. 41
      sapl/templates/protocoloadm/protocolar_materia.html
  38. 2
      sapl/templates/sessao/pauta_sessao_detail.html
  39. 41
      sapl/utils.py

7
.gitignore

@ -91,9 +91,12 @@ whoosh_index
collected_static
bower
bower_components
media
whoosh/
solr-4.10.2/
postgres-data/
data/
solr-*/
solr-*/
# ignora tudo dentro de media, mas cria a pasta no checkout
media/*
!media/.gitkeep

0
media/.gitkeep

126
sapl/api/forms.py

@ -1,11 +1,15 @@
from django.db.models import Q
from django.db.models import Q, F
from django.forms.fields import CharField, MultiValueField
from django.forms.widgets import MultiWidget, TextInput
from django_filters.filters import MethodFilter, ModelChoiceFilter
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from django_filters.filters import MethodFilter, ModelChoiceFilter, DateFilter
from rest_framework import serializers
from rest_framework.compat import django_filters
from rest_framework.filters import FilterSet
from sapl.base.models import Autor, TipoAutor
from sapl.parlamentares.models import Legislatura
from sapl.utils import generic_relations_for_model
@ -38,11 +42,13 @@ class SaplGenericRelationSearchFilterSet(FilterSet):
item.related_query_name(),
field[0])
)
if len(field) == 3 and field[2](qtext) is not None:
q_fs = q_fs | Q(**{'%s__%s%s' % (
item.related_query_name(),
field[0],
field[1]): qtext if len(field) == 2 else field[2](qtext)})
# if len(field) == 3 and field[2](qtext) is not
# None:
q_fs = q_fs | Q(**{'%s__%s%s' % (
item.related_query_name(),
field[0],
field[1]): qtext if len(field) == 2
else field[2](qtext)})
q = q & q_fs
@ -115,3 +121,109 @@ class AutorSearchForFieldFilterSet(AutorChoiceFilterSet):
v = '1' if v == 'True' else '0'
params[key] = v
return queryset.filter(**params).distinct('nome').order_by('nome')
class AutoresPossiveisFilterSet(FilterSet):
data_relativa = DateFilter(method='filter_data_relativa')
tipo = MethodFilter()
class Meta:
model = Autor
fields = ['data_relativa', 'tipo', ]
def filter_data_relativa(self, queryset, name, value):
return queryset
def filter_tipo(self, queryset, value):
try:
tipo = TipoAutor.objects.get(pk=value)
except:
raise serializers.ValidationError(_('Tipo de Autor inexistente.'))
qs = queryset.filter(tipo=tipo)
return qs
@property
def qs(self):
qs = super().qs
data_relativa = self.form.cleaned_data['data_relativa'] \
if 'data_relativa' in self.form.cleaned_data else None
tipo = self.form.cleaned_data['tipo'] \
if 'tipo' in self.form.cleaned_data else None
if not tipo and not data_relativa:
return qs
if tipo:
# não precisa de try except, já foi validado em filter_tipo
tipo = TipoAutor.objects.get(pk=tipo)
if not tipo.content_type:
return qs
filter_for_model = 'filter_%s' % tipo.content_type.model
if not hasattr(self, filter_for_model):
return qs
if not data_relativa:
data_relativa = timezone.now()
return getattr(self, filter_for_model)(qs, data_relativa).distinct()
def filter_parlamentar(self, queryset, data_relativa):
# não leva em conta afastamentos
legislatura_relativa = Legislatura.objects.filter(
data_inicio__lte=data_relativa,
data_fim__gte=data_relativa).first()
params = {
'parlamentar_set__mandato__data_inicio_mandato__lte':
data_relativa,
'parlamentar_set__mandato__data_fim_mandato__gte': data_relativa
}
if legislatura_relativa.atual():
params['parlamentar_set__ativo'] = True
qs = queryset.filter(**params)
return qs
def filter_comissao(self, queryset, data_relativa):
return queryset.filter(
Q(comissao_set__data_extincao__isnull=True,
comissao_set__data_fim_comissao__isnull=True) |
Q(comissao_set__data_extincao__gte=data_relativa,
comissao_set__data_fim_comissao__isnull=True) |
Q(comissao_set__data_extincao__gte=data_relativa,
comissao_set__data_fim_comissao__isnull=True) |
Q(comissao_set__data_extincao__isnull=True,
comissao_set__data_fim_comissao__gte=data_relativa) |
Q(comissao_set__data_extincao__gte=data_relativa,
comissao_set__data_fim_comissao__gte=data_relativa),
comissao_set__data_criacao__lte=data_relativa)
def filter_frente(self, queryset, data_relativa):
return queryset.filter(
Q(frente_set__data_extincao__isnull=True) |
Q(frente_set__data_extincao__gte=data_relativa),
frente_set__data_criacao__lte=data_relativa)
def filter_bancada(self, queryset, data_relativa):
return queryset.filter(
Q(bancada_set__data_extincao__isnull=True) |
Q(bancada_set__data_extincao__gte=data_relativa),
bancada_set__data_criacao__lte=data_relativa)
def filter_bloco(self, queryset, data_relativa):
return queryset.filter(
Q(bloco_set__data_extincao__isnull=True) |
Q(bloco_set__data_extincao__gte=data_relativa),
bloco_set__data_criacao__lte=data_relativa)
def filter_orgao(self, queryset, data_relativa):
# na implementação, não havia regras a implementar para orgao
return queryset

8
sapl/api/urls.py

@ -3,7 +3,8 @@ from django.conf.urls import include, url
from rest_framework.routers import DefaultRouter
from sapl.api.views import (AutorListView, MateriaLegislativaViewSet,
ModelChoiceView, SessaoPlenariaViewSet)
ModelChoiceView, SessaoPlenariaViewSet,
AutoresPossiveisListView, AutoresProvaveisListView)
from .apps import AppConfig
@ -17,6 +18,11 @@ urlpatterns_router = router.urls
urlpatterns_api = [
url(r'^autor/provaveis',
AutoresProvaveisListView.as_view(), name='autores_provaveis_list'),
url(r'^autor/possiveis',
AutoresPossiveisListView.as_view(), name='autores_possiveis_list'),
url(r'^autor', AutorListView.as_view(), name='autor_list'),
url(r'^model/(?P<content_type>\d+)/(?P<pk>\d*)$',

60
sapl/api/views.py

@ -9,7 +9,8 @@ from rest_framework.permissions import (AllowAny, IsAuthenticated,
IsAuthenticatedOrReadOnly)
from rest_framework.viewsets import GenericViewSet, ModelViewSet
from sapl.api.forms import AutorChoiceFilterSet, AutorSearchForFieldFilterSet
from sapl.api.forms import AutorChoiceFilterSet, AutorSearchForFieldFilterSet,\
AutoresPossiveisFilterSet
from sapl.api.serializers import (AutorChoiceSerializer, AutorSerializer,
ChoiceSerializer,
MateriaLegislativaSerializer,
@ -57,14 +58,6 @@ class AutorListView(ListAPIView):
de Autores feita pelo django-filter
-> processo usado nas pesquisas, o mais usado.
= 2 -> para (value, text) usados geralmente
em combobox, radiobox, checkbox, etc com pesquisa básica
de Autores mas feito para Possíveis Autores armazenados
segundo o ContentType associado ao Tipo de Autor via
relacionamento genérico.
Busca feita sem django-filter processada no get_queryset
-> processo no cadastro de autores para seleção e busca
dos possíveis autores
= 3 -> Devolve instancias da classe Autor filtradas pelo
django-filter
@ -78,14 +71,14 @@ class AutorListView(ListAPIView):
o django-filter é desativado e a busca é feita
no model do ContentType associado ao tipo.
- q_0 / q_1 - q_0 faz o código ignorar "q"...
- q_0 / q_1 - q_0 é opcional e quando usado, faz o código ignorar "q"...
q_0 -> campos lookup a serem filtrados em qualquer Model
que implemente SaplGenericRelation
q_1 -> o valor que será pesquisado no lookup de q_0
q_0 e q_1 podem ser separados por ","... isso dará a
possibilidade de filtrar mais de um campo.
possibilidade de filtrar mais de um campo.
http://localhost:8000
@ -114,7 +107,7 @@ class AutorListView(ListAPIView):
não importa o campo que vc passe de qualquer dos Models
ligados... é possível ver que models são esses,
ligados... é possível ver que models são esses,
na ocasião do commit deste texto, executando:
In [6]: from sapl.utils import models_with_gr_for_model
@ -127,15 +120,13 @@ class AutorListView(ListAPIView):
sapl.sessao.models.Bancada,
sapl.sessao.models.Bloco]
qualquer atributo destes models podem ser passados
qualquer atributo destes models podem ser passados
para busca
"""
TR_AUTOR_CHOICE_SERIALIZER = 1
TR_CHOICE_SERIALIZER = 2
TR_AUTOR_SERIALIZER = 3
# FIXME aplicar permissão correta de usuário
permission_classes = (IsAuthenticatedOrReadOnly,)
queryset = Autor.objects.all()
model = Autor
@ -152,7 +143,6 @@ class AutorListView(ListAPIView):
assert tr in (
AutorListView.TR_AUTOR_CHOICE_SERIALIZER,
AutorListView.TR_CHOICE_SERIALIZER,
AutorListView.TR_AUTOR_SERIALIZER), sapl_logger.info(
_("Tipo do Resultado a ser fornecido não existe!"))
except:
@ -161,16 +151,8 @@ class AutorListView(ListAPIView):
return tr
def get(self, request, *args, **kwargs):
"""
desativa o django-filter se a busca for por possiveis autores
parametro tr = TR_CHOICE_SERIALIZER
"""
if self.tr == AutorListView.TR_CHOICE_SERIALIZER:
self.filter_class = None
self.filter_backends = []
self.serializer_class = ChoiceSerializer
elif self.tr == AutorListView.TR_AUTOR_SERIALIZER:
if self.tr == AutorListView.TR_AUTOR_SERIALIZER:
self.serializer_class = AutorSerializer
self.permission_classes = (IsAuthenticated,)
@ -179,12 +161,20 @@ class AutorListView(ListAPIView):
return ListAPIView.get(self, request, *args, **kwargs)
class AutoresProvaveisListView(ListAPIView):
permission_classes = (IsAuthenticatedOrReadOnly,)
queryset = Autor.objects.all()
model = Autor
filter_class = None
filter_backends = []
serializer_class = ChoiceSerializer
def get_queryset(self):
queryset = ListAPIView.get_queryset(self)
if self.filter_backends:
return queryset
params = {'content_type__isnull': False}
tipo = ''
@ -252,6 +242,18 @@ class AutorListView(ListAPIView):
return r
class AutoresPossiveisListView(ListAPIView):
permission_classes = (IsAuthenticatedOrReadOnly,)
queryset = Autor.objects.all()
model = Autor
pagination_class = None
filter_class = AutoresPossiveisFilterSet
serializer_class = AutorChoiceSerializer
class MateriaLegislativaViewSet(ListModelMixin,
RetrieveModelMixin,
GenericViewSet):

7
sapl/base/forms.py

@ -24,7 +24,8 @@ from sapl.sessao.models import SessaoPlenaria
from sapl.settings import MAX_IMAGE_UPLOAD_SIZE
from sapl.utils import (RANGE_ANOS, ChoiceWithoutValidationField,
ImageThumbnailFileInput, RangeWidgetOverride,
autor_label, autor_modal, models_with_gr_for_model)
autor_label, autor_modal, models_with_gr_for_model,
qs_override_django_filter)
from .models import AppConfig, CasaLegislativa
@ -501,6 +502,10 @@ class RelatorioPresencaSessaoFilterSet(django_filters.FilterSet):
row1, form_actions(save_label='Pesquisar'))
)
@property
def qs(self):
return qs_override_django_filter(self)
class RelatorioHistoricoTramitacaoFilterSet(django_filters.FilterSet):

19
sapl/base/migrations/0007_auto_20170808_0850.py

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.13 on 2017-08-08 08:50
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('base', '0006_auto_20170802_1908'),
]
operations = [
migrations.AlterModelOptions(
name='autor',
options={'ordering': ('nome',), 'verbose_name': 'Autor', 'verbose_name_plural': 'Autores'},
),
]

5
sapl/base/models.py

@ -1,15 +1,17 @@
import reversion
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.utils.translation import ugettext_lazy as _
import reversion
from sapl.utils import UF, YES_NO_CHOICES, get_settings_auth_user_model
TIPO_DOCUMENTO_ADMINISTRATIVO = (('O', _('Ostensivo')),
('R', _('Restritivo')))
SEQUENCIA_NUMERACAO = (('A', _('Sequencial por ano')),
('L', _('Sequencial por legislatura')),
('U', _('Sequencial único')))
@ -221,6 +223,7 @@ class Autor(models.Model):
verbose_name = _('Autor')
verbose_name_plural = _('Autores')
unique_together = (('content_type', 'object_id'), )
ordering = ('nome',)
def __str__(self):

8
sapl/base/views.py

@ -21,7 +21,7 @@ from sapl.base.models import Autor, TipoAutor
from sapl.crud.base import CrudAux
from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa
from sapl.parlamentares.models import Parlamentar
from sapl.sessao.models import OrdemDia, SessaoPlenaria
from sapl.sessao.models import PresencaOrdemDia, SessaoPlenaria
from sapl.utils import sapl_logger
from .forms import (CasaLegislativaForm, ConfiguracoesAppForm,
@ -253,9 +253,9 @@ class RelatorioPresencaSessaoView(FilterView):
sessao_count=0,
ordemdia_count=0)
total_ordemdia = OrdemDia.objects.order_by(
'sessao_plenaria').filter(**param0).distinct(
'sessao_plenaria').count()
total_ordemdia = PresencaOrdemDia.objects.filter(
**param0).distinct('sessao_plenaria__id').order_by(
'sessao_plenaria__id').count()
self.calcular_porcentagem_presenca(
pls,

20
sapl/comissoes/migrations/0002_auto_20170809_1236.py

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.12 on 2017-08-09 12:36
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('comissoes', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='comissao',
name='nome',
field=models.CharField(max_length=100, verbose_name='Nome'),
),
]

2
sapl/comissoes/models.py

@ -35,7 +35,7 @@ class Comissao(models.Model):
tipo = models.ForeignKey(TipoComissao,
on_delete=models.PROTECT,
verbose_name=_('Tipo'))
nome = models.CharField(max_length=60, verbose_name=_('Nome'))
nome = models.CharField(max_length=100, verbose_name=_('Nome'))
sigla = models.CharField(max_length=10, verbose_name=_('Sigla'))
data_criacao = models.DateField(verbose_name=_('Data de Criação'))
data_extincao = models.DateField(

5
sapl/crispy_layout_mixin.py

@ -67,7 +67,10 @@ def get_field_display(obj, fieldname):
ou mesmo uma método no model.
"""
value = getattr(obj, fieldname)
verbose_name = ''
try:
verbose_name = value.model._meta.verbose_name
except AttributeError:
verbose_name = ''
else:
verbose_name = str(field.verbose_name)\

16
sapl/legacy/migration.py

@ -750,8 +750,8 @@ def adjust_proposicao_antes_salvar(new, old):
def adjust_proposicao_depois_salvar(new, old):
if not hasattr(old.dat_envio, 'year') or old.dat_envio.year == 1800:
msg = "O valor do campo data_envio (DateField) da model Proposicao"
"era inválido"
msg = "O valor do campo data_envio (DateField) da model Proposicao"\
" era inválido"
descricao = 'A data 1111-11-11 foi colocada no lugar'
problema = 'O valor da data era nulo ou inválido'
warn(msg + ' => ' + descricao)
@ -898,13 +898,13 @@ def adjust_autor(new, old):
def adjust_comissao(new, old):
if old.dat_extincao:
if date.today() < new.data_extincao:
new.ativa = True
else:
new.ativa = False
if not old.dat_extincao:
if not old.dat_extincao and not old.dat_fim_comissao:
new.ativa = True
elif old.dat_extincao and date.today() < new.data_extincao or \
old.dat_fim_comissao and date.today() < new.data_fim_comissao:
new.ativa = True
else:
new.ativa = False
AJUSTE_ANTES_SALVAR = {

8
sapl/legacy/scripts/fix_tables.sql

@ -1,19 +1,22 @@
-- Apaga as restrições somente para essa sessão
SELECT REPLACE(@@sql_mode,'STRICT_TRANS_TABLES,','');
SELECT REPLACE(@@sql_mode,'STRICT_TRANS_TABLES,','ALLOW_INVALID_DATES');
-- Exclui procedures caso já existam
DROP PROCEDURE IF EXISTS verifica_campos_proposicao;
DROP PROCEDURE IF EXISTS verifica_campos_tipo_materia_legislativa;
DROP PROCEDURE IF EXISTS verifica_campos_sessao_plenaria_presenca;
DROP PROCEDURE IF EXISTS cria_lexml_registro_provedor_e_publicador;
DROP PROCEDURE IF EXISTS cria_tipo_situacao_militar;
DROP PROCEDURE IF EXISTS muda_vinculo_norma_juridica_ind_excluido;
-- Procedure para criar campo num_proposicao em proposicao
CREATE PROCEDURE verifica_campos_proposicao() BEGIN IF NOT EXISTS (SELECT * FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='proposicao' AND column_name='num_proposicao') THEN ALTER TABLE proposicao ADD COLUMN num_proposicao INT(11) NULL after txt_justif_devolucao; END IF; END;
CREATE PROCEDURE verifica_campos_proposicao() BEGIN IF NOT EXISTS (SELECT * FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='proposicao' AND column_name='num_proposicao') THEN UPDATE proposicao SET dat_envio = '1800-01-01' WHERE CAST(dat_envio AS CHAR(20)) = '0000-00-00 00:00:00'; ALTER TABLE proposicao ADD COLUMN num_proposicao INT(11) NULL after txt_justif_devolucao; END IF; END;
-- Procedure para criar campo iind_num_automatica em tipo_materia_legislativa
CREATE PROCEDURE verifica_campos_tipo_materia_legislativa() BEGIN IF NOT EXISTS (SELECT * FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='tipo_materia_legislativa' AND column_name='ind_num_automatica') THEN ALTER TABLE tipo_materia_legislativa ADD COLUMN ind_num_automatica BOOLEAN NULL DEFAULT FALSE after des_tipo_materia; END IF; IF NOT EXISTS (SELECT * FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='tipo_materia_legislativa' AND column_name='quorum_minimo_votacao') THEN ALTER TABLE tipo_materia_legislativa ADD COLUMN quorum_minimo_votacao INT(11) NULL after ind_num_automatica; END IF; END;
-- Procedure para criar campos cod_presenca_sessao (sendo a nova PK da tabela) e dat_sessao em sessao_plenaria_presenca
CREATE PROCEDURE verifica_campos_sessao_plenaria_presenca() BEGIN IF NOT EXISTS (SELECT * FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='sessao_plenaria_presenca' AND column_name='cod_presenca_sessao') THEN ALTER TABLE sessao_plenaria_presenca DROP PRIMARY KEY, ADD cod_presenca_sessao INT AUTO_INCREMENT PRIMARY KEY FIRST; END IF; IF NOT EXISTS (SELECT * FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='sessao_plenaria_presenca' AND column_name='dat_sessao') THEN ALTER TABLE sessao_plenaria_presenca ADD COLUMN dat_sessao DATE NULL after cod_parlamentar; END IF; END;
-- Procedure para criar tabela lexml_registro_provedor e lexml_registro_publicador
CREATE PROCEDURE cria_lexml_registro_provedor_e_publicador() BEGIN IF NOT EXISTS (SELECT * FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='lexml_registro_publicador') THEN CREATE TABLE lexml_registro_publicador (cod_publicador INT AUTO_INCREMENT NOT NULL, id_publicador INT, nom_publicador VARCHAR(255), adm_email VARCHAR(50), sigla VARCHAR(255), nom_responsavel VARCHAR(255), tipo VARCHAR(50), id_responsavel INT, PRIMARY KEY (cod_publicador)); END IF; IF NOT EXISTS (SELECT * FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='lexml_registro_provedor') THEN CREATE TABLE lexml_registro_provedor (cod_provedor INT AUTO_INCREMENT NOT NULL, id_provedor INT, nom_provedor VARCHAR(255), sgl_provedor VARCHAR(15), adm_email VARCHAR(50), nom_responsavel VARCHAR(255), tipo VARCHAR(50), id_responsavel INT, xml_provedor LONGTEXT, PRIMARY KEY (cod_provedor)); END IF; END;
-- Procedure para criar tabela tipo_situacao_militar
CREATE PROCEDURE cria_tipo_situacao_militar() BEGIN IF NOT EXISTS (SELECT * FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='tipo_situacao_militar') THEN CREATE TABLE tipo_situacao_militar (tip_situacao_militar INT AUTO_INCREMENT NOT NULL, des_tipo_situacao VARCHAR(50), ind_excluido INT, PRIMARY KEY (tip_situacao_militar)); END IF; END;
-- Procedure para mudar valor do campo ind_excluido da tabela vinculo_norma_juridica de 0 para string vazia ''
CREATE PROCEDURE muda_vinculo_norma_juridica_ind_excluido() BEGIN UPDATE vinculo_norma_juridica SET ind_excluido = '' WHERE trim(ind_excluido) = '0'; END;
-- Executa as procedures criadas acima
@ -21,4 +24,5 @@ CALL verifica_campos_proposicao;
CALL verifica_campos_tipo_materia_legislativa;
CALL verifica_campos_sessao_plenaria_presenca;
CALL cria_lexml_registro_provedor_e_publicador;
CALL cria_tipo_situacao_militar;
CALL muda_vinculo_norma_juridica_ind_excluido;

129
sapl/materia/forms.py

@ -15,15 +15,12 @@ from django.db import models, transaction
from django.db.models import Max
from django.forms import ModelForm, ModelChoiceField, widgets
from django.forms.forms import Form
from django.forms.widgets import Select
from django.utils import six
from django.forms.models import ModelMultipleChoiceField
from django.forms.widgets import Select, CheckboxSelectMultiple, HiddenInput
from django.utils.encoding import force_text
from django.utils.html import format_html
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _
from django_filters.filterset import STRICTNESS
import django_filters
from sapl.base.models import Autor, TipoAutor
@ -42,7 +39,8 @@ from sapl.settings import MAX_DOC_UPLOAD_SIZE
from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES,
ChoiceWithoutValidationField,
MateriaPesquisaOrderingFilter, RangeWidgetOverride,
autor_label, autor_modal, models_with_gr_for_model)
autor_label, autor_modal, models_with_gr_for_model,
qs_override_django_filter)
import sapl
from .models import (AcompanhamentoMateria, Anexada, Autoria, DespachoInicial,
@ -166,6 +164,7 @@ class AcompanhamentoMateriaForm(ModelForm):
class DocumentoAcessorioForm(ModelForm):
data = forms.DateField(required=True)
class Meta:
model = DocumentoAcessorio
@ -592,42 +591,7 @@ class MateriaLegislativaFilterSet(django_filters.FilterSet):
@property
def qs(self):
if not hasattr(self, '_qs'):
valid = self.is_bound and self.form.is_valid()
if self.is_bound and not valid:
if self.strict == STRICTNESS.RAISE_VALIDATION_ERROR:
raise forms.ValidationError(self.form.errors)
elif bool(self.strict) == STRICTNESS.RETURN_NO_RESULTS:
self._qs = self.queryset.none()
return self._qs
# else STRICTNESS.IGNORE... ignoring
# start with all the results and filter from there
qs = self.queryset.all()
for name, filter_ in six.iteritems(self.filters):
value = None
if valid:
value = self.form.cleaned_data[name]
else:
raw_value = self.form[name].value()
try:
value = self.form.fields[name].clean(raw_value)
except forms.ValidationError:
if self.strict == STRICTNESS.RAISE_VALIDATION_ERROR:
raise
elif bool(self.strict) == STRICTNESS.RETURN_NO_RESULTS:
self._qs = self.queryset.none()
return self._qs
# else STRICTNESS.IGNORE... ignoring
if value is not None: # valid & clean data
qs = qs._next_is_sticky()
qs = filter_.filter(qs, value)
self._qs = qs
return self._qs
return qs_override_django_filter(self)
def pega_ultima_tramitacao():
@ -693,9 +657,11 @@ class AutoriaForm(ModelForm):
tipo_autor = ModelChoiceField(label=_('Tipo Autor'),
required=False,
queryset=
TipoAutor.objects.all().order_by('descricao'),
empty_label='Selecione',)
queryset=TipoAutor.objects.all(),
empty_label=_('Selecione'),)
data_relativa = forms.DateField(
widget=forms.HiddenInput(), required=False)
def __init__(self, *args, **kwargs):
super(AutoriaForm, self).__init__(*args, **kwargs)
@ -707,26 +673,79 @@ class AutoriaForm(ModelForm):
self.helper = FormHelper()
self.helper.layout = Layout(
Fieldset(_('Autoria'),
row1, form_actions(save_label='Salvar')))
row1, 'data_relativa', form_actions(save_label='Salvar')))
if not kwargs['instance']:
self.fields['autor'].choices = []
class Meta:
model = Autoria
fields = ['tipo_autor', 'autor', 'primeiro_autor']
fields = ['tipo_autor', 'autor', 'primeiro_autor', 'data_relativa']
def clean(self):
super(AutoriaForm, self).clean()
cd = super(AutoriaForm, self).clean()
if self.errors:
return self.errors
if Autoria.objects.filter(
materia=self.instance.materia,
autor=self.cleaned_data['autor'],
).exists():
msg = _('Esse Autor já foi cadastrado.')
raise ValidationError(msg)
autorias = Autoria.objects.filter(
materia=self.instance.materia, autor=cd['autor'])
pk = self.instance.pk
return self.cleaned_data
if ((not pk and autorias.exists()) or
(pk and autorias.exclude(pk=pk).exists())):
raise ValidationError(_('Esse Autor já foi cadastrado.'))
return cd
class AutoriaMultiCreateForm(Form):
tipo_autor = ModelChoiceField(label=_('Tipo Autor'),
required=False,
queryset=TipoAutor.objects.all(),
empty_label=_('Selecione'),)
data_relativa = forms.DateField(
widget=forms.HiddenInput(), required=False)
autor = ModelMultipleChoiceField(
queryset=Autor.objects.all(),
label=_('Possiveis Autores'),
required=False,
widget=CheckboxSelectMultiple)
autores = ModelMultipleChoiceField(
queryset=Autor.objects.all(),
required=False,
widget=HiddenInput)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
row1 = to_row([('tipo_autor', 12), ])
row2 = to_row([('autor', 12), ])
self.helper = FormHelper()
self.helper.layout = Layout(
Fieldset(
_('Autorias'), row1, row2, 'data_relativa', 'autores',
form_actions(save_label='Incluir Autores Selecionados')))
self.fields['autor'].choices = []
def clean(self):
cd = super().clean()
if 'autores' in self.errors:
del self.errors['autores']
if 'autor' not in cd or not cd['autor'].exists():
raise ValidationError(
_('Ao menos um autor deve ser selecionado para inclusão'))
return cd
class AcessorioEmLoteFilterSet(django_filters.FilterSet):

19
sapl/materia/migrations/0010_auto_20170808_0850.py

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.13 on 2017-08-08 08:50
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('materia', '0009_auto_20170712_0951'),
]
operations = [
migrations.AlterUniqueTogether(
name='autoria',
unique_together=set([('autor', 'materia')]),
),
]

19
sapl/materia/migrations/0011_auto_20170808_1034.py

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.13 on 2017-08-08 10:34
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('materia', '0010_auto_20170808_0850'),
]
operations = [
migrations.AlterModelOptions(
name='autoria',
options={'ordering': ('-primeiro_autor', 'autor__nome'), 'verbose_name': 'Autoria', 'verbose_name_plural': 'Autorias'},
),
]

2
sapl/materia/models.py

@ -299,6 +299,8 @@ class Autoria(models.Model):
class Meta:
verbose_name = _('Autoria')
verbose_name_plural = _('Autorias')
unique_together = (('autor', 'materia'), )
ordering = ('-primeiro_autor', 'autor__nome')
def __str__(self):
return _('%(autor)s - %(materia)s') % {

22
sapl/materia/tests/test_materia.py

@ -1,9 +1,9 @@
import pytest
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from django.core.files.uploadedfile import SimpleUploadedFile
from django.core.urlresolvers import reverse
from model_mommy import mommy
import pytest
from sapl.base.models import Autor, TipoAutor
from sapl.comissoes.models import Comissao, TipoComissao
@ -133,14 +133,13 @@ def test_autoria_submit(admin_client):
nome='Autor Teste')
# Testa POST
response = admin_client.post(reverse('sapl.materia:autoria_create',
kwargs={'pk': materia_principal.pk}),
{'autor': autor.pk,
'primeiro_autor': True,
'materia_id': materia_principal.pk,
'partido': '',
'salvar': 'salvar'},
follow=True)
response = admin_client.post(
reverse('sapl.materia:autoria_create',
kwargs={'pk': materia_principal.pk}),
{'autor': autor.pk,
'primeiro_autor': True,
'materia_id': materia_principal.pk, },
follow=True)
assert response.status_code == 200
# Verifica se o autor foi realmente criado
@ -227,6 +226,7 @@ def test_documento_acessorio_submit(admin_client):
'data_materia': '2016-03-21',
'autor': autor,
'ementa': 'teste_ementa',
'data': '2016-03-21',
'salvar': 'salvar'},
follow=True)
@ -323,9 +323,7 @@ def test_form_errors_autoria(admin_client):
response = admin_client.post(reverse('sapl.materia:autoria_create',
kwargs={'pk': materia_principal.pk}),
{'materia_id': materia_principal.pk,
'partido': '',
'autor': '',
'salvar': 'salvar'},
'autor_id': '', },
follow=True)
assert (response.context_data['form'].errors['autor'] ==

14
sapl/materia/urls.py

@ -2,8 +2,7 @@ from django.conf.urls import include, url
from sapl.materia.views import (AcompanhamentoConfirmarView,
AcompanhamentoExcluirView,
AcompanhamentoMateriaView,
AdicionarVariasAutorias, AnexadaCrud,
AcompanhamentoMateriaView, AnexadaCrud,
AssuntoMateriaCrud, AutoriaCrud,
ConfirmarProposicao, CriarProtocoloMateriaView,
DespachoInicialCrud, DocumentoAcessorioCrud,
@ -21,11 +20,12 @@ from sapl.materia.views import (AcompanhamentoConfirmarView,
TipoFimRelatoriaCrud, TipoMateriaCrud,
TipoProposicaoCrud, TramitacaoCrud,
TramitacaoEmLoteView, UnidadeTramitacaoCrud,
proposicao_texto, recuperar_materia)
proposicao_texto, recuperar_materia,
AutoriaMultiCreateView)
from . import receivers
from .apps import AppConfig
from . import receivers
app_name = AppConfig.name
@ -60,9 +60,9 @@ urlpatterns_materia = [
AcompanhamentoExcluirView.as_view(),
name='acompanhar_excluir'),
url(r'^materia/(?P<pk>\d+)/adicionar-varias-autorias/',
AdicionarVariasAutorias.as_view(),
name='adicionar_varias_autorias'),
url(r'^materia/(?P<pk>\d+)/autoria/multicreate',
AutoriaMultiCreateView.as_view(),
name='autoria_multicreate'),
url(r'^materia/acessorio-em-lote', DocumentoAcessorioEmLoteView.as_view(),
name='acessorio_em_lote'),

142
sapl/materia/views.py

@ -2,23 +2,6 @@ from datetime import datetime, date
from random import choice
from string import ascii_letters, digits
from .email_utils import do_envia_email_confirmacao
from .forms import (AcessorioEmLoteFilterSet, AcompanhamentoMateriaForm,
AdicionarVariasAutoriasFilterSet, DespachoInicialForm,
DocumentoAcessorioForm, MateriaAssuntoForm,
MateriaLegislativaFilterSet, MateriaSimplificadaForm,
PrimeiraTramitacaoEmLoteFilterSet, ReceberProposicaoForm,
RelatoriaForm, TramitacaoEmLoteFilterSet,
filtra_tramitacao_destino,
filtra_tramitacao_destino_and_status,
filtra_tramitacao_status)
from .models import (AcompanhamentoMateria, Anexada, AssuntoMateria, Autoria,
DespachoInicial, DocumentoAcessorio, MateriaAssunto,
MateriaLegislativa, Numeracao, Orgao, Origem, Proposicao,
RegimeTramitacao, Relatoria, StatusTramitacao,
TipoDocumento, TipoFimRelatoria, TipoMateriaLegislativa,
TipoProposicao, Tramitacao, UnidadeTramitacao)
from .signals import tramitacao_signal
from crispy_forms.helper import FormHelper
from crispy_forms.layout import HTML
from django import forms
@ -39,9 +22,8 @@ from django.views.generic import CreateView, ListView, TemplateView, UpdateView
from django.views.generic.base import RedirectView
from django.views.generic.edit import FormView
from django_filters.views import FilterView
import sapl
from sapl.base.models import Autor, CasaLegislativa
from sapl.comissoes.models import Comissao
from sapl.comissoes.models import Comissao, Participacao
from sapl.compilacao.models import (STATUS_TA_IMMUTABLE_RESTRICT,
STATUS_TA_PRIVATE)
@ -54,15 +36,31 @@ from sapl.crud.base import (ACTION_CREATE, ACTION_DELETE, ACTION_DETAIL,
from sapl.materia.forms import (AnexadaForm, ConfirmarProposicaoForm,
LegislacaoCitadaForm, AutoriaForm, ProposicaoForm,
TipoProposicaoForm, TramitacaoForm,
TramitacaoUpdateForm)
from sapl.materia.models import Autor
TramitacaoUpdateForm, AutoriaMultiCreateForm)
from sapl.norma.models import LegislacaoCitada
from sapl.parlamentares.models import Parlamentar
from sapl.protocoloadm.models import Protocolo
from sapl.utils import (TURNO_TRAMITACAO_CHOICES, YES_NO_CHOICES, autor_label,
autor_modal, gerar_hash_arquivo, get_base_url,
montar_row_autor)
import sapl
from .email_utils import do_envia_email_confirmacao
from .forms import (AcessorioEmLoteFilterSet, AcompanhamentoMateriaForm,
AdicionarVariasAutoriasFilterSet, DespachoInicialForm,
DocumentoAcessorioForm, MateriaAssuntoForm,
MateriaLegislativaFilterSet, MateriaSimplificadaForm,
PrimeiraTramitacaoEmLoteFilterSet, ReceberProposicaoForm,
RelatoriaForm, TramitacaoEmLoteFilterSet,
filtra_tramitacao_destino,
filtra_tramitacao_destino_and_status,
filtra_tramitacao_status)
from .models import (AcompanhamentoMateria, Anexada, AssuntoMateria, Autoria,
DespachoInicial, DocumentoAcessorio, MateriaAssunto,
MateriaLegislativa, Numeracao, Orgao, Origem, Proposicao,
RegimeTramitacao, Relatoria, StatusTramitacao,
TipoDocumento, TipoFimRelatoria, TipoMateriaLegislativa,
TipoProposicao, Tramitacao, UnidadeTramitacao)
from .signals import tramitacao_signal
AssuntoMateriaCrud = Crud.build(AssuntoMateria, 'assunto_materia')
@ -93,8 +91,8 @@ def proposicao_texto(request, pk):
if proposicao.texto_original:
if (not proposicao.data_recebimento and
proposicao.autor.user_id != request.user.id):
raise Http404
proposicao.autor.user_id != request.user.id):
raise Http404
arquivo = proposicao.texto_original
@ -1063,6 +1061,11 @@ class DocumentoAcessorioCrud(MasterDetailCrud):
def __init__(self, **kwargs):
super(MasterDetailCrud.CreateView, self).__init__(**kwargs)
def get_initial(self):
self.initial['data'] = datetime.now().date()
return self.initial
def get_context_data(self, **kwargs):
context = super(
@ -1085,41 +1088,74 @@ class AutoriaCrud(MasterDetailCrud):
parent_field = 'materia'
help_path = ''
public = [RP_LIST, RP_DETAIL]
list_field_names = ['autor', 'autor__tipo__descricao', 'primeiro_autor']
class CreateView(MasterDetailCrud.CreateView):
class LocalBaseMixin():
form_class = AutoriaForm
@property
def layout_key(self):
return 'AutoriaCreate'
return None
def get_context_data(self, **kwargs):
context = super(CreateView, self).get_context_data(**kwargs)
autores_ativos = self.autores_ativos()
class CreateView(LocalBaseMixin, MasterDetailCrud.CreateView):
autores = []
def get_initial(self):
initial = super().get_initial()
materia = MateriaLegislativa.objects.get(id=self.kwargs['pk'])
initial['data_relativa'] = materia.data_apresentacao
initial['autor'] = []
return initial
context['form'].fields['autor'].choices = autores
return context
class UpdateView(LocalBaseMixin, MasterDetailCrud.UpdateView):
def autores_ativos(self):
lista_parlamentares = Parlamentar.objects.filter(ativo=True).values_list('id', flat=True)
model_parlamentar = ContentType.objects.get_for_model(Parlamentar)
autor_parlamentar = Autor.objects.filter(content_type=model_parlamentar, object_id__in=lista_parlamentares)
def get_initial(self):
initial = super().get_initial()
initial.update({
'data_relativa': self.object.materia.data_apresentacao,
'tipo_autor': self.object.autor.tipo.id,
})
return initial
lista_comissoes = Comissao.objects.filter(Q(data_extincao__isnull=True)|Q(data_extincao__gt=date.today())).values_list('id', flat=True)
model_comissao = ContentType.objects.get_for_model(Comissao)
autor_comissoes = Autor.objects.filter(content_type=model_comissao, object_id__in=lista_comissoes)
autores_outros = Autor.objects.exclude(content_type__in=[model_parlamentar, model_comissao])
q = autor_parlamentar | autor_comissoes | autores_outros
return q
class ListView(MasterDetailCrud.ListView):
class AutoriaMultiCreateView(PermissionRequiredForAppCrudMixin, FormView):
app_label = sapl.materia.apps.AppConfig.label
form_class = AutoriaMultiCreateForm
template_name = 'materia/autoria_multicreate_form.html'
def get_queryset(self):
qs = super().get_queryset()
@classmethod
def get_url_regex(cls):
return r'^(?P<pk>\d+)/%s/multicreate' % cls.model._meta.model_name
@property
def layout_key(self):
return None
def get_initial(self):
initial = super().get_initial()
self.materia = MateriaLegislativa.objects.get(id=self.kwargs['pk'])
initial['data_relativa'] = self.materia.data_apresentacao
initial['autores'] = self.materia.autores.all()
return initial
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = '%s <small>(%s)</small>' % (
_('Adicionar Várias Autorias'), self.materia)
return context
def get_success_url(self):
messages.add_message(
self.request, messages.SUCCESS,
_('Autorias adicionadas com sucesso.'))
return reverse(
'sapl.materia:autoria_list', kwargs={'pk': self.materia.pk})
def form_valid(self, form):
autores_selecionados = form.cleaned_data['autor']
for autor in autores_selecionados:
Autoria.objects.create(materia=self.materia, autor=autor)
return qs.order_by('-primeiro_autor', 'autor__nome')
return FormView.form_valid(self, form)
class DespachoInicialCrud(MasterDetailCrud):
@ -1516,9 +1552,9 @@ class AcompanhamentoMateriaView(CreateView):
base_url = get_base_url(request)
destinatario = AcompanhamentoMateria.objects.get(
materia=materia,
email=email,
confirmado=False)
materia=materia,
email=email,
confirmado=False)
casa = CasaLegislativa.objects.first()
do_envia_email_confirmacao(base_url,
@ -1699,10 +1735,10 @@ class TramitacaoEmLoteView(PrimeiraTramitacaoEmLoteView):
context['primeira_tramitacao'] = False
if ('tramitacao__status' in qr and
'tramitacao__unidade_tramitacao_destino' in qr and
qr['tramitacao__status'] and
qr['tramitacao__unidade_tramitacao_destino']
):
'tramitacao__unidade_tramitacao_destino' in qr and
qr['tramitacao__status'] and
qr['tramitacao__unidade_tramitacao_destino']
):
lista = filtra_tramitacao_destino_and_status(
qr['tramitacao__status'],
qr['tramitacao__unidade_tramitacao_destino'])

40
sapl/protocoloadm/forms.py

@ -1,6 +1,5 @@
from datetime import datetime
import django_filters
from crispy_forms.bootstrap import InlineRadios
from crispy_forms.helper import FormHelper
from crispy_forms.layout import HTML, Button, Fieldset, Layout, Submit
@ -9,10 +8,11 @@ from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.db import models
from django.forms import ModelForm
from django.utils.translation import ugettext_lazy as _
import django_filters
from sapl.base.models import Autor
from sapl.base.models import Autor, TipoAutor
from sapl.crispy_layout_mixin import form_actions, to_row
from sapl.materia.models import TipoMateriaLegislativa, UnidadeTramitacao
from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa, UnidadeTramitacao
from sapl.utils import (RANGE_ANOS, AnoNumeroOrderingFilter,
RangeWidgetOverride, autor_label, autor_modal)
@ -20,6 +20,7 @@ from .models import (DocumentoAcessorioAdministrativo, DocumentoAdministrativo,
Protocolo, TipoDocumentoAdministrativo,
TramitacaoAdministrativo)
TIPOS_PROTOCOLO = [('0', 'Recebido'), ('1', 'Enviado'), ('', 'Ambos')]
TIPOS_PROTOCOLO_CREATE = [('0', 'Recebido'), ('1', 'Enviado')]
@ -233,6 +234,18 @@ class AnularProcoloAdmForm(ModelForm):
except ObjectDoesNotExist:
raise forms.ValidationError(
_("Protocolo %s/%s não existe" % (numero, ano)))
exists = False
if protocolo.tipo_materia:
exists = MateriaLegislativa.objects.filter(
numero_protocolo=protocolo.numero, ano=protocolo.ano).exists()
elif protocolo.tipo_documento:
exists = protocolo.documentoadministrativo_set.all(
).order_by('-ano', '-numero').exists()
if exists:
raise forms.ValidationError(
_("Protocolo %s/%s não pode ser removido pois existem"
"documentos vinculados a ele." % (numero, ano)))
class Meta:
model = Protocolo
@ -334,9 +347,14 @@ class ProtocoloDocumentForm(ModelForm):
class ProtocoloMateriaForm(ModelForm):
autor = forms.ModelChoiceField(required=True,
empty_label='------',
queryset=Autor.objects.all()
)
empty_label='------',
queryset=Autor.objects.all()
)
tipo_autor = forms.ModelChoiceField(required=True,
empty_label='------',
queryset=TipoAutor.objects.all()
)
tipo_materia = forms.ModelChoiceField(
label=_('Tipo de Matéria'),
@ -353,12 +371,12 @@ class ProtocoloMateriaForm(ModelForm):
assunto_ementa = forms.CharField(required=True,
widget=forms.Textarea, label='Ementa')
class Meta:
model = Protocolo
fields = ['tipo_materia',
'numero_paginas',
'autor',
'tipo_autor',
'assunto_ementa',
'observacao']
@ -376,9 +394,9 @@ class ProtocoloMateriaForm(ModelForm):
row1 = to_row(
[('tipo_materia', 4),
('numero_paginas', 4)])
row2 = to_row(
[('autor', 4)])
('numero_paginas', 2),
('tipo_autor', 3),
('autor', 3)])
row3 = to_row(
[('assunto_ementa', 12)])
row4 = to_row(
@ -387,7 +405,7 @@ class ProtocoloMateriaForm(ModelForm):
self.helper = FormHelper()
self.helper.layout = Layout(
Fieldset(_('Identificação da Matéria'),
row1, row2, row3,
row1, row3,
row4, form_actions(save_label='Protocolar Matéria')))
super(ProtocoloMateriaForm, self).__init__(

7
sapl/protocoloadm/views.py

@ -287,6 +287,13 @@ class ProtocoloDocumentoView(PermissionRequiredMixin,
if numeracao == 'A':
numero = Protocolo.objects.filter(
ano=date.today().year).aggregate(Max('numero'))
elif numeracao == 'L':
legislatura = Legislatura.objects.last()
data_inicio = legislatura.data_inicio
data_fim = legislatura.data_fim
numero = Protocolo.objects.filter(
data__gte=data_inicio, data__lte=data_fim).aggregate(
Max('numero'))
elif numeracao == 'U':
numero = Protocolo.objects.all().aggregate(Max('numero'))

66
sapl/redireciona_urls/views.py

@ -50,6 +50,20 @@ relatorio_materia_por_ano_autor_tipo = (
historico_tramitacoes = (app_relatorios + ':historico_tramitacoes')
def has_iframe(url, request):
iframe = request.GET.get(
'iframe',
EMPTY_STRING)
if iframe:
iframe_qs= ("iframe=" + iframe)
url += ("&" if "?" in url else "?")
url += iframe_qs
return url
class RedirecionaSAPLIndex(RedirectView):
permanent = True
@ -59,6 +73,9 @@ class RedirecionaSAPLIndex(RedirectView):
url = reverse(url_pattern)
except NoReverseMatch:
raise UnknownUrlNameError(url_pattern)
url = has_iframe(url, self.request)
return url
@ -90,6 +107,8 @@ class RedirecionaParlamentar(RedirectView):
args = '?pk=' + numero_legislatura
url = "%s%s" % (url, args)
url = has_iframe(url, self.request)
return url
@ -112,6 +131,9 @@ class RedirecionaComissao(RedirectView):
url = reverse(comissao_list)
except NoReverseMatch:
raise UnknownUrlNameError(comissao_list)
url = has_iframe(url, self.request)
return url
@ -151,6 +173,8 @@ class RedirecionaPautaSessao(RedirectView):
args += "&tipo=&salvar=Pesquisar"
url = "%s%s" % (url, args)
url = has_iframe(url, self.request)
return url
@ -198,6 +222,8 @@ class RedirecionaSessaoPlenaria(RedirectView):
args += "&tipo=%s&salvar=Pesquisar" % (tipo_sessao)
url = "%s%s" % (url, args)
url = has_iframe(url, self.request)
return url
@ -210,6 +236,9 @@ class RedirecionaRelatoriosList(RedirectView):
url = reverse(relatorios_list)
except NoReverseMatch:
raise UnknownUrlNameError(relatorios_list)
url = has_iframe(url, self.request)
return url
@ -253,6 +282,8 @@ class RedirecionaRelatoriosMateriasEmTramitacaoList(RedirectView):
args += "&salvar=%s" % (salvar)
url = "%s%s" % (url, args)
url = has_iframe(url, self.request)
return url
@ -260,13 +291,18 @@ class RedirecionaMateriaLegislativaDetail(RedirectView):
permanent = True
def get_redirect_url(self):
url = EMPTY_STRING
pk = self.request.GET.get('cod_materia', EMPTY_STRING)
if pk:
kwargs = {'pk': pk}
return reverse(materialegislativa_detail, kwargs=kwargs)
url = reverse(materialegislativa_detail, kwargs=kwargs)
else:
return reverse(materialegislativa_list)
url = reverse(materialegislativa_list)
url = has_iframe(url, self.request)
return url
class RedirecionaMateriaLegislativaList(RedirectView):
@ -339,6 +375,8 @@ class RedirecionaMateriaLegislativaList(RedirectView):
url = "%s%s" % (url, args)
url = has_iframe(url, self.request)
return url
@ -346,11 +384,14 @@ class RedirecionaMesaDiretoraView(RedirectView):
permanent = True
def get_redirect_url(self):
url = EMPTY_STRING
try:
url = reverse(parlamentar_mesa_diretora)
except NoReverseMatch:
raise UnknownUrlNameError(parlamentar_mesa_diretora)
url = has_iframe(url, self.request)
return url
@ -358,13 +399,18 @@ class RedirecionaNormasJuridicasDetail(RedirectView):
permanent = True
def get_redirect_url(self):
url = EMPTY_STRING
pk_norma = self.request.GET.get('cod_norma', EMPTY_STRING)
if pk_norma:
kwargs = {'pk': pk_norma}
return reverse(norma_juridica_detail, kwargs=kwargs)
url = reverse(norma_juridica_detail, kwargs=kwargs)
else:
return reverse(norma_juridica_pesquisa)
url = reverse(norma_juridica_pesquisa)
url = has_iframe(url, self.request)
return url
class RedirecionaNormasJuridicasList(RedirectView):
@ -420,6 +466,8 @@ class RedirecionaNormasJuridicasList(RedirectView):
url = "%s%s" % (url, args)
url = has_iframe(url, self.request)
return url
@ -475,6 +523,8 @@ class RedirecionaHistoricoTramitacoesList(RedirectView):
url = "%s%s" % (url, args)
url = has_iframe(url, self.request)
return url
@ -507,6 +557,8 @@ class RedirecionaAtasList(RedirectView):
url = "%s%s" % (url, args)
url = has_iframe(url, self.request)
return url
@ -539,6 +591,8 @@ class RedirecionaPresencaParlamentares(RedirectView):
url = "%s%s" % (url, args)
url = has_iframe(url, self.request)
return url
@ -553,6 +607,8 @@ class RedirecionaMateriasPorAutor(RedirectView):
except NoReverseMatch:
raise UnknownUrlNameError(relatorio_materia_por_autor)
url = has_iframe(url, self.request)
return url
@ -574,4 +630,6 @@ class RedirecionaMateriasPorAnoAutorTipo(RedirectView):
args += "&salvar=%s" % ('Pesquisar')
url = "%s%s" % (url, args)
url = has_iframe(url, self.request)
return url

25
sapl/sessao/migrations/0010_auto_20170810_1033.py

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.11 on 2017-08-10 10:33
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('sessao', '0009_auto_20170619_1441'),
]
operations = [
migrations.AddField(
model_name='registrovotacao',
name='data_hora_atualizacao',
field=models.DateTimeField(auto_now=True, null=True, verbose_name='Data'),
),
migrations.AddField(
model_name='registrovotacao',
name='data_hora_criacao',
field=models.DateTimeField(auto_now_add=True, null=True, verbose_name='Data Criação'),
),
]

11
sapl/sessao/models.py

@ -400,6 +400,17 @@ class RegistroVotacao(models.Model):
observacao = models.TextField(
blank=True, verbose_name=_('Observações'))
data_hora_criacao = models.DateTimeField(
blank=True, null=True,
auto_now_add=True,
verbose_name=_('Data Criação'))
data_hora_atualizacao = models.DateTimeField(
blank=True, null=True,
auto_now=True,
verbose_name=_('Data'))
class Meta:
verbose_name = _('Votação')
verbose_name_plural = _('Votações')

2
sapl/sessao/views.py

@ -2267,7 +2267,7 @@ class PautaSessaoDetailView(DetailView):
tipo = TipoExpediente.objects.get(
id=e.tipo_id)
conteudo = sub(
'&nbsp;', ' ', strip_tags(e.conteudo))
'&nbsp;', ' ', strip_tags(e.conteudo.replace('<br/>', '\n')))
ex = {'tipo': tipo, 'conteudo': conteudo}
expedientes.append(ex)

10
sapl/settings.py

@ -22,6 +22,7 @@ from unipath import Path
from .temp_suppress_crispy_form_warnings import \
SUPRESS_CRISPY_FORM_WARNINGS_LOGGING
BASE_DIR = Path(__file__).ancestor(1)
PROJECT_DIR = Path(__file__).ancestor(2)
@ -38,7 +39,10 @@ ALLOWED_HOSTS = ['*']
LOGIN_REDIRECT_URL = '/'
LOGIN_URL = '/login/?next='
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
if DEBUG:
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
else:
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
# SAPL business apps in dependency order
@ -101,7 +105,7 @@ if SOLR_URL:
HAYSTACK_CONNECTIONS = {
'default': {
'ENGINE': SEARCH_BACKEND,
SEARCH_URL[0] : SEARCH_URL[1]
SEARCH_URL[0]: SEARCH_URL[1]
},
}
@ -200,7 +204,7 @@ MAX_IMAGE_UPLOAD_SIZE = 2 * 1024 * 1024 # 2MB
# https://docs.djangoproject.com/en/1.8/topics/i18n/
LANGUAGE_CODE = 'pt-br'
LANGUAGES = (
('pt-br','Português'),
('pt-br', 'Português'),
)
TIME_ZONE = 'America/Sao_Paulo'

17
sapl/static/js/app.js

@ -58,7 +58,7 @@ function autorModal() {
autoOpen: false,
modal: true,
width: 500,
height: 300,
height: 340,
show: {
effect: "blind",
duration: 500},
@ -90,11 +90,11 @@ function autorModal() {
$("#pesquisar").click(function() {
var name_in_query = $("#q").val()
var q_0 = "q_0=nome__icontains"
var q_1 = "q_1=" + name_in_query
query = q_0 + "&" + q_1
//var q_0 = "q_0=nome__icontains"
//var q_1 = name_in_query
//query = q_1
$.get("/api/autor?" + query, function(data, status) {
$.get("/api/autor?q=" + name_in_query, function(data, status) {
$("#div-resultado").children().remove();
if (data.pagination.total_entries == 0) {
$("#selecionar").attr("hidden", "hidden");
@ -111,11 +111,12 @@ function autorModal() {
select.append($("<option>").attr('value', item.value).text(item.text));
});
$("#div-resultado").append("<br/>").append(select);
$("#selecionar").removeAttr("hidden", "hidden");
if (data.pagination.total_pages > 1)
$("#div-resultado").prepend('<span><br/>Mostrando 10 primeiros autores relativos a sua busca.<br/></span>');
$("#selecionar").click(function() {
res = $("#resultados option:selected");
id = res.val();
@ -235,4 +236,4 @@ function getCookie(name) {
}
}
return cookieValue;
}
}

4
sapl/static/styles/app.scss

@ -233,8 +233,8 @@ fieldset {
}
.avatar-parlamentar {
height: 84px;
width: 84px;
height: 106px;
width: 141px;
margin: 0 auto;
display: table;
}

5
sapl/templates/base/autor_form.html

@ -28,12 +28,11 @@ $(document).ready(function(){
var update_search = function(pk, atualizar=true) {
var q = $('#id_q').val();
var url = '{% url 'sapl.api:autor_list'%}'
var url = '{% url 'sapl.api:autores_provaveis_list'%}'
var formData = {
'q' : q,
'tipo' : pk,
'tr' : '2' // tipo_resultado = 2 - api fornecerá possíveis Autores
'tipo' : pk
}
$.get(url, formData).done(function(data) {
active('pesquisa');

15
sapl/templates/materia/autoria_form.html

@ -16,12 +16,17 @@
$(document).ready(function() {
$("#id_tipo_autor").change(function() {
var tipo_selecionado = $("#id_tipo_autor").val();
var autor_selecionado = $("#id_autor").val();
$("#id_autor option").remove()
var selected = $("#id_tipo_autor").val();
if (selected !== undefined && selected !== null) {
$.getJSON("/api/autor?tipo=" + selected, function(data){
if (tipo_selecionado !== undefined && tipo_selecionado !== null) {
var json_data = {
tipo : tipo_selecionado,
data_relativa : $("#id_data_relativa").val()
}
$.getJSON("/api/autor/possiveis", json_data, function(data){
if (data) {
var results = data.results.sort(compare);
var results = data.sort(compare);
if (results.length > 1) {
$("#id_autor").append("<option>-----</option>");
}
@ -31,10 +36,12 @@
.attr("value", obj.value)
.text(obj.text));
});
$("#id_autor").val(autor_selecionado);
}
});
}
});
$("#id_tipo_autor").trigger('change');
});
</script>
{% endblock %}

4
sapl/templates/materia/autoria_list.html

@ -6,8 +6,8 @@
{% block more_buttons %}
{% if perms|get_add_perm:view %}
<a href="{% url 'sapl.materia:adicionar_varias_autorias' root_pk %}" class="btn btn-default">
{% blocktrans with verbose_name=view.verbose_name %} Adicionar Várias Autorias {% endblocktrans %}
<a href="{% url 'sapl.materia:autoria_multicreate' root_pk %}" class="btn btn-default">
{% trans "Adicionar Várias Autorias" %}
</a>
{% endif %}

51
sapl/templates/materia/autoria_multicreate_form.html

@ -0,0 +1,51 @@
{% extends "crud/form.html" %}
{% load i18n %}
{% load crispy_forms_tags %}
{% load common_tags %}
{% block extra_js %}
<script language="Javascript">
$(document).ready(function() {
var autores_pre_cadastrados = $.parseJSON($("#id_autores").val());
$("#id_tipo_autor").change(function() {
var tipo_selecionado = $("#id_tipo_autor").val();
$("#id_autor option").remove()
if (tipo_selecionado !== '') {
var json_data = {
tipo : tipo_selecionado,
data_relativa : $("#id_data_relativa").val()
}
$.getJSON("/api/autor/possiveis", json_data, function(data){
$("#div_id_autor .controls").html('');
if (data) {
var results = data;
$.each(results, function(idx, obj) {
if (autores_pre_cadastrados.indexOf(obj.value) !== -1)
return ;
$('<input/>')
.attr('type', 'checkbox')
.attr('name','autor')
.attr('id', 'id_autor_'+idx)
.attr('value', obj.value)
.appendTo(
$('<label/>').text(obj.text)
.appendTo(
$('<div class="checkbox">')
.appendTo($("#div_id_autor .controls")
)
)
)
});
$('[type=checkbox]').checkbox();
}
});
}
});
$("#id_tipo_autor").trigger('change');
});
</script>
{% endblock %}

7
sapl/templates/materia/layouts.yaml

@ -61,13 +61,6 @@ Autoria:
{% trans 'Autoria' %}:
- autor primeiro_autor
AutoriaCreate:
{% trans 'Autoria' %}:
- tipo_autor autor primeiro_autor
AutoriaUpdate:
{% trans 'Autoria' %}:
- tipo_autor autor primeiro_autor
DocumentoAcessorio:
{% trans 'Documento Acessório' %}:

24
sapl/templates/materia/materialegislativa_filter.html

@ -78,17 +78,19 @@
<strong>Data Fim Prazo (Tramitação):</strong>&nbsp;{{m.tramitacao_set.last.data_fim_prazo|default_if_none:""}}</br>
{% endif %}
{% if m.registrovotacao_set.exists %}
<strong>Data da última Votação:</strong>
{% if m.registrovotacao_set.last.ordem %}
<a href="{% url 'sapl.sessao:ordemdia_list' m.registrovotacao_set.last.ordem.sessao_plenaria_id %}">
{{ m.registrovotacao_set.last.ordem.data_ordem }}
</a>
{% elif m.registrovotacao_set.last.expediente %}
<a href="{% url 'sapl.sessao:expedientemateria_list' m.registrovotacao_set.last.expediente.sessao_plenaria_id %}">
{{ m.registrovotacao_set.last.expediente.data_ordem }}
</a>
{% endif %}
</br>
<strong>Data Votação:</strong>
{% for rv in m.registrovotacao_set.all %}
{% if rv.ordem %}
<a href="{% url 'sapl.sessao:ordemdia_list' rv.ordem.sessao_plenaria_id %}">
{{ rv.ordem.data_ordem }}
</a>
{% elif rv.expediente %}
<a href="{% url 'sapl.sessao:expedientemateria_list' rv.expediente.sessao_plenaria_id %}">
{{ rv.expediente.data_ordem }}
</a>
{% endif %}
</br>
{% endfor %}
{% endif %}
{% if m.tramitacao_set.last.data_tramitacao %}
<strong>Data da última Tramitação:</strong> &nbsp;{{m.tramitacao_set.last.data_tramitacao}}</br>

41
sapl/templates/protocoloadm/protocolar_materia.html

@ -13,3 +13,44 @@
{% block detail_content %}
{% crispy form %}
{% endblock detail_content %}
{% block extra_js %}
<script language="Javascript">
function compare(a, b) {
if (a.text < b.text)
return -1;
if (a.text > b.text)
return 1;
return 0;
}
$(document).ready(function() {
$("#id_tipo_autor").change(function() {
var tipo_selecionado = $("#id_tipo_autor").val();
var autor_selecionado = $("#id_autor").val();
$("#id_autor option").remove()
if (tipo_selecionado !== undefined && tipo_selecionado !== null) {
var json_data = {
tipo : tipo_selecionado
}
$.getJSON("/api/autor/possiveis", json_data, function(data){
if (data) {
var results = data.sort(compare);
if (results.length > 1) {
$("#id_autor").append("<option>-----</option>");
}
$.each(results, function(idx, obj) {
$("#id_autor")
.append($("<option></option>")
.attr("value", obj.value)
.text(obj.text));
});
$("#id_autor").prop("selectedIndex", 0);
}
});
}
});
});
</script>
{% endblock %}

2
sapl/templates/sessao/pauta_sessao_detail.html

@ -25,7 +25,7 @@
<tr>
<td>
<b>{{e.tipo}}: </b> <br />
<p style="text-indent: 50px;">{{e.conteudo|safe}}</p>
<p style="text-indent: 50px;">{{e.conteudo|safe|linebreaks}}</p>
</td>
</tr>
{% endfor %}

41
sapl/utils.py

@ -3,6 +3,7 @@ import logging
import os
import re
from datetime import date
from django_filters.filterset import STRICTNESS
from functools import wraps
from subprocess import PIPE, call
from threading import Thread
@ -20,6 +21,7 @@ from django.contrib import admin
from django.contrib.contenttypes.fields import (GenericForeignKey, GenericRel,
GenericRelation)
from django.core.exceptions import ValidationError
from django.utils import six
from django.utils.translation import ugettext_lazy as _
from floppyforms import ClearableFileInput
from reversion.admin import VersionAdmin
@ -557,6 +559,45 @@ def texto_upload_path(instance, filename, subpath='', pk_first=False):
return path
def qs_override_django_filter(self):
if not hasattr(self, '_qs'):
valid = self.is_bound and self.form.is_valid()
if self.is_bound and not valid:
if self.strict == STRICTNESS.RAISE_VALIDATION_ERROR:
raise forms.ValidationError(self.form.errors)
elif bool(self.strict) == STRICTNESS.RETURN_NO_RESULTS:
self._qs = self.queryset.none()
return self._qs
# else STRICTNESS.IGNORE... ignoring
# start with all the results and filter from there
qs = self.queryset.all()
for name, filter_ in six.iteritems(self.filters):
value = None
if valid:
value = self.form.cleaned_data[name]
else:
raw_value = self.form[name].value()
try:
value = self.form.fields[name].clean(raw_value)
except forms.ValidationError:
if self.strict == STRICTNESS.RAISE_VALIDATION_ERROR:
raise
elif bool(self.strict) == STRICTNESS.RETURN_NO_RESULTS:
self._qs = self.queryset.none()
return self._qs
# else STRICTNESS.IGNORE... ignoring
if value is not None: # valid & clean data
qs = qs._next_is_sticky()
qs = filter_.filter(qs, value)
self._qs = qs
return self._qs
def filiacao_data(parlamentar, data):
from sapl.parlamentares.models import Filiacao

Loading…
Cancel
Save