Browse Source

Merge branch 'master' of github.com:interlegis/sapl into static_analysis

pull/1120/head
Victor Arnaud 9 years ago
parent
commit
244ab1f7f5
  1. 2
      .gitignore
  2. 12
      Dockerfile
  3. 1
      MANIFEST.in
  4. 5
      README.rst
  5. 2
      config/env-sample
  6. 2
      config/env_dockerfile
  7. 2
      docker-compose.yml
  8. 20
      docs/deploy.rst
  9. 2
      docs/instacao31.rst
  10. 22
      docs/solr.rst
  11. 1
      requirements/requirements.txt
  12. 58
      sapl/api/forms.py
  13. 24
      sapl/api/serializers.py
  14. 4
      sapl/api/urls.py
  15. 77
      sapl/api/views.py
  16. 20
      sapl/base/migrations/0003_auto_20170519_1106.py
  17. 2
      sapl/base/models.py
  18. 25
      sapl/base/search_indexes.py
  19. 3
      sapl/base/urls.py
  20. 9
      sapl/crud/base.py
  21. 57
      sapl/legacy/migracao_documentos.py
  22. 71
      sapl/legacy/migration.py
  23. 12
      sapl/materia/forms.py
  24. 32
      sapl/materia/migrations/0005_auto_20170522_1051.py
  25. 32
      sapl/materia/migrations/0005_auto_20170522_1904.py
  26. 16
      sapl/materia/migrations/0006_merge.py
  27. 14
      sapl/materia/models.py
  28. 78
      sapl/materia/views.py
  29. 19
      sapl/norma/migrations/0003_auto_20170510_1549.py
  30. 22
      sapl/norma/migrations/0004_auto_20170522_1051.py
  31. 27
      sapl/norma/migrations/0004_auto_20170522_1115.py
  32. 16
      sapl/norma/migrations/0005_merge.py
  33. 7
      sapl/norma/models.py
  34. 12
      sapl/parlamentares/models.py
  35. 4
      sapl/parlamentares/urls.py
  36. 99
      sapl/parlamentares/views.py
  37. 1
      sapl/protocoloadm/views.py
  38. 30
      sapl/relatorios/templates/pdf_sessao_plenaria_gerar.py
  39. 1
      sapl/rules/map_rules.py
  40. 81
      sapl/sessao/forms.py
  41. 1
      sapl/sessao/legacy.yaml
  42. 20
      sapl/sessao/migrations/0002_sessaoplenaria_interativa.py
  43. 35
      sapl/sessao/migrations/0003_resumoordenacao.py
  44. 28
      sapl/sessao/models.py
  45. 17
      sapl/sessao/serializers.py
  46. 6
      sapl/sessao/urls.py
  47. 101
      sapl/sessao/views.py
  48. 21
      sapl/settings.py
  49. 4
      sapl/templates/base.html
  50. 2
      sapl/templates/materia/documentoacessorio_form.html
  51. 17
      sapl/templates/materia/em_lote/acessorio.html
  52. 10
      sapl/templates/materia/materialegislativa_filter.html
  53. 18
      sapl/templates/navbar.yaml
  54. 3
      sapl/templates/parlamentares/composicaomesa_form.html
  55. 2
      sapl/templates/parlamentares/parlamentar_perfil_publico.html
  56. 89
      sapl/templates/parlamentares/public_composicaomesa_form.html
  57. 8
      sapl/templates/protocoloadm/protocolo_filter.html
  58. 4
      sapl/templates/protocoloadm/protocolo_list.html
  59. 10
      sapl/templates/protocoloadm/protocolo_mostrar.html
  60. 8
      sapl/templates/sessao/blocos_resumo/conteudo_multimidia.html
  61. 17
      sapl/templates/sessao/blocos_resumo/expedientes.html
  62. 8
      sapl/templates/sessao/blocos_resumo/identificacao_basica.html
  63. 8
      sapl/templates/sessao/blocos_resumo/lista_presenca.html
  64. 8
      sapl/templates/sessao/blocos_resumo/lista_presenca_ordem_dia.html
  65. 25
      sapl/templates/sessao/blocos_resumo/materias_expediente.html
  66. 25
      sapl/templates/sessao/blocos_resumo/materias_ordem_dia.html
  67. 10
      sapl/templates/sessao/blocos_resumo/mesa_diretora.html
  68. 14
      sapl/templates/sessao/blocos_resumo/oradores_expediente.html
  69. 14
      sapl/templates/sessao/blocos_resumo/oradores_explicacoes.html
  70. 151
      sapl/templates/sessao/resumo.html
  71. 13
      sapl/templates/sessao/resumo_ordenacao.html
  72. 9
      sapl/templates/sessao/votacao/nominal_detail.html
  73. 11
      sapl/templates/sessao/votacao/nominal_edit.html
  74. 2
      sapl/templates/sistema.html
  75. 112
      sapl/utils.py
  76. 2
      start.sh

2
.gitignore

@ -93,5 +93,7 @@ bower
bower_components bower_components
media media
whoosh/ whoosh/
solr-4.10.2/
postgres-data/ postgres-data/
data/ data/
solr-*/

12
Dockerfile

@ -2,7 +2,7 @@ FROM alpine:3.5
ENV BUILD_PACKAGES postgresql-dev graphviz-dev graphviz build-base git pkgconfig \ ENV BUILD_PACKAGES postgresql-dev graphviz-dev graphviz build-base git pkgconfig \
python3-dev libxml2-dev jpeg-dev libressl-dev libffi-dev libxslt-dev nodejs py3-lxml \ python3-dev libxml2-dev jpeg-dev libressl-dev libffi-dev libxslt-dev nodejs py3-lxml \
py3-magic postgresql-client vim py3-magic postgresql-client poppler-utils vim
RUN apk add --no-cache python3 nginx && \ RUN apk add --no-cache python3 nginx && \
python3 -m ensurepip && \ python3 -m ensurepip && \
@ -32,9 +32,15 @@ COPY config/env_dockerfile /var/interlegis/sapl/sapl/.env
# manage.py bower install bug: https://github.com/nvbn/django-bower/issues/51 # manage.py bower install bug: https://github.com/nvbn/django-bower/issues/51
# compilescss - Precompile all occurrences of your SASS/SCSS files for the whole project into css files
RUN python3 manage.py bower_install -- --allow-root --no-input && \ RUN python3 manage.py bower_install -- --allow-root --no-input && \
python3 manage.py collectstatic --no-input && \ python3 manage.py compilescss
rm -rf /var/interlegis/sapl/sapl/.env && \
RUN python3 manage.py collectstatic --noinput --clear
# Remove .env(fake) e sapl.db da imagem
RUN rm -rf /var/interlegis/sapl/sapl/.env && \
rm -rf /var/interlegis/sapl/sapl.db rm -rf /var/interlegis/sapl/sapl.db
RUN chmod +x /var/interlegis/sapl/start.sh && \ RUN chmod +x /var/interlegis/sapl/start.sh && \

1
MANIFEST.in

@ -2,5 +2,6 @@ include README.rst LICENSE.txt
recursive-include sapl *.html *.yaml recursive-include sapl *.html *.yaml
recursive-include sapl/static * recursive-include sapl/static *
recursive-include sapl/relatorios/templates *.py recursive-include sapl/relatorios/templates *.py
recursive-include sapl/compilacao *.sql
global-exclude __pycache__ global-exclude __pycache__
global-exclude *.py[co] global-exclude *.py[co]

5
README.rst

@ -30,6 +30,11 @@ Instruções para Deploy
`Deploy SAPL com Nginx + Gunicorn <https://github.com/interlegis/sapl/blob/master/docs/deploy.rst>`_ `Deploy SAPL com Nginx + Gunicorn <https://github.com/interlegis/sapl/blob/master/docs/deploy.rst>`_
Instalação do Solr
======================
`Instalação e configuração do Solr <https://github.com/interlegis/sapl/blob/master/docs/solr.rst>`_
Instruções para Tradução Instruções para Tradução
======================== ========================

2
config/env-sample

@ -1,6 +1,6 @@
DATABASE_URL = postgresql://postgres:@sapldb:/sapl DATABASE_URL = postgresql://postgres:@sapldb:/sapl
KEY KEY
DEBUG = True DEBUG = False
EMAIL_USE_TLS = True EMAIL_USE_TLS = True
EMAIL_PORT = 587 EMAIL_PORT = 587
EMAIL_HOST = '' EMAIL_HOST = ''

2
config/env_dockerfile

@ -1,6 +1,6 @@
DATABASE_URL = sqlite:///sapl.db DATABASE_URL = sqlite:///sapl.db
SECRET_KEY = 'Dockerfile_Key' SECRET_KEY = 'Dockerfile_Key'
DEBUG = True DEBUG = False
EMAIL_USE_TLS = True EMAIL_USE_TLS = True
EMAIL_PORT = 587 EMAIL_PORT = 587
EMAIL_HOST = '' EMAIL_HOST = ''

2
docker-compose.yml

@ -10,7 +10,7 @@ sapldb:
ports: ports:
- "5532:5432" - "5532:5432"
sapl: sapl:
image: interlegis/sapl:latest image: interlegis/sapl:3.1.2-BETA
volumes: volumes:
- sapl_data:/var/interlegis/sapl/data - sapl_data:/var/interlegis/sapl/data
- sapl_media:/var/interlegis/sapl/media - sapl_media:/var/interlegis/sapl/media

20
docs/deploy.rst

@ -18,6 +18,18 @@ alterando o variável DEBUG para false::
DEBUG = False DEBUG = False
Arquivos Estáticos
------------------
Com o ambiente em produção, os arquivos estáticos devem ser servidos pelo web service, em nosso caso o `NGINX`, logo para ter acesso aos arquivos primeiro devemos rodar o seguinte comando::
python3 manage.py compilescss
para que os arquivos SASS/SCSS sejam compilados em arquivos .css em ambiente de produção, e em seguida rode::
pyhton3 manage.py collectstatic --no-input
para coletar todos os arquivos estáticos do projeto e guarda-los no diretório definido em `STATIC_ROOT`, que será também o diretório no qual o `NGINX` irá referenciar para a aplicação.
Instalando Pacotes Instalando Pacotes
------------------ ------------------
@ -108,3 +120,11 @@ Para multiplas aplicações Django.
http://michal.karzynski.pl/blog/2013/10/29/serving-multiple-django-applications-with-nginx-gunicorn-supervisor/ http://michal.karzynski.pl/blog/2013/10/29/serving-multiple-django-applications-with-nginx-gunicorn-supervisor/
Compilar arquivos SASS/SCSS
https://github.com/jrief/django-sass-processor#offline-compilation
https://github.com/jrief/django-sass-processor/issues/34#issuecomment-252611103
Deploy Arquivos Estáticos
https://docs.djangoproject.com/pt-br/1.11/howto/static-files/deployment/

2
docs/instacao31.rst

@ -28,7 +28,7 @@ Instalar as seguintes dependências do sistema::
pkg-config postgresql postgresql-contrib pgadmin3 python-psycopg2 \ pkg-config postgresql postgresql-contrib pgadmin3 python-psycopg2 \
software-properties-common build-essential libxml2-dev libjpeg-dev \ software-properties-common build-essential libxml2-dev libjpeg-dev \
libmysqlclient-dev libssl-dev libffi-dev libxslt1-dev python3-setuptools \ libmysqlclient-dev libssl-dev libffi-dev libxslt1-dev python3-setuptools \
python3-pip curl python3-pip curl poppler-utils
sudo -i sudo -i
curl -sL https://deb.nodesource.com/setup_5.x | bash - curl -sL https://deb.nodesource.com/setup_5.x | bash -

22
docs/solr.rst

@ -0,0 +1,22 @@
================================
Instruções para instalar o Solr
================================
Solr é a ferramenta utilizada pelo SAPL 3.1 para indexar documentos para que possa ser feita
a Pesquisa Textual.
Dentro do diretório principal siga os seguintes passos::
curl -LO https://archive.apache.org/dist/lucene/solr/4.10.2/solr-4.10.2.tgz
tar xvzf solr-4.10.2.tgz
cd solr-4.10.2
cd example
java -jar start.jar
./manage.py build_solr_schema --filename solr-4.10.2/example/solr/collection1/conf/schema.xml
Após isso, deve-se parar o servidor do Solr e restartar com ``java -jar start.jar``
**OBS: Toda vez que o código da pesquisa textual for modificado, os comandos de build_solr_schema e start.jar devem ser rodados, nessa mesma ordem.**

1
requirements/requirements.txt

@ -28,6 +28,7 @@ pyyaml==3.11
rtyaml==0.0.3 rtyaml==0.0.3
textract==1.5.0 textract==1.5.0
unipath==1.1 unipath==1.1
pysolr==3.6.0
python-magic==0.4.12 python-magic==0.4.12
gunicorn==19.6.0 gunicorn==19.6.0
django-reversion==2.0.8 django-reversion==2.0.8

58
sapl/api/forms.py

@ -1,5 +1,8 @@
from django.db.models import Q from django.db.models import Q
from django.forms.fields import MultiValueField, CharField
from django.forms.widgets import TextInput, MultiWidget
from django_filters.filters import MethodFilter, ModelChoiceFilter from django_filters.filters import MethodFilter, ModelChoiceFilter
from rest_framework.compat import django_filters
from rest_framework.filters import FilterSet from rest_framework.filters import FilterSet
from sapl.base.models import Autor, TipoAutor from sapl.base.models import Autor, TipoAutor
@ -35,10 +38,11 @@ class SaplGenericRelationSearchFilterSet(FilterSet):
item.related_query_name(), item.related_query_name(),
field[0]) field[0])
) )
if len(field) == 3 and field[2](qtext) is not None:
q_fs = q_fs | Q(**{'%s__%s%s' % ( q_fs = q_fs | Q(**{'%s__%s%s' % (
item.related_query_name(), item.related_query_name(),
field[0], field[0],
field[1]): qtext}) field[1]): qtext if len(field) == 2 else field[2](qtext)})
q = q & q_fs q = q & q_fs
@ -48,6 +52,37 @@ class SaplGenericRelationSearchFilterSet(FilterSet):
return queryset return queryset
class SearchForFieldWidget(MultiWidget):
def decompress(self, value):
if value is None:
return [None, None]
return value
def __init__(self, attrs=None):
widgets = (TextInput, TextInput)
MultiWidget.__init__(self, widgets, attrs)
class SearchForFieldField(MultiValueField):
widget = SearchForFieldWidget
def __init__(self, *args, **kwargs):
fields = (
CharField(),
CharField())
super(SearchForFieldField, self).__init__(fields, *args, **kwargs)
def compress(self, parameters):
if parameters:
return parameters
return None
class SearchForFieldFilter(django_filters.filters.MethodFilter):
field_class = SearchForFieldField
class AutorChoiceFilterSet(SaplGenericRelationSearchFilterSet): class AutorChoiceFilterSet(SaplGenericRelationSearchFilterSet):
q = MethodFilter() q = MethodFilter()
tipo = ModelChoiceFilter(queryset=TipoAutor.objects.all()) tipo = ModelChoiceFilter(queryset=TipoAutor.objects.all())
@ -60,4 +95,23 @@ class AutorChoiceFilterSet(SaplGenericRelationSearchFilterSet):
def filter_q(self, queryset, value): def filter_q(self, queryset, value):
return SaplGenericRelationSearchFilterSet.filter_q( return SaplGenericRelationSearchFilterSet.filter_q(
self, queryset, value).order_by('nome') self, queryset, value).distinct('nome').order_by('nome')
class AutorSearchForFieldFilterSet(AutorChoiceFilterSet):
q = SearchForFieldFilter()
class Meta(AutorChoiceFilterSet.Meta):
pass
def filter_q(self, queryset, value):
value[0] = value[0].split(',')
value[1] = value[1].split(',')
params = {}
for key, v in list(zip(value[0], value[1])):
if v in ['True', 'False']:
v = '1' if v == 'True' else '0'
params[key] = v
return queryset.filter(**params).distinct('nome').order_by('nome')

24
sapl/api/serializers.py

@ -2,6 +2,7 @@ from rest_framework import serializers
from sapl.base.models import Autor from sapl.base.models import Autor
from sapl.materia.models import MateriaLegislativa from sapl.materia.models import MateriaLegislativa
from sapl.sessao.models import SessaoPlenaria
class ChoiceSerializer(serializers.Serializer): class ChoiceSerializer(serializers.Serializer):
@ -45,6 +46,7 @@ class AutorSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Autor model = Autor
fields = '__all__'
class MateriaLegislativaSerializer(serializers.ModelSerializer): class MateriaLegislativaSerializer(serializers.ModelSerializer):
@ -52,3 +54,25 @@ class MateriaLegislativaSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = MateriaLegislativa model = MateriaLegislativa
fields = '__all__' fields = '__all__'
class SessaoPlenariaSerializer(serializers.ModelSerializer):
tipo = serializers.StringRelatedField(many=False)
sessao_legislativa = serializers.StringRelatedField(many=False)
legislatura = serializers.StringRelatedField(many=False)
class Meta:
model = SessaoPlenaria
fields = ('pk',
'tipo',
'sessao_legislativa',
'legislatura',
'data_inicio',
'hora_inicio',
'hora_fim',
'url_video',
'iniciada',
'finalizada',
'interativa'
)

4
sapl/api/urls.py

@ -3,7 +3,7 @@ from django.conf.urls import include, url
from rest_framework.routers import DefaultRouter from rest_framework.routers import DefaultRouter
from sapl.api.views import (AutorListView, MateriaLegislativaViewSet, from sapl.api.views import (AutorListView, MateriaLegislativaViewSet,
ModelChoiceView) ModelChoiceView, SessaoPlenariaViewSet)
from .apps import AppConfig from .apps import AppConfig
@ -12,9 +12,9 @@ app_name = AppConfig.name
router = DefaultRouter() router = DefaultRouter()
router.register(r'materia', MateriaLegislativaViewSet) router.register(r'materia', MateriaLegislativaViewSet)
router.register(r'sessao-plenaria', SessaoPlenariaViewSet)
urlpatterns_router = router.urls urlpatterns_router = router.urls
urlpatterns_api = [ urlpatterns_api = [
url(r'^autor', AutorListView.as_view(), name='autor_list'), url(r'^autor', AutorListView.as_view(), name='autor_list'),

77
sapl/api/views.py

@ -6,16 +6,19 @@ from rest_framework.filters import DjangoFilterBackend
from rest_framework.generics import ListAPIView from rest_framework.generics import ListAPIView
from rest_framework.mixins import ListModelMixin, RetrieveModelMixin from rest_framework.mixins import ListModelMixin, RetrieveModelMixin
from rest_framework.permissions import (IsAuthenticated, from rest_framework.permissions import (IsAuthenticated,
IsAuthenticatedOrReadOnly) IsAuthenticatedOrReadOnly,
from rest_framework.viewsets import GenericViewSet AllowAny)
from rest_framework.viewsets import GenericViewSet, ModelViewSet
from sapl.api.forms import AutorChoiceFilterSet from sapl.api.forms import AutorChoiceFilterSet, AutorSearchForFieldFilterSet
from sapl.api.serializers import (AutorChoiceSerializer, AutorSerializer, from sapl.api.serializers import (AutorChoiceSerializer, AutorSerializer,
ChoiceSerializer, ChoiceSerializer,
MateriaLegislativaSerializer, MateriaLegislativaSerializer,
ModelChoiceSerializer) ModelChoiceSerializer,
SessaoPlenariaSerializer)
from sapl.base.models import Autor, TipoAutor from sapl.base.models import Autor, TipoAutor
from sapl.materia.models import MateriaLegislativa from sapl.materia.models import MateriaLegislativa
from sapl.sessao.models import SessaoPlenaria
from sapl.utils import SaplGenericRelation, sapl_logger from sapl.utils import SaplGenericRelation, sapl_logger
@ -76,7 +79,57 @@ class AutorListView(ListAPIView):
o django-filter é desativado e a busca é feita o django-filter é desativado e a busca é feita
no model do ContentType associado ao tipo. no model do ContentType associado ao tipo.
Outros campos - q_0 / q_1 - q_0 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.
http://localhost:8000
/api/autor?tr=1&q_0=parlamentar_set__ativo&q_1=False
/api/autor?tr=1&q_0=parlamentar_set__ativo&q_1=True
/api/autor?tr=3&q_0=parlamentar_set__ativo&q_1=False
/api/autor?tr=3&q_0=parlamentar_set__ativo&q_1=True
http://localhost:8000
/api/autor?tr=1
&q_0=parlamentar_set__nome_completo__icontains,
parlamentar_set__ativo
&q_1=Carvalho,False
/api/autor?tr=1
&q_0=parlamentar_set__nome_completo__icontains,
parlamentar_set__ativo
&q_1=Carvalho,True
/api/autor?tr=3
&q_0=parlamentar_set__nome_completo__icontains,
parlamentar_set__ativo
&q_1=Carvalho,False
/api/autor?tr=3
&q_0=parlamentar_set__nome_completo__icontains,
parlamentar_set__ativo
&q_1=Carvalho,True
não importa o campo que vc passe de qualquer dos Models
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
In [7]: models_with_gr_for_model(Autor)
Out[7]:
[sapl.parlamentares.models.Parlamentar,
sapl.parlamentares.models.Frente,
sapl.comissoes.models.Comissao,
sapl.materia.models.Orgao,
sapl.sessao.models.Bancada,
sapl.sessao.models.Bloco]
qualquer atributo destes models podem ser passados
para busca
""" """
TR_AUTOR_CHOICE_SERIALIZER = 1 TR_AUTOR_CHOICE_SERIALIZER = 1
@ -122,6 +175,9 @@ class AutorListView(ListAPIView):
self.serializer_class = AutorSerializer self.serializer_class = AutorSerializer
self.permission_classes = (IsAuthenticated,) self.permission_classes = (IsAuthenticated,)
if self.filter_class and 'q_0' in request.GET:
self.filter_class = AutorSearchForFieldFilterSet
return ListAPIView.get(self, request, *args, **kwargs) return ListAPIView.get(self, request, *args, **kwargs)
def get_queryset(self): def get_queryset(self):
@ -203,3 +259,14 @@ class MateriaLegislativaViewSet(ListModelMixin,
queryset = MateriaLegislativa.objects.all() queryset = MateriaLegislativa.objects.all()
filter_backends = (DjangoFilterBackend,) filter_backends = (DjangoFilterBackend,)
filter_fields = ('numero', 'ano', 'tipo', ) filter_fields = ('numero', 'ano', 'tipo', )
class SessaoPlenariaViewSet(ListModelMixin,
RetrieveModelMixin,
GenericViewSet):
permission_classes = (AllowAny,)
serializer_class = SessaoPlenariaSerializer
queryset = SessaoPlenaria.objects.all()
filter_backends = (DjangoFilterBackend,)
filter_fields = ('data_inicio', 'data_fim', 'interativa')

20
sapl/base/migrations/0003_auto_20170519_1106.py

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.12 on 2017-05-19 11:06
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('base', '0002_auto_20170331_1900'),
]
operations = [
migrations.AlterField(
model_name='autor',
name='nome',
field=models.CharField(blank=True, max_length=60, verbose_name='Nome do Autor'),
),
]

2
sapl/base/models.py

@ -211,7 +211,7 @@ class Autor(models.Model):
autor_related = GenericForeignKey('content_type', 'object_id') autor_related = GenericForeignKey('content_type', 'object_id')
nome = models.CharField( nome = models.CharField(
max_length=50, blank=True, verbose_name=_('Nome do Autor')) max_length=60, blank=True, verbose_name=_('Nome do Autor'))
cargo = models.CharField(max_length=50, blank=True) cargo = models.CharField(max_length=50, blank=True)

25
sapl/base/search_indexes.py

@ -1,3 +1,4 @@
import logging
import os.path import os.path
import textract import textract
@ -7,6 +8,10 @@ from haystack import indexes
from sapl.materia.models import DocumentoAcessorio, MateriaLegislativa from sapl.materia.models import DocumentoAcessorio, MateriaLegislativa
from sapl.norma.models import NormaJuridica from sapl.norma.models import NormaJuridica
from textract.exceptions import ExtensionNotSupported
from sapl.settings import BASE_DIR
logger = logging.getLogger(BASE_DIR.name)
class DocumentoAcessorioIndex(indexes.SearchIndex, indexes.Indexable): class DocumentoAcessorioIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True) text = indexes.CharField(document=True, use_template=True)
@ -30,17 +35,23 @@ class DocumentoAcessorioIndex(indexes.SearchIndex, indexes.Indexable):
arquivo = getattr(obj, self.filename) arquivo = getattr(obj, self.filename)
if arquivo: if arquivo:
try: if not os.path.exists(arquivo.path):
arquivo.open()
except OSError:
return self.prepared_data return self.prepared_data
if not os.path.splitext(arquivo.path)[1][:1]: if not os.path.splitext(arquivo.path)[1][:1]:
return self.prepared_data return self.prepared_data
try:
extracted_data = textract.process( extracted_data = textract.process(
arquivo.path).decode( arquivo.path,
'utf-8').replace('\n', ' ') language='pt-br').decode('utf-8').replace('\n', ' ')
except ExtensionNotSupported:
return self.prepared_data
except Exception:
msg = 'Erro inesperado processando arquivo: %s' % arquivo.path
print(msg)
logger.error(msg)
return self.prepared_data
extracted_data = extracted_data.replace('\t', ' ') extracted_data = extracted_data.replace('\t', ' ')
@ -48,8 +59,8 @@ class DocumentoAcessorioIndex(indexes.SearchIndex, indexes.Indexable):
# text field with *all* of our metadata visible for templating: # text field with *all* of our metadata visible for templating:
t = loader.select_template(( t = loader.select_template((
'search/indexes/' + self.template_name, )) 'search/indexes/' + self.template_name, ))
data['text'] = t.render(Context({'object': obj, data['text'] = t.render({'object': obj,
'extracted': extracted_data})) 'extracted': extracted_data})
return data return data

3
sapl/base/urls.py

@ -92,7 +92,8 @@ urlpatterns = [
# todos os sublinks de sistema devem vir acima deste # todos os sublinks de sistema devem vir acima deste
url(r'^sistema/$', permission_required('base.view_tabelas_auxiliares') url(r'^sistema/$', permission_required('base.view_tabelas_auxiliares')
(TemplateView.as_view(template_name='sistema.html'))), (TemplateView.as_view(template_name='sistema.html')),
name='sistema'),
url(r'^login/$', views.login, { url(r'^login/$', views.login, {
'template_name': 'base/login.html', 'authentication_form': LoginForm}, 'template_name': 'base/login.html', 'authentication_form': LoginForm},

9
sapl/crud/base.py

@ -9,6 +9,7 @@ from django import forms
from django.conf.urls import url from django.conf.urls import url
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.auth.mixins import PermissionRequiredMixin
from django.core.exceptions import ObjectDoesNotExist
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.db import models from django.db import models
from django.db.models.fields.related import ForeignKey from django.db.models.fields.related import ForeignKey
@ -1133,7 +1134,11 @@ class MasterDetailCrud(Crud):
parent_field = obj.parent_field.split('__') parent_field = obj.parent_field.split('__')
if not obj.is_m2m or len(parent_field) > 1: if not obj.is_m2m or len(parent_field) > 1:
field = self.model._meta.get_field(parent_field[0]) field = self.model._meta.get_field(parent_field[0])
parent = field.related_model.objects.get(pk=self.kwargs['pk']) try:
parent = field.related_model.objects.get(
pk=self.kwargs['pk'])
except ObjectDoesNotExist:
raise Http404()
setattr(form.instance, parent_field[0], parent) setattr(form.instance, parent_field[0], parent)
return form return form
@ -1155,7 +1160,7 @@ class MasterDetailCrud(Crud):
try: try:
parent_object = parent_model.objects.get(**params) parent_object = parent_model.objects.get(**params)
except: except Exception:
raise Http404() raise Http404()
else: else:
parent_model = self.model parent_model = self.model

57
sapl/legacy/migracao_documentos.py

@ -11,6 +11,7 @@ from sapl.materia.models import (DocumentoAcessorio, MateriaLegislativa,
from sapl.norma.models import NormaJuridica from sapl.norma.models import NormaJuridica
from sapl.parlamentares.models import Parlamentar from sapl.parlamentares.models import Parlamentar
from sapl.protocoloadm.models import DocumentoAdministrativo from sapl.protocoloadm.models import DocumentoAdministrativo
from sapl.protocoloadm.models import DocumentoAcessorioAdministrativo
from sapl.sessao.models import SessaoPlenaria from sapl.sessao.models import SessaoPlenaria
from sapl.settings import MEDIA_ROOT from sapl.settings import MEDIA_ROOT
from sapl.utils import delete_texto, save_texto from sapl.utils import delete_texto, save_texto
@ -29,6 +30,17 @@ EXTENSOES = {
'text/html': '.html', 'text/html': '.html',
'text/rtf': '.rtf', 'text/rtf': '.rtf',
'text/x-python': '.py', 'text/x-python': '.py',
'text/plain': '.ksh',
'text/plain': '.c',
'text/plain': '.h',
'text/plain': '.txt',
'text/plain': '.bat',
'text/plain': '.pl',
'text/plain': '.asc',
'text/plain': '.text',
'text/plain': '.pot',
'text/plain': '.brf',
'text/plain': '.srt',
# sem extensao # sem extensao
'application/octet-stream': '', # binário 'application/octet-stream': '', # binário
@ -43,35 +55,41 @@ DOCS = {
Parlamentar: [( Parlamentar: [(
'fotografia', 'fotografia',
'parlamentar/fotos/{}_foto_parlamentar', 'parlamentar/fotos/{}_foto_parlamentar',
'parlamentar/{0}/{0}_foto_parlamentar{1}')], 'public/parlamentar/{0}/{0}_foto_parlamentar{1}')],
MateriaLegislativa: [( MateriaLegislativa: [(
'texto_original', 'texto_original',
'materia/{}_texto_integral', 'materia/{}_texto_integral',
'materialegislativa/{0}/{0}_texto_integral{1}')], 'public/materialegislativa/{2}/{0}/{0}_texto_integral{1}')],
DocumentoAcessorio: [( DocumentoAcessorio: [(
'arquivo', 'arquivo',
'materia/{}', 'materia/{}',
'documentoacessorio/{0}/{0}{1}')], 'public/documentoacessorio/{2}/{0}/{0}{1}')],
NormaJuridica: [( NormaJuridica: [(
'texto_original', 'texto_integral',
'norma_juridica/{}_texto_integral', 'norma_juridica/{}_texto_integral',
'normajuridica/{0}/{0}_texto_integral{1}')], 'public/normajuridica/{2}/{0}/{0}_texto_integral{1}')],
SessaoPlenaria: [ SessaoPlenaria: [
('upload_ata', ('upload_ata',
'ata_sessao/{}_ata_sessao', 'ata_sessao/{}_ata_sessao',
'sessaoplenaria/{0}/ata/{0}_ata_sessao{1}'), 'public/sessaoplenaria/{0}/ata/{0}_ata_sessao{1}'),
('upload_anexo', ('upload_anexo',
'anexo_sessao/{}_texto_anexado', 'anexo_sessao/{}_texto_anexado',
'sessaoplenaria/{0}/anexo/{0}_texto_anexado{1}') 'public/sessaoplenaria/{0}/anexo/{0}_texto_anexado{1}')
], ],
Proposicao: [( Proposicao: [(
'texto_original', 'texto_original',
'proposicao/{}', 'proposicao/{}',
'proposicao/{0}/{0}{1}')], 'private/proposicao/{0}/{0}{1}')],
DocumentoAdministrativo: [( DocumentoAdministrativo: [(
'texto_integral', 'texto_integral',
'administrativo/{}_texto_integral', 'administrativo/{}_texto_integral',
'documentoadministrativo/{0}/{0}_texto_integral{1}')], 'private/documentoadministrativo/{0}/{0}_texto_integral{1}')
],
DocumentoAcessorioAdministrativo: [(
'arquivo',
'administrativo/{}',
'private/documentoacessorioadministrativo/{0}/{0}_acessorio_administrativo{1}')
],
} }
DOCS = {tipo: [(campo, DOCS = {tipo: [(campo,
@ -105,9 +123,14 @@ def migrar_docs_logo():
print('#### Migrando logotipo da casa ####') print('#### Migrando logotipo da casa ####')
[(_, origem, destino)] = DOCS[CasaLegislativa] [(_, origem, destino)] = DOCS[CasaLegislativa]
props_sapl = os.path.dirname(origem) props_sapl = os.path.dirname(origem)
# a pasta props_sapl deve conter apenas o origem e metadatas! # a pasta props_sapl deve conter apenas o origem e metadatas!
# Edit: Aparentemente há diretório que contém properties ao invés de
# metadata. O assert foi modificado para essa situação.
assert set(os.listdir(em_media(props_sapl))) < { assert set(os.listdir(em_media(props_sapl))) < {
'logo_casa.gif', '.metadata', 'logo_casa.gif.metadata'} 'logo_casa.gif', '.metadata', 'logo_casa.gif.metadata',
'.properties', 'logo_casa.gif.properties', '.objects'}
mover_documento(origem, destino) mover_documento(origem, destino)
casa = get_casa_legislativa() casa = get_casa_legislativa()
casa.logotipo = destino casa.logotipo = destino
@ -146,15 +169,22 @@ def migrar_docs_por_ids(tipo):
for arq in os.listdir(dir_origem): for arq in os.listdir(dir_origem):
match = pat.match(arq) match = pat.match(arq)
if match: if match:
# associa documento ao objeto
try:
origem = os.path.join(dir_origem, match.group(0)) origem = os.path.join(dir_origem, match.group(0))
id = match.group(1) id = match.group(1)
obj = tipo.objects.get(pk=id)
extensao = get_extensao(origem) extensao = get_extensao(origem)
if hasattr(obj, "ano"):
destino = base_destino.format(id, extensao, obj.ano)
elif isinstance(obj, DocumentoAcessorio):
destino = base_destino.format(
id, extensao, obj.materia.ano)
else:
destino = base_destino.format(id, extensao) destino = base_destino.format(id, extensao)
mover_documento(origem, destino) mover_documento(origem, destino)
# associa documento ao objeto
try:
obj = tipo.objects.get(pk=id)
setattr(obj, campo, destino) setattr(obj, campo, destino)
obj.save() obj.save()
except tipo.DoesNotExist: except tipo.DoesNotExist:
@ -199,6 +229,7 @@ def migrar_documentos():
SessaoPlenaria, SessaoPlenaria,
Proposicao, Proposicao,
DocumentoAdministrativo, DocumentoAdministrativo,
DocumentoAcessorioAdministrativo,
]: ]:
migrar_docs_por_ids(tipo) migrar_docs_por_ids(tipo)

71
sapl/legacy/migration.py

@ -28,7 +28,7 @@ from sapl.norma.models import (AssuntoNorma, NormaJuridica,
TipoVinculoNormaJuridica, NormaRelacionada) TipoVinculoNormaJuridica, NormaRelacionada)
from sapl.parlamentares.models import Parlamentar from sapl.parlamentares.models import Parlamentar
from sapl.protocoloadm.models import Protocolo, StatusTramitacaoAdministrativo from sapl.protocoloadm.models import Protocolo, StatusTramitacaoAdministrativo
from sapl.sessao.models import ExpedienteMateria, OrdemDia from sapl.sessao.models import ExpedienteMateria, OrdemDia, RegistroVotacao
from sapl.settings import PROJECT_DIR from sapl.settings import PROJECT_DIR
from sapl.utils import delete_texto, normalize, save_texto from sapl.utils import delete_texto, normalize, save_texto
@ -411,26 +411,6 @@ class DataMigrator:
if field_type == 'CharField' or field_type == 'TextField': if field_type == 'CharField' or field_type == 'TextField':
if value is None or value == 'None': if value is None or value == 'None':
value = '' value = ''
if field.model._meta.label == 'sessao.RegistroVotacao' and \
field.name == 'ordem' and \
not isinstance(value, OrdemDia):
try:
new_value = ExpedienteMateria.objects.get(pk=value)
setattr(new, 'expediente', new_value)
setattr(new, field.name, None)
continue
except ObjectDoesNotExist:
msg = 'FK [%s] não encontrada para valor %s ' \
'(em %s %s)' % (
field.name, value,
field.model.__name__, label or '---')
with reversion.create_revision():
value = make_stub(field.related_model, value)
descricao = 'stub criado para entrada orfã!'
warn(msg + ' => ' + descricao)
save_relation(value, [field.name], msg, descricao,
eh_stub=True)
reversion.set_comment('Stub criado pela migração')
setattr(new, field.name, value) setattr(new, field.name, value)
elif field.model.__name__ == 'TipoAutor' and \ elif field.model.__name__ == 'TipoAutor' and \
field.name == 'content_type': field.name == 'content_type':
@ -600,11 +580,27 @@ def migrate(obj=appconfs, interativo=True):
# MIGRATION_ADJUSTMENTS ##################################################### # MIGRATION_ADJUSTMENTS #####################################################
def adjust_ordemdia(new, old): def adjust_ordemdia_antes_salvar(new, old):
# Prestar atenção # Prestar atenção
if not old.tip_votacao: if not old.tip_votacao:
new.tipo_votacao = 1 new.tipo_votacao = 1
if old.num_ordem is None:
new.numero_ordem = 999999999
def adjust_ordemdia_depois_salvar(new, old):
if old.num_ordem is None and new.numero_ordem == 999999999:
with reversion.create_revision():
problema = 'OrdemDia de PK %s tinha seu valor de numero ordem'\
' nulo.' % old.pk
descricao = 'O valor %s foi colocado no lugar.' % new.numero_ordem
warn(problema + ' => ' + descricao)
save_relation(obj=new, problema=problema,
descricao=descricao, eh_stub=False)
reversion.set_comment('OrdemDia sem número da ordem.')
pass
def adjust_parlamentar(new, old): def adjust_parlamentar(new, old):
if old.ind_unid_deliberativa: if old.ind_unid_deliberativa:
@ -655,6 +651,31 @@ def adjust_protocolo(new, old):
new.numero = p['numero__max'] + 1 new.numero = p['numero__max'] + 1
def adjust_registrovotacao_antes_salvar(new, old):
ordem_dia = OrdemDia.objects.filter(
pk=old.cod_ordem, materia=old.cod_materia)
expediente_materia = ExpedienteMateria.objects.filter(
pk=old.cod_ordem, materia=old.cod_materia)
if ordem_dia and not expediente_materia:
new.ordem = ordem_dia[0]
if not ordem_dia and expediente_materia:
new.expediente = expediente_materia[0]
def adjust_registrovotacao_depois_salvar(new, old):
if not new.ordem and not new.expediente:
with reversion.create_revision():
problema = 'RegistroVotacao de PK %s não possui nenhuma OrdemDia'\
' ou ExpedienteMateria.' % old.pk
descricao = 'RevistroVotacao deve ter no mínimo uma ordem do dia'\
' ou expediente vinculado.'
warn(problema + ' => ' + descricao)
save_relation(obj=new, problema=problema,
descricao=descricao, eh_stub=False)
reversion.set_comment('RegistroVotacao sem ordem ou expediente')
def adjust_tipoproposicao(new, old): def adjust_tipoproposicao(new, old):
if old.ind_mat_ou_doc == 'M': if old.ind_mat_ou_doc == 'M':
new.tipo_conteudo_related = TipoMateriaLegislativa.objects.get( new.tipo_conteudo_related = TipoMateriaLegislativa.objects.get(
@ -718,6 +739,7 @@ def adjust_autor(new, old):
new.nome = new.autor_related.nome_parlamentar new.nome = new.autor_related.nome_parlamentar
elif old.cod_comissao: elif old.cod_comissao:
new.autor_related = Comissao.objects.get(pk=old.cod_comissao) new.autor_related = Comissao.objects.get(pk=old.cod_comissao)
new.nome = new.autor_related.nome
if old.col_username: if old.col_username:
if not get_user_model().objects.filter( if not get_user_model().objects.filter(
@ -748,10 +770,11 @@ AJUSTE_ANTES_SALVAR = {
Comissao: adjust_comissao, Comissao: adjust_comissao,
NormaJuridica: adjust_normajuridica_antes_salvar, NormaJuridica: adjust_normajuridica_antes_salvar,
NormaRelacionada: adjust_normarelacionada, NormaRelacionada: adjust_normarelacionada,
OrdemDia: adjust_ordemdia, OrdemDia: adjust_ordemdia_antes_salvar,
Parlamentar: adjust_parlamentar, Parlamentar: adjust_parlamentar,
Participacao: adjust_participacao, Participacao: adjust_participacao,
Protocolo: adjust_protocolo, Protocolo: adjust_protocolo,
RegistroVotacao: adjust_registrovotacao_antes_salvar,
TipoProposicao: adjust_tipoproposicao, TipoProposicao: adjust_tipoproposicao,
StatusTramitacao: adjust_statustramitacao, StatusTramitacao: adjust_statustramitacao,
StatusTramitacaoAdministrativo: adjust_statustramitacaoadm, StatusTramitacaoAdministrativo: adjust_statustramitacaoadm,
@ -760,7 +783,9 @@ AJUSTE_ANTES_SALVAR = {
AJUSTE_DEPOIS_SALVAR = { AJUSTE_DEPOIS_SALVAR = {
NormaJuridica: adjust_normajuridica_depois_salvar, NormaJuridica: adjust_normajuridica_depois_salvar,
OrdemDia: adjust_ordemdia_depois_salvar,
Protocolo: adjust_protocolo_depois_salvar, Protocolo: adjust_protocolo_depois_salvar,
RegistroVotacao: adjust_registrovotacao_depois_salvar,
} }
# CHECKS #################################################################### # CHECKS ####################################################################

12
sapl/materia/forms.py

@ -164,18 +164,6 @@ class DocumentoAcessorioForm(ModelForm):
class Meta: class Meta:
model = DocumentoAcessorio model = DocumentoAcessorio
fields = ['tipo', 'nome', 'data', 'autor', 'ementa', 'arquivo'] fields = ['tipo', 'nome', 'data', 'autor', 'ementa', 'arquivo']
widgets = {'autor': forms.HiddenInput()}
def clean_autor(self):
autor_field = self.cleaned_data['autor']
try:
int(autor_field)
except ValueError:
return autor_field
else:
if autor_field:
return str(Autor.objects.get(id=autor_field))
class RelatoriaForm(ModelForm): class RelatoriaForm(ModelForm):

32
sapl/materia/migrations/0005_auto_20170522_1051.py

@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2017-05-22 10:51
from __future__ import unicode_literals
from django.db import migrations, models
import sapl.materia.models
import sapl.utils
class Migration(migrations.Migration):
dependencies = [
('materia', '0004_auto_20170504_1751'),
]
operations = [
migrations.AlterField(
model_name='documentoacessorio',
name='arquivo',
field=models.FileField(blank=True, null=True, upload_to=sapl.materia.models.anexo_upload_path, validators=[sapl.utils.restringe_tipos_de_arquivo_txt], verbose_name='Texto Integral'),
),
migrations.AlterField(
model_name='materialegislativa',
name='texto_original',
field=models.FileField(blank=True, null=True, upload_to=sapl.materia.models.materia_upload_path, validators=[sapl.utils.restringe_tipos_de_arquivo_txt], verbose_name='Texto Original'),
),
migrations.AlterField(
model_name='proposicao',
name='texto_original',
field=models.FileField(blank=True, null=True, upload_to=sapl.materia.models.materia_upload_path, validators=[sapl.utils.restringe_tipos_de_arquivo_txt], verbose_name='Texto Original'),
),
]

32
sapl/materia/migrations/0005_auto_20170522_1904.py

@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.12 on 2017-05-22 19:04
from __future__ import unicode_literals
from django.db import migrations, models
import sapl.materia.models
import sapl.utils
class Migration(migrations.Migration):
dependencies = [
('materia', '0004_auto_20170504_1751'),
]
operations = [
migrations.AlterField(
model_name='documentoacessorio',
name='arquivo',
field=models.FileField(blank=True, null=True, upload_to=sapl.materia.models.anexo_upload_path, validators=[sapl.utils.restringe_tipos_de_arquivo_txt], verbose_name='Texto Integral'),
),
migrations.AlterField(
model_name='materialegislativa',
name='texto_original',
field=models.FileField(blank=True, null=True, upload_to=sapl.materia.models.materia_upload_path, validators=[sapl.utils.restringe_tipos_de_arquivo_txt], verbose_name='Texto Original'),
),
migrations.AlterField(
model_name='proposicao',
name='texto_original',
field=models.FileField(blank=True, null=True, upload_to=sapl.materia.models.materia_upload_path, validators=[sapl.utils.restringe_tipos_de_arquivo_txt], verbose_name='Texto Original'),
),
]

16
sapl/materia/migrations/0006_merge.py

@ -0,0 +1,16 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2017-05-23 18:20
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('materia', '0005_auto_20170522_1051'),
('materia', '0005_auto_20170522_1904'),
]
operations = [
]

14
sapl/materia/models.py

@ -22,7 +22,6 @@ from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES, SaplGenericForeignKey,
EM_TRAMITACAO = [(1, 'Sim'), EM_TRAMITACAO = [(1, 'Sim'),
(0, 'Não')] (0, 'Não')]
def grupo_autor(): def grupo_autor():
try: try:
grupo = Group.objects.get(name='Autor') grupo = Group.objects.get(name='Autor')
@ -30,7 +29,6 @@ def grupo_autor():
return None return None
return grupo.id return grupo.id
@reversion.register() @reversion.register()
class TipoProposicao(models.Model): class TipoProposicao(models.Model):
descricao = models.CharField(max_length=50, verbose_name=_('Descrição')) descricao = models.CharField(max_length=50, verbose_name=_('Descrição'))
@ -120,6 +118,11 @@ class Origem(models.Model):
TIPO_APRESENTACAO_CHOICES = Choices(('O', 'oral', _('Oral')), TIPO_APRESENTACAO_CHOICES = Choices(('O', 'oral', _('Oral')),
('E', 'escrita', _('Escrita'))) ('E', 'escrita', _('Escrita')))
def materia_upload_path(instance, filename):
return texto_upload_path(instance, filename, subpath=instance.ano)
def anexo_upload_path(instance, filename):
return texto_upload_path(instance, filename, subpath=instance.materia.ano)
@reversion.register() @reversion.register()
class MateriaLegislativa(models.Model): class MateriaLegislativa(models.Model):
@ -194,7 +197,7 @@ class MateriaLegislativa(models.Model):
texto_original = models.FileField( texto_original = models.FileField(
blank=True, blank=True,
null=True, null=True,
upload_to=texto_upload_path, upload_to=materia_upload_path,
verbose_name=_('Texto Original'), verbose_name=_('Texto Original'),
validators=[restringe_tipos_de_arquivo_txt]) validators=[restringe_tipos_de_arquivo_txt])
@ -216,6 +219,7 @@ class MateriaLegislativa(models.Model):
return _('%(tipo)s%(numero)s de %(ano)s') % { return _('%(tipo)s%(numero)s de %(ano)s') % {
'tipo': self.tipo, 'numero': self.numero, 'ano': self.ano} 'tipo': self.tipo, 'numero': self.numero, 'ano': self.ano}
def data_entrada_protocolo(self): def data_entrada_protocolo(self):
''' '''
hack: recuperar a data de entrada do protocolo sem gerar hack: recuperar a data de entrada do protocolo sem gerar
@ -392,7 +396,7 @@ class DocumentoAcessorio(models.Model):
arquivo = models.FileField( arquivo = models.FileField(
blank=True, blank=True,
null=True, null=True,
upload_to=texto_upload_path, upload_to=anexo_upload_path,
verbose_name=_('Texto Integral'), verbose_name=_('Texto Integral'),
validators=[restringe_tipos_de_arquivo_txt]) validators=[restringe_tipos_de_arquivo_txt])
@ -625,7 +629,7 @@ class Proposicao(models.Model):
('I', 'Incorporada')), ('I', 'Incorporada')),
verbose_name=_('Status Proposição')) verbose_name=_('Status Proposição'))
texto_original = models.FileField( texto_original = models.FileField(
upload_to=texto_upload_path, upload_to=materia_upload_path,
blank=True, blank=True,
null=True, null=True,
verbose_name=_('Texto Original'), verbose_name=_('Texto Original'),

78
sapl/materia/views.py

@ -7,7 +7,7 @@ from crispy_forms.layout import HTML
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.decorators import permission_required from django.contrib.auth.decorators import permission_required
from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.auth.mixins import PermissionRequiredMixin
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
from django.core.mail import send_mail from django.core.mail import send_mail
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.http import HttpResponse, JsonResponse from django.http import HttpResponse, JsonResponse
@ -34,7 +34,8 @@ from sapl.crud.base import (ACTION_CREATE, ACTION_DELETE, ACTION_DETAIL,
PermissionRequiredForAppCrudMixin, make_pagination) PermissionRequiredForAppCrudMixin, make_pagination)
from sapl.materia.forms import (AnexadaForm, ConfirmarProposicaoForm, from sapl.materia.forms import (AnexadaForm, ConfirmarProposicaoForm,
LegislacaoCitadaForm, ProposicaoForm, LegislacaoCitadaForm, ProposicaoForm,
TipoProposicaoForm) TipoProposicaoForm, TramitacaoForm,
TramitacaoUpdateForm)
from sapl.norma.models import LegislacaoCitada from sapl.norma.models import LegislacaoCitada
from sapl.protocoloadm.models import Protocolo from sapl.protocoloadm.models import Protocolo
from sapl.utils import (TURNO_TRAMITACAO_CHOICES, YES_NO_CHOICES, autor_label, from sapl.utils import (TURNO_TRAMITACAO_CHOICES, YES_NO_CHOICES, autor_label,
@ -162,19 +163,43 @@ class CriarProtocoloMateriaView(CreateView):
context = super( context = super(
CriarProtocoloMateriaView, self).get_context_data(**kwargs) CriarProtocoloMateriaView, self).get_context_data(**kwargs)
try:
protocolo = Protocolo.objects.get(pk=self.kwargs['pk']) protocolo = Protocolo.objects.get(pk=self.kwargs['pk'])
except ObjectDoesNotExist:
raise Http404()
materias_ano = MateriaLegislativa.objects.filter(
ano=protocolo.ano,
tipo=protocolo.tipo_materia).order_by('-numero')
if materias_ano:
numero = materias_ano.first().numero + 1
else:
numero = 1
context['form'].fields['tipo'].initial = protocolo.tipo_materia context['form'].fields['tipo'].initial = protocolo.tipo_materia
context['form'].fields['numero'].initial = protocolo.numero context['form'].fields['numero'].initial = numero
context['form'].fields['ano'].initial = protocolo.ano context['form'].fields['ano'].initial = protocolo.ano
context['form'].fields['data_apresentacao'].initial = protocolo.data context['form'].fields['data_apresentacao'].initial = protocolo.data
context['form'].fields['numero_protocolo'].initial = protocolo.numero context['form'].fields['numero_protocolo'].initial = protocolo.numero
context['form'].fields['ementa'].initial = protocolo.observacao context['form'].fields['ementa'].initial = protocolo.assunto_ementa
return context return context
def form_valid(self, form): def form_valid(self, form):
materia = form.save() materia = form.save()
try:
protocolo = Protocolo.objects.get(pk=self.kwargs['pk'])
except ObjectDoesNotExist:
raise Http404()
if protocolo.autor:
Autoria.objects.create(
materia=materia,
autor=protocolo.autor,
primeiro_autor=True)
return redirect(self.get_success_url(materia)) return redirect(self.get_success_url(materia))
@ -868,6 +893,7 @@ class TramitacaoCrud(MasterDetailCrud):
ordering = '-data_tramitacao', ordering = '-data_tramitacao',
class CreateView(MasterDetailCrud.CreateView): class CreateView(MasterDetailCrud.CreateView):
form_class = TramitacaoForm
def get_initial(self): def get_initial(self):
local = MateriaLegislativa.objects.get( local = MateriaLegislativa.objects.get(
@ -884,6 +910,7 @@ class TramitacaoCrud(MasterDetailCrud):
return super(CreateView, self).post(request, *args, **kwargs) return super(CreateView, self).post(request, *args, **kwargs)
class UpdateView(MasterDetailCrud.UpdateView): class UpdateView(MasterDetailCrud.UpdateView):
form_class = TramitacaoUpdateForm
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
materia = MateriaLegislativa.objects.get( materia = MateriaLegislativa.objects.get(
@ -951,25 +978,21 @@ class DocumentoAcessorioCrud(MasterDetailCrud):
form_class = DocumentoAcessorioForm form_class = DocumentoAcessorioForm
def __init__(self, **kwargs): def __init__(self, **kwargs):
montar_helper_documento_acessorio(self)
super(MasterDetailCrud.CreateView, self).__init__(**kwargs) super(MasterDetailCrud.CreateView, self).__init__(**kwargs)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super( context = super(
MasterDetailCrud.CreateView, self).get_context_data(**kwargs) MasterDetailCrud.CreateView, self).get_context_data(**kwargs)
context['helper'] = self.helper
return context return context
class UpdateView(MasterDetailCrud.UpdateView): class UpdateView(MasterDetailCrud.UpdateView):
form_class = DocumentoAcessorioForm form_class = DocumentoAcessorioForm
def __init__(self, **kwargs): def __init__(self, **kwargs):
montar_helper_documento_acessorio(self)
super(MasterDetailCrud.UpdateView, self).__init__(**kwargs) super(MasterDetailCrud.UpdateView, self).__init__(**kwargs)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(UpdateView, self).get_context_data(**kwargs) context = super(UpdateView, self).get_context_data(**kwargs)
context['helper'] = self.helper
return context return context
@ -1355,6 +1378,11 @@ class AcompanhamentoMateriaView(CreateView):
acompanhar.usuario = usuario.username acompanhar.usuario = usuario.username
acompanhar.confirmado = False acompanhar.confirmado = False
acompanhar.save() acompanhar.save()
except MultipleObjectsReturned:
AcompanhamentoMateria.objects.filter(
email=email,
materia=materia,
hash=hash_txt).first()
do_envia_email_confirmacao(request, materia, email) do_envia_email_confirmacao(request, materia, email)
@ -1600,15 +1628,15 @@ class DocumentoAcessorioEmLoteView(PermissionRequiredMixin, FilterView):
tipo = TipoDocumento.objects.get(descricao=request.POST['tipo']) tipo = TipoDocumento.objects.get(descricao=request.POST['tipo'])
for materia_id in marcadas: for materia_id in marcadas:
DocumentoAcessorio.objects.create( doc = DocumentoAcessorio()
materia_id=materia_id, doc.materia_id = materia_id
tipo=tipo, doc.tipo = tipo
arquivo=request.POST['arquivo'], doc.arquivo = request.FILES['arquivo']
nome=request.POST['nome'], doc.nome = request.POST['nome']
data=datetime.strptime(request.POST['data'], "%d/%m/%Y"), doc.data = datetime.strptime(request.POST['data'], "%d/%m/%Y")
autor=Autor.objects.get(id=request.POST['autor']), doc.autor = request.POST['autor']
ementa=request.POST['ementa'] doc.ementa = request.POST['ementa']
) doc.save()
msg = _('Documento(s) criado(s).') msg = _('Documento(s) criado(s).')
messages.add_message(request, messages.SUCCESS, msg) messages.add_message(request, messages.SUCCESS, msg)
return self.get(request, self.kwargs) return self.get(request, self.kwargs)
@ -1697,3 +1725,19 @@ class PrimeiraTramitacaoEmLoteView(PermissionRequiredMixin, FilterView):
class TramitacaoEmLoteView(PrimeiraTramitacaoEmLoteView): class TramitacaoEmLoteView(PrimeiraTramitacaoEmLoteView):
filterset_class = TramitacaoEmLoteFilterSet filterset_class = TramitacaoEmLoteFilterSet
def get_context_data(self, **kwargs):
context = super(TramitacaoEmLoteView,
self).get_context_data(**kwargs)
qr = self.request.GET.copy()
if ('tramitacao__status' in qr and
'tramitacao__unidade_tramitacao_destino' in qr):
lista = filtra_tramitacao_destino_and_status(
qr['tramitacao__status'],
qr['tramitacao__unidade_tramitacao_destino'])
context['object_list'] = context['object_list'].filter(
id__in=lista).distinct()
return context

19
sapl/norma/migrations/0003_auto_20170510_1549.py

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.11 on 2017-05-10 15:49
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('norma', '0002_auto_20170403_1505'),
]
operations = [
migrations.AlterModelOptions(
name='assuntonorma',
options={'verbose_name': 'Assunto de Norma Jurídica', 'verbose_name_plural': 'Assuntos de Normas Jurídicas'},
),
]

22
sapl/norma/migrations/0004_auto_20170522_1051.py

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2017-05-22 10:51
from __future__ import unicode_literals
from django.db import migrations, models
import sapl.norma.models
import sapl.utils
class Migration(migrations.Migration):
dependencies = [
('norma', '0003_auto_20170510_1549'),
]
operations = [
migrations.AlterField(
model_name='normajuridica',
name='texto_integral',
field=models.FileField(blank=True, null=True, upload_to=sapl.norma.models.norma_upload_path, validators=[sapl.utils.restringe_tipos_de_arquivo_txt], verbose_name='Texto Integral'),
),
]

27
sapl/norma/migrations/0004_auto_20170522_1115.py

@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.12 on 2017-05-22 11:15
from __future__ import unicode_literals
from django.db import migrations, models
import sapl.norma.models
import sapl.utils
class Migration(migrations.Migration):
dependencies = [
('norma', '0003_auto_20170510_1549'),
]
operations = [
migrations.AlterField(
model_name='normajuridica',
name='texto_integral',
field=models.FileField(blank=True, null=True, upload_to=sapl.norma.models.norma_upload_path, validators=[sapl.utils.restringe_tipos_de_arquivo_txt], verbose_name='Texto Integral'),
),
migrations.AlterField(
model_name='normajuridica',
name='timestamp',
field=models.DateTimeField(null=True),
),
]

16
sapl/norma/migrations/0005_merge.py

@ -0,0 +1,16 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2017-05-23 18:20
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('norma', '0004_auto_20170522_1115'),
('norma', '0004_auto_20170522_1051'),
]
operations = [
]

7
sapl/norma/models.py

@ -59,6 +59,8 @@ class TipoNormaJuridica(models.Model):
def __str__(self): def __str__(self):
return self.descricao return self.descricao
def norma_upload_path(instance, filename):
return texto_upload_path(instance, filename, subpath=instance.ano)
@reversion.register() @reversion.register()
class NormaJuridica(models.Model): class NormaJuridica(models.Model):
@ -67,10 +69,11 @@ class NormaJuridica(models.Model):
('F', 'federal', _('Federal')), ('F', 'federal', _('Federal')),
('M', 'municipal', _('Municipal')), ('M', 'municipal', _('Municipal')),
) )
texto_integral = models.FileField( texto_integral = models.FileField(
blank=True, blank=True,
null=True, null=True,
upload_to=texto_upload_path, upload_to=norma_upload_path,
verbose_name=_('Texto Integral'), verbose_name=_('Texto Integral'),
validators=[restringe_tipos_de_arquivo_txt]) validators=[restringe_tipos_de_arquivo_txt])
tipo = models.ForeignKey( tipo = models.ForeignKey(
@ -113,7 +116,7 @@ class NormaJuridica(models.Model):
AssuntoNorma, blank=True, AssuntoNorma, blank=True,
verbose_name=_('Assuntos')) verbose_name=_('Assuntos'))
data_vigencia = models.DateField(blank=True, null=True) data_vigencia = models.DateField(blank=True, null=True)
timestamp = models.DateTimeField() timestamp = models.DateTimeField(null=True)
texto_articulado = GenericRelation( texto_articulado = GenericRelation(
TextoArticulado, related_query_name='texto_articulado') TextoArticulado, related_query_name='texto_articulado')

12
sapl/parlamentares/models.py

@ -1,9 +1,9 @@
from datetime import datetime from datetime import datetime
import reversion
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from model_utils import Choices from model_utils import Choices
import reversion
from sapl.base.models import Autor from sapl.base.models import Autor
from sapl.utils import (INDICADOR_AFASTAMENTO, UF, YES_NO_CHOICES, from sapl.utils import (INDICADOR_AFASTAMENTO, UF, YES_NO_CHOICES,
@ -206,6 +206,15 @@ def foto_upload_path(instance, filename):
return texto_upload_path(instance, filename, subpath='') return texto_upload_path(instance, filename, subpath='')
def true_false_none(x):
if x == 'True':
return True
elif x == 'False':
return False
else:
return None
@reversion.register() @reversion.register()
class Parlamentar(models.Model): class Parlamentar(models.Model):
FEMININO = 'F' FEMININO = 'F'
@ -303,6 +312,7 @@ class Parlamentar(models.Model):
('nome_completo', '__icontains'), ('nome_completo', '__icontains'),
('nome_parlamentar', '__icontains'), ('nome_parlamentar', '__icontains'),
('filiacao__partido__sigla', '__icontains'), ('filiacao__partido__sigla', '__icontains'),
('ativo', '', true_false_none),
)) ))
class Meta: class Meta:

4
sapl/parlamentares/urls.py

@ -1,6 +1,7 @@
from django.conf.urls import include, url from django.conf.urls import include, url
from sapl.parlamentares.views import (altera_field_mesa, from sapl.parlamentares.views import (altera_field_mesa,
altera_field_mesa_public_view,
CargoMesaCrud, ColigacaoCrud, CargoMesaCrud, ColigacaoCrud,
ComposicaoColigacaoCrud, DependenteCrud, ComposicaoColigacaoCrud, DependenteCrud,
FiliacaoCrud, FrenteCrud, FrenteList, FiliacaoCrud, FrenteCrud, FrenteList,
@ -59,6 +60,9 @@ urlpatterns = [
url(r'^mesa-diretora/altera-field-mesa/$', url(r'^mesa-diretora/altera-field-mesa/$',
altera_field_mesa, name='altera_field_mesa'), altera_field_mesa, name='altera_field_mesa'),
url(r'^mesa-diretora/altera-field-mesa-public-view/$',
altera_field_mesa_public_view, name='altera_field_mesa_public_view'),
url(r'^mesa-diretora/insere-parlamentar-composicao/$', url(r'^mesa-diretora/insere-parlamentar-composicao/$',
insere_parlamentar_composicao, name='insere_parlamentar_composicao'), insere_parlamentar_composicao, name='insere_parlamentar_composicao'),

99
sapl/parlamentares/views.py

@ -373,11 +373,9 @@ class ParlamentarCrud(Crud):
# Caso exista mais de uma filiação nesse intervalo # Caso exista mais de uma filiação nesse intervalo
# Entretanto, NÃO DEVE OCORRER # Entretanto, NÃO DEVE OCORRER
except MultipleObjectsReturned: except MultipleObjectsReturned:
filiacao = parlamentar.filiacao_set.filter(Q( row[2] = (
data__lte=legislatura.data_fim, 'O Parlamentar possui duas filiações conflitantes',
data_desfiliacao__gte=legislatura.data_fim) | Q( None)
data__lte=legislatura.data_fim,
data_desfiliacao__isnull=True)).last()
# Caso encontre UMA filiação nessas condições # Caso encontre UMA filiação nessas condições
else: else:
@ -588,3 +586,94 @@ def remove_parlamentar_composicao(request):
return JsonResponse( return JsonResponse(
{'msg': ( {'msg': (
'Selecione algum parlamentar para ser excluido!', 0)}) 'Selecione algum parlamentar para ser excluido!', 0)})
def partido_parlamentar_sessao_legislativa(sessao, parlamentar):
"""
Função para descobrir o partido do parlamentar durante
o período de uma dada Sessão Legislativa
"""
# As condições para mostrar a filiação são:
# A data de filiacao deve ser menor que a data de fim
# da sessao legislativa e data de desfiliação deve nula, ou maior,
# ou igual a data de fim da sessao
try:
filiacao = parlamentar.filiacao_set.get(Q(
data__lte=sessao.data_fim,
data_desfiliacao__gte=sessao.data_fim) | Q(
data__lte=sessao.data_fim,
data_desfiliacao__isnull=True))
# Caso não exista filiação com essas condições
except ObjectDoesNotExist:
return ''
# Caso exista mais de uma filiação nesse intervalo
# Entretanto, NÃO DEVE OCORRER
except MultipleObjectsReturned:
return 'O Parlamentar possui duas filiações conflitantes'
# Caso encontre UMA filiação nessas condições
else:
return filiacao.partido.sigla
def altera_field_mesa_public_view(request):
"""
Essa função lida com qualquer alteração nos campos
da Mesa Diretora para usuários anônimos,
atualizando os campos após cada alteração
"""
legislatura = request.GET['legislatura']
sessoes = SessaoLegislativa.objects.filter(
legislatura=legislatura).order_by('-data_inicio')
if not sessoes:
return JsonResponse({'msg': ('Nenhuma sessão encontrada!', 0)})
# Verifica se já tem uma sessão selecionada. Ocorre quando
# é alterado o campo de sessão
if request.GET['sessao']:
sessao_selecionada = request.GET['sessao']
# Caso a mudança tenha sido no campo legislatura, a sessão
# atual deve ser a primeira daquela legislatura
else:
sessao_selecionada = sessoes.first().id
# Atualiza os componentes da view após a mudança
lista_sessoes = [(s.id, s.__str__()) for s in sessoes]
composicao_mesa = ComposicaoMesa.objects.filter(
sessao_legislativa=sessao_selecionada)
cargos_ocupados = [(m.cargo.id,
m.cargo.__str__()) for m in composicao_mesa]
parlamentares_ocupados = [(m.parlamentar.id,
m.parlamentar.__str__()
) for m in composicao_mesa]
lista_fotos = []
lista_partidos = []
sessao = SessaoLegislativa.objects.get(id=sessao_selecionada)
for p in parlamentares_ocupados:
parlamentar = Parlamentar.objects.get(id=p[0])
lista_partidos.append(
partido_parlamentar_sessao_legislativa(sessao,
parlamentar))
if parlamentar.fotografia:
lista_fotos.append(parlamentar.fotografia.url)
else:
lista_fotos.append(None)
return JsonResponse(
{'lista_parlamentares': parlamentares_ocupados,
'lista_partidos': lista_partidos,
'lista_cargos': cargos_ocupados,
'lista_sessoes': lista_sessoes,
'lista_fotos': lista_fotos,
'sessao_selecionada': sessao_selecionada,
'msg': ('', 1)})

1
sapl/protocoloadm/views.py

@ -425,6 +425,7 @@ class ProtocoloMateriaView(PermissionRequiredMixin, CreateView):
id=self.request.POST['tipo_materia']) id=self.request.POST['tipo_materia'])
protocolo.numero_paginas = self.request.POST['numero_paginas'] protocolo.numero_paginas = self.request.POST['numero_paginas']
protocolo.observacao = self.request.POST['observacao'] protocolo.observacao = self.request.POST['observacao']
protocolo.assunto_ementa = self.request.POST['assunto_ementa']
protocolo.save() protocolo.save()
return redirect(self.get_success_url(protocolo)) return redirect(self.get_success_url(protocolo))

30
sapl/relatorios/templates/pdf_sessao_plenaria_gerar.py

@ -6,6 +6,8 @@
""" """
import time import time
from sapl.sessao.models import ResumoOrdenacao
from trml2pdf import parseString from trml2pdf import parseString
@ -287,6 +289,33 @@ def principal(cabecalho_dic, rodape_dic, imagem, sessao, inf_basicas_dic, lst_me
tmp += '\t</template>\n' tmp += '\t</template>\n'
tmp += paraStyle() tmp += paraStyle()
tmp += '\t<story>\n' tmp += '\t<story>\n'
ordenacao = ResumoOrdenacao.objects.first()
dict_ord_template = {
'cont_mult': '',
'exp': expedientes(lst_expedientes),
'id_basica': inf_basicas(inf_basicas_dic),
'lista_p': presenca(lst_presenca_sessao),
'lista_p_o_d': presenca_ordem_dia(lst_presenca_ordem_dia),
'mat_exp': expediente_materia(lst_expediente_materia),
'mat_o_d': votacao(lst_votacao),
'mesa_d': mesa(lst_mesa),
'oradores_exped': oradores_expediente(lst_oradores_expediente),
'oradores_expli': oradores(lst_oradores)
}
if ordenacao:
tmp += dict_ord_template[ordenacao.primeiro]
tmp += dict_ord_template[ordenacao.segundo]
tmp += dict_ord_template[ordenacao.terceiro]
tmp += dict_ord_template[ordenacao.quarto]
tmp += dict_ord_template[ordenacao.quinto]
tmp += dict_ord_template[ordenacao.sexto]
tmp += dict_ord_template[ordenacao.setimo]
tmp += dict_ord_template[ordenacao.oitavo]
tmp += dict_ord_template[ordenacao.nono]
tmp += dict_ord_template[ordenacao.decimo]
else:
tmp += inf_basicas(inf_basicas_dic) tmp += inf_basicas(inf_basicas_dic)
tmp += mesa(lst_mesa) tmp += mesa(lst_mesa)
tmp += presenca(lst_presenca_sessao) tmp += presenca(lst_presenca_sessao)
@ -296,6 +325,7 @@ def principal(cabecalho_dic, rodape_dic, imagem, sessao, inf_basicas_dic, lst_me
tmp += presenca_ordem_dia(lst_presenca_ordem_dia) tmp += presenca_ordem_dia(lst_presenca_ordem_dia)
tmp += votacao(lst_votacao) tmp += votacao(lst_votacao)
tmp += oradores(lst_oradores) tmp += oradores(lst_oradores)
tmp += '\t</story>\n' tmp += '\t</story>\n'
tmp += '</document>\n' tmp += '</document>\n'

1
sapl/rules/map_rules.py

@ -251,6 +251,7 @@ rules_group_geral = {
(sessao.TipoResultadoVotacao, __base__), (sessao.TipoResultadoVotacao, __base__),
(sessao.TipoExpediente, __base__), (sessao.TipoExpediente, __base__),
(sessao.Bloco, __base__), (sessao.Bloco, __base__),
(sessao.ResumoOrdenacao, __base__),
(lexml.LexmlProvedor, __base__), (lexml.LexmlProvedor, __base__),
(lexml.LexmlPublicador, __base__), (lexml.LexmlPublicador, __base__),

81
sapl/sessao/forms.py

@ -28,7 +28,7 @@ def recupera_anos():
# apos a adicao do .dates(), por isso o reversed() abaixo # apos a adicao do .dates(), por isso o reversed() abaixo
anos = [(k.year, k.year) for k in reversed(anos_list)] anos = [(k.year, k.year) for k in reversed(anos_list)]
return anos return anos
except: except Exception:
return [] return []
@ -39,6 +39,18 @@ MES_CHOICES = [('', '---------')] + RANGE_MESES
DIA_CHOICES = [('', '---------')] + RANGE_DIAS_MES DIA_CHOICES = [('', '---------')] + RANGE_DIAS_MES
ORDENACAO_RESUMO = [('cont_mult', 'Conteúdo Multimídia'),
('exp', 'Expedientes'),
('id_basica', 'Identificação Básica'),
('lista_p', 'Lista de Presença'),
('lista_p_o_d', 'Lista de Presença Ordem do Dia'),
('mat_exp', 'Matérias do Expediente'),
('mat_o_d', 'Matérias da Ordem do Dia'),
('mesa_d', 'Mesa Diretora'),
('oradores_exped', 'Oradores do Expediente'),
('oradores_expli', 'Oradores das Explicações Pessoais')]
class BancadaForm(ModelForm): class BancadaForm(ModelForm):
class Meta: class Meta:
@ -359,3 +371,70 @@ class OradorExpedienteForm(ModelForm):
class PautaSessaoFilterSet(SessaoPlenariaFilterSet): class PautaSessaoFilterSet(SessaoPlenariaFilterSet):
titulo = _('Pesquisa de Pauta de Sessão') titulo = _('Pesquisa de Pauta de Sessão')
class ResumoOrdenacaoForm(forms.Form):
primeiro = forms.ChoiceField(label=_(''),
choices=ORDENACAO_RESUMO)
segundo = forms.ChoiceField(label=_(''),
choices=ORDENACAO_RESUMO)
terceiro = forms.ChoiceField(label=u'',
choices=ORDENACAO_RESUMO)
quarto = forms.ChoiceField(label=_(''),
choices=ORDENACAO_RESUMO)
quinto = forms.ChoiceField(label=_(''),
choices=ORDENACAO_RESUMO)
sexto = forms.ChoiceField(label=_(''),
choices=ORDENACAO_RESUMO)
setimo = forms.ChoiceField(label=_(''),
choices=ORDENACAO_RESUMO)
oitavo = forms.ChoiceField(label=_(''),
choices=ORDENACAO_RESUMO)
nono = forms.ChoiceField(label=_(''),
choices=ORDENACAO_RESUMO)
decimo = forms.ChoiceField(label=u'10°',
choices=ORDENACAO_RESUMO)
def __init__(self, *args, **kwargs):
super(ResumoOrdenacaoForm, self).__init__(*args, **kwargs)
row1 = to_row(
[('primeiro', 12)])
row2 = to_row(
[('segundo', 12)])
row3 = to_row(
[('terceiro', 12)])
row4 = to_row(
[('quarto', 12)])
row5 = to_row(
[('quinto', 12)])
row6 = to_row(
[('sexto', 12)])
row7 = to_row(
[('setimo', 12)])
row8 = to_row(
[('oitavo', 12)])
row9 = to_row(
[('nono', 12)])
row10 = to_row(
[('decimo', 12)])
self.helper = FormHelper()
self.helper.layout = Layout(
Fieldset(_(''),
row1, row2, row3, row4, row5,
row6, row7, row8, row9, row10,
form_actions(save_label='Atualizar'))
)
def clean(self):
cleaned_data = self.cleaned_data
for c1 in cleaned_data:
i = 0
for c2 in cleaned_data:
if cleaned_data[str(c1)] == cleaned_data[str(c2)]:
i = i + 1
if i > 1:
raise ValidationError(_(
'Não é possível ter campos repetidos'))

1
sapl/sessao/legacy.yaml

@ -65,7 +65,6 @@ RegistroVotacao:
numero_votos_nao: num_votos_nao numero_votos_nao: num_votos_nao
numero_votos_sim: num_votos_sim numero_votos_sim: num_votos_sim
observacao: txt_observacao observacao: txt_observacao
ordem: cod_ordem
tipo_resultado_votacao: tip_resultado_votacao tipo_resultado_votacao: tip_resultado_votacao
VotoParlamentar (RegistroVotacaoParlamentar): VotoParlamentar (RegistroVotacaoParlamentar):

20
sapl/sessao/migrations/0002_sessaoplenaria_interativa.py

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.11 on 2017-05-10 15:49
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('sessao', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='sessaoplenaria',
name='interativa',
field=models.NullBooleanField(choices=[(True, 'Sim'), (False, 'Não')], verbose_name='Sessão interativa'),
),
]

35
sapl/sessao/migrations/0003_resumoordenacao.py

@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2017-05-22 10:51
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('sessao', '0002_sessaoplenaria_interativa'),
]
operations = [
migrations.CreateModel(
name='ResumoOrdenacao',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('primeiro', models.CharField(max_length=30)),
('segundo', models.CharField(max_length=30)),
('terceiro', models.CharField(max_length=30)),
('quarto', models.CharField(max_length=30)),
('quinto', models.CharField(max_length=30)),
('sexto', models.CharField(max_length=30)),
('setimo', models.CharField(max_length=30)),
('oitavo', models.CharField(max_length=30)),
('nono', models.CharField(max_length=30)),
('decimo', models.CharField(max_length=30)),
],
options={
'verbose_name': 'Ordenação do Resumo de uma Sessão',
'verbose_name_plural': 'Ordenação do Resumo de uma Sessão',
},
),
]

28
sapl/sessao/models.py

@ -156,6 +156,9 @@ class SessaoPlenaria(models.Model):
finalizada = models.NullBooleanField(blank=True, finalizada = models.NullBooleanField(blank=True,
choices=YES_NO_CHOICES, choices=YES_NO_CHOICES,
verbose_name=_('Sessão finalizada?')) verbose_name=_('Sessão finalizada?'))
interativa = models.NullBooleanField(blank=True,
choices=YES_NO_CHOICES,
verbose_name=_('Sessão interativa'))
class Meta: class Meta:
verbose_name = _('Sessão Plenária') verbose_name = _('Sessão Plenária')
@ -490,3 +493,28 @@ class Bloco(models.Model):
def __str__(self): def __str__(self):
return self.nome return self.nome
@reversion.register()
class ResumoOrdenacao(models.Model):
'''
Tabela para registrar em qual ordem serão renderizados os componentes
da tela de resumo de uma sessão
'''
primeiro = models.CharField(max_length=30)
segundo = models.CharField(max_length=30)
terceiro = models.CharField(max_length=30)
quarto = models.CharField(max_length=30)
quinto = models.CharField(max_length=30)
sexto = models.CharField(max_length=30)
setimo = models.CharField(max_length=30)
oitavo = models.CharField(max_length=30)
nono = models.CharField(max_length=30)
decimo = models.CharField(max_length=30)
class Meta:
verbose_name = _('Ordenação do Resumo de uma Sessão')
verbose_name_plural = _('Ordenação do Resumo de uma Sessão')
def __str__(self):
return 'Ordenação do Resumo de uma Sessão'

17
sapl/sessao/serializers.py

@ -0,0 +1,17 @@
from rest_framework import serializers
from .models import SessaoPlenaria
class SessaoPlenariaSerializer(serializers.Serializer):
class Meta:
model = SessaoPlenaria
fields = ('tipo',
'sessao_legislativa',
'legislatura',
'data_inicio',
'hora_inicio',
'hora_fim',
'url_video',
'iniciada',
'finalizada'
)

6
sapl/sessao/urls.py

@ -10,7 +10,8 @@ from sapl.sessao.views import (AdicionarVariasMateriasExpediente,
PautaSessaoDetailView, PautaSessaoListView, PautaSessaoDetailView, PautaSessaoListView,
PesquisarPautaSessaoView, PesquisarPautaSessaoView,
PesquisarSessaoPlenariaView, PesquisarSessaoPlenariaView,
PresencaOrdemDiaView, PresencaView, ResumoView, PresencaOrdemDiaView, PresencaView,
ResumoOrdenacaoView, ResumoView,
SessaoCrud, TipoExpedienteCrud, SessaoCrud, TipoExpedienteCrud,
TipoResultadoVotacaoCrud, TipoSessaoCrud, TipoResultadoVotacaoCrud, TipoSessaoCrud,
VotacaoEditView, VotacaoExpedienteEditView, VotacaoEditView, VotacaoExpedienteEditView,
@ -68,6 +69,9 @@ urlpatterns = [
include(BlocoCrud.get_urls())), include(BlocoCrud.get_urls())),
url(r'^sistema/cargo-bancada/', url(r'^sistema/cargo-bancada/',
include(CargoBancadaCrud.get_urls())), include(CargoBancadaCrud.get_urls())),
url(r'^sistema/resumo-ordenacao/',
ResumoOrdenacaoView.as_view(),
name='resumo_ordenacao'),
url(r'^sessao/(?P<pk>\d+)/adicionar-varias-materias-expediente/', url(r'^sessao/(?P<pk>\d+)/adicionar-varias-materias-expediente/',
AdicionarVariasMateriasExpediente.as_view(), AdicionarVariasMateriasExpediente.as_view(),
name='adicionar_varias_materias_expediente'), name='adicionar_varias_materias_expediente'),

101
sapl/sessao/views.py

@ -3,6 +3,7 @@ from re import sub
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.decorators import permission_required from django.contrib.auth.decorators import permission_required
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.forms.utils import ErrorList from django.forms.utils import ErrorList
@ -13,7 +14,7 @@ from django.utils.decorators import method_decorator
from django.utils.html import strip_tags from django.utils.html import strip_tags
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from django.views.generic import ListView, TemplateView from django.views.generic import FormView, ListView, TemplateView
from django.views.generic.base import RedirectView from django.views.generic.base import RedirectView
from django.views.generic.detail import DetailView from django.views.generic.detail import DetailView
from django.views.generic.edit import FormMixin from django.views.generic.edit import FormMixin
@ -36,13 +37,14 @@ from sapl.sessao.forms import ExpedienteMateriaForm, OrdemDiaForm
from .forms import (AdicionarVariasMateriasFilterSet, ExpedienteForm, from .forms import (AdicionarVariasMateriasFilterSet, ExpedienteForm,
ListMateriaForm, MesaForm, OradorExpedienteForm, ListMateriaForm, MesaForm, OradorExpedienteForm,
OradorForm, PautaSessaoFilterSet, PresencaForm, OradorForm, PautaSessaoFilterSet, PresencaForm,
ResumoOrdenacaoForm,
SessaoPlenariaFilterSet, VotacaoEditForm, VotacaoForm, SessaoPlenariaFilterSet, VotacaoEditForm, VotacaoForm,
VotacaoNominalForm) VotacaoNominalForm)
from .models import (Bancada, Bloco, CargoBancada, CargoMesa, from .models import (Bancada, Bloco, CargoBancada, CargoMesa,
ExpedienteMateria, ExpedienteSessao, IntegranteMesa, ExpedienteMateria, ExpedienteSessao, IntegranteMesa,
MateriaLegislativa, Orador, OradorExpediente, OrdemDia, MateriaLegislativa, Orador, OradorExpediente, OrdemDia,
PresencaOrdemDia, RegistroVotacao, SessaoPlenaria, PresencaOrdemDia, RegistroVotacao, ResumoOrdenacao,
SessaoPlenariaPresenca, TipoExpediente, SessaoPlenaria, SessaoPlenariaPresenca, TipoExpediente,
TipoResultadoVotacao, TipoSessaoPlenaria, VotoNominal, TipoResultadoVotacao, TipoSessaoPlenaria, VotoNominal,
VotoParlamentar) VotoParlamentar)
@ -930,6 +932,48 @@ class MesaView(FormMixin, DetailView):
return reverse('sapl.sessao:mesa', kwargs={'pk': pk}) return reverse('sapl.sessao:mesa', kwargs={'pk': pk})
class ResumoOrdenacaoView(PermissionRequiredMixin, FormView):
template_name = 'sessao/resumo_ordenacao.html'
form_class = ResumoOrdenacaoForm
permission_required = 'sessao.change_resumoordenacao'
def get_success_url(self):
return reverse('sapl.base:sistema')
def get_initial(self):
ordenacao = ResumoOrdenacao.objects.first()
if ordenacao:
return {'primeiro': ordenacao.primeiro,
'segundo': ordenacao.segundo,
'terceiro': ordenacao.terceiro,
'quarto': ordenacao.quarto,
'quinto': ordenacao.quinto,
'sexto': ordenacao.sexto,
'setimo': ordenacao.setimo,
'oitavo': ordenacao.oitavo,
'nono': ordenacao.nono,
'decimo': ordenacao.decimo}
return self.initial.copy()
def form_valid(self, form):
ordenacao = ResumoOrdenacao.objects.get_or_create()[0]
ordenacao.primeiro = form.cleaned_data['primeiro']
ordenacao.segundo = form.cleaned_data['segundo']
ordenacao.terceiro = form.cleaned_data['terceiro']
ordenacao.quarto = form.cleaned_data['quarto']
ordenacao.quinto = form.cleaned_data['quinto']
ordenacao.sexto = form.cleaned_data['sexto']
ordenacao.setimo = form.cleaned_data['setimo']
ordenacao.oitavo = form.cleaned_data['oitavo']
ordenacao.nono = form.cleaned_data['nono']
ordenacao.decimo = form.cleaned_data['decimo']
ordenacao.save()
return HttpResponseRedirect(self.get_success_url())
class ResumoView(DetailView): class ResumoView(DetailView):
template_name = 'sessao/resumo.html' template_name = 'sessao/resumo.html'
model = SessaoPlenaria model = SessaoPlenaria
@ -1022,11 +1066,14 @@ class ResumoView(DetailView):
titulo = m.materia titulo = m.materia
numero = m.numero_ordem numero = m.numero_ordem
resultado = m.registrovotacao_set.all() rv = m.registrovotacao_set.first()
if resultado: if rv:
resultado = resultado[0].tipo_resultado_votacao.nome resultado = rv.tipo_resultado_votacao.nome
resultado_observacao = rv.observacao
else: else:
resultado = _('Matéria não votada') resultado = _('Matéria não votada')
resultado_observacao = _(' ')
autoria = Autoria.objects.filter(materia_id=m.materia_id) autoria = Autoria.objects.filter(materia_id=m.materia_id)
autor = [str(x.autor) for x in autoria] autor = [str(x.autor) for x in autoria]
@ -1035,6 +1082,7 @@ class ResumoView(DetailView):
'titulo': titulo, 'titulo': titulo,
'numero': numero, 'numero': numero,
'resultado': resultado, 'resultado': resultado,
'resultado_observacao': resultado_observacao,
'autor': autor 'autor': autor
} }
materias_expediente.append(mat) materias_expediente.append(mat)
@ -1124,6 +1172,47 @@ class ResumoView(DetailView):
oradores_explicacoes.append(oradores) oradores_explicacoes.append(oradores)
context.update({'oradores_explicacoes': oradores_explicacoes}) context.update({'oradores_explicacoes': oradores_explicacoes})
# =====================================================================
# Indica a ordem com a qual o template será renderizado
ordenacao = ResumoOrdenacao.objects.first()
dict_ord_template = {
'cont_mult': 'conteudo_multimidia.html',
'exp': 'expedientes.html',
'id_basica': 'identificacao_basica.html',
'lista_p': 'lista_presenca.html',
'lista_p_o_d': 'lista_presenca_ordem_dia.html',
'mat_exp': 'materias_expediente.html',
'mat_o_d': 'materias_ordem_dia.html',
'mesa_d': 'mesa_diretora.html',
'oradores_exped': 'oradores_expediente.html',
'oradores_expli': 'oradores_explicacoes.html'
}
if ordenacao:
context.update(
{'primeiro_ordenacao': dict_ord_template[ordenacao.primeiro],
'segundo_ordenacao': dict_ord_template[ordenacao.segundo],
'terceiro_ordenacao': dict_ord_template[ordenacao.terceiro],
'quarto_ordenacao': dict_ord_template[ordenacao.quarto],
'quinto_ordenacao': dict_ord_template[ordenacao.quinto],
'sexto_ordenacao': dict_ord_template[ordenacao.sexto],
'setimo_ordenacao': dict_ord_template[ordenacao.setimo],
'oitavo_ordenacao': dict_ord_template[ordenacao.oitavo],
'nono_ordenacao': dict_ord_template[ordenacao.nono],
'decimo_ordenacao': dict_ord_template[ordenacao.decimo]})
else:
context.update(
{'primeiro_ordenacao': dict_ord_template['id_basica'],
'segundo_ordenacao': dict_ord_template['cont_mult'],
'terceiro_ordenacao': dict_ord_template['mesa_d'],
'quarto_ordenacao': dict_ord_template['lista_p'],
'quinto_ordenacao': dict_ord_template['exp'],
'sexto_ordenacao': dict_ord_template['mat_exp'],
'setimo_ordenacao': dict_ord_template['oradores_exped'],
'oitavo_ordenacao': dict_ord_template['lista_p_o_d'],
'nono_ordenacao': dict_ord_template['mat_o_d'],
'decimo_ordenacao': dict_ord_template['oradores_expli']})
return self.render_to_response(context) return self.render_to_response(context)

21
sapl/settings.py

@ -84,11 +84,22 @@ INSTALLED_APPS = (
) + SAPL_APPS ) + SAPL_APPS
# FTS = Full Text Search
SEARCH_BACKEND = 'haystack.backends.whoosh_backend.WhooshEngine'
SEARCH_URL = ('PATH', PROJECT_DIR.child('whoosh'))
SOLR_URL = config('SOLR_URL', cast=str, default='')
if SOLR_URL:
SEARCH_BACKEND = 'haystack.backends.solr_backend.SolrEngine'
SEARCH_URL = ('URL', config('SOLR_URL', cast=str))
# ...or for multicore...
# 'URL': 'http://127.0.0.1:8983/solr/mysite',
HAYSTACK_CONNECTIONS = { HAYSTACK_CONNECTIONS = {
'default': { 'default': {
'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine', 'ENGINE': SEARCH_BACKEND,
'PATH': PROJECT_DIR.child('whoosh'), SEARCH_URL[0] : SEARCH_URL[1]
}, },
} }
@ -111,10 +122,7 @@ MIDDLEWARE_CLASSES = (
REST_FRAMEWORK = { REST_FRAMEWORK = {
"DEFAULT_RENDERER_CLASSES": ( "UNICODE_JSON": False,
"rest_framework.renderers.JSONRenderer",
# "rest_framework.renderers.BrowsableAPIRenderer",
),
"DEFAULT_PARSER_CLASSES": ( "DEFAULT_PARSER_CLASSES": (
"rest_framework.parsers.JSONParser", "rest_framework.parsers.JSONParser",
), ),
@ -208,6 +216,7 @@ LOCALE_PATHS = (
# Static files (CSS, JavaScript, Images) # Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.8/howto/static-files/ # https://docs.djangoproject.com/en/1.8/howto/static-files/
STATIC_URL = '/static/' STATIC_URL = '/static/'
STATIC_ROOT = PROJECT_DIR.child("collected_static") STATIC_ROOT = PROJECT_DIR.child("collected_static")
STATICFILES_DIRS = (BASE_DIR.child("static"),) STATICFILES_DIRS = (BASE_DIR.child("static"),)

4
sapl/templates/base.html

@ -157,11 +157,13 @@
<div class="col-md-4"> <div class="col-md-4">
<a class="footer__logo" href="#"> <a class="footer__logo" href="#">
<a href="http://www.interlegis.leg.br/">
<img src="{% static 'img/logo_interlegis.png' %}" alt="{% trans 'Logo do Interlegis' %} "> <img src="{% static 'img/logo_interlegis.png' %}" alt="{% trans 'Logo do Interlegis' %} ">
</a> </a>
</a>
<p> <p>
<small> <small>
Desenvolvido pelo <a href="#">Interlegis</a> em software livre e aberto. Desenvolvido pelo <a href="http://www.interlegis.leg.br/">Interlegis</a> em software livre e aberto.
</small> </small>
</p> </p>
</div> </div>

2
sapl/templates/materia/documentoacessorio_form.html

@ -2,5 +2,5 @@
{% load i18n crispy_forms_tags %} {% load i18n crispy_forms_tags %}
{% block base_content %} {% block base_content %}
{% crispy form helper %} {% crispy form %}
{% endblock %} {% endblock %}

17
sapl/templates/materia/em_lote/acessorio.html

@ -14,7 +14,7 @@
{% else %} {% else %}
<h3 style="text-align: right;">{% blocktrans with object_list.count as total_materias %}Foram encontradas {{total_materias}} matérias.{% endblocktrans %}</h3> <h3 style="text-align: right;">{% blocktrans with object_list.count as total_materias %}Foram encontradas {{total_materias}} matérias.{% endblocktrans %}</h3>
{% endif %} {% endif %}
<form method="POST"> <form method="POST" enctype="multipart/form-data">
{% csrf_token %} {% csrf_token %}
<fieldset> <fieldset>
<legend>Documento Acessório</legend> <legend>Documento Acessório</legend>
@ -40,20 +40,7 @@
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<label>Autor:</label> <label>Autor:</label>
<span id="nome_autor" name="nome_autor"> </span> <input type="text" name="autor" class="form-control" required="False">
</div>
<div id="modal_autor" title="Selecione o Autor" align="center">
<input id="q" type="text" /> <input id="pesquisar" type="submit" value="Pesquisar" class="btn btn-primary btn-sm"/>
<div id="div-resultado"></div>
<input type="submit" id="selecionar" value="Selecionar" hidden="true" />
</div>
<div class="row-fluid">
<div class="col-md-0"><input id="id_autor" maxlength="50" name="autor" type="hidden" /></div>
<div class="col-md-2"><input type="button" name="pesquisar" value="Pesquisar Autor" class="btn btn btn-primary btn-sm" id="button-id-pesquisar"/></div>
<div class="col-md-10"><input type="button" name="limpar" value="Limpar Autor" class="btn btn btn-primary btn-sm" id="button-id-limpar"/></div>
</div>
</div> </div>
<div class="row"> <div class="row">

10
sapl/templates/materia/materialegislativa_filter.html

@ -58,10 +58,11 @@
{% if m.autoria_set.all %} {% if m.autoria_set.all %}
<strong>Autores:</strong> <strong>Autores:</strong>
{% for a in m.autoria_set.all %} {% for a in m.autoria_set.all %}
{% if a.primeiro_autor %}
{% if not forloop.first %} {% if not forloop.first %}
, &nbsp;&nbsp; {{a.autor}} , &nbsp;&nbsp;
{% else %} {% endif %}
&nbsp;{{a.autor}} &nbsp;{{a.autor.nome}}
{% endif %} {% endif %}
{% endfor %} {% endfor %}
</br> </br>
@ -125,6 +126,9 @@
{% endfor %} {% endfor %}
{% endif %} {% endif %}
<p></p> <p></p>
{% if m.tramitacao_set.all.exists %}
<a href="{% url 'sapl.materia:acompanhar_materia' m.id %}">Acompanhar Matéria</a>
{% endif %}
</tr> </tr>
{% endfor %} {% endfor %}
{% else %} {% else %}

18
sapl/templates/navbar.yaml

@ -5,10 +5,10 @@
- title: {% trans 'Institucional' %} - title: {% trans 'Institucional' %}
children: children:
- title: {% trans 'Mesa Diretora' %}
url: sapl.parlamentares:mesa_diretora
- title: {% trans 'Comissões' %} - title: {% trans 'Comissões' %}
url: sapl.comissoes:comissao_list url: sapl.comissoes:comissao_list
- title: {% trans 'Mesa Diretora' %}
url: sapl.parlamentares:mesa_diretora
- title: {% trans 'Parlamentares' %} - title: {% trans 'Parlamentares' %}
url: sapl.parlamentares:parlamentar_list url: sapl.parlamentares:parlamentar_list
@ -26,21 +26,21 @@
- title: {% trans 'Atividade Legislativa' %} - title: {% trans 'Atividade Legislativa' %}
children: children:
- title: {% trans 'Acessório em Lote' %}
url: sapl.materia:acessorio_em_lote
check_permission: materia.list_documentoacessorio {% comment %} FIXME transformar para checagens de menu_[funcionalidade]{% endcomment%}
- title: {% trans 'Matérias Legislativas' %}
url: sapl.materia:pesquisar_materia
- title: {% trans 'Pautas das Sessões' %}
url: sapl.sessao:pesquisar_pauta
- title: {% trans 'Proposições' %} - title: {% trans 'Proposições' %}
url: sapl.materia:proposicao_list url: sapl.materia:proposicao_list
check_permission: materia.add_proposicao check_permission: materia.add_proposicao
- title: {% trans 'Matérias Legislativas' %}
url: sapl.materia:pesquisar_materia
- title: {% trans 'Sessões Plenárias' %} - title: {% trans 'Sessões Plenárias' %}
url: sapl.sessao:pesquisar_sessao url: sapl.sessao:pesquisar_sessao
- title: {% trans 'Pautas das Sessões' %}
url: sapl.sessao:pesquisar_pauta
- title: {% trans 'Tramitação em Lote' %} - title: {% trans 'Tramitação em Lote' %}
url: sapl.materia:primeira_tramitacao_em_lote url: sapl.materia:primeira_tramitacao_em_lote
check_permission: materia.list_tramitacao {% comment %} FIXME transformar para checagens de menu_[funcionalidade]{% endcomment%} check_permission: materia.list_tramitacao {% comment %} FIXME transformar para checagens de menu_[funcionalidade]{% endcomment%}
- title: {% trans 'Acessório em Lote' %}
url: sapl.materia:acessorio_em_lote
check_permission: materia.list_documentoacessorio {% comment %} FIXME transformar para checagens de menu_[funcionalidade]{% endcomment%}
- title: {% trans 'Normas Jurídicas' %} - title: {% trans 'Normas Jurídicas' %}
children: children:

3
sapl/templates/parlamentares/composicaomesa_form.html

@ -158,8 +158,7 @@
// Atualiza os campos após alguma operação de mudança da Legislatura/Sessao ou // Atualiza os campos após alguma operação de mudança da Legislatura/Sessao ou
// Inserção/Remoção // Inserção/Remoção
function altera_field(id_legislatura, id_sessao=null, msg=null){ function altera_field(id_legislatura, id_sessao=null, msg=null){
// Pega o novo valor do campo que foi modificado (Sessao/Legislatura) ou utiliza // Pega o novo valor dos campos modificados
// o valor da Legislatura, por conveniência, quando há alguma inserção ou remoção
var sessao_value = id_sessao var sessao_value = id_sessao
var legislatura_value = id_legislatura var legislatura_value = id_legislatura

2
sapl/templates/parlamentares/parlamentar_perfil_publico.html

@ -61,7 +61,7 @@
<div class="col-sm-8"> <div class="col-sm-8">
<div id="div_data_nascimento" class="form-group"> <div id="div_data_nascimento" class="form-group">
<p><b>Fax: </b> &nbsp {{object.numero_gab_parlamentar|default_if_none:"Não informado"}}</p> <p><b>Fax: </b> &nbsp {{object.fax|default_if_none:"Não informado"}}</p>
</div> </div>
</div> </div>

89
sapl/templates/parlamentares/public_composicaomesa_form.html

@ -10,7 +10,7 @@
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<label>Escolha uma Legislatura</label> <label>Escolha uma Legislatura</label>
<select name="legislatura" class="form-control" onChange="form.submit();"> <select name="legislatura" id="id_legislatura" class="form-control">
{% for l in legislaturas %} {% for l in legislaturas %}
<option value="{{l.id}}" {% if l == legislatura_selecionada %} selected {% endif %}> <option value="{{l.id}}" {% if l == legislatura_selecionada %} selected {% endif %}>
{{l}} {{l}}
@ -20,7 +20,7 @@
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label>Escolha uma Sessão Legislativa</label> <label>Escolha uma Sessão Legislativa</label>
<select name="sessao" class="form-control" onChange="form.submit();"> <select name="sessao" id="id_sessao_legislativa" class="form-control">
{% for s in sessoes %} {% for s in sessoes %}
<option value="{{s.id}}" {% if s == sessao_selecionada %} selected {% endif %}> <option value="{{s.id}}" {% if s == sessao_selecionada %} selected {% endif %}>
{{s}} {{s}}
@ -33,7 +33,7 @@
<br /> <br />
<fieldset class="form-group"> <fieldset class="form-group">
<legend>Composição da Mesa Diretora</legend> <legend>Composição da Mesa Diretora</legend>
<table class="table table-striped table-hover table-link-ordering"> <table id="tabela-composicao" class="table table-striped table-hover table-link-ordering">
<thead> <thead>
<tr> <tr>
<th></th> <th></th>
@ -46,7 +46,7 @@
{% for p in composicao_mesa %} {% for p in composicao_mesa %}
<tr> <tr>
{% if p.parlamentar.fotografia %} {% if p.parlamentar.fotografia %}
<td><img class="img-responsive" height="60" width="60" src="{{ p.parlamentar.fotografia.url }}"></td> <td><img class="img-responsive" height="84" width="84" src="{{ p.parlamentar.fotografia.url }}"></td>
{% else %} {% else %}
<td></td> <td></td>
{% endif %} {% endif %}
@ -60,3 +60,84 @@
</fieldset> </fieldset>
</form> </form>
{% endblock detail_content %} {% endblock detail_content %}
{% block extra_js %}
<script language="Javascript">
//##############################################################
//# USEFUL FUNCTIONS #
//##############################################################
function retorna_imagem(url){
if (url != null){
image_component = '<img class="img-responsive" height="30" width="30" src="' + url + '">'
return image_component
}
else{
return ''
}
}
//##############################################################
//# EVENT HANDLERS #
//##############################################################
function altera_field(id_legislatura, id_sessao=null){
// Pega o novo valor do campo que foi modificado (Sessao/Legislatura)
var sessao_value = id_sessao
var legislatura_value = id_legislatura
// Limpa o campo que será atualizado
$("#id_sessao_legislativa option").remove();
$("#tabela-composicao tbody").empty();
$.get("/mesa-diretora/altera-field-mesa-public-view",
{legislatura: legislatura_value, sessao: sessao_value},
function(data) {
// Caso não venha nenhum dado da requisição, retorna null
if ($.isEmptyObject(data)){
return null
}
lista_sessoes = data['lista_sessoes'];
lista_parlamentares = data['lista_parlamentares'];
lista_cargos = data['lista_cargos'];
lista_fotos = data['lista_fotos']
lista_partidos = data['lista_partidos']
// Atualiza a listagem de sessões
for (i = 0; i < lista_sessoes.length; i++) {
$('#id_sessao_legislativa').append('<option value="' + lista_sessoes[i][0] + '">' + lista_sessoes[i][1] + '</option>');
}
// Atualiza a tabela de composição
for (i = 0; i < lista_parlamentares.length; i++) {
$("#tabela-composicao tbody").append('<tr> <td>' + retorna_imagem(lista_fotos[i]) +'</td> <td><a href="/parlamentar/' + lista_parlamentares[i][0] + '">' + lista_parlamentares[i][1] + '</a></td> <td>' + lista_partidos[i] + '</td> <td>'+ lista_cargos[i][1] + '</td> </tr>');
}
// Garante que a Sessão atual será a selecionada previamente e, no caso em que
// o campo modificado seja o de Legislatura, que a Sessão seja a última daquela
// legislatura
$("#id_sessao_legislativa").val(data['sessao_selecionada'])
});
}
//#############################################################
//# EVENTS CATCH #
//#############################################################
$("#id_legislatura").change(function(){
legislatura = $("#id_legislatura").val();
altera_field(legislatura);
});
$("#id_sessao_legislativa").change(function(){
legislatura = $("#id_legislatura").val();
sessao = $("#id_sessao_legislativa").val();
altera_field(legislatura, sessao);
});
</script>
{% endblock %}

8
sapl/templates/protocoloadm/protocolo_filter.html

@ -44,7 +44,13 @@
</br> </br>
<strong>Assunto:</strong> {{ p.assunto_ementa|default_if_none:"Não Informado"}}</br> <strong>Assunto:</strong> {{ p.assunto_ementa|default_if_none:"Não Informado"}}</br>
<strong>Data Protocolo:</strong> {{ p.data|date:"d/m/Y"|default_if_none:"Não Informado" }} - Horário: {{ p.hora|date:"G:i:s" }}</br> <strong>Data Protocolo:</strong> {{ p.data|date:"d/m/Y"|default_if_none:"Não Informado" }} - Horário: {{ p.hora|date:"G:i:s" }}</br>
<strong>Interessado:</strong> {{ p.interessado }}</br>
{% if p.tipo_processo == 0 %}
<strong>Interessado:</strong> {{ p.interessado|default_if_none:"Não informado" }}</br>
{% elif p.tipo_processo == 1 %}
<strong>Autor:</strong> {{ p.autor.nome|default_if_none:"Não informado" }}</br>
{% endif %}
<strong>Natureza do Processo:</strong> <strong>Natureza do Processo:</strong>
{% if p.tipo_processo == 0 %} Administrativo {% elif p.tipo_processo == 1 %} Matéria Legislativa {% endif %}</br> {% if p.tipo_processo == 0 %} Administrativo {% elif p.tipo_processo == 1 %} Matéria Legislativa {% endif %}</br>
<strong>Classificação:</strong> {{ p.tipo_documento|default_if_none:p.tipo_materia }} </br> <strong>Classificação:</strong> {{ p.tipo_documento|default_if_none:p.tipo_materia }} </br>

4
sapl/templates/protocoloadm/protocolo_list.html

@ -21,10 +21,10 @@
</a></br> </a></br>
<strong>Assunto:</strong> {{ p.assunto_ementa }}</br> <strong>Assunto:</strong> {{ p.assunto_ementa }}</br>
<strong>Data Protocolo:</strong> {{ p.data|date:"d/m/Y" }} - Horário: {{ p.timestamp|date:"H:m:s" }}</br> <strong>Data Protocolo:</strong> {{ p.data|date:"d/m/Y" }} - Horário: {{ p.timestamp|date:"H:m:s" }}</br>
<strong>Interessado:</strong> {{ p.interessado }}</br>
<strong>Natureza do Processo:</strong> <strong>Natureza do Processo:</strong>
{% if p.tipo_processo == 0 %} {% if p.tipo_processo == 0 %}
Administrativo Administrativo </br>
<strong>Interessado:</strong> {{ p.interessado }}</br>
{% elif p.tipo_processo == 1 %} {% elif p.tipo_processo == 1 %}
Matéria Legislativa Matéria Legislativa
{% endif %}</br> {% endif %}</br>

10
sapl/templates/protocoloadm/protocolo_mostrar.html

@ -6,9 +6,13 @@
<strong>Protocolo: </strong>{{ protocolo.numero|stringformat:'06d' }}/{{ protocolo.ano }}</br> <strong>Protocolo: </strong>{{ protocolo.numero|stringformat:'06d' }}/{{ protocolo.ano }}</br>
<strong>Assunto: </strong> {{ protocolo.assunto_ementa|default:" Não informado." }}</br> <strong>Assunto: </strong> {{ protocolo.assunto_ementa|default:" Não informado." }}</br>
<strong>Data Protocolo: </strong> {{ protocolo.data|date:"d/m/Y" }} - Horário: {{ protocolo.hora|date:"H:i" }}</br> <strong>Data Protocolo: </strong> {{ protocolo.data|date:"d/m/Y" }} - Horário: {{ protocolo.hora|date:"H:i" }}</br>
<strong>Autor:</strong> {{protocolo.autor.nome}} </br>
<strong>Interessado: </strong> {{ protocolo.interessado|default:" Não informado." }}</br> {% if protocolo.tipo_processo == 0 %}
<!-- TODO: convert if-else to custom tag --> <strong>Interessado:</strong> {{ protocolo.interessado|default_if_none:"Não informado" }}</br>
{% elif protocolo.tipo_processo == 1 %}
<strong>Autor:</strong>{{ protocolo.autor.nome|default_if_none:"Não informado" }}</br>
{% endif %} <!-- TODO: convert if-else to custom tag -->
<strong>Natureza do Processo: </strong>{% if protocolo.tipo_processo == 0 %} Administrativo {% elif protocolo.tipo_processo == 1 %} Legislativo {% endif %}</br> <strong>Natureza do Processo: </strong>{% if protocolo.tipo_processo == 0 %} Administrativo {% elif protocolo.tipo_processo == 1 %} Legislativo {% endif %}</br>
<strong>Número de Páginas: </strong> {{ protocolo.numero_paginas }} </br> <strong>Número de Páginas: </strong> {{ protocolo.numero_paginas }} </br>
<strong>Observação: </strong>{{ protocolo.observacao|default:" Não informado." }}</br> <strong>Observação: </strong>{{ protocolo.observacao|default:" Não informado." }}</br>

8
sapl/templates/sessao/blocos_resumo/conteudo_multimidia.html

@ -0,0 +1,8 @@
<fieldset>
<legend>Conteúdo Multimídia</legend>
<div class="row">
<div class="col-md-6">{{multimidia_audio}}</div>
<div class="col-md-6">{{multimidia_video}}</div>
</div>
</fieldset>
<br /><br /><br />

17
sapl/templates/sessao/blocos_resumo/expedientes.html

@ -0,0 +1,17 @@
<fieldset>
<legend>Expedientes</legend>
<table class="table">
<thead class="thead-default">
{% for e in expedientes %}
<tr>
<td>
<b>{{e.tipo}}: </b> <br /><br />
<div contenteditable="false" style="border:0.5px solid #BAB4B1; border-radius: 10px; background-color: rgba(225, 225, 225, .8);">
<p>{{e.conteudo|safe}}</p>
</div>
</td>
</tr>
{% endfor %}
</thead>
</table>
</fieldset>

8
sapl/templates/sessao/blocos_resumo/identificacao_basica.html

@ -0,0 +1,8 @@
<fieldset>
<legend>Identificação Básica</legend>
<div class="row">
{% for b in basica %}
<div class="col-md-4">{{b}}</div>
{% endfor %}
</div>
</fieldset>

8
sapl/templates/sessao/blocos_resumo/lista_presenca.html

@ -0,0 +1,8 @@
<fieldset>
<legend>Lista de Presença na Sessão</legend>
<div class="row">
{% for p in presenca_sessao %}
<div class="col-md-12">{{p.nome_parlamentar}} / {{ p.filiacao_atual }}</div>
{% endfor %}
</div>
</fieldset>

8
sapl/templates/sessao/blocos_resumo/lista_presenca_ordem_dia.html

@ -0,0 +1,8 @@
<fieldset>
<legend>Lista de Presença na Ordem do Dia</legend>
<div class="row">
{% for p in presenca_ordem %}
<div class="col-md-12">{{p.nome_parlamentar}} / {{ p.filiacao_atual }}</div>
{% endfor %}
</div>
</fieldset>

25
sapl/templates/sessao/blocos_resumo/materias_expediente.html

@ -0,0 +1,25 @@
<fieldset>
<legend>Matérias do Expediente</legend>
<table class="table table-striped table-hover">
<thead>
<tr>
<th>Matéria</th>
<th>Ementa</th>
<th>Resultado da Votação</th>
</tr>
</thead>
<tbody>
{% for m in materia_expediente %}
<tr>
<td>
{{m.numero}} - {{m.titulo}}
<br />
<b>Autor{{ m.autor|length|pluralize:"es" }}</b>: {{ m.autor|join:', ' }}
</td>
<td>{{m.ementa|safe}}</td>
<td><b>{{m.resultado}}</b><br/>{{m.resultado_observacao}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</fieldset>

25
sapl/templates/sessao/blocos_resumo/materias_ordem_dia.html

@ -0,0 +1,25 @@
<fieldset>
<legend>Matérias da Ordem do Dia</legend>
<table class="table table-striped table-hover">
<thead>
<tr>
<th>Matéria</th>
<th>Ementa</th>
<th>Resultado da Votação</th>
</tr>
</thead>
<tbody>
{% for m in materias_ordem %}
<tr>
<td>
{{m.numero}} - {{m.titulo}}
<br />
<b>Autor{{ m.autor|length|pluralize:"es" }}</b>: {{ m.autor|join:', ' }}
</td>
<td>{{m.ementa|safe}}</td>
<td><b>{{m.resultado}}</b><br/>{{m.resultado_observacao}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</fieldset>

10
sapl/templates/sessao/blocos_resumo/mesa_diretora.html

@ -0,0 +1,10 @@
<fieldset>
<legend>Mesa Diretora</legend>
<div class="row">
{% for m in mesa %}
<div class="col-md-12"><b>{{m.cargo}}:
</b>{{m.parlamentar.nome_parlamentar}} / {{ m.parlamentar.filiacao_atual }}
</div>
{% endfor %}
</div>
</fieldset>

14
sapl/templates/sessao/blocos_resumo/oradores_expediente.html

@ -0,0 +1,14 @@
<fieldset>
<legend>Oradores do Expediente</legend>
<div class="row">
<div class="col-md-6">Parlamentar</div>
<div class="col-md-6">Discurso</div>
</div>
<div class="row">
{% for o in oradores %}
<div class="col-md-6"><b>{{o.numero_ordem}}</b> - {{o.parlamentar}}</div>
<div class="col-md-6">{{o.url_discurso}}</div>
</br>
{% endfor %}
</div>
</fieldset>

14
sapl/templates/sessao/blocos_resumo/oradores_explicacoes.html

@ -0,0 +1,14 @@
<fieldset>
<legend>Oradores das Explicações Pessoais</legend>
<div class="row">
<div class="col-md-6">Parlamentar</div>
<div class="col-md-6">Discurso</div>
</div>
<div class="row">
{% for o in oradores_explicacoes %}
<div class="col-md-6"><b>{{o.numero_ordem}}</b> - {{o.parlamentar.nome_parlamentar}} / {{ o.parlamentar.filiacao_atual }}</div>
<div class="col-md-6">{{o.url_discurso}}</div>
</br>
{% endfor %}
</div>
</fieldset>

151
sapl/templates/sessao/resumo.html

@ -19,159 +19,36 @@
</strong> </strong>
</p> </p>
</div> </div>
<fieldset>
<legend>Identificação Básica</legend> {% include 'sessao/blocos_resumo/'|add:primeiro_ordenacao %}
<div class="row">
{% for b in basica %}
<div class="col-md-4">{{b}}</div>
{% endfor %}
</div>
</fieldset>
<br /><br /><br /> <br /><br /><br />
<fieldset> {% include 'sessao/blocos_resumo/'|add:segundo_ordenacao %}
<legend>Conteúdo Multimídia</legend>
<div class="row">
<div class="col-md-6">{{multimidia_audio}}</div>
<div class="col-md-6">{{multimidia_video}}</div>
</div>
</fieldset>
<br /><br /><br /> <br /><br /><br />
<fieldset> {% include 'sessao/blocos_resumo/'|add:terceiro_ordenacao %}
<legend>Mesa Diretora</legend>
<div class="row">
{% for m in mesa %}
<div class="col-md-12"><b>{{m.cargo}}:
</b>{{m.parlamentar.nome_parlamentar}} / {{ m.parlamentar.filiacao_atual }}</div>
{% endfor %}
</div>
</fieldset>
<br /><br /><br /> <br /><br /><br />
<fieldset> {% include 'sessao/blocos_resumo/'|add:quarto_ordenacao %}
<legend>Lista de Presença na Sessão</legend>
<div class="row">
{% for p in presenca_sessao %}
<div class="col-md-12">{{p.nome_parlamentar}} / {{ p.filiacao_atual }}</div>
{% endfor %}
</div>
</fieldset>
<br /><br /><br /> <br /><br /><br />
<fieldset> {% include 'sessao/blocos_resumo/'|add:quinto_ordenacao %}
<legend>Expedientes</legend>
<table class="table">
<thead class="thead-default">
{% for e in expedientes %}
<tr>
<td>
<b>{{e.tipo}}: </b> <br /><br />
<div contenteditable="false" style="border:0.5px solid #BAB4B1; border-radius: 10px; background-color: rgba(225, 225, 225, .8);">
<p>{{e.conteudo|safe}}</p>
</div>
</td>
</tr>
{% endfor %}
</thead>
</table>
</fieldset>
<br /><br /><br /> <br /><br /><br />
<fieldset> {% include 'sessao/blocos_resumo/'|add:sexto_ordenacao %}
<legend>Matérias do Expediente</legend>
<table class="table table-striped table-hover">
<thead>
<tr>
<th>Matéria</th>
<th>Ementa</th>
<th>Resultado da Votação</th>
</tr>
</thead>
<tbody>
{% for m in materia_expediente %}
<tr>
<td>
{{m.numero}} - {{m.titulo}}
<br />
<b>Autor{{ m.autor|length|pluralize:"es" }}</b>: {{ m.autor|join:', ' }}
</td>
<td>{{m.ementa|safe}}</td>
<td>{{m.resultado}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</fieldset>
<br /><br /><br /> <br /><br /><br />
<fieldset> {% include 'sessao/blocos_resumo/'|add:setimo_ordenacao %}
<legend>Oradores do Expediente</legend>
<div class="row">
<div class="col-md-6">Parlamentar</div>
<div class="col-md-6">Discurso</div>
</div>
<div class="row">
{% for o in oradores %}
<div class="col-md-6"><b>{{o.numero_ordem}}</b> - {{o.parlamentar}}</div>
<div class="col-md-6">{{o.url_discurso}}</div>
</br>
{% endfor %}
</div>
</fieldset>
<br /><br /><br /> <br /><br /><br />
<fieldset> {% include 'sessao/blocos_resumo/'|add:oitavo_ordenacao %}
<legend>Lista de Presença na Ordem do Dia</legend>
<div class="row">
{% for p in presenca_ordem %}
<div class="col-md-12">{{p.nome_parlamentar}} / {{ p.filiacao_atual }}</div>
{% endfor %}
</div>
</fieldset>
<br /><br /><br /> <br /><br /><br />
<fieldset> {% include 'sessao/blocos_resumo/'|add:nono_ordenacao %}
<legend>Matérias da Ordem do Dia</legend> <br /><br /><br />
<table class="table table-striped table-hover">
<thead> {% include 'sessao/blocos_resumo/'|add:decimo_ordenacao %}
<tr> <br /><br /><br />
<th>Matéria</th>
<th>Ementa</th>
<th>Resultado da Votação</th>
<th>Observação da Votação</th>
</tr>
</thead>
<tbody>
{% for m in materias_ordem %}
<tr>
<td>
{{m.numero}} - {{m.titulo}}
<br />
<b>Autor{{ m.autor|length|pluralize:"es" }}</b>: {{ m.autor|join:', ' }}
</td>
<td>{{m.ementa|safe}}</td>
<td>{{m.resultado}}</td>
<td>{{m.resultado_observacao}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</fieldset>
<fieldset>
<legend>Oradores das Explicações Pessoais</legend>
<div class="row">
<div class="col-md-6">Parlamentar</div>
<div class="col-md-6">Discurso</div>
</div>
<div class="row">
{% for o in oradores_explicacoes %}
<div class="col-md-6"><b>{{o.numero_ordem}}</b> - {{o.parlamentar.nome_parlamentar}} / {{ o.parlamentar.filiacao_atual }}</div>
<div class="col-md-6">{{o.url_discurso}}</div>
</br>
{% endfor %}
</div>
</fieldset>
{% endblock detail_content %} {% endblock detail_content %}

13
sapl/templates/sessao/resumo_ordenacao.html

@ -0,0 +1,13 @@
{% extends "crud/form.html" %}
{% load i18n %}
{% load crispy_forms_tags %}
{% block title %}
<h1 class="page-header">Ordenação do Resumo</h1>
{% endblock %}
{% block base_content %}
{% crispy form %}
{% endblock %}

9
sapl/templates/sessao/votacao/nominal_detail.html

@ -18,8 +18,13 @@
<legend>Votos</legend> <legend>Votos</legend>
<div class="row"> <div class="row">
{% for v in votos %} {% for v in votos %}
<div class="col-md-6">{{v.parlamentar}}</div> <div class="col-md-6">{{v.parlamentar}} -
<div class="col-md-6">{{v.voto}}</div> {% if v.voto == '-1'%}
<b>Voto não Registrado</b>
{% else %}
<b>{{v.voto}}</b>
{% endif %}
</div>
{% endfor %} {% endfor %}
</div> </div>
</fieldset> </fieldset>

11
sapl/templates/sessao/votacao/nominal_edit.html

@ -18,8 +18,15 @@
<legend>Votos</legend> <legend>Votos</legend>
<div class="row"> <div class="row">
{% for v in votos %} {% for v in votos %}
<div class="col-md-6">{{v.parlamentar}}</div>
<div class="col-md-6">{{v.voto}}</div> <div class="col-md-6">{{v.parlamentar}} -
{% if v.voto == '-1'%}
<b>Voto não Registrado</b>
{% else %}
<b>{{v.voto}}</b>
{% endif %}
</div>
{% endfor %} {% endfor %}
</div> </div>
</fieldset> </fieldset>

2
sapl/templates/sistema.html

@ -82,7 +82,7 @@
<div class="col-md-6"><a href="{% url 'sapl.sessao:tiporesultadovotacao_list' %}" class="btn btn-link">Tipo de Resultado da Votação</a></div> <div class="col-md-6"><a href="{% url 'sapl.sessao:tiporesultadovotacao_list' %}" class="btn btn-link">Tipo de Resultado da Votação</a></div>
<div class="col-md-6"><a href="#" class="btn btn-link">Cadastro dos endereços dos computadores dos parlamentares</a></div> <div class="col-md-6"><a href="#" class="btn btn-link">Cadastro dos endereços dos computadores dos parlamentares</a></div>
<div class="col-md-6"><a href="{% url 'sapl.sessao:tipoexpediente_list' %}" class="btn btn-link">Tipo de Expediente</a></div> <div class="col-md-6"><a href="{% url 'sapl.sessao:tipoexpediente_list' %}" class="btn btn-link">Tipo de Expediente</a></div>
<div class="col-md-6"><a href="#" class="btn btn-link">Propriedades do Painel Eletrônico</a></div> <div class="col-md-6"><a href="{% url 'sapl.sessao:resumo_ordenacao' %}" class="btn btn-link">Ordenação do Resumo</a></div>
</div> </div>
<hr /> <hr />

112
sapl/utils.py

@ -1,15 +1,13 @@
import hashlib
import logging
import os
import re
from datetime import date from datetime import date
from functools import wraps from functools import wraps
from unicodedata import normalize as unicodedata_normalize
from subprocess import PIPE, call from subprocess import PIPE, call
from threading import Thread from threading import Thread
from unicodedata import normalize as unicodedata_normalize
import hashlib
import logging
import os
import re
import django_filters
import magic
from crispy_forms.helper import FormHelper from crispy_forms.helper import FormHelper
from crispy_forms.layout import HTML, Button from crispy_forms.layout import HTML, Button
from django import forms from django import forms
@ -22,10 +20,13 @@ from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from floppyforms import ClearableFileInput from floppyforms import ClearableFileInput
from reversion.admin import VersionAdmin from reversion.admin import VersionAdmin
import django_filters
import magic
from sapl.crispy_layout_mixin import SaplFormLayout, form_actions, to_row from sapl.crispy_layout_mixin import SaplFormLayout, form_actions, to_row
from sapl.settings import BASE_DIR, PROJECT_DIR from sapl.settings import BASE_DIR, PROJECT_DIR
sapl_logger = logging.getLogger(BASE_DIR.name) sapl_logger = logging.getLogger(BASE_DIR.name)
@ -159,8 +160,8 @@ class SaplGenericRelation(GenericRelation):
assert isinstance(field, (tuple, list)), _( assert isinstance(field, (tuple, list)), _(
'fields_search deve ser um array de tuplas ou listas.') 'fields_search deve ser um array de tuplas ou listas.')
assert len(field) == 2, _( assert len(field) <= 3, _(
'cada tupla de fields_search deve possuir duas strins') 'cada tupla de fields_search deve possuir até 3 strings')
# TODO implementar assert para validar campos do Model e lookups # TODO implementar assert para validar campos do Model e lookups
@ -391,92 +392,6 @@ def intervalos_tem_intersecao(a_inicio, a_fim, b_inicio, b_fim):
return maior_inicio <= menor_fim return maior_inicio <= menor_fim
"""
def permissoes(nome_grupo, app_label):
lista_permissoes = []
try:
perms = list(Permission.objects.filter(
group__name=nome_grupo))
for p in perms:
lista_permissoes.append('%s.%s' % (app_label, p.codename))
except:
pass
return set(lista_permissoes)
def permission_required_for_app(app_label, login_url=None,
raise_exception=False):
Decorator for views that checks whether a user has a particular permission
enabled, redirecting to the log-in page if necessary.
If the raise_exception parameter is given the PermissionDenied exception
is raised.
def check_perms(user):
if user.has_module_perms(app_label):
return True
# In case the 403 handler should be called raise the exception
if raise_exception:
raise PermissionDenied
# As the last resort, show the login form
return False
return user_passes_test(check_perms, login_url=login_url)
def permissoes_materia():
return permissoes('Operador de Matéria', 'materia')
def permissoes_comissoes():
return permissoes('Operador de Comissões', 'comissoes')
def permissoes_norma():
return permissoes('Operador de Norma Jurídica', 'norma')
def permissoes_protocoloadm():
return permissoes('Operador de Protocolo Administrativo', 'protocoloadm')
def permissoes_adm():
return permissoes('Operador Administrativo', 'protocoloadm')
def permissoes_sessao():
return permissoes('Operador de Sessão Plenária', 'sessao')
def permissoes_painel():
return permissoes('Operador de Painel Eletrônico', 'painel')
def permissoes_autor():
return permissoes('Autor', 'materia')
def permissoes_parlamentares():
lista_permissoes = []
try:
cts = ContentType.objects.filter(app_label='parlamentares')
perms_parlamentares = list(Permission.objects.filter(
content_type__in=cts))
for p in perms_parlamentares:
lista_permissoes.append('parlamentares.' + p.codename)
except:
pass
return set(lista_permissoes)
def permissao_tb_aux(self):
u = self.request.user
if u.groups.filter(name='Operador Geral').exists() or u.is_superuser:
return True
else:
return False
"""
class MateriaPesquisaOrderingFilter(django_filters.OrderingFilter): class MateriaPesquisaOrderingFilter(django_filters.OrderingFilter):
choices = ( choices = (
@ -605,8 +520,8 @@ def texto_upload_path(instance, filename, subpath=''):
seguida para armazenar o arquivo. seguida para armazenar o arquivo.
""" """
if subpath and '/' not in subpath: # if subpath and '/' not in subpath:
subpath = subpath + '/' # subpath = subpath + '/'
""" TODO: Verifique possibilidade de otimização do código de normalização """ TODO: Verifique possibilidade de otimização do código de normalização
do filename... do filename...
@ -624,7 +539,7 @@ def texto_upload_path(instance, filename, subpath=''):
if isinstance(instance, (DocumentoAdministrativo, Proposicao)): if isinstance(instance, (DocumentoAdministrativo, Proposicao)):
prefix = 'private' prefix = 'private'
path = './sapl/%(prefix)s/%(model_name)s/%(pk)s/%(subpath)s%(filename)s' %\ path = './sapl/%(prefix)s/%(model_name)s/%(subpath)s/%(pk)s/%(filename)s' %\
{ {
'prefix': prefix, 'prefix': prefix,
'model_name': instance._meta.model_name, 'model_name': instance._meta.model_name,
@ -637,6 +552,7 @@ def texto_upload_path(instance, filename, subpath=''):
class UpdateIndexCommand(Thread): class UpdateIndexCommand(Thread):
def run(self): def run(self):
call([PROJECT_DIR.child('manage.py'), 'update_index'], call([PROJECT_DIR.child('manage.py'), 'update_index'],
stdout=PIPE) stdout=PIPE)

2
start.sh

@ -27,7 +27,7 @@ create_env() {
echo "SECRET_KEY="$KEY > $FILENAME echo "SECRET_KEY="$KEY > $FILENAME
# now only appends # now only appends
echo "DATABASE_URL = "$DATABASE_URL >> $FILENAME echo "DATABASE_URL = "$DATABASE_URL >> $FILENAME
echo "DEBUG = ""${DEBUG-True}" >> $FILENAME echo "DEBUG = ""${DEBUG-False}" >> $FILENAME
echo "EMAIL_USE_TLS = ""${USE_TLS-True}" >> $FILENAME echo "EMAIL_USE_TLS = ""${USE_TLS-True}" >> $FILENAME
echo "EMAIL_PORT = ""${EMAIL_PORT-587}" >> $FILENAME echo "EMAIL_PORT = ""${EMAIL_PORT-587}" >> $FILENAME
echo "EMAIL_HOST = ""${EMAIL_HOST-''}" >> $FILENAME echo "EMAIL_HOST = ""${EMAIL_HOST-''}" >> $FILENAME

Loading…
Cancel
Save