diff --git a/docker-compose.yml b/docker-compose.yml
index c33273b3f..fa0abdd5d 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -11,7 +11,7 @@ sapldb:
ports:
- "5432:5432"
sapl:
- image: interlegis/sapl:3.1.92
+ image: interlegis/sapl:3.1.102
restart: always
environment:
ADMIN_PASSWORD: interlegis
diff --git a/gunicorn_start.sh b/gunicorn_start.sh
index 4f58022ce..862221a29 100755
--- a/gunicorn_start.sh
+++ b/gunicorn_start.sh
@@ -16,8 +16,9 @@ DJANGODIR=/var/interlegis/sapl/ # Django project directory (*
SOCKFILE=/var/interlegis/sapl/run/gunicorn.sock # we will communicate using this unix socket (*)
USER=`whoami` # the user to run as (*)
GROUP=`whoami` # the group to run as (*)
-NUM_WORKERS=4 # how many worker processes should Gunicorn spawn (*)
+NUM_WORKERS=3 # how many worker processes should Gunicorn spawn (*)
# NUM_WORKERS = 2 * CPUS + 1
+TIMEOUT=60
MAX_REQUESTS=100 # number of requests before restarting worker
DJANGO_SETTINGS_MODULE=sapl.settings # which settings file should Django use (*)
DJANGO_WSGI_MODULE=sapl.wsgi # WSGI module name (*)
@@ -41,6 +42,8 @@ test -d $RUNDIR || mkdir -p $RUNDIR
# Programs meant to be run under supervisor should not daemonize themselves (do not use --daemon)
exec gunicorn ${DJANGO_WSGI_MODULE}:application \
--name $NAME \
+ --log-level debug \
+ --timeout $TIMEOUT \
--workers $NUM_WORKERS \
--max-requests $MAX_REQUESTS \
--user $USER \
diff --git a/requirements/requirements.txt b/requirements/requirements.txt
index 4b80ad130..6d9d79d66 100644
--- a/requirements/requirements.txt
+++ b/requirements/requirements.txt
@@ -36,3 +36,4 @@ django-reversion==2.0.8
WeasyPrint==0.42
whoosh==2.7.4
django-speedinfo==1.3.5
+django-reversion-compare==0.8.4
diff --git a/sapl/audiencia/tests/test_audiencia.py b/sapl/audiencia/tests/test_audiencia.py
index 7ce503c2d..710d70dff 100644
--- a/sapl/audiencia/tests/test_audiencia.py
+++ b/sapl/audiencia/tests/test_audiencia.py
@@ -1,3 +1,25 @@
-from django.test import TestCase
+import pytest
+from django.utils.translation import ugettext as _
+from model_mommy import mommy
-# Create your tests here.
+from sapl.audiencia import forms
+
+@pytest.mark.django_db(transaction=False)
+def test_valida_campos_obrigatorios_audiencia_form():
+ form = forms.AudienciaForm(data={})
+
+ assert not form.is_valid()
+
+ errors = form.errors
+
+ assert errors['nome'] == [_('Este campo é obrigatório.')]
+ assert errors['tema'] == [_('Este campo é obrigatório.')]
+ assert errors['tipo'] == [_('Este campo é obrigatório.')]
+ assert errors['tipo_materia'] == [_('Este campo é obrigatório.')]
+ assert errors['numero_materia'] == [_('Este campo é obrigatório.')]
+ assert errors['ano_materia'] == [_('Este campo é obrigatório.')]
+ assert errors['data'] == [_('Este campo é obrigatório.')]
+ assert errors['hora_inicio'] == [_('Este campo é obrigatório.')]
+ assert errors['hora_fim'] == [_('Este campo é obrigatório.')]
+
+ assert len(errors) == 9
diff --git a/sapl/base/forms.py b/sapl/base/forms.py
index e60ffe072..de6b50403 100644
--- a/sapl/base/forms.py
+++ b/sapl/base/forms.py
@@ -44,16 +44,21 @@ STATUS_USER_CHOICE = [
def get_roles():
- return [(g.id, g.name) for g in Group.objects.all().order_by('name')]
+ roles = [(g.id, g.name) for g in Group.objects.all().order_by('name')
+ if g.name != 'Votante' and g.name != 'Autor']
+ return roles
class UsuarioCreateForm(ModelForm):
- username = forms.CharField(required=True, label="Nome de usuário")
- firstname = forms.CharField(required=True, label="Nome")
- lastname = forms.CharField(required=True, label="Sobrenome")
- password1 = forms.CharField(required=True, widget=forms.PasswordInput, label='Senha')
- password2 = forms.CharField(required=True, widget=forms.PasswordInput, label='Confirmar senha')
+ username = forms.CharField(required=True, label="Nome de usuário",
+ max_length=30)
+ firstname = forms.CharField(required=True, label="Nome", max_length=30)
+ lastname = forms.CharField(required=True, label="Sobrenome", max_length=30)
+ password1 = forms.CharField(required=True, widget=forms.PasswordInput,
+ label='Senha', max_length=128)
+ password2 = forms.CharField(required=True, widget=forms.PasswordInput,
+ label='Confirmar senha', max_length=128)
user_active = forms.ChoiceField(required=False, choices=YES_NO_CHOICES,
label="Usuário ativo?", initial='True')
diff --git a/sapl/base/views.py b/sapl/base/views.py
index 238252245..b41492f4e 100644
--- a/sapl/base/views.py
+++ b/sapl/base/views.py
@@ -420,9 +420,9 @@ class RelatorioMateriasPorAnoAutorTipoView(FilterView):
filterset_class = RelatorioMateriasPorAnoAutorTipoFilterSet
template_name = 'base/RelatorioMateriasPorAnoAutorTipo_filter.html'
- def get_materias_autor_ano(self, ano):
+ def get_materias_autor_ano(self, ano, primeiro_autor):
- autorias = Autoria.objects.filter(materia__ano=ano).values(
+ autorias = Autoria.objects.filter(materia__ano=ano, primeiro_autor=primeiro_autor).values(
'autor',
'materia__tipo__sigla',
'materia__tipo__descricao').annotate(
@@ -488,7 +488,8 @@ class RelatorioMateriasPorAnoAutorTipoView(FilterView):
if 'ano' in self.request.GET and self.request.GET['ano']:
ano = int(self.request.GET['ano'])
- context['relatorio'] = self.get_materias_autor_ano(ano)
+ context['relatorio'] = self.get_materias_autor_ano(ano, True)
+ context['corelatorio'] = self.get_materias_autor_ano(ano, False)
else:
context['relatorio'] = []
diff --git a/sapl/comissoes/forms.py b/sapl/comissoes/forms.py
index 05a0a97b9..db1822e4a 100644
--- a/sapl/comissoes/forms.py
+++ b/sapl/comissoes/forms.py
@@ -61,7 +61,8 @@ class PeriodoForm(forms.ModelForm):
data_fim = cleaned_data['data_fim']
if data_fim and data_fim < data_inicio:
- raise ValidationError('Data início não pode ser superior a data de fim')
+ raise ValidationError('A Data Final não pode ser menor que '
+ 'a Data Inicial')
return cleaned_data
@@ -99,7 +100,7 @@ class ParticipacaoCreateForm(forms.ModelForm):
exclude(id__in=id_part)
eligible = self.verifica()
result = list(set(qs) & set(eligible))
- if not cmp(result, eligible): # se igual a 0 significa que o qs e o eli são iguais!
+ if result == eligible:
self.fields['parlamentar'].queryset = qs
else:
ids = [e.id for e in eligible]
diff --git a/sapl/comissoes/migrations/0017_auto_20180717_0827.py b/sapl/comissoes/migrations/0017_auto_20180717_0827.py
new file mode 100644
index 000000000..496a76b97
--- /dev/null
+++ b/sapl/comissoes/migrations/0017_auto_20180717_0827.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.13 on 2018-07-17 11:27
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('comissoes', '0016_auto_20180613_2121'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='reuniao',
+ name='observacao',
+ field=models.TextField(blank=True, verbose_name='Observação'),
+ ),
+ ]
diff --git a/sapl/comissoes/models.py b/sapl/comissoes/models.py
index a2477ef1d..4c8c09393 100644
--- a/sapl/comissoes/models.py
+++ b/sapl/comissoes/models.py
@@ -223,7 +223,7 @@ class Reuniao(models.Model):
local_reuniao = models.CharField(
max_length=100, blank=True, verbose_name=_('Local da Reunião'))
observacao = models.TextField(
- max_length=150, blank=True, verbose_name=_('Observação'))
+ blank=True, verbose_name=_('Observação'))
url_audio = models.URLField(
max_length=150, blank=True,
verbose_name=_('URL do Arquivo de Áudio (Formatos MP3 / AAC)'))
diff --git a/sapl/comissoes/tests/test_comissoes.py b/sapl/comissoes/tests/test_comissoes.py
index fee303192..4d12ba810 100644
--- a/sapl/comissoes/tests/test_comissoes.py
+++ b/sapl/comissoes/tests/test_comissoes.py
@@ -1,9 +1,11 @@
import pytest
from django.core.urlresolvers import reverse
+from django.utils.translation import ugettext as _
from model_mommy import mommy
-from sapl.comissoes.models import Comissao, Composicao, Periodo, TipoComissao
+from sapl.comissoes.models import Comissao, Composicao, Periodo, TipoComissao, Reuniao
from sapl.parlamentares.models import Filiacao, Parlamentar, Partido
+from sapl.comissoes import forms
def make_composicao(comissao):
@@ -96,3 +98,47 @@ def test_incluir_comissao_errors(admin_client):
['Este campo é obrigatório.'])
assert (response.context_data['form'].errors['data_criacao'] ==
['Este campo é obrigatório.'])
+
+
+@pytest.mark.django_db(transaction=False)
+def test_periodo_invalidas():
+
+ form = forms.PeriodoForm(data={'data_inicio': '10/11/2017',
+ 'data_fim': '09/11/2017'
+ })
+ assert not form.is_valid()
+ assert form.errors['__all__'] == [_('A Data Final não pode ser menor que '
+ 'a Data Inicial')]
+
+
+@pytest.mark.django_db(transaction=False)
+def test_valida_campos_obrigatorios_periodo_form():
+ form = forms.PeriodoForm(data={})
+
+ assert not form.is_valid()
+
+ errors = form.errors
+
+ assert errors['data_inicio'] == [_('Este campo é obrigatório.')]
+
+ assert len(errors) == 1
+
+
+@pytest.mark.django_db(transaction=False)
+def test_valida_campos_obrigatorios_reuniao_form():
+ form = forms.ReuniaoForm(data={})
+
+ assert not form.is_valid()
+
+ errors = form.errors
+
+ assert errors['comissao'] == [_('Este campo é obrigatório.')]
+ assert errors['periodo'] == [_('Este campo é obrigatório.')]
+ assert errors['numero'] == [_('Este campo é obrigatório.')]
+ assert errors['nome'] == [_('Este campo é obrigatório.')]
+ assert errors['data'] == [_('Este campo é obrigatório.')]
+ assert errors['hora_inicio'] == [_('Este campo é obrigatório.')]
+ assert errors['hora_fim'] == [_('Este campo é obrigatório.')]
+
+ assert len(errors) == 7
+
diff --git a/sapl/legacy/migracao.py b/sapl/legacy/migracao.py
index 9ed09360f..dd54a103e 100644
--- a/sapl/legacy/migracao.py
+++ b/sapl/legacy/migracao.py
@@ -51,6 +51,9 @@ def salva_conteudo_do_sde(proposicao, conteudo):
proposicao, 'proposicao_sde_{}.xml'.format(proposicao.pk))
caminho_absoluto = Path(REPO.working_dir, caminho_relativo)
caminho_absoluto.parent.mkdir(parents=True)
+ # ajusta caminhos para folhas de estilo
+ conteudo = conteudo.replace(b'"XSLT/HTML', b'"/XSLT/HTML')
+ conteudo = conteudo.replace(b"'XSLT/HTML", b"'/XSLT/HTML")
with open(caminho_absoluto, 'wb') as arq:
arq.write(conteudo)
proposicao.texto_original = caminho_relativo
diff --git a/sapl/legacy/migracao_dados.py b/sapl/legacy/migracao_dados.py
index 41c7b9934..4c953d5f5 100644
--- a/sapl/legacy/migracao_dados.py
+++ b/sapl/legacy/migracao_dados.py
@@ -22,6 +22,7 @@ from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist
+from django.core.management.commands.flush import Command as FlushCommand
from django.db import connections, transaction
from django.db.models import Max, Q
from pyaml import UnsafePrettyYAMLDumper
@@ -53,6 +54,21 @@ from sapl.utils import normalize
from .scripts.normaliza_dump_mysql import normaliza_dump_mysql
from .timezonesbrasil import get_timezone
+
+# YAML SETUP ###############################################################
+def dict_representer(dumper, data):
+ return dumper.represent_dict(data.items())
+
+yaml.add_representer(OrderedDict, dict_representer)
+
+
+# importante para preservar a ordem ao ler yaml no python 3.5
+def dict_constructor(loader, node):
+ return OrderedDict(loader.construct_pairs(node))
+
+yaml.add_constructor(yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
+ dict_constructor)
+
# BASE ######################################################################
# apps to be migrated, in app dependency order (very important)
appconfs = [apps.get_app_config(n) for n in [
@@ -497,9 +513,10 @@ def checa_registros_votacao_ambiguos_e_remove_nao_usados():
# interrompe migração se houver registros ambíguos
ambiguos = ordem.intersection(expediente)
- assert not ambiguos, '''Existe(m) RegistroVotacao ambíguo(s): {}
- Corrija os dados originais antes de migrar!'''.format(
- ambiguos)
+ if ambiguos:
+ warn('registro_votacao_ambiguos',
+ 'Existe(m) RegistroVotacao ambíguo(s): {cod_votacao}',
+ {'cod_votacao': ambiguos})
# exclui registros não usados (zumbis)
todos = set(primeira_coluna(exec_legado(
@@ -567,8 +584,8 @@ def propaga_exclusoes():
def uniformiza_banco():
exec_legado('SET SESSION sql_mode = "";') # desliga checagens do mysql
- checa_registros_votacao_ambiguos_e_remove_nao_usados()
propaga_exclusoes()
+ checa_registros_votacao_ambiguos_e_remove_nao_usados()
garante_coluna_no_legado('proposicao',
'num_proposicao int(11) NULL')
@@ -649,6 +666,7 @@ sessao_plenaria_presenca | dat_sessao = NULL | dat_sessao = 0
unifica_autores_repetidos_no_legado('cod_parlamentar')
unifica_autores_repetidos_no_legado('cod_comissao')
+ unifica_autores_repetidos_no_legado('col_username')
# é importante reverter a exclusão de autores somente depois, para que a
# unificação possa dar prioridade às informações dos autores não excluídos
@@ -739,13 +757,6 @@ def reinicia_sequence(model, id):
REPO = git.Repo.init(DIR_REPO)
-def dict_representer(dumper, data):
- return dumper.represent_dict(data.items())
-
-
-yaml.add_representer(OrderedDict, dict_representer)
-
-
# configura timezone de migração
match = re.match('sapl_cm_(.*)', NOME_BANCO_LEGADO)
sigla_casa = match.group(1)
@@ -820,8 +831,8 @@ def migrar_dados():
# excluindo database antigo.
info('Excluindo entradas antigas do banco destino.')
- call([PROJECT_DIR.child('manage.py'), 'flush',
- '--database=default', '--no-input'], stdout=PIPE)
+ flush = FlushCommand()
+ flush.handle(database='default', interactive=False, verbosity=0)
# apaga tipos de autor padrão (criados no flush acima)
TipoAutor.objects.all().delete()
@@ -854,12 +865,17 @@ def move_para_depois_de(lista, movido, referencias):
return lista
+TABELAS_LEGADO = [t for (t,) in exec_legado('show tables')]
+EXISTE_REUNIAO_NO_LEGADO = 'reuniao_comissao' in TABELAS_LEGADO
+
+
def get_models_a_migrar():
models = [model for app in appconfs for model in app.models.values()
if model in field_renames]
# retira reuniões quando não existe na base legada
# (só existe no sapl 3.0)
- if 'reuniao_comissao' not in list(exec_legado('show tables')):
+ tabelas_legado = [t for (t,) in exec_legado('show tables')]
+ if not EXISTE_REUNIAO_NO_LEGADO:
models.remove(Reuniao)
# Devido à referência TipoProposicao.tipo_conteudo_related
# a migração de TipoProposicao precisa ser feita
@@ -1094,6 +1110,18 @@ def adjust_protocolo_antes_salvar(new, old):
{'cod_protocolo': old.cod_protocolo})
+ARQUIVO_COMO_RESOLVER_REGISTRO_VOTACAO_AMBIGUO = \
+ 'como_resolver_registro_votacao_ambiguo.yaml'
+
+
+def get_como_resolver_registro_votacao_ambiguo():
+ path = DIR_REPO.child(ARQUIVO_COMO_RESOLVER_REGISTRO_VOTACAO_AMBIGUO)
+ if path.exists():
+ return yaml.load(path.read_file())
+ else:
+ return {}
+
+
def adjust_registrovotacao_antes_salvar(new, old):
ordem_dia = OrdemDia.objects.filter(
pk=old.cod_ordem, materia=old.cod_materia)
@@ -1104,6 +1132,19 @@ def adjust_registrovotacao_antes_salvar(new, old):
new.ordem = ordem_dia[0]
if not ordem_dia and expediente_materia:
new.expediente = expediente_materia[0]
+ # registro de votação ambíguo
+ if ordem_dia and expediente_materia:
+ como_resolver = get_como_resolver_registro_votacao_ambiguo()
+ campo = como_resolver[new.id]
+ if campo.startswith('ordem'):
+ new.ordem = ordem_dia[0]
+ elif campo.startswith('expediente'):
+ new.expediente = expediente_materia[0]
+ else:
+ raise Exception('''
+ Registro de Votação ambíguo: {}
+ Resolva criando o arquivo {}'''.format(
+ new.id, ARQUIVO_COMO_RESOLVER_REGISTRO_VOTACAO_AMBIGUO))
def adjust_tipoafastamento(new, old):
@@ -1180,8 +1221,10 @@ def adjust_normajuridica_depois_salvar():
for model in [AssuntoNorma, NormaJuridica]]
def filtra_assuntos_migrados(cod_assunto):
- return [a for a in map(int, cod_assunto.split(','))
- if a in assuntos_migrados]
+ if not cod_assunto:
+ return []
+ cods = {int(a) for a in cod_assunto.split(',') if a}
+ return sorted(cods.intersection(assuntos_migrados))
norma_para_assuntos = [
(norma, filtra_assuntos_migrados(cod_assunto))
@@ -1357,6 +1400,7 @@ def gravar_marco():
# salva mudanças
REPO.git.add([dir_dados.name])
+ REPO.git.add([arq_backup.name])
if 'master' not in REPO.heads or REPO.index.diff('HEAD'):
# se de fato existe mudança
REPO.index.commit('Grava marco')
diff --git a/sapl/legacy/migracao_documentos.py b/sapl/legacy/migracao_documentos.py
index fd692aa47..23c4afa54 100644
--- a/sapl/legacy/migracao_documentos.py
+++ b/sapl/legacy/migracao_documentos.py
@@ -9,7 +9,7 @@ from image_cropping.fields import ImageCropField
from sapl.base.models import CasaLegislativa
from sapl.comissoes.models import Reuniao
-from sapl.legacy.migracao_dados import exec_legado
+from sapl.legacy.migracao_dados import EXISTE_REUNIAO_NO_LEGADO, exec_legado
from sapl.materia.models import (DocumentoAcessorio, MateriaLegislativa,
Proposicao)
from sapl.norma.models import NormaJuridica
@@ -36,9 +36,9 @@ DOCS = {
}
# acrescenta reuniões (que só existem no sapl 3.0)
-if 'reuniao_comissao' in set(exec_legado('show tables')):
+if EXISTE_REUNIAO_NO_LEGADO:
DOCS[Reuniao] = [('upload_pauta', 'reuniao_comissao/{}_pauta'),
- ('upload_ata', 'reuniao_comissao/{}_ata')],
+ ('upload_ata', 'reuniao_comissao/{}_ata')]
DOCS = {model: [(campo, join('sapl_documentos', origem))
@@ -53,7 +53,7 @@ def mover_documento(repo, origem, destino, ignora_origem_ausente=False):
print('Origem ignorada ao mover documento: {}'.format(origem))
return
os.makedirs(os.path.dirname(destino), exist_ok=True)
- repo.git.mv(origem, destino)
+ os.rename(origem, destino)
def migrar_logotipo(repo, casa, propriedades):
@@ -140,6 +140,7 @@ def migrar_docs_por_ids(repo, model):
if tem_cropping:
# conserta link do git annex (antes do commit)
# pois o conteúdo das imagens é acessado pelo cropping
+ repo.git.add(destino)
repo.git.execute('git annex fix'.split() + [destino])
obj.save()
else:
@@ -162,11 +163,21 @@ def migrar_documentos(repo):
# garante que o conteúdo das fotos dos parlamentares esteja presente
# (necessário para o cropping de imagem)
+<<<<<<< HEAD
repo.git.execute('git annex get sapl_documentos/parlamentar'.split())
+=======
+ if os.path.exists(
+ os.path.join(repo.working_dir, 'sapl_documentos/parlamentar')):
+ repo.git.execute('git annex get sapl_documentos/parlamentar'.split())
+>>>>>>> 3.1.x
for model in DOCS:
migrar_docs_por_ids(repo, model)
+ # versiona modificações
+ repo.git.add('-A', '.')
+ repo.index.commit('Migração dos documentos completa')
+
sobrando = [join(dir, file)
for (dir, _, files) in os.walk(join(repo.working_dir,
'sapl_documentos'))
diff --git a/sapl/legacy/scripts/exporta_zope/exporta_zope.py b/sapl/legacy/scripts/exporta_zope/exporta_zope.py
index 270c90583..391e2f055 100755
--- a/sapl/legacy/scripts/exporta_zope/exporta_zope.py
+++ b/sapl/legacy/scripts/exporta_zope/exporta_zope.py
@@ -13,6 +13,7 @@ import sys
from collections import defaultdict
from contextlib import contextmanager
from functools import partial
+from os.path import exists
import git
import magic
@@ -78,7 +79,8 @@ def br(obj):
def guess_extension(fullname, buffer):
- mime = magic.from_buffer(buffer, mime=True)
+ # um corte de apenas 1024 impediu a detecção correta de .docx
+ mime = magic.from_buffer(buffer[:4096], mime=True)
extensao = EXTENSOES.get(mime)
if extensao is not None:
return extensao
@@ -143,17 +145,21 @@ def get_conteudo_dtml_method(doc):
return doc['raw']
+def print_msg_poskeyerror(id):
+ print('#' * 80)
+ print('#' * 80)
+ print('ATENÇÃO: DIRETÓRIO corrompido: {}'.format(id))
+ print('#' * 80)
+ print('#' * 80)
+
+
def enumerate_by_key_list(folder, key_list, type_key):
for entry in folder.get(key_list, []):
id, meta_type = entry['id'], entry[type_key]
try:
- obj = br(folder.get(id, None))
+ obj = folder.get(id, None)
except POSKeyError:
- print('#' * 80)
- print('#' * 80)
- print('ATENÇÃO: DIRETÓRIO corrompido: {}'.format(id))
- print('#' * 80)
- print('#' * 80)
+ print_msg_poskeyerror(id)
else:
yield id, obj, meta_type
@@ -169,9 +175,12 @@ def enumerate_btree(folder):
contagem_esperada = folder['_count'].value
tree = folder['_tree']
contagem_real = 0 # para o caso em que não haja itens
- for contagem_real, (id, obj) in enumerate(tree.iteritems(), start=1):
- obj, meta_type = br(obj), type(obj).__name__
- yield id, obj, meta_type
+ try:
+ for contagem_real, (id, obj) in enumerate(tree.iteritems(), start=1):
+ meta_type = type(obj).__name__
+ yield id, obj, meta_type
+ except POSKeyError:
+ print_msg_poskeyerror(folder['id'])
# verificação de consistência
if contagem_esperada != contagem_real:
print('ATENÇÃO: contagens diferentes na btree: '
@@ -197,10 +206,10 @@ def logando_nao_identificados():
print('#' * 80)
-def dump_folder(folder, path, salvar, enum=enumerate_folder):
+def dump_folder(folder, path, salvar, mtimes, enum=enumerate_folder):
name = folder['id']
path = os.path.join(path, name)
- if not os.path.exists(path):
+ if not exists(path):
os.makedirs(path)
for id, obj, meta_type in enum(folder):
# pula pastas *_old (presentes em várias bases)
@@ -210,8 +219,20 @@ def dump_folder(folder, path, salvar, enum=enumerate_folder):
if dump == '?':
nao_identificados[meta_type].append(path + '/' + id)
elif dump:
- id_interno = dump(obj, path, salvar)
- assert id == id_interno
+ if isinstance(dump, partial) and dump.func == dump_folder:
+ try:
+ dump(br(obj), path, salvar, mtimes)
+ except POSKeyError as e:
+ print_msg_poskeyerror(id)
+ continue
+ else:
+ # se o objeto for mais recente que o da última exportação
+ mtime = obj._p_mtime
+ fullname = os.path.join(path, id)
+ if mtime > mtimes.get(fullname, 0):
+ id_interno = dump(br(obj), path, salvar)
+ assert id == id_interno
+ mtimes[fullname] = mtime
return name
@@ -223,7 +244,7 @@ def read_sde(element):
def read_properties():
for id, obj, meta_type in enumerate_properties(element):
- yield id, decode_iso8859(obj)
+ yield id, decode_iso8859(br(obj))
def read_children():
for id, obj, meta_type in enumerate_folder(element):
@@ -237,7 +258,7 @@ def read_sde(element):
# ignoramos os scrips python de eventos dos templates
yield {'id': id,
'meta_type': meta_type,
- 'dados': read_sde(obj)}
+ 'dados': read_sde(br(obj))}
data = dict(read_properties())
children = list(read_children())
@@ -302,6 +323,7 @@ def find_sapl(app):
sapl = br(app['sapl'])
return sapl
+<<<<<<< HEAD
def detectar_encoding(fonte):
desc = magic.from_buffer(fonte)
@@ -318,7 +340,23 @@ def autodecode(fonte):
else:
return fonte
+def detectar_encoding(fonte):
+ desc = magic.from_buffer(fonte)
+ for termo, enc in [('ISO-8859', 'latin1'), ('UTF-8', 'utf-8')]:
+ if termo in desc:
+ return enc
+ return None
+
+def autodecode(fonte):
+ if isinstance(fonte, str):
+ enc = detectar_encoding(fonte)
+ return fonte.decode(enc) if enc else fonte
+ else:
+ return fonte
+
+
+>>>>>>> 3.1.x
def dump_propriedades(docs, path, salvar):
props_sapl = br(docs['props_sapl'])
ids = [p['id'] for p in props_sapl['_properties']]
@@ -335,9 +373,18 @@ def dump_usuarios(sapl, path, salvar):
save_as_yaml(path, 'usuarios.yaml', users, salvar)
+<<<<<<< HEAD
def _dump_sapl(data_fs_path, documentos_fs_path, destino, salvar):
assert Path(data_fs_path).exists()
assert Path(documentos_fs_path).exists()
+=======
+def _dump_sapl(data_fs_path, documentos_fs_path, destino, salvar, mtimes):
+ assert exists(data_fs_path)
+ assert exists(documentos_fs_path)
+ # precisamos trabalhar com strings e não Path's para as comparações de mtimes
+ data_fs_path, documentos_fs_path, destino = map(str, (
+ data_fs_path, documentos_fs_path, destino))
+>>>>>>> 3.1.x
app, close_db = get_app(data_fs_path)
try:
@@ -352,12 +399,16 @@ def _dump_sapl(data_fs_path, documentos_fs_path, destino, salvar):
sapl = find_sapl(app)
# extrai folhas XSLT
if 'XSLT' in sapl:
+<<<<<<< HEAD
dump_folder(br(sapl['XSLT']), destino, salvar)
+=======
+ dump_folder(br(sapl['XSLT']), destino, salvar, mtimes)
+>>>>>>> 3.1.x
# extrai documentos
docs = br(sapl['sapl_documentos'])
with logando_nao_identificados():
- dump_folder(docs, destino, salvar)
+ dump_folder(docs, destino, salvar, mtimes)
dump_propriedades(docs, destino, salvar)
finally:
close_db()
@@ -367,37 +418,28 @@ def repo_execute(repo, cmd, *args):
return repo.git.execute(cmd.split() + list(args))
-def get_annex_hashes(repo):
- hashes = repo_execute(
- repo, 'git annex find', '--format=${keyname}\n', '--include=*')
- return {os.path.splitext(h)[0] for h in hashes.splitlines()}
-
-
def ajusta_extensao(fullname, conteudo):
base, extensao = os.path.splitext(fullname)
if extensao not in ['.xsl', '.xslt', '.yaml', '.css']:
extensao = guess_extension(fullname, conteudo)
- return base + extensao
+ return base + extensao, extensao
def build_salvar(repo):
- """Constroi função salvar que pula arquivos que já estão no annex
- """
- hashes = get_annex_hashes(repo)
def salvar(fullname, conteudo):
- sha = hashlib.sha256()
- sha.update(conteudo)
- if sha.hexdigest() in hashes:
- print('- hash encontrado - {}'.format(fullname))
- else:
- fullname = ajusta_extensao(fullname, conteudo)
- if os.path.exists(fullname):
- # destrava arquivo pré-existente (o conteúdo mudou)
- repo_execute(repo, 'git annex unlock', fullname)
- with open(fullname, 'w') as arq:
- arq.write(conteudo)
- print(fullname)
+ fullname, extensao = ajusta_extensao(fullname, conteudo)
+
+ # ajusta caminhos XSLT p conteúdos relacionados ao SDE
+ if extensao in ['.xsl', '.xslt', '.xml']:
+ conteudo = conteudo.replace('"XSLT/HTML', '"/XSLT/HTML')
+
+ if exists(fullname):
+ # destrava arquivo pré-existente (o conteúdo mudou)
+ repo_execute(repo, 'git annex unlock', fullname)
+ with open(fullname, 'w') as arq:
+ arq.write(conteudo)
+ print(fullname)
return salvar
@@ -409,8 +451,13 @@ def dump_sapl(sigla):
'datafs', '{}_cm_{}.fs'.format(prefixo, sigla))
for prefixo in ('Data', 'DocumentosSapl')]
+<<<<<<< HEAD
assert data_fs_path.exists(), 'Origem não existe: {}'.format(data_fs_path)
if not documentos_fs_path.exists():
+=======
+ assert exists(data_fs_path), 'Origem não existe: {}'.format(data_fs_path)
+ if not exists(documentos_fs_path):
+>>>>>>> 3.1.x
documentos_fs_path = data_fs_path
nome_banco_legado = 'sapl_cm_{}'.format(sigla)
@@ -427,12 +474,21 @@ def dump_sapl(sigla):
salvar = build_salvar(repo)
try:
finalizado = False
+<<<<<<< HEAD
_dump_sapl(data_fs_path, documentos_fs_path, destino, salvar)
+=======
+ arq_mtimes = Path(repo.working_dir, 'mtimes.yaml')
+ mtimes = yaml.load(
+ arq_mtimes.read_file()) if arq_mtimes.exists() else {}
+ _dump_sapl(data_fs_path, documentos_fs_path, destino, salvar, mtimes)
+>>>>>>> 3.1.x
finalizado = True
finally:
# grava mundaças
repo_execute(repo, 'git annex add sapl_documentos')
+ arq_mtimes.write_file(yaml.safe_dump(mtimes, allow_unicode=True))
repo.git.add(A=True)
+ # atualiza repo
if 'master' not in repo.heads or repo.index.diff('HEAD'):
# se de fato existe mudança
status = 'completa' if finalizado else 'parcial'
diff --git a/sapl/materia/forms.py b/sapl/materia/forms.py
index 838b8616d..04ee8d0e0 100644
--- a/sapl/materia/forms.py
+++ b/sapl/materia/forms.py
@@ -186,6 +186,7 @@ class MateriaLegislativaForm(ModelForm):
widget=forms.HiddenInput())
self.fields['autor'] = forms.CharField(required=False,
widget=forms.HiddenInput())
+ self.fields['numero_protocolo'].widget.attrs['readonly'] = True
def clean(self):
super(MateriaLegislativaForm, self).clean()
@@ -213,11 +214,16 @@ class MateriaLegislativaForm(ModelForm):
exist_doc = DocumentoAdministrativo.objects.filter(
protocolo_id=protocolo,
ano=ano).exists()
+
if exist_materia or exist_doc:
raise ValidationError(_('Protocolo %s/%s ja possui'
' documento vinculado'
% (protocolo, ano)))
+ p = Protocolo.objects.get(numero=protocolo,ano=ano)
+ if p.tipo_materia != cleaned_data['tipo']:
+ raise ValidationError(_('Tipo do Protocolo deve ser o mesmo do Tipo Matéria'))
+
if data_apresentacao.year != ano:
raise ValidationError(_("O ano da matéria não pode ser "
"diferente do ano na data de apresentação"))
@@ -270,7 +276,7 @@ class UnidadeTramitacaoForm(ModelForm):
del cleaned_data[key]
if len(cleaned_data) != 1:
- msg = _('Somente um campo deve preenchido!')
+ msg = _('Somente um campo deve ser preenchido!')
raise ValidationError(msg)
return cleaned_data
@@ -766,14 +772,10 @@ class MateriaLegislativaFilterSet(django_filters.FilterSet):
def pega_ultima_tramitacao():
- ultimas_tramitacoes = Tramitacao.objects.values(
+ return Tramitacao.objects.values(
'materia_id').annotate(data_encaminhamento=Max(
'data_encaminhamento'),
- id=Max('id')).values_list('id')
-
- lista = [item for sublist in ultimas_tramitacoes for item in sublist]
-
- return lista
+ id=Max('id')).values_list('id', flat=True)
def filtra_tramitacao_status(status):
diff --git a/sapl/materia/tests/test_materia_form.py b/sapl/materia/tests/test_materia_form.py
index 41ad04837..944cabf00 100644
--- a/sapl/materia/tests/test_materia_form.py
+++ b/sapl/materia/tests/test_materia_form.py
@@ -66,3 +66,198 @@ def test_ficha_seleciona_form_valido():
form = forms.FichaSelecionaForm(data={'materia': str(materia.pk)})
assert form.is_valid()
+
+
+@pytest.mark.django_db(transaction=False)
+def test_valida_campos_obrigatorios_materialegislativa_form():
+ form = forms.MateriaLegislativaForm(data={})
+
+ assert not form.is_valid()
+
+ errors = form.errors
+ assert errors['tipo'] == [_('Este campo é obrigatório.')]
+ assert errors['ano'] == [_('Este campo é obrigatório.')]
+ assert errors['data_apresentacao'] == [_('Este campo é obrigatório.')]
+ assert errors['numero'] == [_('Este campo é obrigatório.')]
+ assert errors['ementa'] == [_('Este campo é obrigatório.')]
+ assert errors['regime_tramitacao'] == [_('Este campo é obrigatório.')]
+
+ assert len(errors) == 6
+
+
+@pytest.mark.django_db(transaction=False)
+def test_valida_campos_obrigatorios_unidade_tramitacao_form():
+ form = forms.UnidadeTramitacaoForm(data={})
+
+ assert not form.is_valid()
+ errors = form.errors
+
+ assert errors['__all__'] == [_('Somente um campo deve ser preenchido!')]
+
+@pytest.mark.django_db(transaction=False)
+def test_valida_campos_obrigatorios_orgao_form():
+ form = forms.OrgaoForm(data={})
+
+ assert not form.is_valid()
+ errors = form.errors
+
+ assert errors['nome'] == [_('Este campo é obrigatório.')]
+ assert errors['sigla'] == [_('Este campo é obrigatório.')]
+
+ assert len(errors) == 2
+
+
+@pytest.mark.django_db(transaction=False)
+def test_valida_campos_obrigatorios_materia_assunto_form():
+ form = forms.MateriaAssuntoForm(data={})
+
+ assert not form.is_valid()
+
+ errors = form.errors
+
+ assert errors['assunto'] == [_('Este campo é obrigatório.')]
+ assert errors['materia'] == [_('Este campo é obrigatório.')]
+
+ assert len(errors) == 2
+
+@pytest.mark.django_db(transaction=False)
+def test_valida_campos_obrigatorios_autoria_form():
+ form = forms.AutoriaForm(data={},instance=None)
+
+ assert not form.is_valid()
+
+ errors = form.errors
+
+ assert errors['autor'] == [_('Este campo é obrigatório.')]
+
+ assert len(errors) == 1
+
+
+@pytest.mark.django_db(transaction=False)
+def test_valida_campos_obrigatorios_autoria_multicreate_form():
+ form = forms.AutoriaMultiCreateForm(data={})
+
+ assert not form.is_valid()
+
+ errors = form.errors
+
+ assert errors['__all__'] == [_('Ao menos um autor deve ser selecionado para inclusão')]
+
+ assert len(errors) == 1
+
+
+@pytest.mark.django_db(transaction=False)
+def test_valida_campos_obrigatorios_tipo_proposicao_form():
+ form = forms.TipoProposicaoForm(data={})
+
+ assert not form.is_valid()
+
+ errors = form.errors
+ assert errors['tipo_conteudo_related'] == [_('Este campo é obrigatório.')]
+ assert errors['descricao'] == [_('Este campo é obrigatório.')]
+ assert errors['content_type'] == [_('Este campo é obrigatório.')]
+
+ assert len(errors) == 3
+
+@pytest.mark.django_db(transaction=False)
+def test_valida_campos_obrigatorios_devolver_proposicao_form():
+ form = forms.DevolverProposicaoForm(data={})
+
+ assert not form.is_valid()
+
+ errors = form.errors
+ assert errors['__all__'] == [_('Adicione uma Justificativa para devolução.')]
+
+ assert len(errors) == 1
+
+@pytest.mark.django_db(transaction=False)
+def test_valida_campos_obrigatorios_relatoria_form():
+ form = forms.RelatoriaForm(data={})
+
+ assert not form.is_valid()
+
+ errors = form.errors
+ assert errors['parlamentar'] == [_('Este campo é obrigatório.')]
+ assert errors['data_designacao_relator'] == [_('Este campo é obrigatório.')]
+
+ assert len(errors) == 2
+
+
+@pytest.mark.django_db(transaction=False)
+def test_valida_campos_obrigatorios_tramitacao_form():
+ form = forms.TramitacaoForm(data={})
+
+ assert not form.is_valid()
+
+ errors = form.errors
+
+ assert errors['unidade_tramitacao_local'] == [_('Este campo é obrigatório.')]
+ assert errors['texto'] == [_('Este campo é obrigatório.')]
+ assert errors['status'] == [_('Este campo é obrigatório.')]
+ assert errors['data_tramitacao'] == [_('Este campo é obrigatório.')]
+ assert errors['unidade_tramitacao_destino'] == [_('Este campo é obrigatório.')]
+
+ assert len(errors) == 5
+
+
+@pytest.mark.django_db(transaction=False)
+def test_valida_campos_obrigatorios_tramitacao_update_form():
+ form = forms.TramitacaoUpdateForm(data={})
+
+ assert not form.is_valid()
+
+ errors = form.errors
+
+ assert errors['unidade_tramitacao_local'] == [_('Este campo é obrigatório.')]
+ assert errors['texto'] == [_('Este campo é obrigatório.')]
+ assert errors['status'] == [_('Este campo é obrigatório.')]
+ assert errors['data_tramitacao'] == [_('Este campo é obrigatório.')]
+ assert errors['unidade_tramitacao_destino'] == [_('Este campo é obrigatório.')]
+
+ assert len(errors) == 5
+
+
+@pytest.mark.django_db(transaction=False)
+def test_valida_campos_obrigatorios_legislacao_citada_form():
+ form = forms.LegislacaoCitadaForm(data={})
+
+ assert not form.is_valid()
+
+ errors = form.errors
+ assert errors['tipo'] == [_('Este campo é obrigatório.')]
+ assert errors['ano'] == [_('Este campo é obrigatório.')]
+ assert errors['numero'] == [_('Este campo é obrigatório.')]
+
+ assert len(errors) == 3
+
+
+@pytest.mark.django_db(transaction=False)
+def test_valida_campos_obrigatorios_numeracao_form():
+ form = forms.NumeracaoForm(data={})
+
+ assert not form.is_valid()
+
+ errors = form.errors
+
+ assert errors['tipo_materia'] == [_('Este campo é obrigatório.')]
+ assert errors['ano_materia'] == [_('Este campo é obrigatório.')]
+ assert errors['numero_materia'] == [_('Este campo é obrigatório.')]
+ assert errors['data_materia'] == [_('Este campo é obrigatório.')]
+
+ assert len(errors) == 4
+
+
+@pytest.mark.django_db(transaction=False)
+def test_valida_campos_obrigatorios_anexada_form():
+ form = forms.AnexadaForm(data={})
+
+ assert not form.is_valid()
+
+ errors = form.errors
+
+ assert errors['tipo'] == [_('Este campo é obrigatório.')]
+ assert errors['ano'] == [_('Este campo é obrigatório.')]
+ assert errors['numero'] == [_('Este campo é obrigatório.')]
+ assert errors['data_anexacao'] == [_('Este campo é obrigatório.')]
+
+ assert len(errors) == 4
diff --git a/sapl/materia/views.py b/sapl/materia/views.py
index 6f0b35ef8..f2e3e76db 100644
--- a/sapl/materia/views.py
+++ b/sapl/materia/views.py
@@ -700,12 +700,15 @@ class ProposicaoCrud(Crud):
messages.success(request, _(
'Proposição enviada com sucesso.'))
- Numero = MateriaLegislativa.objects.filter(tipo=p.tipo.tipo_conteudo_related,
- ano=p.ano).last().numero + 1
- messages.success(request, _(
- '%s : nº %s de %s
Atenção! Este número é apenas um provável '
- 'número que pode não corresponder com a realidade'
- % (p.tipo, Numero, p.ano)))
+ try:
+ Numero = MateriaLegislativa.objects.filter(tipo=p.tipo.tipo_conteudo_related,
+ ano=p.ano).last().numero + 1
+ messages.success(request, _(
+ '%s : nº %s de %s
Atenção! Este número é apenas um provável '
+ 'número que pode não corresponder com a realidade'
+ % (p.tipo, Numero, p.ano)))
+ except ValueError:
+ pass
elif action == 'return':
if not p.data_envio:
@@ -857,7 +860,7 @@ class ProposicaoCrud(Crud):
else:
obj.data_recebimento = timezone.localtime(
obj.data_recebimento)
- obj.data_recebimento = obj.data_recebimento = formats.date_format(
+ obj.data_recebimento = formats.date_format(
obj.data_recebimento, "DATETIME_FORMAT")
if obj.data_envio is None:
obj.data_envio = 'Em elaboração...'
@@ -937,8 +940,9 @@ class RelatoriaCrud(MasterDetailCrud):
try:
comissao = Comissao.objects.get(
pk=context['form'].initial['comissao'])
- except ObjectDoesNotExist:
+ except:
pass
+
else:
composicao = comissao.composicao_set.order_by(
'-periodo__data_inicio').first()
@@ -1775,17 +1779,47 @@ class PrimeiraTramitacaoEmLoteView(PermissionRequiredMixin, FilterView):
messages.add_message(request, messages.ERROR, msg)
return self.get(request, self.kwargs)
- if request.POST['data_encaminhamento']:
+ if request.POST['status'] == '':
+ msg = _('Campo Status deve ser preenchido.')
+ messages.add_message(request, messages.ERROR, msg)
+ return self.get(request, self.kwargs)
+
+ if request.POST['unidade_tramitacao_local'] == '':
+ msg = _('Campo Unidade Local deve ser preenchido.')
+ messages.add_message(request, messages.ERROR, msg)
+ return self.get(request, self.kwargs)
+
+ if request.POST['data_tramitacao'] == '':
+ msg = _('Campo Data da Tramitação deve ser preenchido.')
+ messages.add_message(request, messages.ERROR, msg)
+ return self.get(request, self.kwargs)
+
+ if request.POST['unidade_tramitacao_destino'] == '':
+ msg = _('Campo Unidade Destino deve ser preenchido.')
+ messages.add_message(request, messages.ERROR, msg)
+ return self.get(request, self.kwargs)
+
+ if request.POST['urgente'] == '':
+ msg = _('Campo Urgente deve ser preenchido.')
+ messages.add_message(request, messages.ERROR, msg)
+ return self.get(request, self.kwargs)
+
+ if request.POST['texto'] == '':
+ msg = _('Campo Texto da Ação deve ser preenchido.')
+ messages.add_message(request, messages.ERROR, msg)
+ return self.get(request, self.kwargs)
+
+ if request.POST['data_encaminhamento'] == '':
+ data_encaminhamento = None
+ else:
data_encaminhamento = tz.localize(datetime.strptime(
request.POST['data_encaminhamento'], "%d/%m/%Y"))
- else:
- data_encaminhamento = None
- if request.POST['data_fim_prazo']:
+ if request.POST['data_fim_prazo'] == '':
+ data_fim_prazo = None
+ else:
data_fim_prazo = tz.localize(datetime.strptime(
request.POST['data_fim_prazo'], "%d/%m/%Y"))
- else:
- data_fim_prazo = None
# issue https://github.com/interlegis/sapl/issues/1123
# TODO: usar Form
diff --git a/sapl/painel/views.py b/sapl/painel/views.py
index a185fed3b..847518898 100644
--- a/sapl/painel/views.py
+++ b/sapl/painel/views.py
@@ -85,10 +85,16 @@ def votacao_aberta(request):
def votante_view(request):
# Pega o votante relacionado ao usuário
template_name = 'painel/voto_nominal.html'
+ context = {}
try:
votante = Votante.objects.get(user=request.user)
except ObjectDoesNotExist:
- raise Http404()
+ msg = _("Usuário não cadastrado como votante na tela de parlamentares. Contate a administração de sua Casa Legislativa!")
+ context.update({
+ 'error_message':msg
+ })
+
+ return render(request, template_name, context)
context = {'head_title': str(_('Votação Individual'))}
diff --git a/sapl/protocoloadm/forms.py b/sapl/protocoloadm/forms.py
index 8502bf334..22e649459 100644
--- a/sapl/protocoloadm/forms.py
+++ b/sapl/protocoloadm/forms.py
@@ -7,6 +7,7 @@ from django import forms
from django.core.exceptions import (MultipleObjectsReturned,
ObjectDoesNotExist, ValidationError)
from django.db import models
+from django.db.models import Max
from django.forms import ModelForm
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
@@ -708,10 +709,10 @@ class DocumentoAdministrativoForm(ModelForm):
ano=ano_protocolo).exists()
exist_doc = DocumentoAdministrativo.objects.filter(
- protocolo_id=numero_protocolo,
- ano=ano_protocolo).exists()
+ protocolo__numero=numero_protocolo,
+ protocolo__ano=ano_protocolo).exists()
if exist_materia or exist_doc:
- raise ValidationError(_('Protocolo %s/%s ja possui'
+ raise ValidationError(_('Protocolo %s/%s já possui'
' documento vinculado'
% (numero_protocolo, ano_protocolo)))
@@ -873,3 +874,34 @@ class DesvincularMateriaForm(forms.Form):
form_actions(label='Desvincular')
)
)
+
+
+def pega_ultima_tramitacao_adm():
+ return TramitacaoAdministrativo.objects.values(
+ 'materia_id').annotate(data_encaminhamento=Max(
+ 'data_encaminhamento'),
+ id=Max('id')).values_list('id', flat=True)
+
+
+def filtra_tramitacao_adm_status(status):
+ lista = pega_ultima_tramitacao_adm()
+ return TramitacaoAdministrativo.objects.filter(
+ id__in=lista,
+ status=status).distinct().values_list('materia_id', flat=True)
+
+
+def filtra_tramitacao_adm_destino(destino):
+ lista = pega_ultima_tramitacao_adm()
+ return TramitacaoAdministrativo.objects.filter(
+ id__in=lista,
+ unidade_tramitacao_destino=destino).distinct().values_list(
+ 'materia_id', flat=True)
+
+
+def filtra_tramitacao_adm_destino_and_status(status, destino):
+ lista = pega_ultima_tramitacao_adm()
+ return TramitacaoAdministrativo.objects.filter(
+ id__in=lista,
+ status=status,
+ unidade_tramitacao_destino=destino).distinct().values_list(
+ 'materia_id', flat=True)
diff --git a/sapl/protocoloadm/views.py b/sapl/protocoloadm/views.py
index c1a2b185a..3350c81cf 100644
--- a/sapl/protocoloadm/views.py
+++ b/sapl/protocoloadm/views.py
@@ -31,7 +31,8 @@ from .forms import (AnularProcoloAdmForm, DocumentoAcessorioAdministrativoForm,
DocumentoAdministrativoFilterSet,
DocumentoAdministrativoForm, ProtocoloDocumentForm,
ProtocoloFilterSet, ProtocoloMateriaForm,
- TramitacaoAdmEditForm, TramitacaoAdmForm, DesvincularDocumentoForm, DesvincularMateriaForm)
+ TramitacaoAdmEditForm, TramitacaoAdmForm, DesvincularDocumentoForm, DesvincularMateriaForm,
+ filtra_tramitacao_adm_destino_and_status, filtra_tramitacao_adm_destino, filtra_tramitacao_adm_status)
from .models import (DocumentoAcessorioAdministrativo, DocumentoAdministrativo,
StatusTramitacaoAdministrativo,
TipoDocumentoAdministrativo, TramitacaoAdministrativo)
@@ -307,7 +308,7 @@ class ProtocoloDocumentoView(PermissionRequiredMixin,
protocolo.anulado = False
if not protocolo.numero:
protocolo.numero = (numero['numero__max'] + 1) if numero['numero__max'] else 1
- if protocolo.numero < (numero['numero__max'] + 1):
+ elif protocolo.numero < (numero['numero__max'] + 1) if numero['numero__max'] else 0:
msg = _('Número de protocolo deve ser maior que {}').format(numero['numero__max'])
messages.add_message(self.request, messages.ERROR, msg)
return self.render_to_response(self.get_context_data())
@@ -543,13 +544,34 @@ class PesquisarDocumentoAdministrativoView(DocumentoAdministrativoMixin,
kwargs = {'data': self.request.GET or None}
+ status_tramitacao = self.request.GET.get('tramitacao__status')
+ unidade_destino = self.request.GET.get(
+ 'tramitacao__unidade_tramitacao_destino')
+
qs = self.get_queryset()
- qs = qs.distinct()
+ if status_tramitacao and unidade_destino:
+ lista = filtra_tramitacao_adm_destino_and_status(status_tramitacao,
+ unidade_destino)
+ qs = qs.filter(id__in=lista).distinct()
+
+ elif status_tramitacao:
+ lista = filtra_tramitacao_adm_status(status_tramitacao)
+ qs = qs.filter(id__in=lista).distinct()
+
+ elif unidade_destino:
+ lista = filtra_tramitacao_adm_destino(unidade_destino)
+ qs = qs.filter(id__in=lista).distinct()
if 'o' in self.request.GET and not self.request.GET['o']:
qs = qs.order_by('-ano', '-numero')
+ qs = qs.prefetch_related("documentoacessorioadministrativo_set",
+ "tramitacaoadministrativo_set",
+ "tramitacaoadministrativo_set__status",
+ "tramitacaoadministrativo_set__unidade_tramitacao_local",
+ "tramitacaoadministrativo_set__unidade_tramitacao_destino")
+
kwargs.update({
'queryset': qs,
})
diff --git a/sapl/relatorios/templates/pdf_capa_processo_preparar_pysc.py b/sapl/relatorios/templates/pdf_capa_processo_preparar_pysc.py
index eacabf86e..4e741e564 100755
--- a/sapl/relatorios/templates/pdf_capa_processo_preparar_pysc.py
+++ b/sapl/relatorios/templates/pdf_capa_processo_preparar_pysc.py
@@ -144,6 +144,6 @@ sessao = session.id
caminho = context.pdf_capa_processo_gerar(
sessao, imagem, data, protocolos, cabecalho, rodape, filtro)
if caminho == 'aviso':
- return response.redirect('mensagem_emitir_proc')
+ response.redirect('mensagem_emitir_proc')
else:
response.redirect(caminho)
diff --git a/sapl/relatorios/templates/pdf_detalhe_materia_preparar_pysc.py b/sapl/relatorios/templates/pdf_detalhe_materia_preparar_pysc.py
index ebe3be006..19d6271bc 100644
--- a/sapl/relatorios/templates/pdf_detalhe_materia_preparar_pysc.py
+++ b/sapl/relatorios/templates/pdf_detalhe_materia_preparar_pysc.py
@@ -279,6 +279,6 @@ caminho = context.pdf_detalhe_materia_gerar(imagem, rodape, inf_basicas_dic, ori
lst_des_iniciais, dic_tramitacoes, lst_relatorias, lst_numeracoes,
lst_legis_citadas, lst_acessorios, sessao=session.id)
if caminho == 'aviso':
- return response.redirect('mensagem_emitir_proc')
+ response.redirect('mensagem_emitir_proc')
else:
response.redirect(caminho)
diff --git a/sapl/relatorios/templates/pdf_documento_administrativo_preparar_pysc.py b/sapl/relatorios/templates/pdf_documento_administrativo_preparar_pysc.py
index cbaf7d0e3..775ad3d68 100755
--- a/sapl/relatorios/templates/pdf_documento_administrativo_preparar_pysc.py
+++ b/sapl/relatorios/templates/pdf_documento_administrativo_preparar_pysc.py
@@ -130,6 +130,6 @@ sessao = session.id
caminho = context.pdf_documento_administrativo_gerar(
sessao, imagem, data, documentos, cabecalho, rodape, filtro)
if caminho == 'aviso':
- return response.redirect('mensagem_emitir_proc')
+ response.redirect('mensagem_emitir_proc')
else:
response.redirect(caminho)
diff --git a/sapl/relatorios/templates/pdf_espelho_preparar_pysc.py b/sapl/relatorios/templates/pdf_espelho_preparar_pysc.py
index e51fcd43c..43fae0f7c 100644
--- a/sapl/relatorios/templates/pdf_espelho_preparar_pysc.py
+++ b/sapl/relatorios/templates/pdf_espelho_preparar_pysc.py
@@ -205,6 +205,6 @@ sessao = session.id
caminho = context.pdf_espelho_gerar(
sessao, imagem, data, materias, cabecalho, rodape, filtro)
if caminho == 'aviso':
- return response.redirect('mensagem_emitir_proc')
+ response.redirect('mensagem_emitir_proc')
else:
response.redirect(caminho)
diff --git a/sapl/relatorios/templates/pdf_etiqueta_protocolo_gerar.py b/sapl/relatorios/templates/pdf_etiqueta_protocolo_gerar.py
index 38057791f..8b9c3b2cc 100755
--- a/sapl/relatorios/templates/pdf_etiqueta_protocolo_gerar.py
+++ b/sapl/relatorios/templates/pdf_etiqueta_protocolo_gerar.py
@@ -98,7 +98,11 @@ def protocolos(lst_protocolos, dic_cabecalho):
tmp_data += '\t\t
-
+
| Natureza da Propositura | +Quantidade | +
|---|---|
| {{i.0}} | {{i.1}} | +
| QUADRO GERAL | ||||
|---|---|---|---|---|
| Matéria | Ementa | -Autor(es) | +Autor | +Coautor(es) | {{materia.ementa}} |
{% for autor in materia.autoria_set.all %}
+ {% if autor.primeiro_autor %}
{{autor.autor}} + {% endif %} + {% endfor %} + |
+
+ {% for autor in materia.autoria_set.all %}
+ {% if not autor.primeiro_autor %}
+ {{autor.autor}} + {% endif %} {% endfor %} |
diff --git a/sapl/templates/materia/layouts.yaml b/sapl/templates/materia/layouts.yaml
index d31817b94..7c0c17e4e 100644
--- a/sapl/templates/materia/layouts.yaml
+++ b/sapl/templates/materia/layouts.yaml
@@ -100,7 +100,7 @@ Proposicao:
StatusTramitacao:
{% trans 'Status Tramitação' %}:
- - indicador:3 sigla:2 descricao
+ - sigla:2 descricao:6 indicador:4
UnidadeTramitacao:
{% trans 'Unidade Tramitação' %}:
diff --git a/sapl/templates/materia/materialegislativa_filter.html b/sapl/templates/materia/materialegislativa_filter.html
index f44d8788b..b10c20346 100644
--- a/sapl/templates/materia/materialegislativa_filter.html
+++ b/sapl/templates/materia/materialegislativa_filter.html
@@ -4,9 +4,10 @@
{% block actions %}
Autor | {{ protocolo.autor }} | +<<<<<<< HEAD {% else %}
| Assunto | @@ -79,6 +80,17 @@Interessado | {{ protocolo.interessado }} | ||
| Assunto | +{{ protocolo.assunto_ementa }} | +|||
| Interessado | +{{ protocolo.interessado }} | +|||
| Natureza | diff --git a/sapl/templates/protocoloadm/documentoadministrativo_filter.html b/sapl/templates/protocoloadm/documentoadministrativo_filter.html index b15726772..83eb1bd40 100644 --- a/sapl/templates/protocoloadm/documentoadministrativo_filter.html +++ b/sapl/templates/protocoloadm/documentoadministrativo_filter.html @@ -37,11 +37,26 @@||||
| {{d.tipo.sigla}} {{d.numero}}/{{d.ano}} - {{d.tipo}} - Interessado: {{ d.interessado|default_if_none:"Não informado"}} - Assunto: {{ d.assunto|safe }} + Interessado: {{ d.interessado|default_if_none:"Não informado"}} + + Assunto: {{ d.assunto|safe }} + {% if d.protocolo %} Protocolo: {{ d.protocolo}} {% endif %} + {% if d.tramitacaoadministrativo_set.last.unidade_tramitacao_destino %} + Localização Atual: {{d.tramitacaoadministrativo_set.last.unidade_tramitacao_destino}} + + Status: {{d.tramitacaoadministrativo_set.last.status}} + + {% endif %} + {% if d.documentoacessorioadministrativo_set.all.exists %} + Documentos Acessórios: + + {{ d.documentoacessorioadministrativo_set.all.count }} + + + {% endif %} {% if d.texto_integral %} Texto Integral {% endif %} diff --git a/sapl/templates/protocoloadm/protocolo_mostrar.html b/sapl/templates/protocoloadm/protocolo_mostrar.html index 0e434429b..376c730a1 100644 --- a/sapl/templates/protocoloadm/protocolo_mostrar.html +++ b/sapl/templates/protocoloadm/protocolo_mostrar.html @@ -38,11 +38,10 @@ {{materia}} {% endif %} - {% if not protocolo.anulado %}Criar Matéria{% endif %} + {% if not protocolo.anulado%}{% if not materia %}Criar Matéria {% endif %}{% endif %} {% endif %} - Comprovante {% endblock detail_content %} diff --git a/sapl/utils.py b/sapl/utils.py index b674481c3..a656fa5a0 100644 --- a/sapl/utils.py +++ b/sapl/utils.py @@ -25,6 +25,7 @@ from django_filters.filterset import STRICTNESS from easy_thumbnails import source_generators from floppyforms import ClearableFileInput from reversion.admin import VersionAdmin +from reversion_compare.admin import CompareVersionAdmin from unipath.path import Path from sapl.crispy_layout_mixin import SaplFormLayout, form_actions, to_row @@ -228,7 +229,7 @@ def register_all_models_in_admin(module_name): appname = appname[1] if appname[0] == 'sapl' else appname[0] app = apps.get_app_config(appname) for model in app.get_models(): - class CustomModelAdmin(VersionAdmin): + class CustomModelAdmin(CompareVersionAdmin): list_display = [f.name for f in model._meta.fields if f.name != 'id'] @@ -355,6 +356,7 @@ TIPOS_TEXTO_PERMITIDOS = ( 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/xml', + 'application/octet-stream', 'text/xml', 'text/html', ) diff --git a/setup.py b/setup.py index b76d46c98..758b57fc8 100644 --- a/setup.py +++ b/setup.py @@ -49,7 +49,7 @@ install_requires = [ ] setup( name='interlegis-sapl', - version='3.1.92', + version='3.1.102', packages=find_packages(), include_package_data=True, license='GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007', diff --git a/start.sh b/start.sh index 4790d2df4..9695572ef 100755 --- a/start.sh +++ b/start.sh @@ -49,7 +49,7 @@ create_env # manage.py migrate --noinput nao funcionava yes yes | python3 manage.py migrate #python3 manage.py collectstatic --no-input -python3 manage.py rebuild_index --noinput & +# python3 manage.py rebuild_index --noinput & echo "Criando usuário admin..." | ||||