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. 4
      config/env-sample
  6. 2
      config/env_dockerfile
  7. 2
      docker-compose.yml
  8. 38
      docs/deploy.rst
  9. 2
      docs/instacao31.rst
  10. 22
      docs/solr.rst
  11. 1
      requirements/requirements.txt
  12. 64
      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. 27
      sapl/base/search_indexes.py
  19. 3
      sapl/base/urls.py
  20. 9
      sapl/crud/base.py
  21. 63
      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. 18
      sapl/materia/models.py
  28. 80
      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. 48
      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. 109
      sapl/sessao/views.py
  48. 21
      sapl/settings.py
  49. 6
      sapl/templates/base.html
  50. 2
      sapl/templates/materia/documentoacessorio_form.html
  51. 17
      sapl/templates/materia/em_lote/acessorio.html
  52. 12
      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. 167
      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. 116
      sapl/utils.py
  76. 2
      start.sh

2
.gitignore

@ -93,5 +93,7 @@ bower
bower_components
media
whoosh/
solr-4.10.2/
postgres-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 \
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 && \
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
# 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 && \
python3 manage.py collectstatic --no-input && \
rm -rf /var/interlegis/sapl/sapl/.env && \
python3 manage.py compilescss
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
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/static *
recursive-include sapl/relatorios/templates *.py
recursive-include sapl/compilacao *.sql
global-exclude __pycache__
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>`_
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
========================

4
config/env-sample

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

2
config/env_dockerfile

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

2
docker-compose.yml

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

38
docs/deploy.rst

@ -18,24 +18,36 @@ alterando o variável DEBUG para 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
------------------
Instalar o NGINX::
sudo apt-get install nginx
Instalar o Gunicorn::
sudo pip install gunicorn
sudo pip install gunicorn
Preparando o NGINX
------------------
sudo nano /etc/nginx/sites-available/sapl31.conf::
upstream ENDERECO_SITE {
upstream ENDERECO_SITE {
server unix:/var/interlegis/sapl/run/gunicorn.sock fail_timeout=0;
}
@ -95,11 +107,11 @@ Para uma máquina de CPU única o valor seria 3
Para rodar o gunicorn::
workon sapl
/var/interlegis/sapl/.gunicorn_start.sh
#Referências.
http://michal.karzynski.pl/blog/2013/06/09/django-nginx-gunicorn-virtualenv-supervisor/
@ -107,4 +119,12 @@ http://michal.karzynski.pl/blog/2013/06/09/django-nginx-gunicorn-virtualenv-supe
Para multiplas aplicações Django.
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 \
software-properties-common build-essential libxml2-dev libjpeg-dev \
libmysqlclient-dev libssl-dev libffi-dev libxslt1-dev python3-setuptools \
python3-pip curl
python3-pip curl poppler-utils
sudo -i
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
textract==1.5.0
unipath==1.1
pysolr==3.6.0
python-magic==0.4.12
gunicorn==19.6.0
django-reversion==2.0.8

64
sapl/api/forms.py

@ -1,5 +1,8 @@
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 rest_framework.compat import django_filters
from rest_framework.filters import FilterSet
from sapl.base.models import Autor, TipoAutor
@ -35,10 +38,11 @@ class SaplGenericRelationSearchFilterSet(FilterSet):
item.related_query_name(),
field[0])
)
q_fs = q_fs | Q(**{'%s__%s%s' % (
item.related_query_name(),
field[0],
field[1]): qtext})
if len(field) == 3 and field[2](qtext) is not None:
q_fs = q_fs | Q(**{'%s__%s%s' % (
item.related_query_name(),
field[0],
field[1]): qtext if len(field) == 2 else field[2](qtext)})
q = q & q_fs
@ -48,6 +52,37 @@ class SaplGenericRelationSearchFilterSet(FilterSet):
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):
q = MethodFilter()
tipo = ModelChoiceFilter(queryset=TipoAutor.objects.all())
@ -60,4 +95,23 @@ class AutorChoiceFilterSet(SaplGenericRelationSearchFilterSet):
def filter_q(self, queryset, value):
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.materia.models import MateriaLegislativa
from sapl.sessao.models import SessaoPlenaria
class ChoiceSerializer(serializers.Serializer):
@ -45,6 +46,7 @@ class AutorSerializer(serializers.ModelSerializer):
class Meta:
model = Autor
fields = '__all__'
class MateriaLegislativaSerializer(serializers.ModelSerializer):
@ -52,3 +54,25 @@ class MateriaLegislativaSerializer(serializers.ModelSerializer):
class Meta:
model = MateriaLegislativa
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 sapl.api.views import (AutorListView, MateriaLegislativaViewSet,
ModelChoiceView)
ModelChoiceView, SessaoPlenariaViewSet)
from .apps import AppConfig
@ -12,9 +12,9 @@ app_name = AppConfig.name
router = DefaultRouter()
router.register(r'materia', MateriaLegislativaViewSet)
router.register(r'sessao-plenaria', SessaoPlenariaViewSet)
urlpatterns_router = router.urls
urlpatterns_api = [
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.mixins import ListModelMixin, RetrieveModelMixin
from rest_framework.permissions import (IsAuthenticated,
IsAuthenticatedOrReadOnly)
from rest_framework.viewsets import GenericViewSet
IsAuthenticatedOrReadOnly,
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,
ChoiceSerializer,
MateriaLegislativaSerializer,
ModelChoiceSerializer)
ModelChoiceSerializer,
SessaoPlenariaSerializer)
from sapl.base.models import Autor, TipoAutor
from sapl.materia.models import MateriaLegislativa
from sapl.sessao.models import SessaoPlenaria
from sapl.utils import SaplGenericRelation, sapl_logger
@ -76,7 +79,57 @@ class AutorListView(ListAPIView):
o django-filter é desativado e a busca é feita
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
@ -122,6 +175,9 @@ class AutorListView(ListAPIView):
self.serializer_class = AutorSerializer
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)
def get_queryset(self):
@ -203,3 +259,14 @@ class MateriaLegislativaViewSet(ListModelMixin,
queryset = MateriaLegislativa.objects.all()
filter_backends = (DjangoFilterBackend,)
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')
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)

27
sapl/base/search_indexes.py

@ -1,3 +1,4 @@
import logging
import os.path
import textract
@ -7,6 +8,10 @@ from haystack import indexes
from sapl.materia.models import DocumentoAcessorio, MateriaLegislativa
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):
text = indexes.CharField(document=True, use_template=True)
@ -30,17 +35,23 @@ class DocumentoAcessorioIndex(indexes.SearchIndex, indexes.Indexable):
arquivo = getattr(obj, self.filename)
if arquivo:
try:
arquivo.open()
except OSError:
if not os.path.exists(arquivo.path):
return self.prepared_data
if not os.path.splitext(arquivo.path)[1][:1]:
return self.prepared_data
extracted_data = textract.process(
arquivo.path).decode(
'utf-8').replace('\n', ' ')
try:
extracted_data = textract.process(
arquivo.path,
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', ' ')
@ -48,8 +59,8 @@ class DocumentoAcessorioIndex(indexes.SearchIndex, indexes.Indexable):
# text field with *all* of our metadata visible for templating:
t = loader.select_template((
'search/indexes/' + self.template_name, ))
data['text'] = t.render(Context({'object': obj,
'extracted': extracted_data}))
data['text'] = t.render({'object': obj,
'extracted': extracted_data})
return data

3
sapl/base/urls.py

@ -92,7 +92,8 @@ urlpatterns = [
# todos os sublinks de sistema devem vir acima deste
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, {
'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.contrib import messages
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.core.exceptions import ObjectDoesNotExist
from django.core.urlresolvers import reverse
from django.db import models
from django.db.models.fields.related import ForeignKey
@ -1133,7 +1134,11 @@ class MasterDetailCrud(Crud):
parent_field = obj.parent_field.split('__')
if not obj.is_m2m or len(parent_field) > 1:
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)
return form
@ -1155,7 +1160,7 @@ class MasterDetailCrud(Crud):
try:
parent_object = parent_model.objects.get(**params)
except:
except Exception:
raise Http404()
else:
parent_model = self.model

63
sapl/legacy/migracao_documentos.py

@ -11,6 +11,7 @@ from sapl.materia.models import (DocumentoAcessorio, MateriaLegislativa,
from sapl.norma.models import NormaJuridica
from sapl.parlamentares.models import Parlamentar
from sapl.protocoloadm.models import DocumentoAdministrativo
from sapl.protocoloadm.models import DocumentoAcessorioAdministrativo
from sapl.sessao.models import SessaoPlenaria
from sapl.settings import MEDIA_ROOT
from sapl.utils import delete_texto, save_texto
@ -29,6 +30,17 @@ EXTENSOES = {
'text/html': '.html',
'text/rtf': '.rtf',
'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
'application/octet-stream': '', # binário
@ -43,35 +55,41 @@ DOCS = {
Parlamentar: [(
'fotografia',
'parlamentar/fotos/{}_foto_parlamentar',
'parlamentar/{0}/{0}_foto_parlamentar{1}')],
'public/parlamentar/{0}/{0}_foto_parlamentar{1}')],
MateriaLegislativa: [(
'texto_original',
'materia/{}_texto_integral',
'materialegislativa/{0}/{0}_texto_integral{1}')],
'public/materialegislativa/{2}/{0}/{0}_texto_integral{1}')],
DocumentoAcessorio: [(
'arquivo',
'materia/{}',
'documentoacessorio/{0}/{0}{1}')],
'public/documentoacessorio/{2}/{0}/{0}{1}')],
NormaJuridica: [(
'texto_original',
'texto_integral',
'norma_juridica/{}_texto_integral',
'normajuridica/{0}/{0}_texto_integral{1}')],
'public/normajuridica/{2}/{0}/{0}_texto_integral{1}')],
SessaoPlenaria: [
('upload_ata',
'ata_sessao/{}_ata_sessao',
'sessaoplenaria/{0}/ata/{0}_ata_sessao{1}'),
'public/sessaoplenaria/{0}/ata/{0}_ata_sessao{1}'),
('upload_anexo',
'anexo_sessao/{}_texto_anexado',
'sessaoplenaria/{0}/anexo/{0}_texto_anexado{1}')
'public/sessaoplenaria/{0}/anexo/{0}_texto_anexado{1}')
],
Proposicao: [(
'texto_original',
'proposicao/{}',
'proposicao/{0}/{0}{1}')],
'private/proposicao/{0}/{0}{1}')],
DocumentoAdministrativo: [(
'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,
@ -105,9 +123,14 @@ def migrar_docs_logo():
print('#### Migrando logotipo da casa ####')
[(_, origem, destino)] = DOCS[CasaLegislativa]
props_sapl = os.path.dirname(origem)
# 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))) < {
'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)
casa = get_casa_legislativa()
casa.logotipo = destino
@ -146,15 +169,22 @@ def migrar_docs_por_ids(tipo):
for arq in os.listdir(dir_origem):
match = pat.match(arq)
if match:
origem = os.path.join(dir_origem, match.group(0))
id = match.group(1)
extensao = get_extensao(origem)
destino = base_destino.format(id, extensao)
mover_documento(origem, destino)
# associa documento ao objeto
try:
origem = os.path.join(dir_origem, match.group(0))
id = match.group(1)
obj = tipo.objects.get(pk=id)
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)
mover_documento(origem, destino)
setattr(obj, campo, destino)
obj.save()
except tipo.DoesNotExist:
@ -199,6 +229,7 @@ def migrar_documentos():
SessaoPlenaria,
Proposicao,
DocumentoAdministrativo,
DocumentoAcessorioAdministrativo,
]:
migrar_docs_por_ids(tipo)

71
sapl/legacy/migration.py

@ -28,7 +28,7 @@ from sapl.norma.models import (AssuntoNorma, NormaJuridica,
TipoVinculoNormaJuridica, NormaRelacionada)
from sapl.parlamentares.models import Parlamentar
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.utils import delete_texto, normalize, save_texto
@ -411,26 +411,6 @@ class DataMigrator:
if field_type == 'CharField' or field_type == 'TextField':
if value is None or value == 'None':
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)
elif field.model.__name__ == 'TipoAutor' and \
field.name == 'content_type':
@ -600,11 +580,27 @@ def migrate(obj=appconfs, interativo=True):
# MIGRATION_ADJUSTMENTS #####################################################
def adjust_ordemdia(new, old):
def adjust_ordemdia_antes_salvar(new, old):
# Prestar atenção
if not old.tip_votacao:
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):
if old.ind_unid_deliberativa:
@ -655,6 +651,31 @@ def adjust_protocolo(new, old):
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):
if old.ind_mat_ou_doc == 'M':
new.tipo_conteudo_related = TipoMateriaLegislativa.objects.get(
@ -718,6 +739,7 @@ def adjust_autor(new, old):
new.nome = new.autor_related.nome_parlamentar
elif old.cod_comissao:
new.autor_related = Comissao.objects.get(pk=old.cod_comissao)
new.nome = new.autor_related.nome
if old.col_username:
if not get_user_model().objects.filter(
@ -748,10 +770,11 @@ AJUSTE_ANTES_SALVAR = {
Comissao: adjust_comissao,
NormaJuridica: adjust_normajuridica_antes_salvar,
NormaRelacionada: adjust_normarelacionada,
OrdemDia: adjust_ordemdia,
OrdemDia: adjust_ordemdia_antes_salvar,
Parlamentar: adjust_parlamentar,
Participacao: adjust_participacao,
Protocolo: adjust_protocolo,
RegistroVotacao: adjust_registrovotacao_antes_salvar,
TipoProposicao: adjust_tipoproposicao,
StatusTramitacao: adjust_statustramitacao,
StatusTramitacaoAdministrativo: adjust_statustramitacaoadm,
@ -760,7 +783,9 @@ AJUSTE_ANTES_SALVAR = {
AJUSTE_DEPOIS_SALVAR = {
NormaJuridica: adjust_normajuridica_depois_salvar,
OrdemDia: adjust_ordemdia_depois_salvar,
Protocolo: adjust_protocolo_depois_salvar,
RegistroVotacao: adjust_registrovotacao_depois_salvar,
}
# CHECKS ####################################################################

12
sapl/materia/forms.py

@ -164,18 +164,6 @@ class DocumentoAcessorioForm(ModelForm):
class Meta:
model = DocumentoAcessorio
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):

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 = [
]

18
sapl/materia/models.py

@ -22,7 +22,6 @@ from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES, SaplGenericForeignKey,
EM_TRAMITACAO = [(1, 'Sim'),
(0, 'Não')]
def grupo_autor():
try:
grupo = Group.objects.get(name='Autor')
@ -30,7 +29,6 @@ def grupo_autor():
return None
return grupo.id
@reversion.register()
class TipoProposicao(models.Model):
descricao = models.CharField(max_length=50, verbose_name=_('Descrição'))
@ -120,10 +118,15 @@ class Origem(models.Model):
TIPO_APRESENTACAO_CHOICES = Choices(('O', 'oral', _('Oral')),
('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()
class MateriaLegislativa(models.Model):
tipo = models.ForeignKey(TipoMateriaLegislativa,
on_delete=models.PROTECT,
verbose_name=_('Tipo'))
@ -194,7 +197,7 @@ class MateriaLegislativa(models.Model):
texto_original = models.FileField(
blank=True,
null=True,
upload_to=texto_upload_path,
upload_to=materia_upload_path,
verbose_name=_('Texto Original'),
validators=[restringe_tipos_de_arquivo_txt])
@ -215,6 +218,7 @@ class MateriaLegislativa(models.Model):
def __str__(self):
return _('%(tipo)s%(numero)s de %(ano)s') % {
'tipo': self.tipo, 'numero': self.numero, 'ano': self.ano}
def data_entrada_protocolo(self):
'''
@ -231,7 +235,7 @@ class MateriaLegislativa(models.Model):
pass
return ''
def delete(self, using=None, keep_parents=False):
if self.texto_original:
self.texto_original.delete()
@ -392,7 +396,7 @@ class DocumentoAcessorio(models.Model):
arquivo = models.FileField(
blank=True,
null=True,
upload_to=texto_upload_path,
upload_to=anexo_upload_path,
verbose_name=_('Texto Integral'),
validators=[restringe_tipos_de_arquivo_txt])
@ -625,7 +629,7 @@ class Proposicao(models.Model):
('I', 'Incorporada')),
verbose_name=_('Status Proposição'))
texto_original = models.FileField(
upload_to=texto_upload_path,
upload_to=materia_upload_path,
blank=True,
null=True,
verbose_name=_('Texto Original'),

80
sapl/materia/views.py

@ -7,7 +7,7 @@ from crispy_forms.layout import HTML
from django.contrib import messages
from django.contrib.auth.decorators import permission_required
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.urlresolvers import reverse
from django.http import HttpResponse, JsonResponse
@ -34,7 +34,8 @@ from sapl.crud.base import (ACTION_CREATE, ACTION_DELETE, ACTION_DETAIL,
PermissionRequiredForAppCrudMixin, make_pagination)
from sapl.materia.forms import (AnexadaForm, ConfirmarProposicaoForm,
LegislacaoCitadaForm, ProposicaoForm,
TipoProposicaoForm)
TipoProposicaoForm, TramitacaoForm,
TramitacaoUpdateForm)
from sapl.norma.models import LegislacaoCitada
from sapl.protocoloadm.models import Protocolo
from sapl.utils import (TURNO_TRAMITACAO_CHOICES, YES_NO_CHOICES, autor_label,
@ -162,19 +163,43 @@ class CriarProtocoloMateriaView(CreateView):
context = super(
CriarProtocoloMateriaView, self).get_context_data(**kwargs)
protocolo = Protocolo.objects.get(pk=self.kwargs['pk'])
try:
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['numero'].initial = protocolo.numero
context['form'].fields['numero'].initial = numero
context['form'].fields['ano'].initial = protocolo.ano
context['form'].fields['data_apresentacao'].initial = protocolo.data
context['form'].fields['numero_protocolo'].initial = protocolo.numero
context['form'].fields['ementa'].initial = protocolo.observacao
context['form'].fields['ementa'].initial = protocolo.assunto_ementa
return context
def form_valid(self, form):
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))
@ -868,6 +893,7 @@ class TramitacaoCrud(MasterDetailCrud):
ordering = '-data_tramitacao',
class CreateView(MasterDetailCrud.CreateView):
form_class = TramitacaoForm
def get_initial(self):
local = MateriaLegislativa.objects.get(
@ -884,6 +910,7 @@ class TramitacaoCrud(MasterDetailCrud):
return super(CreateView, self).post(request, *args, **kwargs)
class UpdateView(MasterDetailCrud.UpdateView):
form_class = TramitacaoUpdateForm
def post(self, request, *args, **kwargs):
materia = MateriaLegislativa.objects.get(
@ -951,25 +978,21 @@ class DocumentoAcessorioCrud(MasterDetailCrud):
form_class = DocumentoAcessorioForm
def __init__(self, **kwargs):
montar_helper_documento_acessorio(self)
super(MasterDetailCrud.CreateView, self).__init__(**kwargs)
def get_context_data(self, **kwargs):
context = super(
MasterDetailCrud.CreateView, self).get_context_data(**kwargs)
context['helper'] = self.helper
return context
class UpdateView(MasterDetailCrud.UpdateView):
form_class = DocumentoAcessorioForm
def __init__(self, **kwargs):
montar_helper_documento_acessorio(self)
super(MasterDetailCrud.UpdateView, self).__init__(**kwargs)
def get_context_data(self, **kwargs):
context = super(UpdateView, self).get_context_data(**kwargs)
context['helper'] = self.helper
return context
@ -1355,6 +1378,11 @@ class AcompanhamentoMateriaView(CreateView):
acompanhar.usuario = usuario.username
acompanhar.confirmado = False
acompanhar.save()
except MultipleObjectsReturned:
AcompanhamentoMateria.objects.filter(
email=email,
materia=materia,
hash=hash_txt).first()
do_envia_email_confirmacao(request, materia, email)
@ -1600,15 +1628,15 @@ class DocumentoAcessorioEmLoteView(PermissionRequiredMixin, FilterView):
tipo = TipoDocumento.objects.get(descricao=request.POST['tipo'])
for materia_id in marcadas:
DocumentoAcessorio.objects.create(
materia_id=materia_id,
tipo=tipo,
arquivo=request.POST['arquivo'],
nome=request.POST['nome'],
data=datetime.strptime(request.POST['data'], "%d/%m/%Y"),
autor=Autor.objects.get(id=request.POST['autor']),
ementa=request.POST['ementa']
)
doc = DocumentoAcessorio()
doc.materia_id = materia_id
doc.tipo = tipo
doc.arquivo = request.FILES['arquivo']
doc.nome = request.POST['nome']
doc.data = datetime.strptime(request.POST['data'], "%d/%m/%Y")
doc.autor = request.POST['autor']
doc.ementa = request.POST['ementa']
doc.save()
msg = _('Documento(s) criado(s).')
messages.add_message(request, messages.SUCCESS, msg)
return self.get(request, self.kwargs)
@ -1697,3 +1725,19 @@ class PrimeiraTramitacaoEmLoteView(PermissionRequiredMixin, FilterView):
class TramitacaoEmLoteView(PrimeiraTramitacaoEmLoteView):
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):
return self.descricao
def norma_upload_path(instance, filename):
return texto_upload_path(instance, filename, subpath=instance.ano)
@reversion.register()
class NormaJuridica(models.Model):
@ -67,10 +69,11 @@ class NormaJuridica(models.Model):
('F', 'federal', _('Federal')),
('M', 'municipal', _('Municipal')),
)
texto_integral = models.FileField(
blank=True,
null=True,
upload_to=texto_upload_path,
upload_to=norma_upload_path,
verbose_name=_('Texto Integral'),
validators=[restringe_tipos_de_arquivo_txt])
tipo = models.ForeignKey(
@ -113,7 +116,7 @@ class NormaJuridica(models.Model):
AssuntoNorma, blank=True,
verbose_name=_('Assuntos'))
data_vigencia = models.DateField(blank=True, null=True)
timestamp = models.DateTimeField()
timestamp = models.DateTimeField(null=True)
texto_articulado = GenericRelation(
TextoArticulado, related_query_name='texto_articulado')

12
sapl/parlamentares/models.py

@ -1,9 +1,9 @@
from datetime import datetime
import reversion
from django.db import models
from django.utils.translation import ugettext_lazy as _
from model_utils import Choices
import reversion
from sapl.base.models import Autor
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='')
def true_false_none(x):
if x == 'True':
return True
elif x == 'False':
return False
else:
return None
@reversion.register()
class Parlamentar(models.Model):
FEMININO = 'F'
@ -303,6 +312,7 @@ class Parlamentar(models.Model):
('nome_completo', '__icontains'),
('nome_parlamentar', '__icontains'),
('filiacao__partido__sigla', '__icontains'),
('ativo', '', true_false_none),
))
class Meta:

4
sapl/parlamentares/urls.py

@ -1,6 +1,7 @@
from django.conf.urls import include, url
from sapl.parlamentares.views import (altera_field_mesa,
altera_field_mesa_public_view,
CargoMesaCrud, ColigacaoCrud,
ComposicaoColigacaoCrud, DependenteCrud,
FiliacaoCrud, FrenteCrud, FrenteList,
@ -59,6 +60,9 @@ urlpatterns = [
url(r'^mesa-diretora/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/$',
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
# Entretanto, NÃO DEVE OCORRER
except MultipleObjectsReturned:
filiacao = parlamentar.filiacao_set.filter(Q(
data__lte=legislatura.data_fim,
data_desfiliacao__gte=legislatura.data_fim) | Q(
data__lte=legislatura.data_fim,
data_desfiliacao__isnull=True)).last()
row[2] = (
'O Parlamentar possui duas filiações conflitantes',
None)
# Caso encontre UMA filiação nessas condições
else:
@ -588,3 +586,94 @@ def remove_parlamentar_composicao(request):
return JsonResponse(
{'msg': (
'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'])
protocolo.numero_paginas = self.request.POST['numero_paginas']
protocolo.observacao = self.request.POST['observacao']
protocolo.assunto_ementa = self.request.POST['assunto_ementa']
protocolo.save()
return redirect(self.get_success_url(protocolo))

48
sapl/relatorios/templates/pdf_sessao_plenaria_gerar.py

@ -6,6 +6,8 @@
"""
import time
from sapl.sessao.models import ResumoOrdenacao
from trml2pdf import parseString
@ -287,15 +289,43 @@ def principal(cabecalho_dic, rodape_dic, imagem, sessao, inf_basicas_dic, lst_me
tmp += '\t</template>\n'
tmp += paraStyle()
tmp += '\t<story>\n'
tmp += inf_basicas(inf_basicas_dic)
tmp += mesa(lst_mesa)
tmp += presenca(lst_presenca_sessao)
tmp += expedientes(lst_expedientes)
tmp += expediente_materia(lst_expediente_materia)
tmp += oradores_expediente(lst_oradores_expediente)
tmp += presenca_ordem_dia(lst_presenca_ordem_dia)
tmp += votacao(lst_votacao)
tmp += oradores(lst_oradores)
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 += mesa(lst_mesa)
tmp += presenca(lst_presenca_sessao)
tmp += expedientes(lst_expedientes)
tmp += expediente_materia(lst_expediente_materia)
tmp += oradores_expediente(lst_oradores_expediente)
tmp += presenca_ordem_dia(lst_presenca_ordem_dia)
tmp += votacao(lst_votacao)
tmp += oradores(lst_oradores)
tmp += '\t</story>\n'
tmp += '</document>\n'

1
sapl/rules/map_rules.py

@ -251,6 +251,7 @@ rules_group_geral = {
(sessao.TipoResultadoVotacao, __base__),
(sessao.TipoExpediente, __base__),
(sessao.Bloco, __base__),
(sessao.ResumoOrdenacao, __base__),
(lexml.LexmlProvedor, __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
anos = [(k.year, k.year) for k in reversed(anos_list)]
return anos
except:
except Exception:
return []
@ -39,6 +39,18 @@ MES_CHOICES = [('', '---------')] + RANGE_MESES
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 Meta:
@ -359,3 +371,70 @@ class OradorExpedienteForm(ModelForm):
class PautaSessaoFilterSet(SessaoPlenariaFilterSet):
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_sim: num_votos_sim
observacao: txt_observacao
ordem: cod_ordem
tipo_resultado_votacao: tip_resultado_votacao
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,
choices=YES_NO_CHOICES,
verbose_name=_('Sessão finalizada?'))
interativa = models.NullBooleanField(blank=True,
choices=YES_NO_CHOICES,
verbose_name=_('Sessão interativa'))
class Meta:
verbose_name = _('Sessão Plenária')
@ -490,3 +493,28 @@ class Bloco(models.Model):
def __str__(self):
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,
PesquisarPautaSessaoView,
PesquisarSessaoPlenariaView,
PresencaOrdemDiaView, PresencaView, ResumoView,
PresencaOrdemDiaView, PresencaView,
ResumoOrdenacaoView, ResumoView,
SessaoCrud, TipoExpedienteCrud,
TipoResultadoVotacaoCrud, TipoSessaoCrud,
VotacaoEditView, VotacaoExpedienteEditView,
@ -68,6 +69,9 @@ urlpatterns = [
include(BlocoCrud.get_urls())),
url(r'^sistema/cargo-bancada/',
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/',
AdicionarVariasMateriasExpediente.as_view(),
name='adicionar_varias_materias_expediente'),

109
sapl/sessao/views.py

@ -3,6 +3,7 @@ from re import sub
from django.contrib import messages
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.urlresolvers import reverse
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.translation import ugettext_lazy as _
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.detail import DetailView
from django.views.generic.edit import FormMixin
@ -36,13 +37,14 @@ from sapl.sessao.forms import ExpedienteMateriaForm, OrdemDiaForm
from .forms import (AdicionarVariasMateriasFilterSet, ExpedienteForm,
ListMateriaForm, MesaForm, OradorExpedienteForm,
OradorForm, PautaSessaoFilterSet, PresencaForm,
ResumoOrdenacaoForm,
SessaoPlenariaFilterSet, VotacaoEditForm, VotacaoForm,
VotacaoNominalForm)
from .models import (Bancada, Bloco, CargoBancada, CargoMesa,
ExpedienteMateria, ExpedienteSessao, IntegranteMesa,
MateriaLegislativa, Orador, OradorExpediente, OrdemDia,
PresencaOrdemDia, RegistroVotacao, SessaoPlenaria,
SessaoPlenariaPresenca, TipoExpediente,
PresencaOrdemDia, RegistroVotacao, ResumoOrdenacao,
SessaoPlenaria, SessaoPlenariaPresenca, TipoExpediente,
TipoResultadoVotacao, TipoSessaoPlenaria, VotoNominal,
VotoParlamentar)
@ -930,6 +932,48 @@ class MesaView(FormMixin, DetailView):
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):
template_name = 'sessao/resumo.html'
model = SessaoPlenaria
@ -1022,11 +1066,14 @@ class ResumoView(DetailView):
titulo = m.materia
numero = m.numero_ordem
resultado = m.registrovotacao_set.all()
if resultado:
resultado = resultado[0].tipo_resultado_votacao.nome
rv = m.registrovotacao_set.first()
if rv:
resultado = rv.tipo_resultado_votacao.nome
resultado_observacao = rv.observacao
else:
resultado = _('Matéria não votada')
resultado_observacao = _(' ')
autoria = Autoria.objects.filter(materia_id=m.materia_id)
autor = [str(x.autor) for x in autoria]
@ -1035,6 +1082,7 @@ class ResumoView(DetailView):
'titulo': titulo,
'numero': numero,
'resultado': resultado,
'resultado_observacao': resultado_observacao,
'autor': autor
}
materias_expediente.append(mat)
@ -1117,13 +1165,54 @@ class ResumoView(DetailView):
if not partido_sigla:
partido_sigla = ''
oradores = {
'numero_ordem': orador.numero_ordem,
'parlamentar': parlamentar,
'sgl_partido': partido_sigla
}
'numero_ordem': orador.numero_ordem,
'parlamentar': parlamentar,
'sgl_partido': partido_sigla
}
oradores_explicacoes.append(oradores)
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)

21
sapl/settings.py

@ -84,11 +84,22 @@ INSTALLED_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 = {
'default': {
'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
'PATH': PROJECT_DIR.child('whoosh'),
'ENGINE': SEARCH_BACKEND,
SEARCH_URL[0] : SEARCH_URL[1]
},
}
@ -111,10 +122,7 @@ MIDDLEWARE_CLASSES = (
REST_FRAMEWORK = {
"DEFAULT_RENDERER_CLASSES": (
"rest_framework.renderers.JSONRenderer",
# "rest_framework.renderers.BrowsableAPIRenderer",
),
"UNICODE_JSON": False,
"DEFAULT_PARSER_CLASSES": (
"rest_framework.parsers.JSONParser",
),
@ -208,6 +216,7 @@ LOCALE_PATHS = (
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.8/howto/static-files/
STATIC_URL = '/static/'
STATIC_ROOT = PROJECT_DIR.child("collected_static")
STATICFILES_DIRS = (BASE_DIR.child("static"),)

6
sapl/templates/base.html

@ -157,11 +157,13 @@
<div class="col-md-4">
<a class="footer__logo" href="#">
<img src="{% static 'img/logo_interlegis.png' %}" alt="{% trans 'Logo do Interlegis' %} ">
<a href="http://www.interlegis.leg.br/">
<img src="{% static 'img/logo_interlegis.png' %}" alt="{% trans 'Logo do Interlegis' %} ">
</a>
</a>
<p>
<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>
</p>
</div>

2
sapl/templates/materia/documentoacessorio_form.html

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

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

@ -14,7 +14,7 @@
{% else %}
<h3 style="text-align: right;">{% blocktrans with object_list.count as total_materias %}Foram encontradas {{total_materias}} matérias.{% endblocktrans %}</h3>
{% endif %}
<form method="POST">
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
<fieldset>
<legend>Documento Acessório</legend>
@ -40,22 +40,9 @@
<div class="row">
<div class="col-md-12">
<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 class="row">
<div class="col-md-12">
<label>Ementa</label>

12
sapl/templates/materia/materialegislativa_filter.html

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

18
sapl/templates/navbar.yaml

@ -5,10 +5,10 @@
- title: {% trans 'Institucional' %}
children:
- title: {% trans 'Mesa Diretora' %}
url: sapl.parlamentares:mesa_diretora
- title: {% trans 'Comissões' %}
url: sapl.comissoes:comissao_list
- title: {% trans 'Mesa Diretora' %}
url: sapl.parlamentares:mesa_diretora
- title: {% trans 'Parlamentares' %}
url: sapl.parlamentares:parlamentar_list
@ -26,21 +26,21 @@
- title: {% trans 'Atividade Legislativa' %}
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' %}
url: sapl.materia:proposicao_list
check_permission: materia.add_proposicao
- title: {% trans 'Matérias Legislativas' %}
url: sapl.materia:pesquisar_materia
- title: {% trans 'Sessões Plenárias' %}
url: sapl.sessao:pesquisar_sessao
- title: {% trans 'Pautas das Sessões' %}
url: sapl.sessao:pesquisar_pauta
- title: {% trans 'Tramitação em Lote' %}
url: sapl.materia:primeira_tramitacao_em_lote
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' %}
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
// Inserção/Remoção
function altera_field(id_legislatura, id_sessao=null, msg=null){
// Pega o novo valor do campo que foi modificado (Sessao/Legislatura) ou utiliza
// o valor da Legislatura, por conveniência, quando há alguma inserção ou remoção
// Pega o novo valor dos campos modificados
var sessao_value = id_sessao
var legislatura_value = id_legislatura

2
sapl/templates/parlamentares/parlamentar_perfil_publico.html

@ -61,7 +61,7 @@
<div class="col-sm-8">
<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>

89
sapl/templates/parlamentares/public_composicaomesa_form.html

@ -10,7 +10,7 @@
<div class="row">
<div class="col-md-6">
<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 %}
<option value="{{l.id}}" {% if l == legislatura_selecionada %} selected {% endif %}>
{{l}}
@ -20,7 +20,7 @@
</div>
<div class="col-md-6">
<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 %}
<option value="{{s.id}}" {% if s == sessao_selecionada %} selected {% endif %}>
{{s}}
@ -33,7 +33,7 @@
<br />
<fieldset class="form-group">
<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>
<tr>
<th></th>
@ -46,7 +46,7 @@
{% for p in composicao_mesa %}
<tr>
{% 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 %}
<td></td>
{% endif %}
@ -60,3 +60,84 @@
</fieldset>
</form>
{% 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>
<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>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>
{% 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>

4
sapl/templates/protocoloadm/protocolo_list.html

@ -21,10 +21,10 @@
</a></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>Interessado:</strong> {{ p.interessado }}</br>
<strong>Natureza do Processo:</strong>
{% if p.tipo_processo == 0 %}
Administrativo
Administrativo </br>
<strong>Interessado:</strong> {{ p.interessado }}</br>
{% elif p.tipo_processo == 1 %}
Matéria Legislativa
{% endif %}</br>

10
sapl/templates/protocoloadm/protocolo_mostrar.html

@ -6,9 +6,13 @@
<strong>Protocolo: </strong>{{ protocolo.numero|stringformat:'06d' }}/{{ protocolo.ano }}</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>Autor:</strong> {{protocolo.autor.nome}} </br>
<strong>Interessado: </strong> {{ protocolo.interessado|default:" Não informado." }}</br>
<!-- TODO: convert if-else to custom tag -->
{% if protocolo.tipo_processo == 0 %}
<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>Número de Páginas: </strong> {{ protocolo.numero_paginas }} </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>

167
sapl/templates/sessao/resumo.html

@ -19,159 +19,36 @@
</strong>
</p>
</div>
<fieldset>
<legend>Identificação Básica</legend>
<div class="row">
{% for b in basica %}
<div class="col-md-4">{{b}}</div>
{% endfor %}
</div>
</fieldset>
<br /><br /><br />
<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 />
{% include 'sessao/blocos_resumo/'|add:primeiro_ordenacao %}
<br /><br /><br />
<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>
<br /><br /><br />
{% include 'sessao/blocos_resumo/'|add:segundo_ordenacao %}
<br /><br /><br />
<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>
<br /><br /><br />
{% include 'sessao/blocos_resumo/'|add:terceiro_ordenacao %}
<br /><br /><br />
<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>
<br /><br /><br />
{% include 'sessao/blocos_resumo/'|add:quarto_ordenacao %}
<br /><br /><br />
<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>{{m.resultado}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</fieldset>
<br /><br /><br />
{% include 'sessao/blocos_resumo/'|add:quinto_ordenacao %}
<br /><br /><br />
<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>
<br /><br /><br />
{% include 'sessao/blocos_resumo/'|add:sexto_ordenacao %}
<br /><br /><br />
<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>
<br /><br /><br />
{% include 'sessao/blocos_resumo/'|add:setimo_ordenacao %}
<br /><br /><br />
<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>
<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>
{% include 'sessao/blocos_resumo/'|add:oitavo_ordenacao %}
<br /><br /><br />
{% include 'sessao/blocos_resumo/'|add:nono_ordenacao %}
<br /><br /><br />
{% include 'sessao/blocos_resumo/'|add:decimo_ordenacao %}
<br /><br /><br />
<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 %}

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>
<div class="row">
{% 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 %}
</div>
</fieldset>

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

@ -18,8 +18,15 @@
<legend>Votos</legend>
<div class="row">
{% 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 %}
</div>
</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="#" 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="#" 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>
<hr />

116
sapl/utils.py

@ -1,15 +1,13 @@
import hashlib
import logging
import os
import re
from datetime import date
from functools import wraps
from unicodedata import normalize as unicodedata_normalize
from subprocess import PIPE, call
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.layout import HTML, Button
from django import forms
@ -22,10 +20,13 @@ from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _
from floppyforms import ClearableFileInput
from reversion.admin import VersionAdmin
import django_filters
import magic
from sapl.crispy_layout_mixin import SaplFormLayout, form_actions, to_row
from sapl.settings import BASE_DIR, PROJECT_DIR
sapl_logger = logging.getLogger(BASE_DIR.name)
@ -159,8 +160,8 @@ class SaplGenericRelation(GenericRelation):
assert isinstance(field, (tuple, list)), _(
'fields_search deve ser um array de tuplas ou listas.')
assert len(field) == 2, _(
'cada tupla de fields_search deve possuir duas strins')
assert len(field) <= 3, _(
'cada tupla de fields_search deve possuir até 3 strings')
# TODO implementar assert para validar campos do Model e lookups
@ -368,8 +369,8 @@ def fabrica_validador_de_tipos_de_arquivo(lista, nome):
def restringe_tipos_de_arquivo(value):
if not os.path.splitext(value.path)[1][:1]:
raise ValidationError(_(
'Não é possível fazer upload de arquivos sem extensão.'))
raise ValidationError(_(
'Não é possível fazer upload de arquivos sem extensão.'))
mime = magic.from_buffer(value.read(), mime=True)
if mime not in lista:
@ -391,92 +392,6 @@ def intervalos_tem_intersecao(a_inicio, a_fim, b_inicio, b_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):
choices = (
@ -605,8 +520,8 @@ def texto_upload_path(instance, filename, subpath=''):
seguida para armazenar o arquivo.
"""
if subpath and '/' not in subpath:
subpath = subpath + '/'
# if subpath and '/' not in subpath:
# subpath = subpath + '/'
""" TODO: Verifique possibilidade de otimização do código de normalização
do filename...
@ -624,7 +539,7 @@ def texto_upload_path(instance, filename, subpath=''):
if isinstance(instance, (DocumentoAdministrativo, Proposicao)):
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,
'model_name': instance._meta.model_name,
@ -637,6 +552,7 @@ def texto_upload_path(instance, filename, subpath=''):
class UpdateIndexCommand(Thread):
def run(self):
call([PROJECT_DIR.child('manage.py'), 'update_index'],
stdout=PIPE)

2
start.sh

@ -27,7 +27,7 @@ create_env() {
echo "SECRET_KEY="$KEY > $FILENAME
# now only appends
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_PORT = ""${EMAIL_PORT-587}" >> $FILENAME
echo "EMAIL_HOST = ""${EMAIL_HOST-''}" >> $FILENAME

Loading…
Cancel
Save