Browse Source

Merge branch 'master' into 1012-bug-mesa-diretora

pull/1022/head
Edward 9 years ago
committed by GitHub
parent
commit
8a58e991b6
  1. 4
      .gitignore
  2. 4
      .travis.yml
  3. 99
      Dockerfile
  4. 0
      bug.txt
  5. 10
      busy-wait.sh
  6. 8
      config/env-sample
  7. 8
      config/env_dockerfile
  8. 31
      config/nginx/nginx.conf
  9. 39
      config/nginx/sapl.conf
  10. 33
      create_admin.py
  11. 19
      docker-compose.yml
  12. 6
      docker-env.sh
  13. 1
      envfile
  14. 7
      genkey.py
  15. 15
      gunicorn_start.sh
  16. 2
      requirements/requirements.txt
  17. 2
      sapl/.env_test
  18. 51
      sapl/base/migrations/0002_auto_20170331_1900.py
  19. 33
      sapl/base/models.py
  20. 8
      sapl/env-backup
  21. 12
      sapl/legacy/management/commands/recria_constraints.py
  22. 28
      sapl/legacy/migracao_documentos.py
  23. 177
      sapl/legacy/migration.py
  24. 4
      sapl/legacy/scripts/fix_tables.sql
  25. 6
      sapl/legacy/scripts/migra_um_db.sh
  26. 32
      sapl/materia/migrations/0004_auto_20170504_1751.py
  27. 33
      sapl/materia/models.py
  28. 22
      sapl/materia/signals.py
  29. 8
      sapl/norma/legacy.yaml
  30. 22
      sapl/norma/signals.py
  31. 25
      sapl/parlamentares/migrations/0002_auto_20170504_1751.py
  32. 5
      sapl/parlamentares/models.py
  33. 43
      sapl/parlamentares/views.py
  34. 43
      sapl/relatorios/templates/pdf_sessao_plenaria_gerar.py
  35. 70
      sapl/relatorios/views.py
  36. 2
      sapl/rules/map_rules.py
  37. 13
      sapl/rules/tests/test_rules.py
  38. 22
      sapl/sessao/forms.py
  39. 90
      sapl/sessao/views.py
  40. 8
      sapl/static/styles/style_tinymce.css
  41. 2
      sapl/templates/base/RelatorioPresencaSessao_filter.html
  42. 11
      sapl/templates/materia/materialegislativa_filter.html
  43. 2
      sapl/templates/parlamentares/parlamentar_perfil_publico.html
  44. 2
      sapl/templates/parlamentares/public_composicaomesa_form.html
  45. 1
      sapl/templates/protocoloadm/protocolo_mostrar.html
  46. 4
      sapl/templates/sessao/presenca.html
  47. 4
      sapl/templates/sessao/presenca_ordemdia.html
  48. 28
      sapl/templates/sessao/resumo.html
  49. 10
      sapl/templates/sessao/votacao/votacao_edit.html
  50. 20
      sapl/utils.py
  51. 3
      scripts_docker/shell_sapl.sh
  52. 69
      start.sh

4
.gitignore

@ -92,4 +92,6 @@ collected_static
bower bower
bower_components bower_components
media media
whoosh/ whoosh/
postgres-data/
data/

4
.travis.yml

@ -20,3 +20,7 @@ script:
- ./manage.py bower install - ./manage.py bower install
- py.test --create-db - py.test --create-db
# - ./test_and_check_qa.sh # - ./test_and_check_qa.sh
addons:
hosts:
- 127.0.0.1 sapldb

99
Dockerfile

@ -1,53 +1,46 @@
FROM ubuntu:15.04 FROM alpine:3.5
RUN locale-gen en_US.UTF-8 ENV BUILD_PACKAGES postgresql-dev graphviz-dev graphviz build-base git pkgconfig \
ENV LANG en_US.UTF-8 python3-dev libxml2-dev jpeg-dev libressl-dev libffi-dev libxslt-dev nodejs py3-lxml \
ENV LANGUAGE en_US:en py3-magic postgresql-client vim
ENV LC_ALL en_US.UTF-8
RUN apk add --no-cache python3 nginx && \
RUN mkdir /sapl python3 -m ensurepip && \
rm -r /usr/lib/python*/ensurepip && \
RUN echo "deb http://archive.ubuntu.com/ubuntu/ vivid universe" | tee -a "/etc/apt/sources.list" pip3 install --upgrade pip setuptools && \
rm -r /root/.cache && \
RUN \ rm -f /etc/nginx/conf.d/*
apt-get update && \
apt-get install -y -f \ RUN mkdir -p /var/interlegis/sapl && \
software-properties-common \ apk add --update --no-cache $BUILD_PACKAGES && \
libpq-dev \ npm install -g bower && \
graphviz-dev \ npm cache clean
graphviz \
build-essential \ WORKDIR /var/interlegis/sapl/
git \
pkg-config \ ADD . /var/interlegis/sapl/
python3-dev \
libxml2-dev \ COPY start.sh /var/interlegis/sapl/
libjpeg-dev \ COPY config/nginx/sapl.conf /etc/nginx/conf.d
libssl-dev \ COPY config/nginx/nginx.conf /etc/nginx/nginx.conf
libffi-dev \
libxslt1-dev \ RUN pip install -r /var/interlegis/sapl/requirements/requirements.txt --upgrade setuptools && \
python3-setuptools \ rm -r /root/.cache && \
curl rm -r /tmp/*
# use python3 in pip COPY config/env_dockerfile /var/interlegis/sapl/sapl/.env
RUN easy_install3 pip lxml
# manage.py bower install bug: https://github.com/nvbn/django-bower/issues/51
# install nodejs
RUN DEBIAN_FRONTEND=noninteractive curl -sL https://deb.nodesource.com/setup_5.x | bash - RUN python3 manage.py bower_install -- --allow-root --no-input && \
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y nodejs python3 manage.py collectstatic --no-input && \
rm -rf /var/interlegis/sapl/sapl/.env && \
# install bower rm -rf /var/interlegis/sapl/sapl.db
RUN npm install -g bower
RUN chmod +x /var/interlegis/sapl/start.sh && \
# Bower aceitar root ln -sf /dev/stdout /var/log/nginx/access.log && \
RUN touch /root/.bowerrc ln -sf /dev/stderr /var/log/nginx/error.log
RUN chmod 751 /root/.bowerrc
RUN echo "{ \"allow_root\": true }" >> /root/.bowerrc VOLUME ["/var/interlegis/sapl/data", "/var/interlegis/sapl/media"]
WORKDIR /sapl CMD ["/var/interlegis/sapl/start.sh"]
ADD . /sapl
RUN pip install -r requirements/dev-requirements.txt
RUN pip install --upgrade setuptools
# RUN python3 manage.py bower install

0
bug.txt

10
busy-wait.sh

@ -0,0 +1,10 @@
#!/bin/sh
while true; do
COUNT_PG=`psql $1 -c '\l \q' | grep sapl | wc -l`
if ! [ "$COUNT_PG" -eq "0" ]; then
break
fi
echo "Esperando Database Setup"
sleep 10
done

8
config/env-sample

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

8
config/env_dockerfile

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

31
config/nginx/nginx.conf

@ -0,0 +1,31 @@
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile off;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
}

39
config/nginx/sapl.conf

@ -0,0 +1,39 @@
upstream sapl_server {
server unix:/var/interlegis/sapl/run/gunicorn.sock fail_timeout=0;
}
server {
listen 80;
server_name sapl.test;
client_max_body_size 4G;
location /static/ {
alias /var/interlegis/sapl/collected_static/;
}
location /media/ {
alias /var/interlegis/sapl/media/;
}
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
if (!-f $request_filename) {
proxy_pass http://sapl_server;
break;
}
}
error_page 500 502 503 504 /500.html;
location = /500.html {
root /var/interlegis/sapl/sapl/static/;
}
}

33
create_admin.py

@ -0,0 +1,33 @@
import sys
import os
import django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "sapl.settings")
django.setup()
from django.contrib.auth.models import User
def create_superuser():
username = "admin"
password = os.environ['ADMIN_PASSWORD'] if 'ADMIN_PASSWORD' in os.environ else None
email = os.environ['ADMIN_EMAIL'] if 'ADMIN_EMAIL' in os.environ else ''
if User.objects.filter(username=username).exists():
print("[SUPERUSER] User %s already exists. Exiting without change." % username)
sys.exit('ADMIN_USER_EXISTS')
else:
if not password:
print("[SUPERUSER] Environment variable $ADMIN_PASSWORD for user %s was not set. Leaving..." % username)
sys.exit('MISSING_ADMIN_PASSWORD')
print("[SUPERUSER] Creating superuser...")
u = User.objects.create_superuser(username=username, password=password, email=email)
u.save()
print("[SUPERUSER] Done.")
sys.exit(0)
if __name__ == '__main__':
create_superuser()

19
docker-compose.yml

@ -1,17 +1,20 @@
localhost: sapldb:
image: postgres image: postgres
environment: environment:
POSTGRES_PASSWORD: sapl POSTGRES_PASSWORD: sapl
POSTGRES_USER: sapl POSTGRES_USER: sapl
POSTGRES_DB: sapl POSTGRES_DB: sapl
PGDATA : /var/lib/postgresql/data/
volumes:
- sapldb_data:/var/lib/postgresql/data/
ports: ports:
- "5532:5432" - "5532:5432"
web: sapl:
build: . image: interlegis/sapl:latest
command: /bin/sh -c "/bin/sh busy-wait.sh && python3 manage.py bower install && python3 manage.py migrate && /bin/sh gunicorn_start.sh /sapl"
volumes: volumes:
- .:/sapl - sapl_data:/var/interlegis/sapl/data
ports: - sapl_media:/var/interlegis/sapl/media
- "8000:8000"
links: links:
- localhost - sapldb
ports:
- "80:80"

6
docker-env.sh

@ -0,0 +1,6 @@
#/bin/bash
KEY=`python gen-key.py`
echo $KEY
sed -e "s/SECRET_KEY = None/SECRET_KEY = $KEY/g" config/env-sample

1
envfile

@ -0,0 +1 @@
EMAIL_HOST_USER=foo

7
genkey.py

@ -0,0 +1,7 @@
import random
def generate_secret():
return ''.join([random.SystemRandom().choice('abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)') for i in range(50)])
if __name__ == '__main__':
print(generate_secret())

15
gunicorn_start.sh

@ -12,7 +12,7 @@ then
fi fi
NAME="SAPL" # Name of the application (*) NAME="SAPL" # Name of the application (*)
DJANGODIR=/var/interlegis/sapl # Django project directory (*) DJANGODIR=/var/interlegis/sapl/ # Django project directory (*)
SOCKFILE=/var/interlegis/sapl/run/gunicorn.sock # we will communicate using this unix socket (*) SOCKFILE=/var/interlegis/sapl/run/gunicorn.sock # we will communicate using this unix socket (*)
USER=`whoami` # the user to run as (*) USER=`whoami` # the user to run as (*)
GROUP=`whoami` # the group to run as (*) GROUP=`whoami` # the group to run as (*)
@ -23,11 +23,14 @@ DJANGO_WSGI_MODULE=sapl.wsgi # WSGI module name (*)
echo "Starting $NAME as `whoami` on base dir $SAPL_DIR" echo "Starting $NAME as `whoami` on base dir $SAPL_DIR"
# Activate the virtual environment # parameter can be passed to run without virtualenv
cd $DJANGODIR if [[ "$@" != "no-venv" ]]; then
source /var/interlegis/.virtualenvs/sapl/bin/activate # Activate the virtual environment
export DJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULE cd $DJANGODIR
export PYTHONPATH=$DJANGODIR:$PYTHONPATH source /var/interlegis/.virtualenvs/sapl/bin/activate
export DJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULE
export PYTHONPATH=$DJANGODIR:$PYTHONPATH
fi
# Create the run directory if it doesn't exist # Create the run directory if it doesn't exist
RUNDIR=$(dirname $SOCKFILE) RUNDIR=$(dirname $SOCKFILE)

2
requirements/requirements.txt

@ -15,7 +15,7 @@ django-extra-views==0.8.0
django-filter==0.15.3 django-filter==0.15.3
django-floppyforms==1.6.2 django-floppyforms==1.6.2
django-model-utils==2.5 django-model-utils==2.5
django-sass-processor==0.4.6 django-sass-processor==0.5.3
djangorestframework djangorestframework
drfdocs drfdocs
easy-thumbnails==2.3 easy-thumbnails==2.3

2
sapl/.env_test

@ -1,4 +1,4 @@
DATABASE_URL = postgresql://postgres:@localhost:/sapl DATABASE_URL = postgresql://postgres:@sapldb:/sapl
SECRET_KEY=TravisTest SECRET_KEY=TravisTest
DEBUG=False DEBUG=False
EMAIL_USE_TLS = True EMAIL_USE_TLS = True

51
sapl/base/migrations/0002_auto_20170331_1900.py

@ -0,0 +1,51 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.12 on 2017-03-31 19:00
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('base', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='Argumento',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('argumento', models.CharField(max_length=50, verbose_name='Argumento')),
],
options={
'verbose_name': 'Argumento da constraint',
'verbose_name_plural': 'Argumentos da constraint',
},
),
migrations.CreateModel(
name='Constraint',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('nome_tabela', models.CharField(max_length=50, verbose_name='Nome da tabela')),
('nome_constraint', models.CharField(max_length=100, verbose_name='Nome da constraint')),
('nome_model', models.CharField(max_length=50, verbose_name='Nome da model')),
('tipo_constraint', models.CharField(max_length=50, verbose_name='Tipo da constraint')),
],
options={
'verbose_name': 'Constraint removida',
'verbose_name_plural': 'Constraints removidas',
},
),
migrations.AddField(
model_name='problemamigracao',
name='eh_importante',
field=models.BooleanField(default=False, verbose_name='É importante?'),
),
migrations.AddField(
model_name='argumento',
name='constraint',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='base.Constraint'),
),
]

33
sapl/base/models.py

@ -61,16 +61,45 @@ class ProblemaMigracao(models.Model):
content_object = GenericForeignKey('content_type', 'object_id') content_object = GenericForeignKey('content_type', 'object_id')
nome_campo = models.CharField(max_length=100, nome_campo = models.CharField(max_length=100,
blank=True, blank=True,
verbose_name='Nome do(s) Campo(s)') verbose_name=_('Nome do(s) Campo(s)'))
problema = models.CharField(max_length=300, verbose_name=_('Problema')) problema = models.CharField(max_length=300, verbose_name=_('Problema'))
descricao = models.CharField(max_length=300, verbose_name=_('Descrição')) descricao = models.CharField(max_length=300, verbose_name=_('Descrição'))
eh_stub = models.BooleanField(verbose_name='É stub?') eh_stub = models.BooleanField(verbose_name=_('É stub?'))
eh_importante = models.BooleanField(
default=False, verbose_name=_('É importante?'))
class Meta: class Meta:
verbose_name = _('Problema na Migração') verbose_name = _('Problema na Migração')
verbose_name_plural = _('Problemas na Migração') verbose_name_plural = _('Problemas na Migração')
@reversion.register()
class Constraint(models.Model):
nome_tabela = models.CharField(
max_length=50, verbose_name=_('Nome da tabela'))
nome_constraint = models.CharField(
max_length=100, verbose_name=_('Nome da constraint'))
nome_model = models.CharField(
max_length=50, verbose_name=_('Nome da model'))
tipo_constraint = models.CharField(
max_length=50, verbose_name=_('Tipo da constraint'))
class Meta:
verbose_name = _('Constraint removida')
verbose_name_plural = _('Constraints removidas')
@reversion.register()
class Argumento(models.Model):
constraint = models.ForeignKey(Constraint)
argumento = models.CharField(
max_length=50, verbose_name=_('Argumento'))
class Meta:
verbose_name = _('Argumento da constraint')
verbose_name_plural = _('Argumentos da constraint')
@reversion.register() @reversion.register()
class AppConfig(models.Model): class AppConfig(models.Model):

8
sapl/env-backup

@ -0,0 +1,8 @@
SECRET_KEY=qbl0+_+h6^u_k0ggkv&+ff&56s!d(*k+vk^_eu3-to_w&k-kp@
DATABASE_URL = postgresql://sapl:sapl@localhost:5432/sapl
DEBUG = True
EMAIL_USE_TLS = True
EMAIL_PORT = 587
EMAIL_HOST = ''
EMAIL_HOST_USER = ''
EMAIL_HOST_PASSWORD = ''

12
sapl/legacy/management/commands/recria_constraints.py

@ -0,0 +1,12 @@
from django.core.management.base import BaseCommand
from sapl.legacy.migration import recria_constraints
class Command(BaseCommand):
help = (u'Recria constraints do PostgreSQL excluidas durante '
'migração de dados')
def handle(self, *args, **options):
recria_constraints()

28
sapl/legacy/migracao_documentos.py

@ -4,6 +4,7 @@ import re
import magic import magic
from django.db.models.signals import post_delete, post_save
from sapl.base.models import CasaLegislativa from sapl.base.models import CasaLegislativa
from sapl.materia.models import (DocumentoAcessorio, MateriaLegislativa, from sapl.materia.models import (DocumentoAcessorio, MateriaLegislativa,
Proposicao) Proposicao)
@ -12,6 +13,8 @@ from sapl.parlamentares.models import Parlamentar
from sapl.protocoloadm.models import DocumentoAdministrativo from sapl.protocoloadm.models import DocumentoAdministrativo
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
# MIGRAÇÃO DE DOCUMENTOS ################################################### # MIGRAÇÃO DE DOCUMENTOS ###################################################
EXTENSOES = { EXTENSOES = {
@ -160,7 +163,29 @@ def migrar_docs_por_ids(tipo):
tipo.__name__, id, destino)) tipo.__name__, id, destino))
def desconecta_sinais_indexacao():
post_save.disconnect(save_texto, NormaJuridica)
post_save.disconnect(save_texto, DocumentoAcessorio)
post_save.disconnect(save_texto, MateriaLegislativa)
post_delete.disconnect(delete_texto, NormaJuridica)
post_delete.disconnect(delete_texto, DocumentoAcessorio)
post_delete.disconnect(delete_texto, MateriaLegislativa)
def conecta_sinais_indexacao():
post_save.connect(save_texto, NormaJuridica)
post_save.connect(save_texto, DocumentoAcessorio)
post_save.connect(save_texto, MateriaLegislativa)
post_delete.connect(delete_texto, NormaJuridica)
post_delete.connect(delete_texto, DocumentoAcessorio)
post_delete.connect(delete_texto, MateriaLegislativa)
def migrar_documentos(): def migrar_documentos():
# precisamos excluir os sinais de post_save e post_delete para não que o
# computador não trave com a criação de threads desnecessárias
desconecta_sinais_indexacao()
# aqui supomos que uma pasta chamada sapl_documentos está em MEDIA_ROOT # aqui supomos que uma pasta chamada sapl_documentos está em MEDIA_ROOT
# com o conteúdo da pasta de mesmo nome do zope # com o conteúdo da pasta de mesmo nome do zope
# Os arquivos da pasta serão movidos para a nova estrutura e a pasta será # Os arquivos da pasta serão movidos para a nova estrutura e a pasta será
@ -186,3 +211,6 @@ def migrar_documentos():
len(sobrando))) len(sobrando)))
for doc in sobrando: for doc in sobrando:
print(' {}'. format(doc)) print(' {}'. format(doc))
#
# reconexão dos sinais desligados no inicio da migração de documentos
conecta_sinais_indexacao()

177
sapl/legacy/migration.py

@ -13,22 +13,24 @@ from django.core.exceptions import ObjectDoesNotExist
from django.db import OperationalError, ProgrammingError, connections, models from django.db import OperationalError, ProgrammingError, connections, models
from django.db.models import CharField, Max, ProtectedError, TextField from django.db.models import CharField, Max, ProtectedError, TextField
from django.db.models.base import ModelBase from django.db.models.base import ModelBase
from django.db.models.signals import post_delete, post_save
from model_mommy import mommy from model_mommy import mommy
from model_mommy.mommy import foreign_key_required, make from model_mommy.mommy import foreign_key_required, make
from sapl.base.models import Autor, ProblemaMigracao from sapl.base.models import Argumento, Autor, Constraint, ProblemaMigracao
from sapl.comissoes.models import Comissao, Composicao, Participacao from sapl.comissoes.models import Comissao, Composicao, Participacao
from sapl.legacy.models import Protocolo as ProtocoloLegado from sapl.legacy.models import Protocolo as ProtocoloLegado
from sapl.materia.models import (StatusTramitacao, TipoDocumento, from sapl.materia.models import (DocumentoAcessorio, MateriaLegislativa,
StatusTramitacao, TipoDocumento,
TipoMateriaLegislativa, TipoProposicao, TipoMateriaLegislativa, TipoProposicao,
Tramitacao) Tramitacao)
from sapl.norma.models import (AssuntoNorma, NormaJuridica, from sapl.norma.models import (AssuntoNorma, NormaJuridica,
TipoVinculoNormaJuridica) 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
from sapl.settings import PROJECT_DIR from sapl.settings import PROJECT_DIR
from sapl.utils import normalize from sapl.utils import delete_texto, normalize, save_texto
# BASE ###################################################################### # BASE ######################################################################
# apps to be migrated, in app dependency order (very important) # apps to be migrated, in app dependency order (very important)
@ -109,6 +111,10 @@ def warn(msg):
print('CUIDADO! ' + msg) print('CUIDADO! ' + msg)
def erro(msg):
print('ERRO: ' + msg)
def get_fk_related(field, value, label=None): def get_fk_related(field, value, label=None):
if value is None and field.null is False: if value is None and field.null is False:
value = 0 value = 0
@ -124,9 +130,7 @@ def get_fk_related(field, value, label=None):
if not field.null: if not field.null:
fields_dict = get_fields_dict(field.related_model) fields_dict = get_fields_dict(field.related_model)
# Cria stub ao final da tabela para evitar erros # Cria stub ao final da tabela para evitar erros
pk = 1 pk = get_last_value(field.related_model)
if hasattr(field.related_model.objects.last(), 'pk'):
pk = field.related_model.objects.last().pk
with reversion.create_revision(): with reversion.create_revision():
reversion.set_comment('Stub criado pela migração') reversion.set_comment('Stub criado pela migração')
value = mommy.make( value = mommy.make(
@ -197,6 +201,12 @@ def iter_sql_records(sql, db):
record.__dict__.update(zip(fieldnames, row)) record.__dict__.update(zip(fieldnames, row))
yield record yield record
# Todos os models têm no máximo uma constraint unique together
# Isso é necessário para que o método delete_constraints funcione corretamente
assert all(len(model._meta.unique_together) <= 1
for app in appconfs
for model in app.models.values())
def delete_constraints(model): def delete_constraints(model):
# pega nome da unique constraint dado o nome da tabela # pega nome da unique constraint dado o nome da tabela
@ -210,40 +220,66 @@ def delete_constraints(model):
for r in result: for r in result:
if r[0].endswith('key'): if r[0].endswith('key'):
words_list = r[0].split('_') words_list = r[0].split('_')
one_to_one_constraints.append([table, r[0], words_list, model]) constraint = Constraint.objects.create(
nome_tabela=table, nome_constraint=r[0],
nome_model=model.__name__, tipo_constraint='one_to_one')
for w in words_list:
Argumento.objects.create(constraint=constraint, argumento=w)
else: else:
args = None
args_list = []
if model._meta.unique_together: if model._meta.unique_together:
args = model._meta.unique_together[0] args_list = model._meta.unique_together[0]
args_list = list(args) constraint = Constraint.objects.create(
unique_constraints.append([table, r[0], args_list, model]) nome_tabela=table, nome_constraint=r[0],
nome_model=model.__name__,
tipo_constraint='unique_together')
for a in args_list:
Argumento.objects.create(constraint=constraint,
argumento=a)
warn('Excluindo unique constraint de nome %s' % r[0]) warn('Excluindo unique constraint de nome %s' % r[0])
exec_sql("ALTER TABLE %s DROP CONSTRAINT %s;" % exec_sql("ALTER TABLE %s DROP CONSTRAINT %s;" %
(table, r[0])) (table, r[0]))
def recreate_constraints(): def recria_constraints():
if one_to_one_constraints: constraints = Constraint.objects.all()
for constraint in one_to_one_constraints: for con in constraints:
table, name, args, model = constraint if con.tipo_constraint == 'one_to_one':
nome_tabela = con.nome_tabela
nome_constraint = con.nome_constraint
args = [a.argumento for a in con.argumento_set.all()]
args_string = '' args_string = ''
args_string = "(" + "_".join(map(str, args[2:-1])) + ")" args_string = "(" + "_".join(map(str, args[2:-1])) + ")"
exec_sql("ALTER TABLE %s ADD CONSTRAINT %s UNIQUE %s;" % try:
(table, name, args_string)) exec_sql("ALTER TABLE %s ADD CONSTRAINT %s UNIQUE %s;" %
if unique_constraints: (nome_tabela, nome_constraint, args_string))
for constraint in unique_constraints: except ProgrammingError:
table, name, args, model = constraint info('A constraint %s já foi recriada!' % nome_constraint)
if con.tipo_constraint == 'unique_together':
nome_tabela = con.nome_tabela
nome_constraint = con.nome_constraint
# Pegando explicitamente o primeiro valor do filter,
# pois pode ser que haja mais de uma ocorrência
model = ContentType.objects.filter(
model=con.nome_model.lower())[0].model_class()
args = [a.argumento for a in con.argumento_set.all()]
for i in range(len(args)): for i in range(len(args)):
if isinstance(model._meta.get_field(args[i]), if isinstance(model._meta.get_field(args[i]),
models.ForeignKey): models.ForeignKey):
args[i] = args[i] + '_id' args[i] = args[i] + '_id'
args_string = '' args_string = ''
args_string += "(" + ', '.join(map(str, args)) + ")" args_string += "(" + ', '.join(map(str, args)) + ")"
exec_sql("ALTER TABLE %s ADD CONSTRAINT %s UNIQUE %s;" % try:
(table, name, args_string)) exec_sql("ALTER TABLE %s ADD CONSTRAINT %s UNIQUE %s;" %
one_to_one_constraints.clear() (nome_tabela, nome_constraint, args_string))
unique_constraints.clear() except ProgrammingError:
info('A constraint %s já foi recriada!' % nome_constraint)
except Exception as err:
problema = re.findall('\(.*?\)', err.args[0])
erro('A constraint [%s] da tabela [%s] não pode ser recriada' %
(nome_constraint, nome_tabela))
erro('Os dados %s = %s estão duplicados. '
'Arrume antes de recriar as constraints!' %
(problema[0], problema[1]))
def obj_desnecessario(obj): def obj_desnecessario(obj):
@ -262,8 +298,8 @@ def get_last_value(model):
def alter_sequence(model, id): def alter_sequence(model, id):
sequence_name = '%s_id_seq' % model._meta.db_table sequence_name = '%s_id_seq' % model._meta.db_table
exec_sql('ALTER SEQUENCE %s RESTART WITH %s MINVALUE %s;' % ( exec_sql('ALTER SEQUENCE %s RESTART WITH %s MINVALUE -1;' % (
sequence_name, id, id)) sequence_name, id))
def save_with_id(new, id): def save_with_id(new, id):
@ -278,8 +314,7 @@ def save_relation(obj, nome_campo='', problema='', descricao='',
eh_stub=False): eh_stub=False):
link = ProblemaMigracao( link = ProblemaMigracao(
content_object=obj, nome_campo=nome_campo, problema=problema, content_object=obj, nome_campo=nome_campo, problema=problema,
descricao=descricao, eh_stub=eh_stub, descricao=descricao, eh_stub=eh_stub,)
)
link.save() link.save()
@ -302,20 +337,34 @@ def get_fields_dict(model):
def fill_vinculo_norma_juridica(): def fill_vinculo_norma_juridica():
lista = [('A', 'Altera a norma'), lista = [('A', 'Altera o(a)',
('R', 'Revoga integralmente a norma'), 'Alterado(a) pelo(a)'),
('P', 'Revoga parcialmente a norma'), ('R', 'Revoga integralmente o(a)',
('T', 'Revoga integralmente por consolidação'), 'Revogado(a) integralmente pelo(a)'),
('C', 'Norma Correlata'), ('P', 'Revoga parcialmente o(a)',
('S', 'Ressalva a Norma'), 'Revogado(a) parcialmente pelo(a)'),
('E', 'Reedita a Norma'), ('T', 'Revoga integralmente por consolidação',
('I', 'Reedita a Norma com Alteração'), 'Revogado(a) integralmente por consolidação'),
('G', 'Regulamenta a Norma'), ('C', 'Norma correlata',
('K', 'Suspende parcialmente a norma'), 'Norma correlata'),
('L', 'Suspende integralmente a norma'), ('S', 'Ressalva o(a)',
('N', 'Julgada integralmente inconstitucional'), 'Ressalvada pelo(a)'),
('O', 'Julgada parcialmente inconstitucional')] ('E', 'Reedita o(a)',
lista_objs = [TipoVinculoNormaJuridica(sigla=item[0], descricao=item[1]) 'Reeditada pelo(a)'),
('I', 'Reedita com alteração o(a)',
'Reeditada com alteração pelo(a)'),
('G', 'Regulamenta o(a)',
'Regulamentada pelo(a)'),
('K', 'Suspende parcialmente o(a)',
'Suspenso(a) parcialmente pelo(a)'),
('L', 'Suspende integralmente o(a)',
'Suspenso(a) integralmente pelo(a)'),
('N', 'Julga integralmente inconstitucional',
'Julgada integralmente inconstitucional'),
('O', 'Julga parcialmente inconstitucional',
'Julgada parcialmente inconstitucional')]
lista_objs = [TipoVinculoNormaJuridica(
sigla=item[0], descricao_ativa=item[1], descricao_passiva=item[2])
for item in lista] for item in lista]
TipoVinculoNormaJuridica.objects.bulk_create(lista_objs) TipoVinculoNormaJuridica.objects.bulk_create(lista_objs)
@ -416,6 +465,9 @@ class DataMigrator:
call([PROJECT_DIR.child('manage.py'), 'flush', call([PROJECT_DIR.child('manage.py'), 'flush',
'--database=default', '--no-input'], stdout=PIPE) '--database=default', '--no-input'], stdout=PIPE)
desconecta_sinais_indexacao()
fill_vinculo_norma_juridica()
info('Começando migração: %s...' % obj) info('Começando migração: %s...' % obj)
self._do_migrate(obj) self._do_migrate(obj)
@ -427,7 +479,7 @@ class DataMigrator:
for obj in self.to_delete: for obj in self.to_delete:
msg = 'A entrada de PK %s da model %s não pode ser ' \ msg = 'A entrada de PK %s da model %s não pode ser ' \
'excluida' % (obj.pk, obj._meta.model_name) 'excluida' % (obj.pk, obj._meta.model_name)
descricao = 'Um ou mais objetos protegidos ' descricao = 'Um ou mais objetos protegidos'
warn(msg + ' => ' + descricao) warn(msg + ' => ' + descricao)
save_relation(obj=obj, problema=msg, save_relation(obj=obj, problema=msg,
descricao=descricao, eh_stub=False) descricao=descricao, eh_stub=False)
@ -435,8 +487,8 @@ class DataMigrator:
info('Deletando stubs desnecessários...') info('Deletando stubs desnecessários...')
while self.delete_stubs(): while self.delete_stubs():
pass pass
info('Recriando unique constraints...')
# recreate_constraints() conecta_sinais_indexacao()
def _do_migrate(self, obj): def _do_migrate(self, obj):
if isinstance(obj, AppConfig): if isinstance(obj, AppConfig):
@ -504,6 +556,10 @@ class DataMigrator:
if getattr(old, 'ind_excluido', False): if getattr(old, 'ind_excluido', False):
self.to_delete.append(new) self.to_delete.append(new)
# necessário para ajustar sequence da tabela para o ultimo valor de id
ultimo_valor = get_last_value(model)
alter_sequence(model, ultimo_valor+1)
def delete_ind_excluido(self): def delete_ind_excluido(self):
excluidos = 0 excluidos = 0
for obj in self.to_delete: for obj in self.to_delete:
@ -580,6 +636,12 @@ def adjust_participacao(new, old):
new.composicao = composicao new.composicao = composicao
def adjust_normarelacionada(new, old):
tipo = TipoVinculoNormaJuridica.objects.filter(sigla=old.tip_vinculo)
assert len(tipo) == 1
new.tipo_vinculo = tipo[0]
def adjust_protocolo(new, old): def adjust_protocolo(new, old):
if new.numero is None and not primeira_vez: if new.numero is None and not primeira_vez:
p = ProtocoloLegado.objects.filter( p = ProtocoloLegado.objects.filter(
@ -685,6 +747,7 @@ AJUSTE_ANTES_SALVAR = {
Autor: adjust_autor, Autor: adjust_autor,
Comissao: adjust_comissao, Comissao: adjust_comissao,
NormaJuridica: adjust_normajuridica_antes_salvar, NormaJuridica: adjust_normajuridica_antes_salvar,
NormaRelacionada: adjust_normarelacionada,
OrdemDia: adjust_ordemdia, OrdemDia: adjust_ordemdia,
Parlamentar: adjust_parlamentar, Parlamentar: adjust_parlamentar,
Participacao: adjust_participacao, Participacao: adjust_participacao,
@ -731,3 +794,23 @@ def make_with_log(model, _quantity=None, make_m2m=False, **attrs):
return stub return stub
make_with_log.required = foreign_key_required make_with_log.required = foreign_key_required
# DISCONNECT SIGNAL ########################################################
def desconecta_sinais_indexacao():
post_save.disconnect(save_texto, NormaJuridica)
post_save.disconnect(save_texto, DocumentoAcessorio)
post_save.disconnect(save_texto, MateriaLegislativa)
post_delete.disconnect(delete_texto, NormaJuridica)
post_delete.disconnect(delete_texto, DocumentoAcessorio)
post_delete.disconnect(delete_texto, MateriaLegislativa)
def conecta_sinais_indexacao():
post_save.connect(save_texto, NormaJuridica)
post_save.connect(save_texto, DocumentoAcessorio)
post_save.connect(save_texto, MateriaLegislativa)
post_delete.connect(delete_texto, NormaJuridica)
post_delete.connect(delete_texto, DocumentoAcessorio)
post_delete.connect(delete_texto, MateriaLegislativa)

4
sapl/legacy/scripts/fix_tables.sql

@ -2,15 +2,19 @@
SELECT REPLACE(@@sql_mode,'STRICT_TRANS_TABLES,',''); SELECT REPLACE(@@sql_mode,'STRICT_TRANS_TABLES,','');
-- Exclui procedures caso já existam -- Exclui procedures caso já existam
DROP PROCEDURE IF EXISTS verifica_campos_proposicao; DROP PROCEDURE IF EXISTS verifica_campos_proposicao;
DROP PROCEDURE IF EXISTS verifica_campos_tipo_materia_legislativa;
DROP PROCEDURE IF EXISTS verifica_campos_sessao_plenaria_presenca; DROP PROCEDURE IF EXISTS verifica_campos_sessao_plenaria_presenca;
DROP PROCEDURE IF EXISTS cria_lexml_registro_provedor_e_publicador; DROP PROCEDURE IF EXISTS cria_lexml_registro_provedor_e_publicador;
-- Procedure para criar campo num_proposicao em proposicao -- Procedure para criar campo num_proposicao em proposicao
CREATE PROCEDURE verifica_campos_proposicao() BEGIN IF NOT EXISTS (SELECT * FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='proposicao' AND column_name='num_proposicao') THEN ALTER TABLE proposicao ADD COLUMN num_proposicao INT(11) NULL after txt_justif_devolucao; END IF; END; CREATE PROCEDURE verifica_campos_proposicao() BEGIN IF NOT EXISTS (SELECT * FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='proposicao' AND column_name='num_proposicao') THEN ALTER TABLE proposicao ADD COLUMN num_proposicao INT(11) NULL after txt_justif_devolucao; END IF; END;
-- Procedure para criar campo iind_num_automatica em tipo_materia_legislativa
CREATE PROCEDURE verifica_campos_tipo_materia_legislativa() BEGIN IF NOT EXISTS (SELECT * FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='tipo_materia_legislativa' AND column_name='ind_num_automatica') THEN ALTER TABLE tipo_materia_legislativa ADD COLUMN ind_num_automatica BOOLEAN NULL DEFAULT FALSE after des_tipo_materia; END IF; IF NOT EXISTS (SELECT * FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='tipo_materia_legislativa' AND column_name='quorum_minimo_votacao') THEN ALTER TABLE tipo_materia_legislativa ADD COLUMN quorum_minimo_votacao INT(11) NULL after ind_num_automatica; END IF; END;
-- Procedure para criar campos cod_presenca_sessao (sendo a nova PK da tabela) e dat_sessao em sessao_plenaria_presenca -- Procedure para criar campos cod_presenca_sessao (sendo a nova PK da tabela) e dat_sessao em sessao_plenaria_presenca
CREATE PROCEDURE verifica_campos_sessao_plenaria_presenca() BEGIN IF NOT EXISTS (SELECT * FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='sessao_plenaria_presenca' AND column_name='cod_presenca_sessao') THEN ALTER TABLE sessao_plenaria_presenca DROP PRIMARY KEY, ADD cod_presenca_sessao INT AUTO_INCREMENT PRIMARY KEY FIRST; END IF; IF NOT EXISTS (SELECT * FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='sessao_plenaria_presenca' AND column_name='dat_sessao') THEN ALTER TABLE sessao_plenaria_presenca ADD COLUMN dat_sessao DATE NULL after cod_parlamentar; END IF; END; CREATE PROCEDURE verifica_campos_sessao_plenaria_presenca() BEGIN IF NOT EXISTS (SELECT * FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='sessao_plenaria_presenca' AND column_name='cod_presenca_sessao') THEN ALTER TABLE sessao_plenaria_presenca DROP PRIMARY KEY, ADD cod_presenca_sessao INT AUTO_INCREMENT PRIMARY KEY FIRST; END IF; IF NOT EXISTS (SELECT * FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='sessao_plenaria_presenca' AND column_name='dat_sessao') THEN ALTER TABLE sessao_plenaria_presenca ADD COLUMN dat_sessao DATE NULL after cod_parlamentar; END IF; END;
-- Procedure para criar tabela lexml_registro_provedor e lexml_registro_publicador -- Procedure para criar tabela lexml_registro_provedor e lexml_registro_publicador
CREATE PROCEDURE cria_lexml_registro_provedor_e_publicador() BEGIN IF NOT EXISTS (SELECT * FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='lexml_registro_publicador') THEN CREATE TABLE lexml_registro_publicador (cod_publicador INT AUTO_INCREMENT NOT NULL, id_publicador INT, nom_publicador VARCHAR(255), adm_email VARCHAR(50), sigla VARCHAR(255), nom_responsavel VARCHAR(255), tipo VARCHAR(50), id_responsavel INT, PRIMARY KEY (cod_publicador)); END IF; IF NOT EXISTS (SELECT * FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='lexml_registro_provedor') THEN CREATE TABLE lexml_registro_provedor (cod_provedor INT AUTO_INCREMENT NOT NULL, id_provedor INT, nom_provedor VARCHAR(255), sgl_provedor VARCHAR(15), adm_email VARCHAR(50), nom_responsavel VARCHAR(255), tipo VARCHAR(50), id_responsavel INT, xml_provedor LONGTEXT, PRIMARY KEY (cod_provedor)); END IF; END; CREATE PROCEDURE cria_lexml_registro_provedor_e_publicador() BEGIN IF NOT EXISTS (SELECT * FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='lexml_registro_publicador') THEN CREATE TABLE lexml_registro_publicador (cod_publicador INT AUTO_INCREMENT NOT NULL, id_publicador INT, nom_publicador VARCHAR(255), adm_email VARCHAR(50), sigla VARCHAR(255), nom_responsavel VARCHAR(255), tipo VARCHAR(50), id_responsavel INT, PRIMARY KEY (cod_publicador)); END IF; IF NOT EXISTS (SELECT * FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='lexml_registro_provedor') THEN CREATE TABLE lexml_registro_provedor (cod_provedor INT AUTO_INCREMENT NOT NULL, id_provedor INT, nom_provedor VARCHAR(255), sgl_provedor VARCHAR(15), adm_email VARCHAR(50), nom_responsavel VARCHAR(255), tipo VARCHAR(50), id_responsavel INT, xml_provedor LONGTEXT, PRIMARY KEY (cod_provedor)); END IF; END;
-- Executa as procedures criadas acima -- Executa as procedures criadas acima
CALL verifica_campos_proposicao; CALL verifica_campos_proposicao;
CALL verifica_campos_tipo_materia_legislativa;
CALL verifica_campos_sessao_plenaria_presenca; CALL verifica_campos_sessao_plenaria_presenca;
CALL cria_lexml_registro_provedor_e_publicador; CALL cria_lexml_registro_provedor_e_publicador;

6
sapl/legacy/scripts/migra_um_db.sh

@ -23,3 +23,9 @@ echo "--- MIGRACAO DE DADOS ---" | tee -a $LOG
echo >> $LOG echo >> $LOG
DATABASE_NAME=$1 ./manage.py migracao_25_31 -f --settings sapl.legacy_migration_settings |& tee -a $LOG DATABASE_NAME=$1 ./manage.py migracao_25_31 -f --settings sapl.legacy_migration_settings |& tee -a $LOG
echo >> $LOG echo >> $LOG
echo "--- RECRIANDO CONSTRAINTS ---" | tee -a $LOG
echo >> $LOG
DATABASE_NAME=$1 ./manage.py recria_constraints --settings sapl.legacy_migration_settings |& tee -a $LOG
echo >> $LOG

32
sapl/materia/migrations/0004_auto_20170504_1751.py

@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.11 on 2017-05-04 17:51
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('materia', '0003_auto_20170403_1505'),
]
operations = [
migrations.AlterField(
model_name='assuntomateria',
name='dispositivo',
field=models.CharField(blank=True, default='', max_length=200, verbose_name='Descrição do Dispositivo Legal'),
preserve_default=False,
),
migrations.AlterField(
model_name='materiaassunto',
name='assunto',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='materia.AssuntoMateria', verbose_name='Assunto'),
),
migrations.AlterField(
model_name='materiaassunto',
name='materia',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='materia.MateriaLegislativa', verbose_name='Matéria'),
),
]

33
sapl/materia/models.py

@ -4,6 +4,7 @@ import reversion
from django.contrib.auth.models import Group from django.contrib.auth.models import Group
from django.contrib.contenttypes.fields import GenericRelation from django.contrib.contenttypes.fields import GenericRelation
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist
from django.db import models from django.db import models
from django.utils import formats from django.utils import formats
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
@ -215,6 +216,22 @@ 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):
'''
hack: recuperar a data de entrada do protocolo sem gerar
dependência circular
'''
from sapl.protocoloadm.models import Protocolo
if self.ano and self.numero_protocolo:
try:
return Protocolo.objects.get(
ano=self.ano,
numero=self.numero_protocolo).data
except ObjectDoesNotExist:
pass
return ''
def delete(self, using=None, keep_parents=False): def delete(self, using=None, keep_parents=False):
if self.texto_original: if self.texto_original:
self.texto_original.delete() self.texto_original.delete()
@ -668,9 +685,19 @@ class Proposicao(models.Model):
) )
def __str__(self): def __str__(self):
return '%s %s/%s' % (Proposicao._meta.verbose_name, if self.ano and self.numero_proposicao:
self.numero_proposicao, return '%s %s/%s' % (Proposicao._meta.verbose_name,
self.ano) self.numero_proposicao,
self.ano)
else:
if len(self.descricao) < 30:
descricao = self.descricao[:28] + ' ...'
else:
descricao = self.descricao
return '%s %s/%s' % (Proposicao._meta.verbose_name,
self.id,
descricao)
def delete(self, using=None, keep_parents=False): def delete(self, using=None, keep_parents=False):
if self.texto_original: if self.texto_original:

22
sapl/materia/signals.py

@ -1,29 +1,9 @@
from subprocess import PIPE, call
from threading import Thread
from django.db.models.signals import post_delete, post_save from django.db.models.signals import post_delete, post_save
from sapl.utils import save_texto, delete_texto
from sapl.settings import PROJECT_DIR
from .models import DocumentoAcessorio, MateriaLegislativa from .models import DocumentoAcessorio, MateriaLegislativa
class UpdateIndexCommand(Thread):
def run(self):
call([PROJECT_DIR.child('manage.py'), 'update_index'],
stdout=PIPE)
def save_texto(sender, instance, **kwargs):
update_index = UpdateIndexCommand()
update_index.start()
def delete_texto(sender, instance, **kwargs):
update_index = UpdateIndexCommand()
update_index.start()
post_save.connect(save_texto, sender=MateriaLegislativa) post_save.connect(save_texto, sender=MateriaLegislativa)
post_save.connect(save_texto, sender=DocumentoAcessorio) post_save.connect(save_texto, sender=DocumentoAcessorio)
post_delete.connect(delete_texto, sender=MateriaLegislativa) post_delete.connect(delete_texto, sender=MateriaLegislativa)

8
sapl/norma/legacy.yaml

@ -42,8 +42,6 @@ LegislacaoCitada:
subsecao: des_subsecao subsecao: des_subsecao
titulo: des_titulo titulo: des_titulo
# TODO Descomentar quando a issue #832 for concluida NormaRelacionada (VinculoNormaJuridica):
# TipoVinculoNormaJuridica (VinculoNormaJuridica): norma_principal: cod_norma_referente
# norma_referente: cod_norma_referente norma_relacionada: cod_norma_referida
# norma_referida: cod_norma_referida
# tipo_vinculo: tip_vinculo

22
sapl/norma/signals.py

@ -1,28 +1,8 @@
from subprocess import PIPE, call
from threading import Thread
from django.db.models.signals import post_delete, post_save from django.db.models.signals import post_delete, post_save
from sapl.utils import save_texto, delete_texto
from sapl.settings import PROJECT_DIR
from .models import NormaJuridica from .models import NormaJuridica
class UpdateIndexCommand(Thread):
def run(self):
call([PROJECT_DIR.child('manage.py'), 'update_index'],
stdout=PIPE)
def save_texto(sender, instance, **kwargs):
update_index = UpdateIndexCommand()
update_index.start()
def delete_texto(sender, instance, **kwargs):
update_index = UpdateIndexCommand()
update_index.start()
post_save.connect(save_texto, sender=NormaJuridica) post_save.connect(save_texto, sender=NormaJuridica)
post_delete.connect(delete_texto, sender=NormaJuridica) post_delete.connect(delete_texto, sender=NormaJuridica)

25
sapl/parlamentares/migrations/0002_auto_20170504_1751.py

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.11 on 2017-05-04 17:51
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('parlamentares', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='coligacao',
name='numero_votos',
field=models.PositiveIntegerField(blank=True, null=True, verbose_name='Nº Votos Recebidos (Coligação)'),
),
migrations.AlterField(
model_name='mandato',
name='votos_recebidos',
field=models.PositiveIntegerField(blank=True, null=True, verbose_name='Votos Recebidos (Mandato)'),
),
]

5
sapl/parlamentares/models.py

@ -85,7 +85,8 @@ class Coligacao(models.Model):
verbose_name=_('Legislatura')) verbose_name=_('Legislatura'))
nome = models.CharField(max_length=50, verbose_name=_('Nome')) nome = models.CharField(max_length=50, verbose_name=_('Nome'))
numero_votos = models.PositiveIntegerField( numero_votos = models.PositiveIntegerField(
blank=True, null=True, verbose_name=_('Nº Votos Recebidos')) blank=True, null=True,
verbose_name=_('Nº Votos Recebidos (Coligação)'))
class Meta: class Meta:
verbose_name = _('Coligação') verbose_name = _('Coligação')
@ -448,7 +449,7 @@ class Mandato(models.Model):
tipo_causa_fim_mandato = models.PositiveIntegerField(blank=True, null=True) tipo_causa_fim_mandato = models.PositiveIntegerField(blank=True, null=True)
data_fim_mandato = models.DateField(verbose_name=_('Fim do Mandato')) data_fim_mandato = models.DateField(verbose_name=_('Fim do Mandato'))
votos_recebidos = models.PositiveIntegerField( votos_recebidos = models.PositiveIntegerField(
blank=True, null=True, verbose_name=_('Votos Recebidos')) blank=True, null=True, verbose_name=_('Votos Recebidos (Mandato)'))
data_expedicao_diploma = models.DateField( data_expedicao_diploma = models.DateField(
verbose_name=_('Expedição do Diploma')) verbose_name=_('Expedição do Diploma'))
titular = models.BooleanField( titular = models.BooleanField(

43
sapl/parlamentares/views.py

@ -1,7 +1,7 @@
from django.contrib import messages from django.contrib import messages
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
from django.core.urlresolvers import reverse, reverse_lazy from django.core.urlresolvers import reverse, reverse_lazy
from django.db.models import F from django.db.models import F, Q
from django.http import JsonResponse from django.http import JsonResponse
from django.http.response import HttpResponseRedirect from django.http.response import HttpResponseRedirect
from django.utils.datastructures import MultiValueDictKeyError from django.utils.datastructures import MultiValueDictKeyError
@ -345,8 +345,47 @@ class ParlamentarCrud(Crud):
# Tira Link do avatar_html e coloca no nome # Tira Link do avatar_html e coloca no nome
for row in context['rows']: for row in context['rows']:
# Coloca a filiação atual ao invés da última
if row[0][1]:
# Pega o Parlamentar por meio da pk
parlamentar = Parlamentar.objects.get(
id=(row[0][1].split('/')[-1]))
# Pega a Legislatura
legislatura = Legislatura.objects.get(
id=context['legislatura_id'])
# As condições para mostrar a filiação são:
# A data de filiacao deve ser menor que a data de fim
# da legislatura e data de desfiliação deve nula, ou maior,
# ou igual a data de fim da legislatura
try:
filiacao = parlamentar.filiacao_set.get(Q(
data__lte=legislatura.data_fim,
data_desfiliacao__gte=legislatura.data_fim) | Q(
data__lte=legislatura.data_fim,
data_desfiliacao__isnull=True))
# Caso não exista filiação com essas condições
except ObjectDoesNotExist:
row[2] = ('Não possui filiação', None)
# 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()
# Caso encontre UMA filiação nessas condições
else:
row[2] = (filiacao.partido.sigla, None)
row[1] = (row[1][0], row[0][1]) row[1] = (row[1][0], row[0][1])
row[0] = (row[0][0], None) row[0] = (row[0][0], None)
return context return context

43
sapl/relatorios/templates/pdf_sessao_plenaria_gerar.py

@ -101,7 +101,7 @@ def inf_basicas(inf_basicas_dic):
tmp += '\t\t<para style="P1">Informações Básicas</para>\n' tmp += '\t\t<para style="P1">Informações Básicas</para>\n'
tmp += '\t\t<para style="P2">\n' tmp += '\t\t<para style="P2">\n'
tmp += '\t\t\t<font color="white"> </font>\n' tmp += '\t\t\t<font color="white"> <br/></font>\n'
tmp += '\t\t</para>\n' tmp += '\t\t</para>\n'
tmp += '\t\t<para style="P2" spaceAfter="5"><b>Tipo da Sessão: </b> ' + \ tmp += '\t\t<para style="P2" spaceAfter="5"><b>Tipo da Sessão: </b> ' + \
nom_sessao + '</para>\n' nom_sessao + '</para>\n'
@ -120,7 +120,7 @@ def mesa(lst_mesa):
tmp = '' tmp = ''
tmp += '\t\t<para style="P1">Mesa Diretora</para>\n' tmp += '\t\t<para style="P1">Mesa Diretora</para>\n'
tmp += '\t\t<para style="P2">\n' tmp += '\t\t<para style="P2">\n'
tmp += '\t\t\t<font color="white"> </font>\n' tmp += '\t\t\t<font color="white"> <br/></font>\n'
tmp += '\t\t</para>\n' tmp += '\t\t</para>\n'
for mesa in lst_mesa: for mesa in lst_mesa:
tmp += '\t\t<para style="P2" spaceAfter="5"><b>' + \ tmp += '\t\t<para style="P2" spaceAfter="5"><b>' + \
@ -136,7 +136,7 @@ def presenca(lst_presenca_sessao):
tmp = '' tmp = ''
tmp += '\t\t<para style="P1">Lista de Presença da Sessão</para>\n' tmp += '\t\t<para style="P1">Lista de Presença da Sessão</para>\n'
tmp += '\t\t<para style="P2">\n' tmp += '\t\t<para style="P2">\n'
tmp += '\t\t\t<font color="white"> </font>\n' tmp += '\t\t\t<font color="white"> <br/></font>\n'
tmp += '\t\t</para>\n' tmp += '\t\t</para>\n'
for presenca in lst_presenca_sessao: for presenca in lst_presenca_sessao:
tmp += '\t\t<para style="P2" spaceAfter="5">' + \ tmp += '\t\t<para style="P2" spaceAfter="5">' + \
@ -154,10 +154,10 @@ def expedientes(lst_expedientes):
tmp += '\t\t<para style="P2">\n' tmp += '\t\t<para style="P2">\n'
tmp += '\t\t\t<font color="white"> </font>\n' tmp += '\t\t\t<font color="white"> </font>\n'
tmp += '\t\t</para>\n' tmp += '\t\t</para>\n'
for expediente in lst_expedientes: for idx, expediente in enumerate(lst_expedientes):
tmp += '\t\t<para style="P2"><b>' + \ tmp += '\t\t<para style="P2"><b>' + '<br/> ' + \
expediente['nom_expediente'] + ': </b></para>\n' + \ expediente['nom_expediente'] + ': </b></para>\n' + \
'<para style="P2">' + \ '<para style="P2">' + \
expediente['txt_expediente'] + '</para>\n' expediente['txt_expediente'] + '</para>\n'
tmp += '\t\t<para style="P2">\n' tmp += '\t\t<para style="P2">\n'
tmp += '\t\t\t<font color="white"> </font>\n' tmp += '\t\t\t<font color="white"> </font>\n'
@ -171,18 +171,22 @@ def expediente_materia(lst_expediente_materia):
tmp = '' tmp = ''
tmp += '\t\t<para style="P1">Matérias do Expediente</para>\n\n' tmp += '\t\t<para style="P1">Matérias do Expediente</para>\n\n'
tmp += '\t\t<para style="P2">\n' tmp += '\t\t<para style="P2">\n'
tmp += '\t\t\t<font color="white"> </font>\n' tmp += '\t\t\t<font color="white"> <br/></font>\n'
tmp += '\t\t</para>\n' tmp += '\t\t</para>\n'
tmp += '<blockTable style="repeater" repeatRows="1">\n' tmp += '<blockTable style="repeater" repeatRows="1">\n'
tmp += '<tr><td >Matéria</td><td>Ementa</td><td>Resultado da Votação</td></tr>\n' tmp += '<tr><td >Matéria</td><td>Ementa</td><td>Resultado da Votação</td></tr>\n'
for expediente_materia in lst_expediente_materia: for expediente_materia in lst_expediente_materia:
tmp += '<tr><td><para style="P3"><b>' + str(expediente_materia['num_ordem']) + '</b> - ' + expediente_materia['id_materia'] + '</para>\n' + '<para style="P3"><b>Turno: </b>' + expediente_materia[ tmp += '<tr><td><para style="P3"><b>' + str(expediente_materia['num_ordem']) + '</b> - ' + expediente_materia['id_materia'] + '</para>\n' + '<para style="P3"><b>Turno: </b>' + str(expediente_materia[
'des_turno'] + '</para>\n' + '<para style="P3"><b>Autor: </b>' + expediente_materia['nom_autor'] + '</para></td>\n' 'des_turno']) + '</para>\n' + '<para style="P3"><b>Autor: </b>' + str(expediente_materia['nom_autor']) + '</para></td>\n'
txt_ementa = expediente_materia['txt_ementa'].replace('&', '&amp;') txt_ementa = expediente_materia['txt_ementa'].replace('&', '&amp;')
tmp += '<td><para style="P4">' + txt_ementa + '</para></td>\n' tmp += '<td><para style="P4">' + txt_ementa + '</para></td>\n'
tmp += '<td><para style="P3"><b>' + \ tmp += '<td><para style="P3"><b>' + \
str(expediente_materia['nom_resultado']) + '</b></para>\n' + '<para style="P3">' + \ str(expediente_materia['nom_resultado']) + '</b></para>\n' + '<para style="P3">'
'</para></td></tr>\n' if expediente_materia['votacao_observacao'] != txt_ementa:
tmp += str(expediente_materia['votacao_observacao'])
else:
tmp += ' '
tmp += '</para></td></tr>\n'
tmp += '\t\t</blockTable>\n' tmp += '\t\t</blockTable>\n'
return tmp return tmp
@ -195,7 +199,7 @@ def oradores_expediente(lst_oradores_expediente):
tmp = '' tmp = ''
tmp += '\t\t<para style="P1">Oradores do Expediente</para>\n' tmp += '\t\t<para style="P1">Oradores do Expediente</para>\n'
tmp += '\t\t<para style="P2">\n' tmp += '\t\t<para style="P2">\n'
tmp += '\t\t\t<font color="white"> </font>\n' tmp += '\t\t\t<font color="white"> <br/></font>\n'
tmp += '\t\t</para>\n' tmp += '\t\t</para>\n'
for orador_expediente in lst_oradores_expediente: for orador_expediente in lst_oradores_expediente:
tmp += '\t\t<para style="P2" spaceAfter="5"><b>' + str(orador_expediente['num_ordem']) + '</b> - ' + orador_expediente[ tmp += '\t\t<para style="P2" spaceAfter="5"><b>' + str(orador_expediente['num_ordem']) + '</b> - ' + orador_expediente[
@ -210,7 +214,7 @@ def presenca_ordem_dia(lst_presenca_ordem_dia):
tmp = '' tmp = ''
tmp += '\t\t<para style="P1">Lista de Presença da Ordem do Dia</para>\n' tmp += '\t\t<para style="P1">Lista de Presença da Ordem do Dia</para>\n'
tmp += '\t\t<para style="P2">\n' tmp += '\t\t<para style="P2">\n'
tmp += '\t\t\t<font color="white"> </font>\n' tmp += '\t\t\t<font color="white"> <br/></font>\n'
tmp += '\t\t</para>\n' tmp += '\t\t</para>\n'
for presenca_ordem_dia in lst_presenca_ordem_dia: for presenca_ordem_dia in lst_presenca_ordem_dia:
tmp += '\t\t<para style="P2" spaceAfter="5">' + \ tmp += '\t\t<para style="P2" spaceAfter="5">' + \
@ -226,7 +230,7 @@ def votacao(lst_votacao):
tmp = '' tmp = ''
tmp += '<para style="P1">Matérias da Ordem do Dia</para>\n\n' tmp += '<para style="P1">Matérias da Ordem do Dia</para>\n\n'
tmp += '\t\t<para style="P2">\n' tmp += '\t\t<para style="P2">\n'
tmp += '\t\t\t<font color="white"> </font>\n' tmp += '\t\t\t<font color="white"> <br/></font>\n'
tmp += '\t\t</para>\n' tmp += '\t\t</para>\n'
tmp += '<blockTable style="repeater" repeatRows="1">\n' tmp += '<blockTable style="repeater" repeatRows="1">\n'
tmp += '<tr><td >Matéria</td><td>Ementa</td><td>Resultado da Votação</td></tr>\n' tmp += '<tr><td >Matéria</td><td>Ementa</td><td>Resultado da Votação</td></tr>\n'
@ -236,8 +240,12 @@ def votacao(lst_votacao):
txt_ementa = votacao['txt_ementa'].replace('&', '&amp;') txt_ementa = votacao['txt_ementa'].replace('&', '&amp;')
tmp += '<td><para style="P4">' + txt_ementa + '</para></td>\n' tmp += '<td><para style="P4">' + txt_ementa + '</para></td>\n'
tmp += '<td><para style="P3"><b>' + \ tmp += '<td><para style="P3"><b>' + \
str(votacao['nom_resultado']) + '</b></para>\n' + '<para style="P3">' + \ str(votacao['nom_resultado']) + '</b></para>\n' + '<para style="P3">'
'</para></td></tr>\n' if votacao['votacao_observacao'] != txt_ementa:
tmp += str(votacao['votacao_observacao'])
else:
tmp += ' '
tmp += '</para></td></tr>\n'
tmp += '\t\t</blockTable>\n' tmp += '\t\t</blockTable>\n'
return tmp return tmp
@ -250,7 +258,7 @@ def oradores(lst_oradores):
tmp = '' tmp = ''
tmp += '\t\t<para style="P1">Oradores das Explicações Pessoais</para>\n' tmp += '\t\t<para style="P1">Oradores das Explicações Pessoais</para>\n'
tmp += '\t\t<para style="P2">\n' tmp += '\t\t<para style="P2">\n'
tmp += '\t\t\t<font color="white"> </font>\n' tmp += '\t\t\t<font color="white"> <br/></font>\n'
tmp += '\t\t</para>\n' tmp += '\t\t</para>\n'
for orador in lst_oradores: for orador in lst_oradores:
tmp += '\t\t<para style="P2" spaceAfter="5"><b>' + \ tmp += '\t\t<para style="P2" spaceAfter="5"><b>' + \
@ -290,6 +298,7 @@ def principal(cabecalho_dic, rodape_dic, imagem, sessao, inf_basicas_dic, lst_me
tmp += oradores(lst_oradores) tmp += oradores(lst_oradores)
tmp += '\t</story>\n' tmp += '\t</story>\n'
tmp += '</document>\n' tmp += '</document>\n'
tmp_pdf = parseString(tmp) tmp_pdf = parseString(tmp)
return tmp_pdf return tmp_pdf
# if hasattr(context.temp_folder,arquivoPdf): # if hasattr(context.temp_folder,arquivoPdf):

70
sapl/relatorios/views.py

@ -1,5 +1,8 @@
from datetime import datetime from datetime import datetime
import re
import html
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.http import Http404, HttpResponse from django.http import Http404, HttpResponse
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
@ -518,14 +521,28 @@ def get_sessao_plenaria(sessao, casa):
# Exibe os Expedientes # Exibe os Expedientes
lst_expedientes = [] lst_expedientes = []
for tip_expediente in TipoExpediente.objects.all(): expedientes = ExpedienteSessao.objects.filter(
for expediente in ExpedienteSessao.objects.filter( sessao_plenaria=sessao).order_by('tipo__nome')
sessao_plenaria=sessao, tipo=tip_expediente):
dic_expedientes = {} for e in expedientes:
dic_expedientes["nom_expediente"] = str(tip_expediente)
dic_expedientes["txt_expediente"] = (expediente.conteudo) dic_expedientes = {}
if dic_expedientes: dic_expedientes["nom_expediente"] = e.tipo.nome
lst_expedientes.append(dic_expedientes) conteudo = e.conteudo
# unescape HTML codes
# https://github.com/interlegis/sapl/issues/1046
conteudo = re.sub('style=".*?"', '', conteudo)
conteudo = html.unescape(conteudo)
# escape special character '&'
# https://github.com/interlegis/sapl/issues/1009
conteudo = conteudo.replace('&', '&amp;')
dic_expedientes["txt_expediente"] = conteudo
if dic_expedientes:
lst_expedientes.append(dic_expedientes)
# Lista das matérias do Expediente, incluindo o resultado das votacoes # Lista das matérias do Expediente, incluindo o resultado das votacoes
lst_expediente_materia = [] lst_expediente_materia = []
@ -594,18 +611,16 @@ def get_sessao_plenaria(sessao, casa):
dic_expediente_materia["nom_autor"] = 'Desconhecido' dic_expediente_materia["nom_autor"] = 'Desconhecido'
dic_expediente_materia["votacao_observacao"] = ' ' dic_expediente_materia["votacao_observacao"] = ' '
if not expediente_materia.resultado: resultados = expediente_materia.registrovotacao_set.all()
resultado = RegistroVotacao.objects.filter( if resultados:
tipo_resultado_votacao=expediente_materia.tipo_votacao) for i in resultados:
for i in resultado:
dic_expediente_materia["nom_resultado"] = ( dic_expediente_materia["nom_resultado"] = (
i.tipo_resultado_votacao.nome) i.tipo_resultado_votacao.nome)
dic_expediente_materia["votacao_observacao"] = ( dic_expediente_materia["votacao_observacao"] = (
expediente_materia.observacao) i.observacao)
else: else:
dic_expediente_materia["nom_resultado"] = _("Matéria não votada") dic_expediente_materia["nom_resultado"] = _("Matéria não votada")
dic_expediente_materia["votacao_observacao"] = _("Vazio") dic_expediente_materia["votacao_observacao"] = _(" ")
lst_expediente_materia.append(dic_expediente_materia) lst_expediente_materia.append(dic_expediente_materia)
# Lista dos oradores do Expediente # Lista dos oradores do Expediente
@ -664,9 +679,9 @@ def get_sessao_plenaria(sessao, casa):
materia=votacao.materia).first() materia=votacao.materia).first()
if numeracao is not None: if numeracao is not None:
dic_votacao["des_numeracao"] = ( dic_votacao["des_numeracao"] = (
str(numeracao.numero) + str(numeracao.numero_materia) +
'/' + '/' +
str(numeracao.ano)) str(numeracao.ano_materia))
dic_votacao["des_turno"] = ' ' dic_votacao["des_turno"] = ' '
tramitacao = Tramitacao.objects.filter( tramitacao = Tramitacao.objects.filter(
@ -710,16 +725,15 @@ def get_sessao_plenaria(sessao, casa):
dic_votacao["nom_autor"] = 'Desconhecido' dic_votacao["nom_autor"] = 'Desconhecido'
dic_votacao["votacao_observacao"] = ' ' dic_votacao["votacao_observacao"] = ' '
if not votacao.resultado: resultados = votacao.registrovotacao_set.all()
resultado = RegistroVotacao.objects.filter( if resultados:
tipo_resultado_votacao=votacao.tipo_votacao) for i in resultados:
for i in resultado:
dic_votacao["nom_resultado"] = i.tipo_resultado_votacao.nome dic_votacao["nom_resultado"] = i.tipo_resultado_votacao.nome
if votacao.observacao: if votacao.observacao:
dic_votacao["votacao_observacao"] = votacao.observacao dic_votacao["votacao_observacao"] = i.observacao
else: else:
dic_votacao["nom_resultado"] = _("Matéria não votada") dic_votacao["nom_resultado"] = _("Matéria não votada")
dic_votacao["votacao_observacao"] = _("Vazio") dic_votacao["votacao_observacao"] = _(" ")
lst_votacao.append(dic_votacao) lst_votacao.append(dic_votacao)
# Lista dos oradores nas Explicações Pessoais # Lista dos oradores nas Explicações Pessoais
@ -1086,11 +1100,11 @@ def get_pauta_sessao(sessao, casa):
dic_votacao["ordem_observacao"] = votacao.observacao dic_votacao["ordem_observacao"] = votacao.observacao
dic_votacao["des_numeracao"] = ' ' dic_votacao["des_numeracao"] = ' '
numeracao = Numeracao.objects.filter(materia=materia) # numeracao = Numeracao.objects.filter(materia=materia)
# if numeracao is not None: # if numeracao is not None:
# numeracao = numeracao.first() # numeracao = numeracao.first()
# dic_votacao["des_numeracao"] = str( # dic_votacao["des_numeracao"] = str(
# numeracao.numero) + '/' + str(numeracao.ano) # numeracao.numero_materia) + '/' + str(numeracao.ano_materia)
dic_votacao["nom_autor"] = ' ' dic_votacao["nom_autor"] = ' '
autoria = Autoria.objects.filter( autoria = Autoria.objects.filter(

2
sapl/rules/map_rules.py

@ -197,6 +197,8 @@ rules_group_geral = {
(base.CasaLegislativa, __listdetailchange__), (base.CasaLegislativa, __listdetailchange__),
(base.ProblemaMigracao, []), (base.ProblemaMigracao, []),
(base.Argumento, []),
(base.Constraint, []),
(base.TipoAutor, __base__), (base.TipoAutor, __base__),
(base.Autor, __base__), (base.Autor, __base__),

13
sapl/rules/tests/test_rules.py

@ -6,7 +6,8 @@ from django.contrib.contenttypes.models import ContentType
from django.utils import six from django.utils import six
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from sapl.base.models import CasaLegislativa, ProblemaMigracao from sapl.base.models import (CasaLegislativa, ProblemaMigracao, Argumento,
Constraint)
from sapl.compilacao.models import (PerfilEstruturalTextoArticulado, from sapl.compilacao.models import (PerfilEstruturalTextoArticulado,
TipoDispositivo, TipoDispositivo,
TipoDispositivoRelationship) TipoDispositivoRelationship)
@ -56,11 +57,15 @@ def test_models_in_rules_patterns(model_item):
__fp__in__test_permission_of_models_in_rules_patterns = { __fp__in__test_permission_of_models_in_rules_patterns = {
map_rules.RP_ADD: [CasaLegislativa, map_rules.RP_ADD: [CasaLegislativa,
ProblemaMigracao, ProblemaMigracao,
Argumento,
Constraint,
TipoDispositivo, TipoDispositivo,
TipoDispositivoRelationship, TipoDispositivoRelationship,
PerfilEstruturalTextoArticulado], PerfilEstruturalTextoArticulado],
map_rules.RP_CHANGE: [ProblemaMigracao, map_rules.RP_CHANGE: [ProblemaMigracao,
Argumento,
Constraint,
AcompanhamentoMateria, AcompanhamentoMateria,
TipoDispositivo, TipoDispositivo,
TipoDispositivoRelationship, TipoDispositivoRelationship,
@ -68,17 +73,23 @@ __fp__in__test_permission_of_models_in_rules_patterns = {
map_rules.RP_DELETE: [CasaLegislativa, map_rules.RP_DELETE: [CasaLegislativa,
ProblemaMigracao, ProblemaMigracao,
Argumento,
Constraint,
TipoDispositivo, TipoDispositivo,
TipoDispositivoRelationship, TipoDispositivoRelationship,
PerfilEstruturalTextoArticulado], PerfilEstruturalTextoArticulado],
map_rules.RP_LIST: [ProblemaMigracao, map_rules.RP_LIST: [ProblemaMigracao,
Argumento,
Constraint,
AcompanhamentoMateria, AcompanhamentoMateria,
TipoDispositivo, TipoDispositivo,
TipoDispositivoRelationship, TipoDispositivoRelationship,
PerfilEstruturalTextoArticulado], PerfilEstruturalTextoArticulado],
map_rules.RP_DETAIL: [ProblemaMigracao, map_rules.RP_DETAIL: [ProblemaMigracao,
Argumento,
Constraint,
AcompanhamentoMateria, AcompanhamentoMateria,
TipoDispositivo, TipoDispositivo,
TipoDispositivoRelationship, TipoDispositivoRelationship,

22
sapl/sessao/forms.py

@ -83,11 +83,11 @@ class ExpedienteMateriaForm(ModelForm):
def clean_numero_ordem(self): def clean_numero_ordem(self):
sessao = self.instance.sessao_plenaria sessao = self.instance.sessao_plenaria
ex = ExpedienteMateria.objects.filter( numero_ordem_exists = ExpedienteMateria.objects.filter(
sessao_plenaria=sessao, sessao_plenaria=sessao,
numero_ordem=self.cleaned_data['numero_ordem']).count() numero_ordem=self.cleaned_data['numero_ordem']).exists()
if ex >= 1: if numero_ordem_exists:
msg = _('Esse número de ordem já existe.') msg = _('Esse número de ordem já existe.')
raise ValidationError(msg) raise ValidationError(msg)
@ -139,6 +139,22 @@ class OrdemDiaForm(ExpedienteMateriaForm):
def clean_data_ordem(self): def clean_data_ordem(self):
return self.instance.sessao_plenaria.data_inicio return self.instance.sessao_plenaria.data_inicio
def clean_numero_ordem(self):
sessao = self.instance.sessao_plenaria
numero_ordem_exists = OrdemDia.objects.filter(
sessao_plenaria=sessao,
numero_ordem=self.cleaned_data[
'numero_ordem']).exists()
if numero_ordem_exists:
msg = _('Esse número de ordem já existe.')
raise ValidationError(msg)
return self.cleaned_data['numero_ordem']
def clean(self): def clean(self):
cleaned_data = self.cleaned_data cleaned_data = self.cleaned_data
sessao = self.instance.sessao_plenaria sessao = self.instance.sessao_plenaria

90
sapl/sessao/views.py

@ -28,7 +28,7 @@ from sapl.materia.models import (Autoria, DocumentoAcessorio,
TipoMateriaLegislativa, Tramitacao) TipoMateriaLegislativa, Tramitacao)
from sapl.materia.views import MateriaLegislativaPesquisaView from sapl.materia.views import MateriaLegislativaPesquisaView
from sapl.norma.models import NormaJuridica from sapl.norma.models import NormaJuridica
from sapl.parlamentares.models import (Legislatura, Parlamentar, from sapl.parlamentares.models import (Filiacao, Legislatura, Parlamentar,
SessaoLegislativa) SessaoLegislativa)
from sapl.sessao.apps import AppConfig from sapl.sessao.apps import AppConfig
from sapl.sessao.forms import ExpedienteMateriaForm, OrdemDiaForm from sapl.sessao.forms import ExpedienteMateriaForm, OrdemDiaForm
@ -164,7 +164,10 @@ class MateriaOrdemDiaCrud(MasterDetailCrud):
def get_rows(self, object_list): def get_rows(self, object_list):
for obj in object_list: for obj in object_list:
if not obj.resultado: exist_resultado = obj.registrovotacao_set.filter(
materia=obj.materia
).exists()
if not exist_resultado:
if obj.votacao_aberta: if obj.votacao_aberta:
url = '' url = ''
if obj.tipo_votacao == 1: if obj.tipo_votacao == 1:
@ -208,6 +211,11 @@ class MateriaOrdemDiaCrud(MasterDetailCrud):
else: else:
obj.resultado = '''Não há resultado''' obj.resultado = '''Não há resultado'''
else: else:
resultado = obj.registrovotacao_set.get(
materia_id=obj.materia_id)
resultado_descricao = resultado.tipo_resultado_votacao.nome
resultado_observacao = resultado.observacao
if self.request.user.has_module_perms(AppConfig.label): if self.request.user.has_module_perms(AppConfig.label):
url = '' url = ''
if obj.tipo_votacao == 1: if obj.tipo_votacao == 1:
@ -228,10 +236,14 @@ class MateriaOrdemDiaCrud(MasterDetailCrud):
'pk': obj.sessao_plenaria_id, 'pk': obj.sessao_plenaria_id,
'oid': obj.materia_id, 'oid': obj.materia_id,
'mid': obj.pk}) 'mid': obj.pk})
obj.resultado = '<a href="%s">%s</a>' % (url, obj.resultado = ('<a href="%s">%s</a><br/>%s' %
obj.resultado) (url,
resultado_descricao,
resultado_observacao))
else: else:
obj.resultado = '%s' % (obj.resultado) obj.resultado = ('%s<br/>%s' %
(resultado_descricao,
resultado_observacao))
return [self._as_row(obj) for obj in object_list] return [self._as_row(obj) for obj in object_list]
@ -268,7 +280,10 @@ class ExpedienteMateriaCrud(MasterDetailCrud):
def get_rows(self, object_list): def get_rows(self, object_list):
for obj in object_list: for obj in object_list:
if not obj.resultado: exist_resultado = obj.registrovotacao_set.filter(
materia=obj.materia
).exists()
if not exist_resultado:
if obj.votacao_aberta: if obj.votacao_aberta:
url = '' url = ''
if obj.tipo_votacao == 1: if obj.tipo_votacao == 1:
@ -310,7 +325,10 @@ class ExpedienteMateriaCrud(MasterDetailCrud):
obj.resultado = btn_abrir obj.resultado = btn_abrir
else: else:
url = '' url = ''
resultado = obj.registrovotacao_set.get(
materia_id=obj.materia_id)
resultado_descricao = resultado.tipo_resultado_votacao.nome
resultado_observacao = resultado.observacao
if self.request.user.has_module_perms(AppConfig.label): if self.request.user.has_module_perms(AppConfig.label):
if obj.tipo_votacao == 1: if obj.tipo_votacao == 1:
url = reverse( url = reverse(
@ -331,8 +349,14 @@ class ExpedienteMateriaCrud(MasterDetailCrud):
'pk': obj.sessao_plenaria_id, 'pk': obj.sessao_plenaria_id,
'oid': obj.materia_id, 'oid': obj.materia_id,
'mid': obj.pk}) 'mid': obj.pk})
obj.resultado = '<a href="%s">%s</a>' % (url, obj.resultado = ('<a href="%s">%s</a><br/>%s' %
obj.resultado) (url,
resultado_descricao,
resultado_observacao))
else:
obj.resultado = ('%s<br/>%s' %
(resultado_descricao,
resultado_observacao))
return [self._as_row(obj) for obj in object_list] return [self._as_row(obj) for obj in object_list]
class CreateView(MasterDetailCrud.CreateView): class CreateView(MasterDetailCrud.CreateView):
@ -982,12 +1006,14 @@ class ResumoView(DetailView):
materias_expediente = [] materias_expediente = []
for m in materias: for m in materias:
ementa = m.observacao ementa = m.observacao
titulo = m.materia titulo = m.materia
numero = m.numero_ordem numero = m.numero_ordem
if m.resultado: resultado = m.registrovotacao_set.all()
resultado = m.resultado if resultado:
resultado = resultado[0].tipo_resultado_votacao.nome
else: else:
resultado = _('Matéria não votada') resultado = _('Matéria não votada')
@ -1039,7 +1065,6 @@ class ResumoView(DetailView):
# Matérias Ordem do Dia # Matérias Ordem do Dia
ordem = OrdemDia.objects.filter( ordem = OrdemDia.objects.filter(
sessao_plenaria_id=self.object.id) sessao_plenaria_id=self.object.id)
materias_ordem = [] materias_ordem = []
for o in ordem: for o in ordem:
ementa = o.observacao ementa = o.observacao
@ -1047,8 +1072,9 @@ class ResumoView(DetailView):
numero = o.numero_ordem numero = o.numero_ordem
# Verificar resultado # Verificar resultado
if o.resultado: resultado = o.registrovotacao_set.filter(materia=o.materia)
resultado = o.resultado if resultado:
resultado = resultado[0].tipo_resultado_votacao.nome
else: else:
resultado = _('Matéria não votada') resultado = _('Matéria não votada')
@ -1066,6 +1092,24 @@ class ResumoView(DetailView):
context.update({'materias_ordem': materias_ordem}) context.update({'materias_ordem': materias_ordem})
# =====================================================================
# Oradores nas Explicações Pessoais
oradores_explicacoes = []
for orador in Orador.objects.filter(sessao_plenaria_id=self.object.id):
for parlamentar in Parlamentar.objects.filter(
id=orador.parlamentar.id):
partido_sigla = Filiacao.objects.filter(
parlamentar=parlamentar).last().partido.sigla
if not partido_sigla:
partido_sigla = ''
oradores = {
'numero_ordem': orador.numero_ordem,
'parlamentar': parlamentar,
'sgl_partido': partido_sigla
}
oradores_explicacoes.append(oradores)
context.update({'oradores_explicacoes': oradores_explicacoes})
return self.render_to_response(context) return self.render_to_response(context)
@ -1196,6 +1240,7 @@ class VotacaoEditView(SessaoPermissionMixin):
ordem_id=ordem_id).last() ordem_id=ordem_id).last()
votacao_existente = {'observacao': sub( votacao_existente = {'observacao': sub(
'&nbsp;', ' ', strip_tags(votacao.observacao)), '&nbsp;', ' ', strip_tags(votacao.observacao)),
'resultado': votacao.tipo_resultado_votacao.nome,
'tipo_resultado': 'tipo_resultado':
votacao.tipo_resultado_votacao_id} votacao.tipo_resultado_votacao_id}
context.update({'votacao_titulo': titulo, context.update({'votacao_titulo': titulo,
@ -1506,6 +1551,7 @@ class VotacaoNominalEditView(SessaoPermissionMixin):
votacao_existente = {'observacao': sub( votacao_existente = {'observacao': sub(
'&nbsp;', ' ', strip_tags(votacao.observacao)), '&nbsp;', ' ', strip_tags(votacao.observacao)),
'resultado': votacao.tipo_resultado_votacao.nome,
'tipo_resultado': 'tipo_resultado':
votacao.tipo_resultado_votacao_id} votacao.tipo_resultado_votacao_id}
context.update({'votacao': votacao_existente, context.update({'votacao': votacao_existente,
@ -1738,6 +1784,7 @@ class VotacaoNominalExpedienteEditView(SessaoPermissionMixin):
votacao_existente = {'observacao': sub( votacao_existente = {'observacao': sub(
'&nbsp;', ' ', strip_tags(votacao.observacao)), '&nbsp;', ' ', strip_tags(votacao.observacao)),
'resultado': votacao.tipo_resultado_votacao.nome,
'tipo_resultado': 'tipo_resultado':
votacao.tipo_resultado_votacao_id} votacao.tipo_resultado_votacao_id}
context.update({'votacao': votacao_existente, context.update({'votacao': votacao_existente,
@ -1972,6 +2019,7 @@ class VotacaoExpedienteEditView(SessaoPermissionMixin):
expediente_id=expediente_id).last() expediente_id=expediente_id).last()
votacao_existente = {'observacao': sub( votacao_existente = {'observacao': sub(
'&nbsp;', ' ', strip_tags(votacao.observacao)), '&nbsp;', ' ', strip_tags(votacao.observacao)),
'resultado': votacao.tipo_resultado_votacao.nome,
'tipo_resultado': 'tipo_resultado':
votacao.tipo_resultado_votacao_id} votacao.tipo_resultado_votacao_id}
context.update({'votacao_titulo': titulo, context.update({'votacao_titulo': titulo,
@ -2065,9 +2113,9 @@ class PautaSessaoDetailView(DetailView):
situacao = m.materia.tramitacao_set.last().status situacao = m.materia.tramitacao_set.last().status
if situacao is None: if situacao is None:
situacao = _("Não informada") situacao = _("Não informada")
resultado = m.registrovotacao_set.all()
if m.resultado: if resultado:
resultado = m.resultado resultado = resultado[0].tipo_resultado_votacao.nome
else: else:
resultado = _('Matéria não votada') resultado = _('Matéria não votada')
@ -2118,8 +2166,9 @@ class PautaSessaoDetailView(DetailView):
numero = o.numero_ordem numero = o.numero_ordem
# Verificar resultado # Verificar resultado
if o.resultado: resultado = o.registrovotacao_set.all()
resultado = o.resultado if resultado:
resultado = resultado[0].tipo_resultado_votacao.nome
else: else:
resultado = _('Matéria não votada') resultado = _('Matéria não votada')
@ -2413,12 +2462,13 @@ class AdicionarVariasMateriasOrdemDia(AdicionarVariasMateriasExpediente):
@csrf_exempt @csrf_exempt
@permission_required('sessao.change_expedientemateria',
'sessao.change_ordemdia')
def mudar_ordem_materia_sessao(request): def mudar_ordem_materia_sessao(request):
# Pega os dados vindos da requisição # Pega os dados vindos da requisição
posicao_inicial = int(request.POST['pos_ini']) + 1 posicao_inicial = int(request.POST['pos_ini']) + 1
posicao_final = int(request.POST['pos_fim']) + 1 posicao_final = int(request.POST['pos_fim']) + 1
pk_sessao = int(request.POST['pk_sessao']) pk_sessao = int(request.POST['pk_sessao'])
pk_list = request.POST.getlist('pk_list[]')
materia = request.POST['materia'] materia = request.POST['materia']

8
sapl/static/styles/style_tinymce.css

@ -1,8 +0,0 @@
.mce-content-body {
font-family: "Open Sans" "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif;
font-style: normal;
font-weight: normal;
line-height: 1.5;
font-size: 1em;
color: #444444;
}

2
sapl/templates/base/RelatorioPresencaSessao_filter.html

@ -37,7 +37,7 @@
<tbody> <tbody>
{% for p in parlamentares %} {% for p in parlamentares %}
<tr> <tr>
<td><b>{{p}}</b> / {{p.filiacao_set.first.partido.sigla|default:"Sem Partido"}}</td> <td><b>{{p}}</b> / {{p.filiacao_atual|default:"Sem Partido"}}</td>
<td>{{p.sessao_count}}</td> <td>{{p.sessao_count}}</td>
<td>{{p.sessao_porc}}</td> <td>{{p.sessao_porc}}</td>
<td>{{p.ordemdia_count}}</td> <td>{{p.ordemdia_count}}</td>

11
sapl/templates/materia/materialegislativa_filter.html

@ -47,6 +47,14 @@
<strong>Apresentação: </strong>{{ m.data_apresentacao }} <strong>Apresentação: </strong>{{ m.data_apresentacao }}
</br> </br>
{% endif %} {% endif %}
{% if m.numeracao_set.first %}
<strong>Processo: </strong>{{ m.numeracao_set.first.numero_materia }} / {{ m.numeracao_set.first.ano_materia }}
</br>
{% endif %}
{% if m.numero_protocolo %}
<strong>Protocolo: </strong>{{m.numero_protocolo}}/{{m.ano}} &nbsp;&nbsp; <strong>Data Entrada:</strong> {{m.data_entrada_protocolo}} </br>
{% endif %}
{% 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 %}
@ -57,7 +65,7 @@
{% endif %} {% endif %}
{% endfor %} {% endfor %}
</br> </br>
{% endif %} {% endif %}
{% if m.tramitacao_set.last.unidade_tramitacao_destino %} {% if m.tramitacao_set.last.unidade_tramitacao_destino %}
<strong>Localização Atual:</strong> &nbsp;{{m.tramitacao_set.last.unidade_tramitacao_destino}}</br> <strong>Localização Atual:</strong> &nbsp;{{m.tramitacao_set.last.unidade_tramitacao_destino}}</br>
{% endif %} {% endif %}
@ -79,6 +87,7 @@
{% endif %} {% endif %}
{% if m.tramitacao_set.last.data_tramitacao %} {% if m.tramitacao_set.last.data_tramitacao %}
<strong>Data da última Tramitação:</strong> &nbsp;{{m.tramitacao_set.last.data_tramitacao}}</br> <strong>Data da última Tramitação:</strong> &nbsp;{{m.tramitacao_set.last.data_tramitacao}}</br>
<strong>Ultima Ação:</strong> &nbsp; {{m.tramitacao_set.last.texto}}</br>
{% endif %} {% endif %}
{% if m.anexo_de.exists %} {% if m.anexo_de.exists %}
{% for a in m.materia_anexada_set.all %} {% for a in m.materia_anexada_set.all %}

2
sapl/templates/parlamentares/parlamentar_perfil_publico.html

@ -31,7 +31,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>Partido: </b> &nbsp {{object.filiacao_set.first.partido|default_if_none:"Não informado"}}</p> <p><b>Partido: </b> &nbsp {{object.filiacao_atual|default_if_none:"Não informado"}}</p>
</div> </div>
</div> </div>

2
sapl/templates/parlamentares/public_composicaomesa_form.html

@ -51,7 +51,7 @@
<td></td> <td></td>
{% endif %} {% endif %}
<td><a href="{% url 'sapl.parlamentares:parlamentar_detail' p.parlamentar.pk %}">{{p.parlamentar.nome_parlamentar}}</a></td> <td><a href="{% url 'sapl.parlamentares:parlamentar_detail' p.parlamentar.pk %}">{{p.parlamentar.nome_parlamentar}}</a></td>
<td>{{p.parlamentar.filiacao_set.last.partido.sigla}}</td> <td>{{p.parlamentar.filiacao_atual}}</td>
<td>{{p.cargo}}</td> <td>{{p.cargo}}</td>
</tr> </tr>
{% endfor %} {% endfor %}

1
sapl/templates/protocoloadm/protocolo_mostrar.html

@ -6,6 +6,7 @@
<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> <strong>Interessado: </strong> {{ protocolo.interessado|default:" Não informado." }}</br>
<!-- TODO: convert if-else to custom tag --> <!-- 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>

4
sapl/templates/sessao/presenca.html

@ -23,7 +23,7 @@
<div class="checkbox"> <div class="checkbox">
<label for="id_presenca_{{forloop.counter}}"> <label for="id_presenca_{{forloop.counter}}">
<input type="checkbox" id="id_presenca_{{forloop.counter}}" name="presenca" value="{{ parlamentar.id }}" {% if check %} checked {% endif %}/> <input type="checkbox" id="id_presenca_{{forloop.counter}}" name="presenca" value="{{ parlamentar.id }}" {% if check %} checked {% endif %}/>
{{ parlamentar }} {{ parlamentar.nome_parlamentar }} / {{ parlamentar.filiacao_atual }}
</label> </label>
</div> </div>
{% endfor %} {% endfor %}
@ -42,7 +42,7 @@
{% for parlamentar, check in view.get_presencas %} {% for parlamentar, check in view.get_presencas %}
{% if check %} {% if check %}
<div class="row"> <div class="row">
<div class="col-md-6"><label for="parlamentar"> - {{ parlamentar }}</label></div> <div class="col-md-6"><label for="parlamentar"> - {{ parlamentar.nome_parlamentar }} / {{ parlamentar.filiacao_atual }}</label></div>
</div> </div>
{% endif %} {% endif %}
{% endfor %} {% endfor %}

4
sapl/templates/sessao/presenca_ordemdia.html

@ -24,7 +24,7 @@
<div class="checkbox"> <div class="checkbox">
<label for="id_presenca_{{forloop.counter}}"> <label for="id_presenca_{{forloop.counter}}">
<input type="checkbox" id="id_presenca_{{forloop.counter}}" name="presenca" value="{{ parlamentar.id }}" {% if check %} checked {% endif %}/> <input type="checkbox" id="id_presenca_{{forloop.counter}}" name="presenca" value="{{ parlamentar.id }}" {% if check %} checked {% endif %}/>
{{ parlamentar }} {{ parlamentar.nome_parlamentar }} / {{ parlamentar.filiacao_atual }}
</label> </label>
</div> </div>
{% endfor %} {% endfor %}
@ -44,7 +44,7 @@
{% for parlamentar, check in view.get_presencas_ordem %} {% for parlamentar, check in view.get_presencas_ordem %}
{% if check %} {% if check %}
<div class="row"> <div class="row">
<div class="col-md-6"><label for="parlamentar"> - {{ parlamentar }}</label></div> <div class="col-md-6"><label for="parlamentar"> - {{ parlamentar.nome_parlamentar }} / {{ parlamentar.filiacao_atual }}</label></div>
</div> </div>
{% endif %} {% endif %}
{% endfor %} {% endfor %}

28
sapl/templates/sessao/resumo.html

@ -42,7 +42,8 @@
<legend>Mesa Diretora</legend> <legend>Mesa Diretora</legend>
<div class="row"> <div class="row">
{% for m in mesa %} {% for m in mesa %}
<div class="col-md-12"><b>{{m.cargo}}: </b>{{m.parlamentar.nome_parlamentar}}</div> <div class="col-md-12"><b>{{m.cargo}}:
</b>{{m.parlamentar.nome_parlamentar}} / {{ m.parlamentar.filiacao_atual }}</div>
{% endfor %} {% endfor %}
</div> </div>
</fieldset> </fieldset>
@ -52,7 +53,7 @@
<legend>Lista de Presença na Sessão</legend> <legend>Lista de Presença na Sessão</legend>
<div class="row"> <div class="row">
{% for p in presenca_sessao %} {% for p in presenca_sessao %}
<div class="col-md-12">{{p}}</div> <div class="col-md-12">{{p.nome_parlamentar}} / {{ p.filiacao_atual }}</div>
{% endfor %} {% endfor %}
</div> </div>
</fieldset> </fieldset>
@ -112,8 +113,9 @@
</div> </div>
<div class="row"> <div class="row">
{% for o in oradores %} {% for o in oradores %}
<div class="col-md-6"><b>{{o.numero_ordem}}</b> - {{o.parlamentar}}</div> <div class="col-md-6"><b>{{o.numero_ordem}}</b> - {{o.parlamentar}}</div>
<div class="col-md-6">{{o.url_discurso}}</div> <div class="col-md-6">{{o.url_discurso}}</div>
</br>
{% endfor %} {% endfor %}
</div> </div>
</fieldset> </fieldset>
@ -123,7 +125,7 @@
<legend>Lista de Presença na Ordem do Dia</legend> <legend>Lista de Presença na Ordem do Dia</legend>
<div class="row"> <div class="row">
{% for p in presenca_ordem %} {% for p in presenca_ordem %}
<div class="col-md-12">{{p}}</div> <div class="col-md-12">{{p.nome_parlamentar}} / {{ p.filiacao_atual }}</div>
{% endfor %} {% endfor %}
</div> </div>
</fieldset> </fieldset>
@ -154,4 +156,20 @@
</tbody> </tbody>
</table> </table>
</fieldset> </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 %}

10
sapl/templates/sessao/votacao/votacao_edit.html

@ -26,12 +26,10 @@
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
Resultado da Votação: <br/>
{% for tipo in view.get_tipos_votacao %} Resultado da Votação: <strong>{{ votacao.resultado }}</strong>
{% if votacao.tipo_resultado == tipo.id %} <br/>
<b>{{ tipo.nome }}</b> <br/>
{% endif %}
{% endfor %}
</div> </div>
</div> </div>

20
sapl/utils.py

@ -5,6 +5,8 @@ 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 unicodedata import normalize as unicodedata_normalize
from subprocess import PIPE, call
from threading import Thread
import django_filters import django_filters
import magic import magic
@ -22,7 +24,7 @@ from floppyforms import ClearableFileInput
from reversion.admin import VersionAdmin from reversion.admin import VersionAdmin
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 from sapl.settings import BASE_DIR, PROJECT_DIR
sapl_logger = logging.getLogger(BASE_DIR.name) sapl_logger = logging.getLogger(BASE_DIR.name)
@ -632,3 +634,19 @@ def texto_upload_path(instance, filename, subpath=''):
} }
return path return path
class UpdateIndexCommand(Thread):
def run(self):
call([PROJECT_DIR.child('manage.py'), 'update_index'],
stdout=PIPE)
def save_texto(sender, instance, **kwargs):
update_index = UpdateIndexCommand()
update_index.start()
def delete_texto(sender, instance, **kwargs):
update_index = UpdateIndexCommand()
update_index.start()

3
scripts_docker/shell_sapl.sh

@ -1,2 +1,3 @@
#!/bin/bash #!/bin/bash
docker run -ti sapl_web /bin/bash
docker run --rm -ti sapl_web /bin/sh

69
start.sh

@ -0,0 +1,69 @@
#!/bin/sh
create_env() {
echo "[ENV FILE] creating .env file..."
# check if file exists
if [ -f "/var/interlegis/sapl/data/secret.key" ]; then
KEY=`cat /var/interlegis/sapl/data/secret.key`
else
KEY=`python3 genkey.py`
echo $KEY > data/secret.key
fi
FILENAME="/var/interlegis/sapl/sapl/.env"
if [ -z "${DATABASE_URL:-}" ]; then
DATABASE_URL="postgresql://sapl:sapl@sapldb:5432/sapl"
fi
# ALWAYS replace the content of .env variable
# If want to conditionally create only if absent then use IF below
# if [ ! -f $FILENAME ]; then
touch $FILENAME
# explicitly use '>' to erase any previous content
echo "SECRET_KEY="$KEY > $FILENAME
# now only appends
echo "DATABASE_URL = "$DATABASE_URL >> $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
echo "EMAIL_HOST_USER = ""${EMAIL_HOST_USER-''}" >> $FILENAME
echo "EMAIL_HOST_PASSWORD = ""${EMAIL_HOST_PASSWORD-''}" >> $FILENAME
echo "[ENV FILE] done."
}
create_env
#python3 manage.py bower install
/bin/sh busy-wait.sh $DATABASE_URL
python3 manage.py migrate
#python3 manage.py collectstatic --no-input
python3 manage.py rebuild_index --noinput &
user_created=$(python3 create_admin.py 2>&1)
cmd=$(echo $user_created | grep 'ADMIN_USER_EXISTS')
user_exists=$?
cmd=$(echo $user_created | grep 'MISSING_ADMIN_PASSWORD')
lack_pwd=$?
if [ $user_exists -eq 0 ]; then
echo "[SUPERUSER CREATION] User admin already exists. Not creating"
fi
if [ $lack_pwd -eq 0 ]; then
echo "[SUPERUSER] Environment variable $ADMIN_PASSWORD for superuser admin was not set. Leaving container"
# return -1
fi
/bin/sh gunicorn_start.sh no-venv &
/usr/sbin/nginx -g "daemon off;"
Loading…
Cancel
Save