Browse Source

Inicia a resolução da issue

pull/1510/head
Eduardo Calil 8 years ago
parent
commit
271256087b
  1. 2
      docker-compose.yml
  2. 34
      docs/credits.txt
  3. 15
      sapl/compilacao/models.py
  4. 4
      sapl/decorators.py
  5. 42
      sapl/legacy/migracao_documentos.py
  6. 69
      sapl/legacy/migracao_usuarios.py
  7. 41
      sapl/legacy/migration.py
  8. 13
      sapl/legacy/models.py
  9. 6
      sapl/materia/email_utils.py
  10. 32
      sapl/materia/forms.py
  11. 5
      sapl/materia/models.py
  12. 43
      sapl/materia/views.py
  13. 4
      sapl/norma/forms.py
  14. 11
      sapl/norma/views.py
  15. 1
      sapl/painel/views.py
  16. 6
      sapl/parlamentares/forms.py
  17. 38
      sapl/parlamentares/migrations/0010_corrige_data_inicio_mandato.py
  18. 6
      sapl/parlamentares/models.py
  19. 13
      sapl/parlamentares/views.py
  20. 74
      sapl/protocoloadm/forms.py
  21. 19
      sapl/protocoloadm/migrations/0002_remove_documentoadministrativo_numero_protocolo.py
  22. 22
      sapl/protocoloadm/tests/test_protocoloadm.py
  23. 88
      sapl/protocoloadm/views.py
  24. 1
      sapl/relatorios/templates/pdf_etiqueta_protocolo_gerar.py
  25. 12
      sapl/relatorios/views.py
  26. 4
      sapl/sessao/forms.py
  27. 9
      sapl/sessao/views.py
  28. 2
      sapl/settings.py
  29. 4
      sapl/templates/materia/prop_devolvidas_list.html
  30. 3
      sapl/templates/materia/prop_pendentes_list.html
  31. 3
      sapl/templates/materia/prop_recebidas_list.html
  32. 4
      sapl/templates/materia/proposicao_detail.html
  33. 3
      sapl/templates/materia/recibo_proposicao.html
  34. 2
      sapl/templates/protocoloadm/protocolo_filter.html
  35. 4
      sapl/templates/protocoloadm/protocolo_list.html
  36. 3
      sapl/templates/protocoloadm/protocolo_mostrar.html
  37. 12
      sapl/templates/protocoloadm/tramitacaoadministrativo_form.html
  38. 6
      sapl/utils.py

2
docker-compose.yml

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

34
docs/credits.txt

@ -0,0 +1,34 @@
Créditos do SAPL - até versão 2.5
=================================
Obrigado aos colaboradores:
- Adriano Gomes
- Angelo Marcondes Neto
- Claudio Morale
- Daniel C. Azevedo
- Davi Lima de Medeiros
- Edson Ma
- Fernando Ciciliati Júnior
- Gustavo Lepri
- Halison Casimiro
- Helder Vieira
- Jean Rodrigo Ferri
- João Lima
- José Borges
- Leandro Roberto
- Leonardo Caballero
- Luciano Di Fázio
- Luis Fernando Pires Machado
- Marcio Mazza
- Marcos Fragomeni
- Maria Cristina André de Mello
- Marta Maria Pincowsca Cardoso Maia
- Paulo Fernandes de Souza Júnior
- Petronio Barbosa Carvalho
- Ricardo Esperandio
- Rodrigo Barbosa Luz
- Sesóstris Vieira
- Sérgio Damiati
- Wilton Souza Alencar
- Wu Man Qi

15
sapl/compilacao/models.py

@ -1,4 +1,3 @@
from datetime import datetime
import reversion
from django.contrib import messages
@ -10,6 +9,7 @@ from django.db.models.aggregates import Max
from django.db.models.deletion import PROTECT
from django.http.response import Http404
from django.template import defaultfilters
from django.utils import timezone
from django.utils.decorators import classonlymethod
from django.utils.encoding import force_text
from django.utils.translation import ugettext_lazy as _
@ -357,9 +357,9 @@ class TextoArticulado(TimestampedMixin):
if not ta.data:
ta.data = getattr(obj, map_fields['data']
if map_fields['data'] else 'xxx',
datetime.now())
timezone.now())
if not ta.data:
ta.data = datetime.now()
ta.data = timezone.now()
ta.ementa = getattr(
obj, map_fields['ementa']
@ -370,15 +370,16 @@ class TextoArticulado(TimestampedMixin):
obj, map_fields['observacao']
if map_fields['observacao'] else 'xxx', '')
now = timezone.now()
ta.numero = getattr(
obj, map_fields['numero']
if map_fields['numero'] else 'xxx', int('%s%s%s' % (
int(datetime.now().year),
int(datetime.now().month),
int(datetime.now().day))))
int(now.year),
int(now.month),
int(now.day))))
ta.ano = getattr(obj, map_fields['ano']
if map_fields['ano'] else 'xxx', datetime.now().year)
if map_fields['ano'] else 'xxx', now.year)
ta.save()
return ta

4
sapl/decorators.py

@ -1,6 +1,6 @@
from datetime import date
from functools import wraps
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
@ -18,7 +18,7 @@ def vigencia_atual(decorated_method):
string_displayed = decorated_method(self)
if hasattr(self, 'data_inicio') and hasattr(self, 'data_fim'):
today = date.today()
today = timezone.now().today().date()
e_atual = self.data_inicio <= today <= self.data_fim
string_displayed = "{} {}".format(
string_displayed, "(Atual)" if e_atual else "")

42
sapl/legacy/migracao_documentos.py

@ -5,6 +5,7 @@ import re
import magic
from sapl.base.models import CasaLegislativa
from sapl.legacy.migration import warn
from sapl.materia.models import (DocumentoAcessorio, MateriaLegislativa,
Proposicao)
from sapl.norma.models import NormaJuridica
@ -21,6 +22,7 @@ EXTENSOES = {
'application/vnd.oasis.opendocument.text': '.odt',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document': '.docx', # noqa
'application/xml': '.xml',
'text/xml': '.xml',
'application/zip': '.zip',
'image/jpeg': '.jpeg',
'image/png': '.png',
@ -90,11 +92,11 @@ DOCS = {
],
}
DOCS = {tipo: [(campo,
DOCS = {model: [(campo,
os.path.join('sapl_documentos', origem),
os.path.join('sapl', destino))
for campo, origem, destino in campos]
for tipo, campos in DOCS.items()}
for model, campos in DOCS.items()}
def em_media(caminho):
@ -125,10 +127,14 @@ def migrar_docs_logo():
# a pasta props_sapl deve conter apenas o origem e metadatas!
# Edit: Aparentemente há diretório que contém properties ao invés de
# metadata. O assert foi modificado para essa situação.
assert set(os.listdir(em_media(props_sapl))) < {
sobrando = set(os.listdir(em_media(props_sapl))) - {
'logo_casa.gif', '.metadata', 'logo_casa.gif.metadata',
'.properties', 'logo_casa.gif.properties', '.objects'}
if sobrando:
warn('Os seguintes arquivos da pasta props_sapl foram ignorados: ' +
', '.join(sobrando))
mover_documento(origem, destino)
casa = get_casa_legislativa()
casa.logotipo = destino
@ -152,9 +158,9 @@ def get_extensao(caminho):
)) from e
def migrar_docs_por_ids(tipo):
for campo, base_origem, base_destino in DOCS[tipo]:
print('#### Migrando {} de {} ####'.format(campo, tipo.__name__))
def migrar_docs_por_ids(model):
for campo, base_origem, base_destino in DOCS[model]:
print('#### Migrando {} de {} ####'.format(campo, model.__name__))
dir_origem, nome_origem = os.path.split(em_media(base_origem))
pat = re.compile('^{}$'.format(nome_origem.format('(\d+)')))
@ -168,11 +174,15 @@ def migrar_docs_por_ids(tipo):
match = pat.match(arq)
if match:
# associa documento ao objeto
try:
origem = os.path.join(dir_origem, match.group(0))
id = match.group(1)
obj = tipo.objects.get(pk=id)
try:
obj = model.objects.get(pk=id)
except model.DoesNotExist:
msg = ' {} (pk={}) não encontrado para documento em [{}]'
print(msg.format(
model.__name__, id, origem))
else:
extensao = get_extensao(origem)
if hasattr(obj, "ano"):
destino = base_destino.format(id, extensao, obj.ano)
@ -185,19 +195,19 @@ def migrar_docs_por_ids(tipo):
setattr(obj, campo, destino)
obj.save()
except tipo.DoesNotExist:
msg = ' {} (pk={}) não encontrado para documento em [{}]'
print(msg.format(
tipo.__name__, id, destino))
def migrar_documentos():
# aqui supomos que uma pasta chamada sapl_documentos está em MEDIA_ROOT
# 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á
# apagada
#
# Isto significa que para rodar novamente esta função é preciso
# restaurar a pasta sapl_documentos ao estado inicial
migrar_docs_logo()
for tipo in [
for model in [
Parlamentar,
MateriaLegislativa,
DocumentoAcessorio,
@ -207,7 +217,7 @@ def migrar_documentos():
DocumentoAdministrativo,
DocumentoAcessorioAdministrativo,
]:
migrar_docs_por_ids(tipo)
migrar_docs_por_ids(model)
sobrando = [os.path.join(dir, file)
for (dir, _, files) in os.walk(em_media('sapl_documentos'))

69
sapl/legacy/migracao_usuarios.py

@ -0,0 +1,69 @@
import yaml
from django.conf import settings
from django.contrib.auth.models import Group, User
def le_yaml_dados_zope(caminho_yaml):
with open(caminho_yaml, 'r') as f:
dados = yaml.load(f.read())
return dados
PERFIL_LEGADO_PARA_NOVO = [
('Autor', 'Autor'),
('Operador', 'Operador Geral'),
('Operador Comissao', 'Operador de Comissões'),
('Operador Materia', 'Operador de Matéria'),
('Operador Modulo Administrativo', 'Operador Administrativo'),
('Operador Norma', 'Operador de Norma Jurídica'),
('Operador Parlamentar', 'Parlamentar'),
('Operador Protocolo', 'Operador de Protocolo Administrativo'),
('Operador Sessao Plenaria', 'Operador de Sessão Plenária'),
]
ADMINISTRADORES = ['Administrador', 'Manager']
VOTANTE = Group.objects.get(name='Votante')
def migra_usuarios(caminho_yaml):
"""
Existe um método em nosso projeto interno de **consulta a todos os sapls**
que exporta os dados de usuários (e nome da casa e url interna)
como um yaml.
Esse yaml é lido por essa rotina e os usuários são criados se necessário
e seus perfis ajustados.
Os seguintes perfis no legado não correspondem a nenhum no código atual
e estão sendo **ignorados**:
* Operador Mesa Diretora
Contei apenas **8 usuários**, em todas as bases, que tem esse perfil
e não tem nem "Operador" nem "Operador Sessao Plenaria"
* Operador Ordem Dia
Contei apenas **16 usuários**, em todas as bases, que tem esse perfil
e não tem nem "Operador" nem "Operador Sessao Plenaria"
* Operador Tabela Auxiliar
A edição das tabelas auxiliares deve ser feita por um administrador
* Operador Lexml
Também podemos assumir que essa é uma tarefa de um administrador
"""
dados = le_yaml_dados_zope(caminho_yaml)
db = settings.DATABASES['legacy']['NAME']
nome, url, usuarios_perfis = dados[db]
for nome, perfis in usuarios_perfis:
usuario, _ = User.objects.get_or_create(username=nome)
for legado, novo in PERFIL_LEGADO_PARA_NOVO:
if legado in perfis:
grupo = Group.objects.get(name=novo)
usuario.groups.add(grupo)
# Manager
if any(a in perfis for a in ADMINISTRADORES):
usuario.is_staff = True
usuario.save()
# Votante
if 'Parlamentar' in perfis:
usuario.groups.add(VOTANTE)

41
sapl/legacy/migration.py

@ -16,8 +16,10 @@ from django.db import connections, transaction
from django.db.models import Count, Max
from django.db.models.base import ModelBase
from sapl.base.models import AppConfig as AppConf
from sapl.base.models import Autor, ProblemaMigracao, TipoAutor
from sapl.comissoes.models import Comissao, Composicao, Participacao
from sapl.legacy.models import TipoNumeracaoProtocolo
from sapl.materia.models import (AcompanhamentoMateria, Proposicao,
StatusTramitacao, TipoDocumento,
TipoMateriaLegislativa, TipoProposicao,
@ -206,6 +208,22 @@ def fill_vinculo_norma_juridica():
TipoVinculoNormaJuridica.objects.bulk_create(lista_objs)
def fill_tipo_numeracao_protocolo():
letra = 'A'
try:
tipo = TipoNumeracaoProtocolo.objects.latest('dat_inicial_protocolo')
if 'POR ANO' in tipo.des_numeracao_protocolo:
letra = 'A'
elif 'POR LEGISLATURA' in tipo.des_numeracao_protocolo:
letra = 'L'
elif 'CONSECUTIVO' in tipo.des_numeracao_protocolo:
letra = 'U'
except Exception as e:
pass
appconf = AppConf(sequencia_numeracao=letra)
appconf.save()
# Uma anomalia no sapl 2.5 causa a duplicação de registros de votação.
# Essa duplicação deve ser eliminada para que não haja erro no sapl 3.1
def excluir_registrovotacao_duplicados():
@ -234,6 +252,21 @@ def excluir_registrovotacao_duplicados():
assert 0
def delete_old(legacy_model, cols_values):
def eq_clause(col, value):
if value is None:
return '{} IS NULL'.format(col)
else:
return '{}="{}"'.format(col, value)
delete_sql = 'delete from {} where {}'.format(
legacy_model._meta.db_table,
' and '.join([eq_clause(col, value)
for col, value in cols_values.items()]))
exec_sql(delete_sql, 'legacy')
class DataMigrator:
def __init__(self):
@ -305,6 +338,7 @@ class DataMigrator:
'--database=default', '--no-input'], stdout=PIPE)
fill_vinculo_norma_juridica()
fill_tipo_numeracao_protocolo()
info('Começando migração: %s...' % obj)
self._do_migrate(obj)
@ -345,6 +379,10 @@ class DataMigrator:
with reversion.create_revision():
new.save()
reversion.set_comment('Objeto criado pela migração')
# apaga registro do legado
delete_old(legacy_model, old.__dict__)
old_records = iter_sql_records(
'select * from ' + legacy_model._meta.db_table, 'legacy')
else:
@ -355,6 +393,9 @@ class DataMigrator:
new.save()
reversion.set_comment('Objeto criado pela migração')
# apaga registro do legado
delete_old(legacy_model, {legacy_pk_name: new.id})
old_records = legacy_model.objects.all().order_by(legacy_pk_name)
ajuste_antes_salvar = AJUSTE_ANTES_SALVAR.get(model)

13
sapl/legacy/models.py

@ -968,6 +968,19 @@ class TipoNormaJuridica(models.Model):
db_table = 'tipo_norma_juridica'
class TipoNumeracaoProtocolo(models.Model):
seq_tip_num_protocolo = models.AutoField(primary_key=True)
tip_numeracao_protocolo = models.IntegerField()
des_numeracao_protocolo = models.CharField(max_length=50)
dat_inicial_protocolo = models.DateTimeField()
vlr_inicial_protocolo = models.IntegerField()
ind_excluido = models.IntegerField()
class Meta:
managed = False
db_table = 'tipo_numeracao_protocolo'
class TipoProposicao(models.Model):
tip_proposicao = models.AutoField(primary_key=True)
des_tipo_proposicao = models.CharField(max_length=50)

6
sapl/materia/email_utils.py

@ -1,8 +1,9 @@
from datetime import datetime
from datetime import datetime as dt
from django.core.mail import EmailMultiAlternatives, get_connection, send_mail
from django.core.urlresolvers import reverse
from django.template import Context, loader
from django.utils import timezone
from sapl.base.models import CasaLegislativa
from sapl.settings import EMAIL_SEND_USER
@ -150,7 +151,8 @@ def criar_email_tramitacao(base_url, casa_legislativa, materia, status,
templates = load_email_templates(['email/tramitacao.txt',
'email/tramitacao.html'],
{"casa_legislativa": casa_nome,
"data_registro": datetime.now().strftime(
"data_registro": dt.strftime(
timezone.now(),
"%d/%m/%Y"),
"cod_materia": materia.id,
"logotipo": casa_legislativa.logotipo,

32
sapl/materia/forms.py

@ -1,6 +1,5 @@
import os
from datetime import date, datetime
import django_filters
from crispy_forms.bootstrap import Alert, FormActions, InlineRadios
@ -18,6 +17,7 @@ from django.forms import ModelChoiceField, ModelForm, widgets
from django.forms.forms import Form
from django.forms.models import ModelMultipleChoiceField
from django.forms.widgets import CheckboxSelectMultiple, HiddenInput, Select
from django.utils import timezone
from django.utils.encoding import force_text
from django.utils.html import format_html
from django.utils.safestring import mark_safe
@ -215,7 +215,7 @@ class TramitacaoForm(ModelForm):
def __init__(self, *args, **kwargs):
super(TramitacaoForm, self).__init__(*args, **kwargs)
self.fields['data_tramitacao'].initial = datetime.now().date()
self.fields['data_tramitacao'].initial = timezone.now().date()
def clean(self):
cleaned_data = super(TramitacaoForm, self).clean()
@ -245,7 +245,7 @@ class TramitacaoForm(ModelForm):
'destino da última adicionada!')
raise ValidationError(msg)
if cleaned_data['data_tramitacao'] > datetime.now().date():
if cleaned_data['data_tramitacao'] > timezone.now().date():
msg = _(
'A data de tramitação deve ser ' +
'menor ou igual a data de hoje!')
@ -1164,10 +1164,10 @@ class ProposicaoForm(forms.ModelForm):
return super().save(commit)
inst.ano = datetime.now().year
inst.ano = timezone.now().year
numero__max = Proposicao.objects.filter(
autor=inst.autor,
ano=datetime.now().year).aggregate(Max('numero_proposicao'))
ano=timezone.now().year).aggregate(Max('numero_proposicao'))
numero__max = numero__max['numero_proposicao__max']
inst.numero_proposicao = (
numero__max + 1) if numero__max else 1
@ -1376,7 +1376,7 @@ class ConfirmarProposicaoForm(ProposicaoForm):
cd = self.cleaned_data
if 'devolver' in self.data:
self.instance.data_devolucao = datetime.now()
self.instance.data_devolucao = timezone.now()
self.instance.data_recebimento = None
self.instance.data_envio = None
self.instance.save()
@ -1398,7 +1398,7 @@ class ConfirmarProposicaoForm(ProposicaoForm):
elif 'incorporar' in self.data:
self.instance.justificativa_devolucao = ''
self.instance.data_devolucao = None
self.instance.data_recebimento = datetime.now()
self.instance.data_recebimento = timezone.now()
self.instance.materia_de_vinculo = cd['materia_de_vinculo']
if self.instance.texto_articulado.exists():
@ -1435,7 +1435,7 @@ class ConfirmarProposicaoForm(ProposicaoForm):
) == TipoMateriaLegislativa:
numero__max = MateriaLegislativa.objects.filter(
tipo=proposicao.tipo.tipo_conteudo_related,
ano=datetime.now().year).aggregate(Max('numero'))
ano=timezone.now().year).aggregate(Max('numero'))
numero__max = numero__max['numero__max']
# dados básicos
@ -1443,8 +1443,8 @@ class ConfirmarProposicaoForm(ProposicaoForm):
materia.numero = (numero__max + 1) if numero__max else 1
materia.tipo = proposicao.tipo.tipo_conteudo_related
materia.ementa = proposicao.descricao
materia.ano = datetime.now().year
materia.data_apresentacao = datetime.now()
materia.ano = timezone.now().year
materia.data_apresentacao = timezone.now()
materia.em_tramitacao = True
materia.regime_tramitacao = cd['regime_tramitacao']
@ -1483,7 +1483,7 @@ class ConfirmarProposicaoForm(ProposicaoForm):
anexada = Anexada()
anexada.materia_principal = proposicao.materia_de_vinculo
anexada.materia_anexada = materia
anexada.data_anexacao = datetime.now()
anexada.data_anexacao = timezone.now()
anexada.save()
self.instance.results['messages']['success'].append(_(
@ -1555,18 +1555,18 @@ class ConfirmarProposicaoForm(ProposicaoForm):
numeracao = sapl.base.models.AppConfig.attr('sequencia_numeracao')
if numeracao == 'A':
nm = Protocolo.objects.filter(
ano=date.today().year).aggregate(Max('numero'))
ano=timezone.now().year).aggregate(Max('numero'))
elif numeracao == 'U':
nm = Protocolo.objects.all().aggregate(Max('numero'))
protocolo = Protocolo()
protocolo.numero = (nm['numero__max'] + 1) if nm['numero__max'] else 1
protocolo.ano = date.today().year
protocolo.data = date.today()
protocolo.hora = datetime.now().time()
protocolo.ano = timezone.now().year
protocolo.data = timezone.now()
protocolo.hora = timezone.now().time()
# TODO transformar campo timestamp em auto_now_add
protocolo.timestamp = datetime.now()
protocolo.timestamp = timezone.now()
protocolo.tipo_protocolo = '1'
protocolo.interessado = str(proposicao.autor)

5
sapl/materia/models.py

@ -1,4 +1,3 @@
from datetime import datetime
import reversion
from django.contrib.auth.models import Group
@ -6,7 +5,7 @@ from django.contrib.contenttypes.fields import GenericRelation
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist
from django.db import models
from django.utils import formats
from django.utils import formats, timezone
from django.utils.translation import ugettext_lazy as _
from model_utils import Choices
@ -711,7 +710,7 @@ class Proposicao(models.Model):
def title_type(self):
return '%s nº _____ %s' % (
self.tipo, formats.date_format(
self.data_envio if self.data_envio else datetime.now(),
self.data_envio if self.data_envio else timezone.now(),
"\d\e d \d\e F \d\e Y"))
class Meta:

43
sapl/materia/views.py

@ -14,7 +14,7 @@ from django.http import HttpResponse, JsonResponse
from django.http.response import Http404, HttpResponseRedirect
from django.shortcuts import get_object_or_404, redirect
from django.template import RequestContext, loader
from django.utils import formats
from django.utils import formats, timezone
from django.utils.translation import ugettext_lazy as _
from django.views.generic import CreateView, ListView, TemplateView, UpdateView
from django.views.generic.base import RedirectView
@ -284,7 +284,7 @@ def recuperar_materia(request):
ano = request.GET.get('ano', '')
param = {'tipo': tipo}
param['data_apresentacao__year'] = ano if ano else datetime.now().year
param['data_apresentacao__year'] = ano if ano else timezone.now().year
materia = MateriaLegislativa.objects.filter(**param).order_by(
'tipo', 'ano', 'numero').values_list('numero', 'ano').last()
@ -293,7 +293,7 @@ def recuperar_materia(request):
'ano': materia[1]})
else:
response = JsonResponse(
{'numero': 1, 'ano': ano if ano else datetime.now().year})
{'numero': 1, 'ano': ano if ano else timezone.now().year})
return response
@ -323,14 +323,14 @@ def criar_materia_proposicao(proposicao):
tipo_materia = TipoMateriaLegislativa.objects.get(
descricao=proposicao.tipo.descricao)
numero = MateriaLegislativa.objects.filter(
ano=datetime.now().year).order_by('numero').last().numero + 1
ano=timezone.now().year).order_by('numero').last().numero + 1
regime = RegimeTramitacao.objects.get(descricao='Normal')
return MateriaLegislativa.objects.create(
tipo=tipo_materia,
ano=datetime.now().year,
ano=timezone.now().year,
numero=numero,
data_apresentacao=datetime.now(),
data_apresentacao=timezone.now(),
regime_tramitacao=regime,
em_tramitacao=True,
ementa=proposicao.descricao,
@ -616,7 +616,7 @@ class ProposicaoCrud(Crud):
'Texto associado.')
else:
p.data_devolucao = None
p.data_envio = datetime.now()
p.data_envio = timezone.now()
p.save()
if p.texto_articulado.exists():
@ -940,7 +940,7 @@ class TramitacaoCrud(MasterDetailCrud):
] = local.unidade_tramitacao_destino.pk
else:
self.initial['unidade_tramitacao_local'] = ''
self.initial['data_tramitacao'] = datetime.now()
self.initial['data_tramitacao'] = timezone.now()
return self.initial
def get_context_data(self, **kwargs):
@ -1023,7 +1023,11 @@ class TramitacaoCrud(MasterDetailCrud):
url = reverse('sapl.materia:tramitacao_list',
kwargs={'pk': tramitacao.materia.id})
if tramitacao.pk != materia.tramitacao_set.last().pk:
ultima_tramitacao = materia.tramitacao_set.order_by(
'-data_tramitacao',
'-id').first()
if tramitacao.pk != ultima_tramitacao.pk:
msg = _('Somente a última tramitação pode ser deletada!')
messages.add_message(request, messages.ERROR, msg)
return HttpResponseRedirect(url)
@ -1067,7 +1071,7 @@ class DocumentoAcessorioCrud(MasterDetailCrud):
super(MasterDetailCrud.CreateView, self).__init__(**kwargs)
def get_initial(self):
self.initial['data'] = datetime.now().date()
self.initial['data'] = timezone.now().date()
return self.initial
@ -1629,13 +1633,16 @@ class DocumentoAcessorioEmLoteView(PermissionRequiredMixin, FilterView):
tipo = TipoDocumento.objects.get(descricao=request.POST['tipo'])
tz = timezone.get_current_timezone()
for materia_id in marcadas:
doc = DocumentoAcessorio()
doc.materia_id = materia_id
doc.tipo = tipo
doc.arquivo = request.FILES['arquivo']
doc.nome = request.POST['nome']
doc.data = datetime.strptime(request.POST['data'], "%d/%m/%Y")
doc.data = tz.localize(datetime.strptime(
request.POST['data'], "%d/%m/%Y"))
doc.autor = request.POST['autor']
doc.ementa = request.POST['ementa']
doc.save()
@ -1692,20 +1699,22 @@ class PrimeiraTramitacaoEmLoteView(PermissionRequiredMixin, FilterView):
def post(self, request, *args, **kwargs):
marcadas = request.POST.getlist('materia_id')
tz = timezone.get_current_timezone()
if len(marcadas) == 0:
msg = _('Nenhuma máteria foi selecionada.')
messages.add_message(request, messages.ERROR, msg)
return self.get(request, self.kwargs)
if request.POST['data_encaminhamento']:
data_encaminhamento = datetime.strptime(
request.POST['data_encaminhamento'], "%d/%m/%Y")
data_encaminhamento = tz.localize(datetime.strptime(
request.POST['data_encaminhamento'], "%d/%m/%Y"))
else:
data_encaminhamento = None
if request.POST['data_fim_prazo']:
data_fim_prazo = datetime.strptime(
request.POST['data_fim_prazo'], "%d/%m/%Y")
data_fim_prazo = tz.localize(datetime.strptime(
request.POST['data_fim_prazo'], "%d/%m/%Y"))
else:
data_fim_prazo = None
@ -1716,8 +1725,8 @@ class PrimeiraTramitacaoEmLoteView(PermissionRequiredMixin, FilterView):
for materia_id in marcadas:
t = Tramitacao(
materia_id=materia_id,
data_tramitacao=datetime.strptime(
request.POST['data_tramitacao'], "%d/%m/%Y"),
data_tramitacao=tz.localize(datetime.strptime(
request.POST['data_tramitacao'], "%d/%m/%Y")),
data_encaminhamento=data_encaminhamento,
data_fim_prazo=data_fim_prazo,
unidade_tramitacao_local_id=request.POST[

4
sapl/norma/forms.py

@ -1,4 +1,3 @@
from datetime import datetime
import django_filters
from crispy_forms.helper import FormHelper
@ -7,6 +6,7 @@ from django import forms
from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.db import models
from django.forms import ModelForm, widgets
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from sapl.crispy_layout_mixin import form_actions, to_row
@ -154,7 +154,7 @@ class NormaJuridicaForm(ModelForm):
def save(self, commit=False):
norma = self.instance
norma.timestamp = datetime.now()
norma.timestamp = timezone.now()
norma.materia = self.cleaned_data['materia']
norma = super(NormaJuridicaForm, self).save(commit=True)
return norma

11
sapl/norma/views.py

@ -1,8 +1,8 @@
from datetime import datetime
from django.core.exceptions import ObjectDoesNotExist
from django.core.urlresolvers import reverse
from django.http import JsonResponse
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from django.views.generic.base import RedirectView
from django_filters.views import FilterView
@ -64,6 +64,13 @@ class NormaPesquisaView(FilterView):
filterset_class = NormaFilterSet
paginate_by = 10
def get_queryset(self):
qs = super().get_queryset()
qs.select_related('tipo', 'materia')
return qs
def get_context_data(self, **kwargs):
context = super(NormaPesquisaView, self).get_context_data(**kwargs)
@ -197,7 +204,7 @@ def recuperar_numero_norma(request):
ano = request.GET.get('ano', '')
param = {'tipo': tipo}
param['ano'] = ano if ano else datetime.now().year
param['ano'] = ano if ano else timezone.now().year
norma = NormaJuridica.objects.filter(**param).order_by(
'tipo', 'ano', 'numero').values_list('numero', 'ano').last()

1
sapl/painel/views.py

@ -1,5 +1,4 @@
from django.contrib import messages
from django.contrib.auth.decorators import user_passes_test
from django.core.exceptions import ObjectDoesNotExist

6
sapl/parlamentares/forms.py

@ -1,4 +1,4 @@
from datetime import date, timedelta
from datetime import timedelta
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Fieldset, Layout
@ -9,6 +9,7 @@ from django.core.exceptions import ValidationError
from django.db import transaction
from django.db.models import Q
from django.forms import ModelForm
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from floppyforms.widgets import ClearableFileInput
@ -168,7 +169,8 @@ def validar_datas(data_filiacao, data_desfiliacao, parlamentar, filiacao):
return [True, '']
# data ficticia de desfiliacao
df_desfiliacao = data_desfiliacao if data_desfiliacao else date.today()
today = timezone.now()
df_desfiliacao = data_desfiliacao if data_desfiliacao else today
# se não puder haver filiação no mesmo dia de desfiliação, basta
# retirar os timedelta abaixo

38
sapl/parlamentares/migrations/0010_corrige_data_inicio_mandato.py

@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations
import json
import os
from datetime import timedelta
from django.core.management import call_command
def altera_data_inicio_mandato(apps, schema_editor):
Mandato = apps.get_model("parlamentares", "Mandato")
mandatos = Mandato.objects.all()
for mandato in mandatos:
data_inicio = mandato.data_inicio_mandato
data_inicio_legislatura = mandato.legislatura.data_inicio
days = abs((data_inicio - data_inicio_legislatura).days)
if days >= 60:
mandato.data_inicio_mandato = data_inicio_legislatura
mandato.save()
class Migration(migrations.Migration):
dependencies = [
# A dependencia real desse script é o arquivo 0001_initial.py, mas
# isso gera um erro (Conflicting migrations detected; multiple leaf
# nodes in the migration graph). para não ocasionar problemas de migração,
# vamos manter a ordem padrão do django.
('parlamentares', '0009_auto_20170905_1617'),
]
operations = [
migrations.RunPython(altera_data_inicio_mandato),
]

6
sapl/parlamentares/models.py

@ -1,7 +1,7 @@
from datetime import datetime
import reversion
from django.db import models
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from model_utils import Choices
@ -26,7 +26,7 @@ class Legislatura(models.Model):
verbose_name_plural = _('Legislaturas')
def atual(self):
current_year = datetime.now().year
current_year = timezone.now().year
return self.data_inicio.year <= current_year <= self.data_fim.year
@vigencia_atual
@ -489,7 +489,7 @@ class Mandato(models.Model):
self.legislatura.data_inicio,
self.legislatura.data_fim,
f.data,
f.data_desfiliacao or datetime.max.date())]
f.data_desfiliacao or timezone.datetime.max.date())]
@reversion.register()

13
sapl/parlamentares/views.py

@ -10,6 +10,7 @@ from django.db.models.aggregates import Count
from django.http import JsonResponse
from django.http.response import HttpResponseRedirect
from django.templatetags.static import static
from django.utils import timezone
from django.utils.datastructures import MultiValueDictKeyError
from django.utils.translation import ugettext_lazy as _
from django.views.decorators.clickjacking import xframe_options_exempt
@ -226,10 +227,8 @@ def json_date_convert(date):
string "dd/mm/yyyy"
:return:
'''
dia, mes, ano = date.split('/')
return datetime.date(day=int(dia),
month=int(mes),
year=int(ano))
return datetime.strptime(date, "%d/%m/%Y").date()
def frente_atualiza_lista_parlamentares(request):
@ -660,7 +659,7 @@ class MesaDiretoraView(FormView):
sessoes = SessaoLegislativa.objects.filter(
legislatura=legislatura).order_by("data_inicio")
year = datetime.now().year
year = timezone.now().year
sessao_atual = sessoes.filter(data_inicio__year=year).first()
mesa = sessao_atual.composicaomesa_set.all() if sessao_atual else []
@ -715,7 +714,7 @@ def altera_field_mesa(request):
# Caso a mudança tenha sido no campo legislatura, a sessão
# atual deve ser a primeira daquela legislatura
else:
year = datetime.now().year
year = timezone.now().year
try:
sessao_selecionada = sessoes.get(data_inicio__year=year).id
except ObjectDoesNotExist:
@ -883,7 +882,7 @@ def altera_field_mesa_public_view(request):
# atual deve ser a primeira daquela legislatura
else:
try:
year = datetime.now().year
year = timezone.now().year
sessao_selecionada = sessoes.get(data_inicio__year=year).id
except ObjectDoesNotExist as e:
sessao_selecionada = sessoes.first().id

74
sapl/protocoloadm/forms.py

@ -1,13 +1,14 @@
from datetime import datetime
import django_filters
from crispy_forms.bootstrap import InlineRadios
from crispy_forms.helper import FormHelper
from crispy_forms.layout import HTML, Button, Fieldset, Layout
from django import forms
from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.core.exceptions import (MultipleObjectsReturned,
ObjectDoesNotExist, ValidationError)
from django.db import models
from django.forms import ModelForm
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from sapl.base.models import Autor, TipoAutor
@ -441,28 +442,26 @@ class TramitacaoAdmForm(ModelForm):
'texto',
]
widgets = {
'data_tramitacao': forms.DateInput(format='%d/%m/%Y'),
'data_encaminhamento': forms.DateInput(format='%d/%m/%Y'),
'data_fim_prazo': forms.DateInput(format='%d/%m/%Y'),
}
def clean(self):
super(TramitacaoAdmForm, self).clean()
cleaned_data = super(TramitacaoAdmForm, self).clean()
data_enc_form = self.cleaned_data['data_encaminhamento']
data_prazo_form = self.cleaned_data['data_fim_prazo']
data_tram_form = self.cleaned_data['data_tramitacao']
if 'data_encaminhamento' in cleaned_data:
data_enc_form = cleaned_data['data_encaminhamento']
if 'data_fim_prazo' in cleaned_data:
data_prazo_form = cleaned_data['data_fim_prazo']
if 'data_tramitacao' in cleaned_data:
data_tram_form = cleaned_data['data_tramitacao']
if self.errors:
return self.errors
if not self.is_valid():
return cleaned_data
ultima_tramitacao = TramitacaoAdministrativo.objects.filter(
documento_id=self.instance.documento_id).exclude(
id=self.instance.id).last()
id=self.instance.id).order_by(
'-data_tramitacao',
'-id').first()
if not self.instance.data_tramitacao:
if ultima_tramitacao:
destino = ultima_tramitacao.unidade_tramitacao_destino
if (destino != self.cleaned_data['unidade_tramitacao_local']):
@ -470,7 +469,7 @@ class TramitacaoAdmForm(ModelForm):
'destino da última adicionada!')
raise ValidationError(msg)
if self.cleaned_data['data_tramitacao'] > datetime.now().date():
if self.cleaned_data['data_tramitacao'] > timezone.now().date():
msg = _(
'A data de tramitação deve ser ' +
'menor ou igual a data de hoje!')
@ -516,25 +515,33 @@ class TramitacaoAdmEditForm(TramitacaoAdmForm):
'texto',
]
widgets = {
'data_encaminhamento': forms.DateInput(format='%d/%m/%Y'),
'data_fim_prazo': forms.DateInput(format='%d/%m/%Y'),
}
def clean(self):
super(TramitacaoAdmEditForm, self).clean()
local = self.instance.unidade_tramitacao_local
data_tram = self.instance.data_tramitacao
ultima_tramitacao = TramitacaoAdministrativo.objects.filter(
documento_id=self.instance.documento_id).order_by(
'-data_tramitacao',
'-id').first()
# Se a Tramitação que está sendo editada não for a mais recente,
# ela não pode ter seu destino alterado.
if ultima_tramitacao != self.instance:
if self.cleaned_data['unidade_tramitacao_destino'] != \
self.instance.unidade_tramitacao_destino:
raise ValidationError(
'Você não pode mudar a Unidade de Destino desta '
'tramitação, pois irá conflitar com a Unidade '
'Local da tramitação seguinte')
self.cleaned_data['data_tramitacao'] = \
self.instance.data_tramitacao
self.cleaned_data['unidade_tramitacao_local'] = \
self.instance.unidade_tramitacao_local
self.cleaned_data['data_tramitacao'] = data_tram
self.cleaned_data['unidade_tramitacao_local'] = local
return super(TramitacaoAdmEditForm, self).clean()
class DocumentoAdministrativoForm(ModelForm):
data = forms.DateField(initial=datetime.today)
data = forms.DateField(initial=timezone.now)
ano_protocolo = forms.ChoiceField(required=False,
label=Protocolo._meta.
@ -575,8 +582,8 @@ class DocumentoAdministrativoForm(ModelForm):
if not self.is_valid():
return cleaned_data
numero_protocolo = cleaned_data['numero_protocolo']
ano_protocolo = cleaned_data['ano_protocolo']
numero_protocolo = self.data['numero_protocolo']
ano_protocolo = self.data['ano_protocolo']
# campos opcionais, mas que se informados devem ser válidos
if numero_protocolo and ano_protocolo:
@ -588,6 +595,11 @@ class DocumentoAdministrativoForm(ModelForm):
msg = _('Protocolo %s/%s inexistente.' % (
numero_protocolo, ano_protocolo))
raise ValidationError(msg)
except MultipleObjectsReturned:
msg = _(
'Existe mais de um Protocolo com este ano e número.' % (
numero_protocolo, ano_protocolo))
raise ValidationError(msg)
return self.cleaned_data

19
sapl/protocoloadm/migrations/0002_remove_documentoadministrativo_numero_protocolo.py

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.3 on 2017-09-20 21:52
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('protocoloadm', '0001_initial'),
]
operations = [
migrations.RemoveField(
model_name='documentoadministrativo',
name='numero_protocolo',
),
]

22
sapl/protocoloadm/tests/test_protocoloadm.py

@ -1,4 +1,4 @@
import datetime
from datetime import date, timedelta
import pytest
from django.core.urlresolvers import reverse
@ -145,7 +145,7 @@ def test_create_tramitacao(admin_client):
unidade_tramitacao_destino=unidade_tramitacao_destino_1,
status=status,
documento=documento_adm,
data_tramitacao=datetime.date(2016, 8, 21))
data_tramitacao=date(2016, 8, 21))
response = admin_client.post(
reverse(
@ -155,7 +155,7 @@ def test_create_tramitacao(admin_client):
'unidade_tramitacao_destino': unidade_tramitacao_local_1.pk,
'documento': documento_adm.pk,
'status': status.pk,
'data_tramitacao': datetime.date(2016, 8, 21)},
'data_tramitacao': date(2016, 8, 21)},
follow=True)
msg = force_text(_('A origem da nova tramitação deve ser igual ao '
@ -173,7 +173,7 @@ def test_create_tramitacao(admin_client):
'unidade_tramitacao_destino': unidade_tramitacao_destino_2.pk,
'documento': documento_adm.pk,
'status': status.pk,
'data_tramitacao': datetime.date(2016, 8, 20)},
'data_tramitacao': date(2016, 8, 20)},
follow=True)
msg = _('A data da nova tramitação deve ser ' +
@ -191,7 +191,7 @@ def test_create_tramitacao(admin_client):
'unidade_tramitacao_destino': unidade_tramitacao_destino_2.pk,
'documento': documento_adm.pk,
'status': status.pk,
'data_tramitacao': datetime.date.today() + datetime.timedelta(
'data_tramitacao': date.today() + timedelta(
days=1)},
follow=True)
@ -210,8 +210,8 @@ def test_create_tramitacao(admin_client):
'unidade_tramitacao_destino': unidade_tramitacao_destino_2.pk,
'documento': documento_adm.pk,
'status': status.pk,
'data_tramitacao': datetime.date(2016, 8, 21),
'data_encaminhamento': datetime.date(2016, 8, 20)},
'data_tramitacao': date(2016, 8, 21),
'data_encaminhamento': date(2016, 8, 20)},
follow=True)
msg = force_text(_('A data de encaminhamento deve ser ' +
@ -229,8 +229,8 @@ def test_create_tramitacao(admin_client):
'unidade_tramitacao_destino': unidade_tramitacao_destino_2.pk,
'documento': documento_adm.pk,
'status': status.pk,
'data_tramitacao': datetime.date(2016, 8, 21),
'data_fim_prazo': datetime.date(2016, 8, 20)},
'data_tramitacao': date(2016, 8, 21),
'data_fim_prazo': date(2016, 8, 20)},
follow=True)
msg = _('A data fim de prazo deve ser ' +
@ -248,12 +248,12 @@ def test_create_tramitacao(admin_client):
'unidade_tramitacao_destino': unidade_tramitacao_destino_2.pk,
'documento': documento_adm.pk,
'status': status.pk,
'data_tramitacao': datetime.date(2016, 8, 21)},
'data_tramitacao': date(2016, 8, 21)},
follow=True)
tramitacao = TramitacaoAdministrativo.objects.last()
# Verifica se a tramitacao que obedece as regras de negócios é criada
assert tramitacao.data_tramitacao == datetime.date(2016, 8, 21)
assert tramitacao.data_tramitacao == date(2016, 8, 21)
@pytest.mark.django_db(transaction=False)

88
sapl/protocoloadm/views.py

@ -1,5 +1,3 @@
from datetime import date, datetime
from braces.views import FormValidMessageMixin
from django.contrib import messages
from django.contrib.auth.mixins import PermissionRequiredMixin
@ -8,7 +6,9 @@ from django.core.exceptions import ObjectDoesNotExist
from django.core.urlresolvers import reverse
from django.db.models import Max, Q
from django.http import Http404, HttpResponse, JsonResponse
from django.http.response import HttpResponseRedirect
from django.shortcuts import redirect
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from django.views.generic import CreateView, ListView
from django.views.generic.base import RedirectView, TemplateView
@ -121,7 +121,7 @@ class DocumentoAdministrativoCrud(Crud):
if self.object.protocolo:
p = self.object.protocolo
return {'ano_protocolo': p.ano,
'protocolo__numero': p.numero}
'numero_protocolo': p.numero}
class DetailView(DocumentoAdministrativoMixin, Crud.DetailView):
@ -295,7 +295,7 @@ class ProtocoloDocumentoView(PermissionRequiredMixin,
if numeracao == 'A':
numero = Protocolo.objects.filter(
ano=date.today().year).aggregate(Max('numero'))
ano=timezone.now().year).aggregate(Max('numero'))
elif numeracao == 'L':
legislatura = Legislatura.objects.last()
data_inicio = legislatura.data_inicio
@ -309,10 +309,10 @@ class ProtocoloDocumentoView(PermissionRequiredMixin,
f.tipo_processo = '0' # TODO validar o significado
f.anulado = False
f.numero = (numero['numero__max'] + 1) if numero['numero__max'] else 1
f.ano = datetime.now().year
f.data = datetime.now().date()
f.hora = datetime.now().time()
f.timestamp = datetime.now()
f.ano = timezone.now().year
f.data = timezone.now()
f.hora = timezone.now().time()
f.timestamp = timezone.now()
f.assunto_ementa = self.request.POST['assunto']
f.save()
@ -334,7 +334,7 @@ class CriarDocumentoProtocolo(PermissionRequiredMixin, CreateView):
kwargs={'pk': self.kwargs['pk']})
def criar_documento(self, protocolo):
curr_year = datetime.now().date().year
curr_year = timezone.now().year
numero_max = DocumentoAdministrativo.objects.filter(
tipo=protocolo.tipo_documento, ano=curr_year
@ -343,8 +343,8 @@ class CriarDocumentoProtocolo(PermissionRequiredMixin, CreateView):
doc = {}
doc['tipo'] = protocolo.tipo_documento
doc['ano'] = curr_year
doc['data'] = datetime.today()
doc['protocolo__numero'] = protocolo.numero
doc['data'] = timezone.now()
doc['numero_protocolo'] = protocolo.numero
doc['ano_protocolo'] = protocolo.ano
doc['protocolo'] = protocolo.id
doc['assunto'] = protocolo.assunto_ementa
@ -430,7 +430,7 @@ class ProtocoloMateriaView(PermissionRequiredMixin, CreateView):
if numeracao == 'A':
numero = Protocolo.objects.filter(
ano=date.today().year).aggregate(Max('numero'))
ano=timezone.now().year).aggregate(Max('numero'))
elif numeracao == 'U':
numero = Protocolo.objects.all().aggregate(Max('numero'))
@ -441,10 +441,10 @@ class ProtocoloMateriaView(PermissionRequiredMixin, CreateView):
protocolo.numero = (
numero['numero__max'] + 1) if numero['numero__max'] else 1
protocolo.ano = datetime.now().year
protocolo.data = datetime.now().date()
protocolo.hora = datetime.now().time()
protocolo.timestamp = datetime.now()
protocolo.ano = timezone.now().year
protocolo.data = timezone.now().date()
protocolo.hora = timezone.now().time()
protocolo.timestamp = timezone.now()
protocolo.tipo_protocolo = 0
protocolo.tipo_processo = '1' # TODO validar o significado
@ -482,7 +482,7 @@ class ProtocoloMateriaView(PermissionRequiredMixin, CreateView):
lista_comissoes = Comissao.objects.filter(Q(
data_extincao__isnull=True) | Q(
data_extincao__gt=date.today())).values_list('id', flat=True)
data_extincao__gt=timezone.now())).values_list('id', flat=True)
model_comissao = ContentType.objects.get_for_model(Comissao)
autor_comissoes = Autor.objects.filter(
content_type=model_comissao, object_id__in=lista_comissoes)
@ -585,6 +585,33 @@ class TramitacaoAdmCrud(MasterDetailCrud):
class CreateView(MasterDetailCrud.CreateView):
form_class = TramitacaoAdmForm
def get_initial(self):
local = DocumentoAdministrativo.objects.get(
pk=self.kwargs['pk']).tramitacaoadministrativo_set.order_by(
'-data_tramitacao',
'-id').first()
if local:
self.initial['unidade_tramitacao_local'
] = local.unidade_tramitacao_destino.pk
else:
self.initial['unidade_tramitacao_local'] = ''
self.initial['data_tramitacao'] = timezone.now().date()
return self.initial
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
primeira_tramitacao = not(TramitacaoAdministrativo.objects.filter(
documento_id=int(kwargs['root_pk'])).exists())
# Se não for a primeira tramitação daquela matéria, o campo
# não pode ser modificado
if not primeira_tramitacao:
context['form'].fields[
'unidade_tramitacao_local'].widget.attrs['disabled'] = True
return context
class UpdateView(MasterDetailCrud.UpdateView):
form_class = TramitacaoAdmEditForm
@ -593,12 +620,37 @@ class TramitacaoAdmCrud(MasterDetailCrud):
def get_queryset(self):
qs = super(MasterDetailCrud.ListView, self).get_queryset()
kwargs = {self.crud.parent_field: self.kwargs['pk']}
return qs.filter(**kwargs).order_by('-data_tramitacao', '-id')
return qs.filter(**kwargs).order_by('-data_tramitacao',
'-id')
class DetailView(DocumentoAdministrativoMixin,
MasterDetailCrud.DetailView):
pass
class DeleteView(MasterDetailCrud.DeleteView):
def delete(self, request, *args, **kwargs):
tramitacao = TramitacaoAdministrativo.objects.get(
id=self.kwargs['pk'])
documento = DocumentoAdministrativo.objects.get(
id=tramitacao.documento.id)
url = reverse(
'sapl.protocoloadm:tramitacaoadministrativo_list',
kwargs={'pk': tramitacao.documento.id})
ultima_tramitacao = \
documento.tramitacaoadministrativo_set.order_by(
'-data_tramitacao',
'-id').first()
if tramitacao.pk != ultima_tramitacao.pk:
msg = _('Somente a última tramitação pode ser deletada!')
messages.add_message(request, messages.ERROR, msg)
return HttpResponseRedirect(url)
else:
tramitacao.delete()
return HttpResponseRedirect(url)
class DocumentoAcessorioAdministrativoCrud(MasterDetailCrud):
model = DocumentoAcessorioAdministrativo

1
sapl/relatorios/templates/pdf_etiqueta_protocolo_gerar.py

@ -121,6 +121,7 @@ def principal(imagem, lst_protocolos, dic_cabecalho, lst_rodape):
tmp_data += paraStyle()
tmp_data += protocolos(lst_protocolos, dic_cabecalho)
tmp_data += '</document>\n'
import ipdb; ipdb.set_trace()
tmp_pdf = parseString(tmp_data)
return tmp_pdf

12
sapl/relatorios/views.py

@ -1,9 +1,10 @@
import html
import re
from datetime import datetime
from datetime import datetime as dt
from django.core.exceptions import ObjectDoesNotExist
from django.http import Http404, HttpResponse
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from sapl.base.models import Autor, CasaLegislativa
@ -88,7 +89,7 @@ def get_rodape(casa):
linha2 = linha2 + " - "
linha2 = linha2 + str(_("E-mail: ")) + casa.email
data_emissao = datetime.today().strftime("%d/%m/%Y")
data_emissao = dt.strftime(timezone.now(), "%d/%m/%Y")
return [linha1, linha2, data_emissao]
@ -932,8 +933,11 @@ def get_etiqueta_protocolos(prots):
dic = {}
dic['titulo'] = str(p.numero) + '/' + str(p.ano)
tz_hora = timezone.localtime(p.timestamp)
dic['data'] = '<b>Data: </b>' + p.data.strftime(
"%d/%m/%Y") + ' - <b>Horário: </b>' + p.hora.strftime("%H:%M")
"%d/%m/%Y") + ' - <b>Horário: </b>' + tz_hora.strftime("%H:%M")
dic['txt_assunto'] = p.assunto_ementa
dic['txt_interessado'] = p.interessado
@ -952,7 +956,7 @@ def get_etiqueta_protocolos(prots):
dic['num_documento'] = ''
for documento in DocumentoAdministrativo.objects.filter(
numero_protocolo=p.numero):
protocolo=p):
dic['num_documento'] = str(documento)
dic['ident_processo'] = dic['num_materia'] or dic['num_documento']

4
sapl/sessao/forms.py

@ -15,7 +15,7 @@ from sapl.materia.models import (MateriaLegislativa, StatusTramitacao,
from sapl.parlamentares.models import Parlamentar
from sapl.utils import (RANGE_DIAS_MES, RANGE_MESES,
MateriaPesquisaOrderingFilter, autor_label,
autor_modal)
autor_modal, timezone)
from .models import (Bancada, ExpedienteMateria, Orador, OradorExpediente,
OrdemDia, SessaoPlenaria, SessaoPlenariaPresenca)
@ -132,7 +132,7 @@ class ExpedienteMateriaForm(ModelForm):
data_ordem = forms.CharField(
label='Data Sessão',
initial=datetime.now().strftime('%d/%m/%Y'),
initial=datetime.strftime(timezone.now(), '%d/%m/%Y'),
widget=forms.TextInput(attrs={'readonly': 'readonly'}))
class Meta:

9
sapl/sessao/views.py

@ -1,4 +1,3 @@
from datetime import datetime
from re import sub
from django.contrib import messages
@ -10,6 +9,7 @@ from django.db.models import Max, Q
from django.forms.utils import ErrorList
from django.http import JsonResponse
from django.http.response import Http404, HttpResponseRedirect
from django.utils import timezone
from django.utils.datastructures import MultiValueDictKeyError
from django.utils.decorators import method_decorator
from django.utils.html import strip_tags
@ -2429,7 +2429,8 @@ class PesquisarSessaoPlenariaView(FilterView):
kwargs = {'data': self.request.GET or None}
qs = self.get_queryset()
qs = self.get_queryset().select_related(
'tipo', 'sessao_legislativa', 'legislatura')
qs = qs.distinct().order_by(
'-legislatura__numero', '-data_inicio', '-numero')
@ -2575,7 +2576,7 @@ class AdicionarVariasMateriasExpediente(PermissionRequiredForAppCrudMixin,
expediente.numero_ordem = posicao
else:
expediente.numero_ordem = 1
expediente.data_ordem = datetime.now()
expediente.data_ordem = timezone.now()
expediente.tipo_votacao = request.POST['tipo_votacao_%s' % m]
expediente.save()
@ -2644,7 +2645,7 @@ class AdicionarVariasMateriasOrdemDia(AdicionarVariasMateriasExpediente):
ordem_dia.numero_ordem = posicao
else:
ordem_dia.numero_ordem = 1
ordem_dia.data_ordem = datetime.now()
ordem_dia.data_ordem = timezone.now()
ordem_dia.tipo_votacao = tipo_votacao
ordem_dia.save()

2
sapl/settings.py

@ -209,7 +209,7 @@ LANGUAGES = (
TIME_ZONE = 'America/Sao_Paulo'
USE_I18N = True
USE_L10N = True
USE_TZ = False
USE_TZ = True
# DATE_FORMAT = 'N j, Y'
DATE_FORMAT = 'd/m/Y'
SHORT_DATE_FORMAT = 'd/m/Y'

4
sapl/templates/materia/prop_devolvidas_list.html

@ -1,6 +1,6 @@
{% extends "base.html" %}
{% load i18n %}
{% load tz %}
{% block base_content %}
<fieldset>
<legend>Proposições Não Incorporadas</legend>
@ -20,7 +20,7 @@
<tbody>
{% for prop in object_list %}
<tr>
<td><a href="{% url 'sapl.materia:proposicao_detail' prop.pk %}">{{ prop.data_devolucao|date:"d/m/Y H:i:s" }}</a></td>
<td><a href="{% url 'sapl.materia:proposicao_detail' prop.pk %}">{{ prop.data_devolucao|localtime|date:"d/m/Y H:i:s" }}</a></td>
<td>{{ prop.tipo.descricao }}</td>
<td>{{ prop.descricao }}</td>
<td>{{ prop.autor }}</td>

3
sapl/templates/materia/prop_pendentes_list.html

@ -1,5 +1,6 @@
{% extends "base.html" %}
{% load i18n %}
{% load tz %}
{% block base_content %}
<fieldset>
<legend>Proposições Não Recebidas</legend>
@ -18,7 +19,7 @@
<tbody>
{% for prop in object_list %}
<tr>
<td><a href="{% url 'sapl.materia:proposicao_detail' prop.pk %}">{{ prop.data_envio|date:"d/m/Y H:i:s" }}</a></td>
<td><a href="{% url 'sapl.materia:proposicao_detail' prop.pk %}">{{ prop.data_envio|localtime|date:"d/m/Y H:i:s" }}</a></td>
<td>{{ prop.tipo.descricao }}</td>
<td>{{ prop.descricao }}</td>
<td>{{ prop.autor }}</td>

3
sapl/templates/materia/prop_recebidas_list.html

@ -1,5 +1,6 @@
{% extends "base.html" %}
{% load i18n %}
{% load tz %}
{% block base_content %}
<fieldset>
<legend>Proposições Incorporadas</legend>
@ -19,7 +20,7 @@
<tbody>
{% for prop in object_list %}
<tr>
<td><a href="{% url 'sapl.materia:proposicao_detail' prop.pk %}">{{ prop.data_recebimento|date:"d/m/Y H:i:s" }}</a></td>
<td><a href="{% url 'sapl.materia:proposicao_detail' prop.pk %}">{{ prop.data_recebimento|localtime|date:"d/m/Y H:i:s" }}</a></td>
<td>{{ prop.tipo.descricao }}</td>
<td>{{ prop.descricao }}</td>
<td>{{ prop.autor }}</td>

4
sapl/templates/materia/proposicao_detail.html

@ -1,6 +1,6 @@
{% extends "crud/detail.html" %}
{% load i18n common_tags %}
{% load tz %}
{% block sub_actions %}{{block.super}}
<div class="actions btn-group btn-group-sm {%block sub_actions_pull%}{% endblock%}" role="group">
{% if object.texto_articulado.exists %}
@ -62,7 +62,7 @@
<div class="col-sm-9">
<div class="alert alert-danger alert-dismissible fade in" role="alert">
<strong>{% trans "Proposição devolvida em:" %} {{ object.data_devolucao|date:"DATETIME_FORMAT"}}</strong>
<strong>{% trans "Proposição devolvida em:" %} {{ object.data_devolucao|localtime|date:"DATETIME_FORMAT"}}</strong>
<div >{% trans "Justificativa:" %} {{object.justificativa_devolucao}}</div>
</div>
</div>

3
sapl/templates/materia/recibo_proposicao.html

@ -1,4 +1,5 @@
{% load i18n %}
{% load tz %}
{% load crispy_forms_tags %}
{% load static %}
@ -49,7 +50,7 @@
</tr>
<tr>
<td>Autor: <b>{{proposicao.autor}}</b></td>
<td>Data de Envio: <b>{{proposicao.data_envio|date:"d/m/Y H:i:s"}}</b></td>
<td>Data de Envio: <b>{{proposicao.data_envio|localtime|date:"d/m/Y H:i:s"}}</b></td>
</tr>
<tr>
<td>Descrição: <b>{{proposicao.descricao}}</b></td>

2
sapl/templates/protocoloadm/protocolo_filter.html

@ -1,5 +1,6 @@
{% extends "protocoloadm/protocoloadm_detail.html" %}
{% load i18n %}
{% load tz %}
{% load crispy_forms_tags %}
{% load static %}
@ -44,6 +45,7 @@
</br>
<strong>Assunto:</strong> {{ p.assunto_ementa|default_if_none:"Não informado"}}</br>
<strong>Data Protocolo:</strong> {{ p.data|date:"d/m/Y"|default_if_none:"Não informado" }} - Horário: {{ p.hora|date:"G:i:s" }}</br>
<strong>Data Protocolo:</strong> {{ p.data|date:"d/m/Y"|default_if_none:"Não informado" }} - Horário: {{ p.timestamp|localtime|date:"G:i:s" }}</br>
{% if p.tipo_processo == 0 %}
<strong>Interessado:</strong> {{ p.interessado|default_if_none:"Não informado" }}</br>

4
sapl/templates/protocoloadm/protocolo_list.html

@ -1,8 +1,8 @@
{% extends "protocoloadm/protocoloadm_detail.html" %}
{% load i18n %}
{% load tz %}
{% load crispy_forms_tags %}
{% load static %}
{% block detail_content %}
{% if protocolos %}
<table>
@ -20,7 +20,7 @@
<img src="{% static 'img/etiqueta.png' %}" alt="Etiqueta Individual">
</a></br>
<strong>Assunto:</strong> {{ p.assunto_ementa }}</br>
<strong>Data Protocolo:</strong> {{ p.data|date:"d/m/Y" }} - Horário: {{ p.hora|date:"H:m:s" }}</br>
<strong>Data Protocolo:</strong> {{ p.data|date:"d/m/Y" }} - Horário: {{ p.timestamp|localtime|date:"H:m:s" }}</br>
<strong>Natureza do Processo:</strong>
{% if p.tipo_processo == 0 %}
Administrativo </br>

3
sapl/templates/protocoloadm/protocolo_mostrar.html

@ -1,11 +1,12 @@
{% extends "protocoloadm/protocoloadm_detail.html" %}
{% load i18n %}
{% load tz %}
{% load crispy_forms_tags %}
{% block detail_content %}
<strong>Protocolo: </strong>{{ protocolo.numero|stringformat:'06d' }}/{{ protocolo.ano }}</br>
<strong>Assunto: </strong> {{ protocolo.assunto_ementa|default:"Não informado" }}</br>
<strong>Data Protocolo: </strong> {{ protocolo.data|date:"d/m/Y" }} - Horário: {{ protocolo.hora|date:"H:i" }}</br>
<strong>Data Protocolo: </strong> {{ protocolo.data|date:"d/m/Y" }} - Horário: {{ protocolo.timestamp|localtime|date:"H:i" }}</br>
{% if protocolo.tipo_processo == 0 %}
<strong>Interessado:</strong> {{ protocolo.interessado|default_if_none:"Não informado" }}</br>

12
sapl/templates/protocoloadm/tramitacaoadministrativo_form.html

@ -0,0 +1,12 @@
{% extends "crud/form.html" %}
{% load i18n %}
{% block extra_js %}
<script language="Javascript">
// Caso o campo esteja desabilitado (quando não é a primeira tramitação),
// habilita ele no momento do submit para que o valor de unidade local
// não seja enviado como vazio
$('form').submit(function(){
$('#id_unidade_tramitacao_local').prop('disabled', false);
});
</script>
{% endblock extra_js %}

6
sapl/utils.py

@ -2,7 +2,6 @@ import hashlib
import logging
import os
import re
from datetime import date
from functools import wraps
from unicodedata import normalize as unicodedata_normalize
@ -18,7 +17,7 @@ from django.contrib.contenttypes.fields import (GenericForeignKey, GenericRel,
GenericRelation)
from django.core.exceptions import ValidationError
from django.db.models import Q
from django.utils import six
from django.utils import six, timezone
from django.utils.translation import ugettext_lazy as _
from django_filters.filterset import STRICTNESS
from floppyforms import ClearableFileInput
@ -303,7 +302,8 @@ UF = [
('EX', 'Exterior'),
]
RANGE_ANOS = [(year, year) for year in range(date.today().year, 1889, -1)]
RANGE_ANOS = [(year, year) for year in range(timezone.now().year,
1889, -1)]
RANGE_MESES = [
(1, 'Janeiro'),

Loading…
Cancel
Save