Browse Source

Merge branch 'master' into 1827-migra-para-django-1.10

pull/1828/head
Victor Fabre 8 years ago
committed by GitHub
parent
commit
29c8db6272
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      docker-compose.yml
  2. 19
      release.sh
  3. 2
      requirements/migration-requirements.txt
  4. 9
      sapl/base/forms.py
  5. 19
      sapl/base/urls.py
  6. 25
      sapl/base/views.py
  7. 23
      sapl/comissoes/migrations/0014_auto_20180503_1055.py
  8. 2
      sapl/comissoes/models.py
  9. 7
      sapl/comissoes/views.py
  10. 3
      sapl/hashers.py
  11. 24
      sapl/legacy/management/commands/migracao_25_31.py
  12. 87
      sapl/legacy/migracao.py
  13. 204
      sapl/legacy/migracao_dados.py
  14. 214
      sapl/legacy/migracao_documentos.py
  15. 8
      sapl/legacy/migracao_usuarios.py
  16. 2
      sapl/legacy/models.py
  17. 4
      sapl/legacy/router.py
  18. 211
      sapl/legacy/scripts/exporta_zope/exporta_zope.py
  19. 7
      sapl/legacy/scripts/exporta_zope/requirements.txt
  20. 4
      sapl/legacy/scripts/exporta_zope/variaveis_comuns.py
  21. 26
      sapl/legacy/scripts/migra_um_db.sh
  22. 40
      sapl/legacy/scripts/normaliza_dump_mysql.py
  23. 28
      sapl/legacy/scripts/normaliza_dump_mysql.sh
  24. 5
      sapl/legacy/scripts/recria_um_db_postgres.sh
  25. 10
      sapl/legacy_migration_settings.py
  26. 12
      sapl/materia/forms.py
  27. 20
      sapl/materia/migrations/0028_auto_20180418_1629.py
  28. 16
      sapl/materia/models.py
  29. 3
      sapl/norma/views.py
  30. 2
      sapl/parlamentares/views.py
  31. 31
      sapl/protocoloadm/forms.py
  32. 20
      sapl/protocoloadm/migrations/0004_documentoadministrativo_numero_externo.py
  33. 4
      sapl/protocoloadm/models.py
  34. 2
      sapl/sessao/forms.py
  35. 78
      sapl/sessao/views.py
  36. 1
      sapl/settings.py
  37. 11
      sapl/static/XSLT/HTML/.objects
  38. 95
      sapl/static/XSLT/HTML/estilo.css
  39. 51
      sapl/static/XSLT/HTML/indicacao.xsl
  40. 41
      sapl/static/XSLT/HTML/mocao.xsl
  41. 45
      sapl/static/XSLT/HTML/mocao2.xsl
  42. 41
      sapl/static/XSLT/HTML/parecer.xsl
  43. 47
      sapl/static/XSLT/HTML/pedido.xsl
  44. 53
      sapl/static/XSLT/HTML/pedido2.xsl
  45. 105
      sapl/static/XSLT/HTML/pl.xsl
  46. 100
      sapl/static/XSLT/HTML/pl2.xsl
  47. 52
      sapl/static/XSLT/HTML/requerimento.xsl
  48. 57
      sapl/static/XSLT/HTML/requerimento2.xsl
  49. 2
      sapl/templates/norma/normajuridica_detail.html
  50. 3
      sapl/templates/norma/subnav.yaml
  51. 1
      sapl/templates/protocoloadm/layouts.yaml
  52. 2
      sapl/templates/sessao/blocos_resumo/expedientes.html
  53. 1
      sapl/templates/sessao/expediente.html
  54. 4
      sapl/test_urls.py
  55. 5
      sapl/urls.py
  56. 5
      sapl/utils.py
  57. 7
      scripts_docker/remove-all-containers.sh
  58. 2
      setup.py

2
docker-compose.yml

@ -11,7 +11,7 @@ sapldb:
ports:
- "5432:5432"
sapl:
image: interlegis/sapl:3.1.71
image: interlegis/sapl:3.1.76
restart: always
environment:
ADMIN_PASSWORD: interlegis

19
release.sh

@ -0,0 +1,19 @@
#/bin/bash
VERSION=`git describe --tags --abbrev=0`
LAST_DIGIT=`echo $VERSION | cut -f 3 -d '.'`
MAIN_REV=`echo $VERSION | cut -f 1,2 -d '.'`
NEXT_NUMBER=$(($LAST_DIGIT + 1))
NEXT_VERSION=$MAIN_REV'.'$NEXT_NUMBER
sed -e s/$VERSION/$NEXT_VERSION/g docker-compose.yml > tmp1
mv tmp1 docker-compose.yml
sed -e s/$VERSION/$NEXT_VERSION/g setup.py > tmp2
mv tmp2 setup.py
git add docker-compose.yml setup.py
git commit -m "Release: $NEXT_VERSION"
git tag $NEXT_VERSION
git push origin $NEXT_VERSION
git push origin

2
requirements/migration-requirements.txt

@ -1,2 +1,4 @@
-r dev-requirements.txt
GitPython
mysqlclient==1.3.12
pyaml

9
sapl/base/forms.py

@ -525,6 +525,11 @@ class RelatorioAtasFilterSet(django_filters.FilterSet):
model = SessaoPlenaria
fields = ['data_inicio']
@property
def qs(self):
parent = super(RelatorioAtasFilterSet, self).qs
return parent.distinct().prefetch_related('tipo').order_by('-ano', 'tipo', 'numero')
def __init__(self, *args, **kwargs):
super(RelatorioAtasFilterSet, self).__init__(
*args, **kwargs)
@ -588,7 +593,7 @@ class RelatorioHistoricoTramitacaoFilterSet(django_filters.FilterSet):
@property
def qs(self):
parent = super(RelatorioHistoricoTramitacaoFilterSet, self).qs
return parent.distinct().order_by('-ano', 'tipo', 'numero')
return parent.distinct().prefetch_related('tipo').order_by('-ano', 'tipo', 'numero')
class Meta:
model = MateriaLegislativa
@ -628,7 +633,7 @@ class RelatorioDataFimPrazoTramitacaoFilterSet(django_filters.FilterSet):
@property
def qs(self):
parent = super(RelatorioDataFimPrazoTramitacaoFilterSet, self).qs
return parent.distinct().order_by('-ano', 'tipo', 'numero')
return parent.distinct().prefetch_related('tipo').order_by('-ano', 'tipo', 'numero')
class Meta:
model = MateriaLegislativa

19
sapl/base/urls.py

@ -1,20 +1,22 @@
import os
from django.conf.urls import include, url
from django.contrib.auth import views
from django.contrib.auth.decorators import permission_required
from django.contrib.auth.views import (password_reset, password_reset_complete,
password_reset_confirm,
password_reset_done)
from django.views.generic.base import TemplateView
from django.views.generic.base import RedirectView, TemplateView
from sapl.base.views import AutorCrud, ConfirmarEmailView, TipoAutorCrud
from sapl.settings import EMAIL_SEND_USER
from sapl.settings import EMAIL_SEND_USER, MEDIA_URL
from .apps import AppConfig
from .forms import LoginForm, NovaSenhaForm, RecuperarSenhaForm
from .views import (AlterarSenha, AppConfigCrud, CasaLegislativaCrud,
CreateUsuarioView, DeleteUsuarioView, EditUsuarioView,
HelpTopicView, ListarUsuarioView, RelatorioAtasView,
RelatorioDataFimPrazoTramitacaoView,
HelpTopicView, ListarUsuarioView, LogotipoView,
RelatorioAtasView, RelatorioDataFimPrazoTramitacaoView,
RelatorioHistoricoTramitacaoView,
RelatorioMateriasPorAnoAutorTipoView,
RelatorioMateriasPorAutorView,
@ -120,4 +122,13 @@ urlpatterns = [
url(r'^sistema/search/', SaplSearchView(), name='haystack_search'),
# Folhas XSLT e extras referenciadas por documentos migrados do sapl 2.5
url(r'^(sapl/)?XSLT/HTML/(?P<path>.*)$', RedirectView.as_view(
url=os.path.join(MEDIA_URL, 'sapl/public/XSLT/HTML/%(path)s'),
permanent=False)),
# url do logotipo usada em documentos migrados do sapl 2.5
url(r'^(sapl/)?sapl_documentos/props_sapl/logo_casa',
LogotipoView.as_view(), name='logotipo'),
] + recuperar_senha + alterar_senha + admin_user

25
sapl/base/views.py

@ -1,5 +1,6 @@
from django.conf import settings
from django.contrib.auth import get_user_model, update_session_auth_hash
import os
from django.contrib.auth import get_user_model
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.contrib.auth.models import Group
from django.contrib.auth.tokens import default_token_generator
@ -12,14 +13,15 @@ from django.template import TemplateDoesNotExist
from django.template.loader import get_template
from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_decode, urlsafe_base64_encode
from django.utils.translation import string_concat
from django.utils.translation import ugettext_lazy as _
from django.views.generic import (CreateView, DeleteView, DetailView, FormView,
ListView, UpdateView)
from django.views.generic.base import TemplateView
from django.utils.translation import string_concat
from django.views.generic import (CreateView, DeleteView, FormView, ListView,
UpdateView)
from django.views.generic.base import RedirectView, TemplateView
from django_filters.views import FilterView
from haystack.views import SearchView
from sapl import settings
from sapl.base.forms import AutorForm, AutorFormForAdmin, TipoAutorForm
from sapl.base.models import Autor, TipoAutor
from sapl.crud.base import CrudAux, make_pagination
@ -759,3 +761,14 @@ class AlterarSenha(FormView):
user.save()
return super().form_valid(form)
STATIC_LOGO = os.path.join(settings.STATIC_URL, 'img/logo.png')
class LogotipoView(RedirectView):
def get_redirect_url(self, *args, **kwargs):
casa = get_casalegislativa()
logo = casa and casa.logotipo and casa.logotipo.name
return os.path.join(settings.MEDIA_URL, logo) if logo else STATIC_LOGO

23
sapl/comissoes/migrations/0014_auto_20180503_1055.py

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.11 on 2018-05-03 13:55
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('comissoes', '0013_auto_20180312_1533'),
]
operations = [
migrations.AlterModelOptions(
name='composicao',
options={'ordering': ['periodo'], 'verbose_name': 'Composição de Comissão', 'verbose_name_plural': 'Composições de Comissão'},
),
migrations.AlterModelOptions(
name='periodo',
options={'ordering': ['-data_inicio', '-data_fim'], 'verbose_name': 'Período de composição de Comissão', 'verbose_name_plural': 'Períodos de composição de Comissão'},
),
]

2
sapl/comissoes/models.py

@ -105,6 +105,7 @@ class Periodo(models.Model): # PeriodoCompComissao
class Meta:
verbose_name = _('Período de composição de Comissão')
verbose_name_plural = _('Períodos de composição de Comissão')
ordering = ['-data_inicio', '-data_fim']
def __str__(self):
if self.data_inicio and self.data_fim:
@ -140,6 +141,7 @@ class Composicao(models.Model): # IGNORE
class Meta:
verbose_name = _('Composição de Comissão')
verbose_name_plural = _('Composições de Comissão')
ordering = ['periodo']
def __str__(self):
return '%s: %s' % (self.comissao.sigla, self.periodo)

7
sapl/comissoes/views.py

@ -51,6 +51,9 @@ class PeriodoComposicaoCrud(CrudAux):
class UpdateView(CrudAux.UpdateView):
form_class = PeriodoForm
# class ListView(CrudAux.ListView):
class ParticipacaoCrud(MasterDetailCrud):
model = Participacao
parent_field = 'composicao__comissao'
@ -112,7 +115,9 @@ class ComposicaoCrud(MasterDetailCrud):
composicao_pk = self.take_composicao_pk()
if composicao_pk == 0:
ultima_composicao = context['composicao_list'].last()
# Composicao eh ordenada por Periodo, que por sua vez esta em
# ordem descrescente de data de inicio (issue #1920)
ultima_composicao = context['composicao_list'].first()
if ultima_composicao:
context['composicao_pk'] = ultima_composicao.pk
else:

3
sapl/hashers.py

@ -46,11 +46,12 @@ ZOPE_SHA1_PREFIX = '{SSHA}'
def zope_encoded_password_to_django(encoded):
"Migra um hash de senha do zope para uso com o ZopeSHA1PasswordHasher"
if encoded.startswith(ZOPE_SHA1_PREFIX):
if encoded and encoded.startswith(ZOPE_SHA1_PREFIX):
data = encoded[len(ZOPE_SHA1_PREFIX):]
salt = get_salt_from_zope_sha1(data)
hasher = ZopeSHA1PasswordHasher()
return super(ZopeSHA1PasswordHasher, hasher).encode(data, salt)
else:
# assume it's a plain password and use the default hashing
# a None password blocks login, forcing a password reset
return make_password(encoded)

24
sapl/legacy/management/commands/migracao_25_31.py

@ -1,33 +1,13 @@
from django.core import management
from django.core.management.base import BaseCommand
from sapl.legacy.migracao import migrar, migrar_dados
from sapl.legacy.migracao import migrar
class Command(BaseCommand):
help = 'Migração de dados do SAPL 2.5 para o SAPL 3.1'
def add_arguments(self, parser):
parser.add_argument(
'--force',
action='store_true',
default=False,
dest='force',
help='Não interativa: pula confirmação de exclusão dos dados',
)
parser.add_argument(
'--dados',
action='store_true',
default=False,
dest='dados',
help='migra somente dados',
)
def handle(self, *args, **options):
management.call_command('migrate')
somente_dados, interativo = options['dados'], not options['force']
if somente_dados:
migrar_dados(interativo=interativo)
else:
migrar(interativo=interativo)
migrar(interativo=False)

87
sapl/legacy/migracao.py

@ -1,42 +1,75 @@
import subprocess
import tarfile
from getpass import getpass
from django.conf import settings
import requests
from unipath import Path
from sapl.legacy.migracao_dados import migrar_dados
from sapl.legacy.migracao_dados import (REPO, TAG_MARCO, gravar_marco, info,
migrar_dados)
from sapl.legacy.migracao_documentos import migrar_documentos
from sapl.legacy.migracao_usuarios import migrar_usuarios
from sapl.legacy.scripts.exporta_zope.variaveis_comuns import TAG_ZOPE
from sapl.legacy_migration_settings import DIR_REPO, NOME_BANCO_LEGADO
from sapl.materia.models import Proposicao
def migrar(interativo=False):
migrar_dados(interativo=interativo)
migrar_usuarios()
migrar_documentos()
def adornar_msg(msg):
return '\n{1}\n{0}\n{1}'.format(msg, '#' * len(msg))
# fonte: https://stackoverflow.com/a/17081026/1877490
def make_tarfile(output_filename, source_dir):
with tarfile.open(output_filename, "w:gz") as tar:
tar.add(source_dir, arcname=os.path.basename(source_dir))
def migrar(interativo=False):
if TAG_MARCO in REPO.tags:
info('A migração já está feita.')
return
assert TAG_ZOPE in REPO.tags, adornar_msg(
'Antes de migrar '
'é necessário fazer a exportação de documentos do zope')
migrar_dados(interativo=interativo)
migrar_usuarios(REPO.working_dir)
migrar_documentos(REPO)
gravar_marco()
def gerar_pacote():
banco = settings.DATABASES['legacy']['NAME']
# backup do banco
print('Gerando backup do banco... ', end='', flush=True)
arq_backup = settings.MEDIA_ROOT.child('{}.backup'.format(banco))
backup_cmd = '''
pg_dump --host localhost --port 5432 --username postgres --no-password
--format custom --blobs --verbose --file {} {}'''.format(
arq_backup, banco)
subprocess.check_output(backup_cmd.split(), stderr=subprocess.DEVNULL)
print('SUCESSO')
def compactar_media():
# tar de media/sapl
print('Criando tar de media... ', end='', flush=True)
tar_media = settings.MEDIA_ROOT.child('{}.media.tgz'.format(banco))
dir_media = settings.MEDIA_ROOT.child('sapl')
with tarfile.open(tar_media, "w:gz") as tar:
tar.add(dir_media, arcname=dir_media.name)
arq_tar = DIR_REPO.child('{}.media.tar'.format(NOME_BANCO_LEGADO))
arq_tar.remove()
subprocess.check_output(['tar', 'cfh', arq_tar, '-C', DIR_REPO, 'sapl'])
print('SUCESSO')
PROPOSICAO_UPLOAD_TO = Proposicao._meta.get_field('texto_original').upload_to
def salva_conteudo_do_sde(proposicao, conteudo):
caminho_relativo = PROPOSICAO_UPLOAD_TO(
proposicao, 'proposicao_sde_{}.xml'.format(proposicao.pk))
caminho_absoluto = Path(REPO.working_dir, caminho_relativo)
caminho_absoluto.parent.mkdir(parents=True)
with open(caminho_absoluto, 'wb') as arq:
arq.write(conteudo)
proposicao.texto_original = caminho_relativo
proposicao.save()
def scrap_sde(url, usuario, senha=None):
if not senha:
senha = getpass()
# login
session = requests.session()
res = session.post('{}?retry=1'.format(url),
{'__ac_name': usuario, '__ac_password': senha})
assert res.status_code == 200
url_proposicao = '{}/sapl_documentos/proposicao/{}/renderXML?xsl=__default__' # noqa
total = Proposicao.objects.count()
for num, proposicao in enumerate(Proposicao.objects.all()):
pk = proposicao.pk
res = session.get(url_proposicao.format(url, pk))
print("pk: {} status: {} (progresso: {:.2%})".format(
pk, res.status_code, num / total))
if res.status_code == 200:
salva_conteudo_do_sde(proposicao, res.content)

204
sapl/legacy/migracao_dados.py

@ -1,4 +1,7 @@
import datetime
import os
import re
import subprocess
import traceback
from collections import OrderedDict, defaultdict, namedtuple
from datetime import date
@ -7,10 +10,13 @@ from itertools import groupby
from operator import xor
from subprocess import PIPE, call
import git
import pkg_resources
import pyaml
import pytz
import reversion
import yaml
from bs4 import BeautifulSoup
from django.apps import apps
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group
@ -18,13 +24,18 @@ from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist
from django.db import connections, transaction
from django.db.models import Max, Q
from pyaml import UnsafePrettyYAMLDumper
from unipath import Path
from sapl.base.models import AppConfig as AppConf
from sapl.base.models import Autor, TipoAutor, cria_models_tipo_autor
from sapl.comissoes.models import Comissao, Composicao, Participacao
from sapl.legacy import scripts
from sapl.legacy.models import NormaJuridica as OldNormaJuridica
from sapl.legacy.models import TipoNumeracaoProtocolo
from sapl.legacy_migration_settings import (DATABASES, DIR_DADOS_MIGRACAO,
DIR_REPO, NOME_BANCO_LEGADO,
PROJECT_DIR)
from sapl.materia.models import (AcompanhamentoMateria, MateriaLegislativa,
Proposicao, StatusTramitacao, TipoDocumento,
TipoMateriaLegislativa, TipoProposicao,
@ -35,11 +46,11 @@ from sapl.parlamentares.models import (Legislatura, Mandato, Parlamentar,
Partido, TipoAfastamento)
from sapl.protocoloadm.models import (DocumentoAdministrativo, Protocolo,
StatusTramitacaoAdministrativo)
from sapl.sessao.models import (ExpedienteMateria, OrdemDia, RegistroVotacao,
TipoResultadoVotacao)
from sapl.settings import DATABASES, PROJECT_DIR
from sapl.sessao.models import (ExpedienteMateria, ExpedienteSessao, OrdemDia,
RegistroVotacao, TipoResultadoVotacao)
from sapl.utils import normalize
from .scripts.normaliza_dump_mysql import normaliza_dump_mysql
from .timezonesbrasil import get_timezone
# BASE ######################################################################
@ -136,6 +147,7 @@ for nome_novo, nome_antigo in (('comissao', 'cod_comissao'),
class CampoVirtual(namedtuple('CampoVirtual', 'model related_model')):
null = True
CAMPOS_VIRTUAIS_PROPOSICAO = {
TipoMateriaLegislativa: CampoVirtual(Proposicao, MateriaLegislativa),
TipoDocumento: CampoVirtual(Proposicao, DocumentoAdministrativo)
@ -143,6 +155,15 @@ CAMPOS_VIRTUAIS_PROPOSICAO = {
for campo_virtual in CAMPOS_VIRTUAIS_PROPOSICAO.values():
campos_novos_para_antigos[campo_virtual] = 'cod_mat_ou_doc'
CAMPOS_VIRTUAIS_TIPO_PROPOSICAO = {
'M': CampoVirtual(TipoProposicao, TipoMateriaLegislativa),
'D': CampoVirtual(TipoProposicao, TipoDocumento)
}
for campo_virtual in CAMPOS_VIRTUAIS_TIPO_PROPOSICAO.values():
campos_novos_para_antigos[campo_virtual] = 'tip_mat_ou_doc'
# campos virtuais de Autor para funcionar com get_fk_related
CAMPOS_VIRTUAIS_AUTOR = {related: CampoVirtual(Autor, related)
for related in (Parlamentar, Comissao, Partido)}
@ -159,6 +180,7 @@ for related, campo_antigo in [(Parlamentar, 'cod_parlamentar'),
def info(msg):
print('INFO: ' + msg)
ocorrencias = defaultdict(list)
@ -201,7 +223,7 @@ class ForeignKeyFaltando(ObjectDoesNotExist):
campo = campos_novos_para_antigos[self.field]
_, tabela, campos_pk = get_estrutura_legado(self.field.model)
pk = {c: getattr(self.old, c) for c in campos_pk}
sql = 'select * from {} where {}'.format(
sql = 'select * from {} where {};'.format(
tabela,
' and '.join(['{} = {}'.format(k, v) for k, v in pk.items()]))
return OrderedDict((('campo', campo),
@ -494,6 +516,8 @@ PROPAGACOES_DE_EXCLUSAO = [
('parlamentar', 'dependente', 'cod_parlamentar'),
('parlamentar', 'filiacao', 'cod_parlamentar'),
('parlamentar', 'mandato', 'cod_parlamentar'),
('parlamentar', 'composicao_mesa', 'cod_parlamentar'),
('parlamentar', 'composicao_comissao', 'cod_parlamentar'),
# comissao
('comissao', 'composicao_comissao', 'cod_comissao'),
@ -518,6 +542,11 @@ PROPAGACOES_DE_EXCLUSAO = [
('materia_legislativa', 'anexada', 'cod_materia_principal'),
('materia_legislativa', 'anexada', 'cod_materia_anexada'),
('materia_legislativa', 'documento_acessorio', 'cod_materia'),
('materia_legislativa', 'numeracao', 'cod_materia'),
# norma
('norma_juridica', 'vinculo_norma_juridica', 'cod_norma_referente'),
('norma_juridica', 'vinculo_norma_juridica', 'cod_norma_referida'),
# documento administrativo
('documento_administrativo', 'tramitacao_administrativo', 'cod_documento'),
@ -548,6 +577,9 @@ def uniformiza_banco():
garante_coluna_no_legado('tipo_materia_legislativa',
'quorum_minimo_votacao int(11) NULL')
garante_coluna_no_legado('materia_legislativa',
'txt_resultado TEXT NULL')
# Cria campos cod_presenca_sessao (sendo a nova PK da tabela)
# e dat_sessao em sessao_plenaria_presenca
if not existe_coluna_no_legado('sessao_plenaria_presenca',
@ -695,31 +727,26 @@ def fill_dados_basicos():
appconf.save()
def get_last_pk(model):
last_value = model.objects.all().aggregate(Max('pk'))
return last_value['pk__max'] or 0
def reinicia_sequence(model, id):
sequence_name = '%s_id_seq' % model._meta.db_table
exec_sql('ALTER SEQUENCE %s RESTART WITH %s MINVALUE -1;' % (
sequence_name, id))
DIR_DADOS_MIGRACAO = Path('~/migracao_sapl/').expand()
PATH_TABELA_TIMEZONES = DIR_DADOS_MIGRACAO.child('tabela_timezones.yaml')
DIR_RESULTADOS = DIR_DADOS_MIGRACAO.child('resultados')
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
nome_banco_legado = DATABASES['legacy']['NAME']
match = re.match('sapl_cm_(.*)', nome_banco_legado)
match = re.match('sapl_cm_(.*)', NOME_BANCO_LEGADO)
sigla_casa = match.group(1)
PATH_TABELA_TIMEZONES = DIR_DADOS_MIGRACAO.child('tabela_timezones.yaml')
with open(PATH_TABELA_TIMEZONES, 'r') as arq:
tabela_timezones = yaml.load(arq)
municipio, uf, nome_timezone = tabela_timezones[sigla_casa]
@ -762,7 +789,27 @@ def populate_renamed_fields(new, old):
setattr(new, field.name, value)
def roda_comando_shell(cmd):
res = os.system(cmd)
assert res == 0, 'O comando falhou: {}'.format(cmd)
def migrar_dados(interativo=True):
# restaura dump
arq_dump = Path(DIR_DADOS_MIGRACAO.child(
'dumps_mysql', '{}.sql'.format(NOME_BANCO_LEGADO)))
assert arq_dump.exists(), 'Dump do mysql faltando: {}'.format(arq_dump)
info('Restaurando dump mysql de [{}]'.format(arq_dump))
normaliza_dump_mysql(arq_dump)
roda_comando_shell('mysql -uroot < {}'.format(arq_dump))
# executa ajustes pré-migração, se existirem
arq_ajustes_pre_migracao = DIR_DADOS_MIGRACAO.child(
'ajustes_pre_migracao', '{}.sql'.format(sigla_casa))
if arq_ajustes_pre_migracao.exists():
exec_legado(arq_ajustes_pre_migracao.read_file())
uniformiza_banco()
# excluindo database antigo.
@ -789,19 +836,16 @@ def migrar_dados(interativo=True):
info('Começando migração: ...')
try:
ocorrencias.clear()
dir_ocorrencias = DIR_RESULTADOS.child(date.today().isoformat())
dir_ocorrencias.mkdir(parents=True)
migrar_todos_os_models()
except Exception as e:
ocorrencias['traceback'] = str(traceback.format_exc())
raise e
finally:
# grava ocorrências
arq_ocorrencias = dir_ocorrencias.child(
nome_banco_legado + '.yaml')
arq_ocorrencias = Path(REPO.working_dir, 'ocorrencias.yaml')
with open(arq_ocorrencias, 'w') as arq:
dump = yaml.dump(dict(ocorrencias), allow_unicode=True)
arq.write(dump.replace('\n- ', '\n\n- '))
pyaml.dump(ocorrencias, arq, vspacing=1)
REPO.git.add([arq_ocorrencias.name])
info('Ocorrências salvas em\n {}'.format(arq_ocorrencias))
# recria tipos de autor padrão que não foram criados pela migração
@ -816,7 +860,7 @@ def move_para_depois_de(lista, movido, referencias):
return lista
def migrar_todos_os_models():
def get_models_a_migrar():
models = [model for app in appconfs for model in app.models.values()
if model in field_renames]
# Devido à referência TipoProposicao.tipo_conteudo_related
@ -829,7 +873,11 @@ def migrar_todos_os_models():
move_para_depois_de(models, Proposicao,
[MateriaLegislativa, DocumentoAdministrativo])
for model in models:
return models
def migrar_todos_os_models():
for model in get_models_a_migrar():
migrar_model(model)
@ -852,10 +900,14 @@ def migrar_model(model):
def get_id_do_legado(old):
return getattr(old, nome_pk)
ultima_pk_legado = model_legado.objects.all().aggregate(
Max('pk'))['pk__max'] or 0
else:
# a pk no legado tem mais de um campo
old_records = iter_sql_records(tabela_legado)
get_id_do_legado = None
ultima_pk_legado = model_legado.objects.count()
ajuste_antes_salvar = AJUSTE_ANTES_SALVAR.get(model)
ajuste_depois_salvar = AJUSTE_DEPOIS_SALVAR.get(model)
@ -898,10 +950,13 @@ def migrar_model(model):
if ajuste_depois_salvar:
ajuste_depois_salvar()
# se configuramos ids explicitamente devemos reiniciar a sequence
# reiniciamos a sequence logo após a última pk do legado
#
# É importante que seja do legado (e não da nova base),
# pois numa nova versão da migração podemos inserir registros
# não migrados antes sem conflito com pks criadas até lá
if get_id_do_legado:
last_pk = get_last_pk(model)
reinicia_sequence(model, last_pk + 1)
reinicia_sequence(model, ultima_pk_legado + 1)
# apaga registros migrados do legado
if sql_delete_legado:
@ -1061,22 +1116,16 @@ def adjust_tipoafastamento(new, old):
new.indicador = 'F'
TIPO_MATERIA_OU_TIPO_DOCUMENTO = {'M': TipoMateriaLegislativa,
'D': TipoDocumento}
def set_generic_fk(new, campo_virtual, old):
new.content_type = content_types[campo_virtual.related_model]
new.object_id = get_fk_related(campo_virtual, old)
def adjust_tipoproposicao(new, old):
"Aponta para o tipo relacionado de matéria ou documento"
value = old.tip_mat_ou_doc
model_tipo = TIPO_MATERIA_OU_TIPO_DOCUMENTO[old.ind_mat_ou_doc]
tipo = model_tipo.objects.filter(pk=value)
if tipo:
new.tipo_conteudo_related = tipo[0]
else:
raise ForeignKeyFaltando(
field=TipoProposicao.tipo_conteudo_related,
value=(model_tipo.__name__, value),
label={'ind_mat_ou_doc': old.ind_mat_ou_doc})
if old.tip_mat_ou_doc:
campo_virtual = CAMPOS_VIRTUAIS_TIPO_PROPOSICAO[old.ind_mat_ou_doc]
set_generic_fk(new, campo_virtual, old)
def adjust_proposicao_antes_salvar(new, old):
@ -1085,8 +1134,7 @@ def adjust_proposicao_antes_salvar(new, old):
if old.cod_mat_ou_doc:
tipo_mat_ou_doc = type(new.tipo.tipo_conteudo_related)
campo_virtual = CAMPOS_VIRTUAIS_PROPOSICAO[tipo_mat_ou_doc]
new.content_type = content_types[campo_virtual.related_model]
new.object_id = get_fk_related(campo_virtual, old)
set_generic_fk(new, campo_virtual, old)
def adjust_statustramitacao(new, old):
@ -1199,6 +1247,21 @@ def adjust_tiporesultadovotacao(new, old):
{'pk': new.pk, 'nome': new.nome})
def remove_style(conteudo):
if 'style' not in conteudo:
return conteudo # atalho que acelera muito os casos sem style
soup = BeautifulSoup(conteudo, 'html.parser')
for tag in soup.recursiveChildGenerator():
if hasattr(tag, 'attrs'):
tag.attrs = {k: v for k, v in tag.attrs.items() if k != 'style'}
return str(soup)
def adjust_expediente_sessao(new, old):
new.conteudo = remove_style(new.conteudo)
AJUSTE_ANTES_SALVAR = {
Autor: adjust_autor,
TipoAutor: adjust_tipo_autor,
@ -1220,10 +1283,71 @@ AJUSTE_ANTES_SALVAR = {
StatusTramitacaoAdministrativo: adjust_statustramitacaoadm,
Tramitacao: adjust_tramitacao,
TipoResultadoVotacao: adjust_tiporesultadovotacao,
ExpedienteSessao: adjust_expediente_sessao,
}
AJUSTE_DEPOIS_SALVAR = {
NormaJuridica: adjust_normajuridica_depois_salvar,
}
# CHECKS ####################################################################
# MARCO ######################################################################
TIME_FORMAT = '%H:%M:%S'
# permite a gravação de tempos puros pelo pretty-yaml
def time_representer(dumper, data):
return dumper.represent_scalar('!time', data.strftime(TIME_FORMAT))
UnsafePrettyYAMLDumper.add_representer(datetime.time, time_representer)
# permite a leitura de tempos puros pelo pyyaml (no padrão gravado acima)
def time_constructor(loader, node):
value = loader.construct_scalar(node)
return datetime.datetime.strptime(value, TIME_FORMAT).time()
yaml.add_constructor(u'!time', time_constructor)
TAG_MARCO = 'marco'
def gravar_marco():
"""Grava um dump de todos os dados como arquivos yaml no repo de marco
"""
# prepara ou localiza repositorio
dir_dados = Path(REPO.working_dir, 'dados')
# exporta dados como arquivos yaml
user_model = get_user_model()
models = get_models_a_migrar() + [
Composicao, user_model, Group, ContentType]
for model in models:
info('Gravando marco de [{}]'.format(model.__name__))
dir_model = dir_dados.child(model._meta.app_label, model.__name__)
dir_model.mkdir(parents=True)
for data in model.objects.all().values():
nome_arq = Path(dir_model, '{}.yaml'.format(data['id']))
with open(nome_arq, 'w') as arq:
pyaml.dump(data, arq)
# backup do banco
print('Gerando backup do banco... ', end='', flush=True)
arq_backup = DIR_REPO.child('{}.backup'.format(NOME_BANCO_LEGADO))
arq_backup.remove()
backup_cmd = '''
pg_dump --host localhost --port 5432 --username postgres --no-password
--format custom --blobs --verbose --file {} {}'''.format(
arq_backup, NOME_BANCO_LEGADO)
subprocess.check_output(backup_cmd.split(), stderr=subprocess.DEVNULL)
print('SUCESSO')
# salva mudanças
REPO.git.add([dir_dados.name])
if 'master' not in REPO.heads or REPO.index.diff('HEAD'):
# se de fato existe mudança
REPO.index.commit('Grava marco')
REPO.git.execute('git tag -f'.split() + [TAG_MARCO])

214
sapl/legacy/migracao_documentos.py

@ -1,12 +1,14 @@
import mimetypes
import os
import re
from glob import glob
from os.path import join
import yaml
from django.db import transaction
from image_cropping.fields import ImageCropField
from sapl.base.models import CasaLegislativa
from sapl.legacy.migracao_dados import exec_legado, warn
from sapl.legacy.migracao_dados import exec_legado
from sapl.materia.models import (DocumentoAcessorio, MateriaLegislativa,
Proposicao)
from sapl.norma.models import NormaJuridica
@ -14,107 +16,56 @@ from sapl.parlamentares.models import Parlamentar
from sapl.protocoloadm.models import (DocumentoAcessorioAdministrativo,
DocumentoAdministrativo)
from sapl.sessao.models import SessaoPlenaria
from sapl.settings import MEDIA_ROOT
# MIGRAÇÃO DE DOCUMENTOS ###################################################
def get_ano(obj):
return [obj.ano]
def ___(obj):
return []
DOCS = {
CasaLegislativa: [
('logotipo',
'props_sapl/{}.*',
'public/casa/logotipo/',
___)
],
Parlamentar: [
('fotografia',
'parlamentar/fotos/{}_foto_parlamentar',
'public/parlamentar/{0}/',
___)
],
MateriaLegislativa: [
('texto_original',
'materia/{}_texto_integral',
'public/materialegislativa/{1}/{0}/',
get_ano)
],
DocumentoAcessorio: [
('arquivo',
'materia/{}',
'public/documentoacessorio/{1}/{0}/',
lambda obj: [obj.materia.ano])
],
NormaJuridica: [
('texto_integral',
'norma_juridica/{}_texto_integral',
'public/normajuridica/{1}/{0}/',
get_ano)
],
SessaoPlenaria: [
('upload_pauta',
'pauta_sessao/{}_pauta_sessao',
'public/sessaoplenaria/{0}/pauta/',
___),
('upload_ata',
'ata_sessao/{}_ata_sessao',
'public/sessaoplenaria/{0}/ata/',
___),
('upload_anexo',
'anexo_sessao/{}_texto_anexado',
'public/sessaoplenaria/{0}/anexo/',
___)
],
Proposicao: [
('texto_original',
'proposicao/{}',
'private/proposicao/{0}/',
get_ano)
],
DocumentoAdministrativo: [
('texto_integral',
'administrativo/{}_texto_integral',
'private/documentoadministrativo/{0}/',
get_ano)
],
DocumentoAcessorioAdministrativo: [
('arquivo',
'administrativo/{}',
'private/documentoacessorioadministrativo/{0}/',
___)
],
CasaLegislativa: [('logotipo', 'props_sapl/{}.*')],
Parlamentar: [('fotografia', 'parlamentar/fotos/{}_foto_parlamentar')],
MateriaLegislativa: [('texto_original', 'materia/{}_texto_integral')],
DocumentoAcessorio: [('arquivo', 'materia/{}')],
NormaJuridica: [('texto_integral', 'norma_juridica/{}_texto_integral')],
SessaoPlenaria: [('upload_pauta', 'pauta_sessao/{}_pauta_sessao'),
('upload_ata', 'ata_sessao/{}_ata_sessao'),
('upload_anexo', 'anexo_sessao/{}_texto_anexado')],
Proposicao: [('texto_original', 'proposicao/{}')],
DocumentoAdministrativo: [('texto_integral',
'administrativo/{}_texto_integral')],
DocumentoAcessorioAdministrativo: [('arquivo', 'administrativo/{}')],
}
DOCS = {model: [(campo,
os.path.join('sapl_documentos', origem),
os.path.join('sapl', destino),
get_extra_args)
for campo, origem, destino, get_extra_args in campos]
DOCS = {model: [(campo, join('sapl_documentos', origem))
for campo, origem, in campos]
for model, campos in DOCS.items()}
def em_media(caminho):
return os.path.join(MEDIA_ROOT, caminho)
def mover_documento(origem, destino):
origem, destino = [em_media(c) if not os.path.isabs(c) else c
def mover_documento(repo, origem, destino):
origem, destino = [join(repo.working_dir, c) if not os.path.isabs(c) else c
for c in (origem, destino)]
os.makedirs(os.path.dirname(destino), exist_ok=True)
os.rename(origem, destino)
repo.git.mv(origem, destino)
def migrar_propriedades_da_casa():
def migrar_logotipo(repo, casa, propriedades):
print('.... Migrando logotipo da casa ....')
[(campo, origem)] = DOCS[CasaLegislativa]
# a extensão do logo pode ter sido ajustada pelo tipo real do arquivo
nome_nas_propriedades = os.path.splitext(propriedades['id_logo'])[0]
arquivos = glob(join(repo.working_dir, origem.format(nome_nas_propriedades)))
if arquivos:
assert len(arquivos) == 1, 'Há mais de um logotipo para a casa'
[logo] = arquivos
destino = join(CasaLegislativa._meta.get_field(campo).upload_to,
os.path.basename(logo))
mover_documento(repo, logo, destino)
casa.logotipo = destino
def migrar_propriedades_da_casa(repo):
print('#### Migrando propriedades da casa ####')
caminho = em_media('sapl_documentos/propriedades.yaml')
caminho = join(repo.working_dir, 'sapl_documentos/propriedades.yaml')
repo.git.execute('git annex get'.split() + [caminho])
with open(caminho, 'r') as arquivo:
propriedades = yaml.safe_load(arquivo)
casa = CasaLegislativa.objects.first()
@ -134,67 +85,73 @@ def migrar_propriedades_da_casa():
for campo, prop in campos_para_propriedades:
setattr(casa, campo, propriedades[prop])
# Localidade
# localidade
sql_localidade = '''
select nom_localidade, sgl_uf from localidade
where cod_localidade = {}'''.format(propriedades['cod_localidade'])
[(casa.municipio, casa.uf)] = exec_legado(sql_localidade)
print('.... Migrando logotipo da casa ....')
[(_, origem, destino, __)] = DOCS[CasaLegislativa]
# a extensão do logo pode ter sido ajustada pelo tipo real do arquivo
id_logo = os.path.splitext(propriedades['id_logo'])[0]
[origem] = glob(em_media(origem.format(id_logo)))
destino = os.path.join(destino, os.path.basename(origem))
mover_documento(origem, destino)
casa.logotipo = destino
# logotipo
migrar_logotipo(repo, casa, propriedades)
casa.save()
os.remove(caminho)
repo.git.rm(caminho)
def migrar_docs_por_ids(model):
for campo, base_origem, base_destino, get_extra_args in DOCS[model]:
def migrar_docs_por_ids(repo, model):
for campo, base_origem in DOCS[model]:
print('#### Migrando {} de {} ####'.format(campo, model.__name__))
dir_origem, nome_origem = os.path.split(em_media(base_origem))
dir_origem, nome_origem = os.path.split(
join(repo.working_dir, base_origem))
nome_origem = nome_origem.format('(\d+)')
pat = re.compile('^{}\.\w+$'.format(nome_origem))
if not os.path.isdir(dir_origem):
print(' >>> O diretório {} não existe! Abortado.'.format(
dir_origem))
continue
for arq in os.listdir(dir_origem):
match = pat.match(arq)
if match:
matches = [pat.match(arq) for arq in os.listdir(dir_origem)]
ids_origens = [(int(m.group(1)),
join(dir_origem, m.group(0)))
for m in matches if m]
objetos = {obj.id: obj for obj in model.objects.all()}
upload_to = model._meta.get_field(campo).upload_to
tem_cropping = isinstance(model._meta.get_field(campo), ImageCropField)
with transaction.atomic():
for id, origem in ids_origens:
# associa documento ao objeto
origem = os.path.join(dir_origem, match.group(0))
id = match.group(1)
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:
destino = os.path.join(
base_destino.format(id, *get_extra_args(obj)),
os.path.basename(origem))
mover_documento(origem, destino)
obj = objetos.get(id)
if obj:
destino = upload_to(obj, os.path.basename(origem))
mover_documento(repo, origem, destino)
setattr(obj, campo, destino)
if tem_cropping:
# conserta link do git annex (antes do commit)
# pois o conteúdo das imagens é acessado pelo cropping
repo.git.execute('git annex fix'.split() + [destino])
obj.save()
else:
msg = ' {} (pk={}) não encontrado para documento em [{}]'
print(msg.format(model.__name__, id, origem))
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!
# A pasta, após conferência do que não foi migrado, deve ser apagada.
def migrar_documentos(repo):
# aqui supomos que as pastas XSLT e sapl_documentos estão em
# <repo.working_dir> com o conteúdo exportado do zope
# Os arquivos das pastas serão (git) MOVIDOS para a nova estrutura!
#
# Isto significa que para rodar novamente esta função é preciso
# restaurar a pasta sapl_documentos ao estado inicial
# restaurar o repo ao estado anterior
mover_documento(repo, 'XSLT', 'sapl/public/XSLT')
migrar_propriedades_da_casa(repo)
migrar_propriedades_da_casa()
# garante que o conteúdo das fotos dos parlamentares esteja presente
# (necessário para o cropping de imagem)
repo.git.execute('git annex get sapl_documentos/parlamentar'.split())
for model in [
Parlamentar,
@ -206,14 +163,13 @@ def migrar_documentos():
DocumentoAdministrativo,
DocumentoAcessorioAdministrativo,
]:
migrar_docs_por_ids(model)
migrar_docs_por_ids(repo, model)
sobrando = [os.path.join(dir, file)
for (dir, _, files) in os.walk(em_media('sapl_documentos'))
sobrando = [join(dir, file)
for (dir, _, files) in os.walk(join(repo.working_dir,
'sapl_documentos'))
for file in files]
if sobrando:
print('\n#### Encerrado ####\n\n'
'{} documentos sobraram sem ser migrados!!!'.format(
len(sobrando)))
for doc in sobrando:
print(' {}'. format(doc))

8
sapl/legacy/migracao_usuarios.py

@ -1,8 +1,8 @@
import yaml
from django.contrib.auth.models import Group, User
from unipath import Path
from sapl.hashers import zope_encoded_password_to_django
from sapl.settings import MEDIA_ROOT
PERFIL_LEGADO_PARA_NOVO = {legado: Group.objects.get(name=novo)
for legado, novo in [
@ -44,9 +44,9 @@ def decode_nome(nome):
return nome
def migrar_usuarios():
def migrar_usuarios(dir_repo):
"""
o arquivo media/usuarios.yaml e importa os usuários nele listados,
o arquivo <dir_repo>/usuarios.yaml e importa os usuários nele listados,
com senhas e perfis.
Os usuários são criados se necessário e seus perfis ajustados.
@ -68,7 +68,7 @@ def migrar_usuarios():
Também podemos assumir que essa é uma tarefa de um administrador
"""
ARQUIVO_USUARIOS = MEDIA_ROOT.child('usuarios.yaml')
ARQUIVO_USUARIOS = Path(dir_repo).child('usuarios.yaml')
with open(ARQUIVO_USUARIOS, 'r') as f:
usuarios = yaml.load(f)
# conferimos de que só há um nome de usuário

2
sapl/legacy/models.py

@ -433,7 +433,7 @@ class MateriaLegislativa(models.Model):
cod_regime_tramitacao = models.IntegerField()
dat_publicacao = models.DateField(blank=True, null=True)
tip_origem_externa = models.IntegerField(blank=True, null=True)
num_origem_externa = models.CharField(max_length=5, blank=True, null=True)
num_origem_externa = models.CharField(max_length=10, blank=True, null=True)
ano_origem_externa = models.SmallIntegerField(blank=True, null=True)
dat_origem_externa = models.DateField(blank=True, null=True)
cod_local_origem_externa = models.IntegerField(blank=True, null=True)

4
sapl/legacy/router.py

@ -16,7 +16,7 @@ class LegacyRouter:
return True
return None
def allow_migrate(self, db, model):
if model._meta.app_label == 'legacy':
def allow_migrate(self, db, app_label, model_name=None, **hints):
if app_label == 'legacy':
return False
return None

211
sapl/legacy/scripts/exporta_zope/exporta_zope.py

@ -5,6 +5,8 @@
# Esse script precisa rodar em python 2
# e depende apenas do descrito no arquivo requiments.txt
import cStringIO
import hashlib
import mimetypes
import os
import sys
@ -12,21 +14,32 @@ from collections import defaultdict
from contextlib import contextmanager
from functools import partial
import git
import magic
import yaml
import ZODB.DB
import ZODB.FileStorage
from unipath import Path
from ZODB.broken import Broken
from variaveis_comuns import DIR_DADOS_MIGRACAO, TAG_ZOPE
EXTENSOES = {
'application/msword': '.doc',
'application/pdf': '.pdf',
'application/vnd.oasis.opendocument.text': '.odt',
'application/vnd.ms-excel': '.xls',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document': '.docx', # noqa
'application/vnd.oasis.opendocument.text-template': '.ott',
'application/vnd.ms-powerpoint': '.ppt',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': '.xlsx', # noqa
'application/vnd.oasis.opendocument.spreadsheet': '.ods',
'application/xml': '.xml',
'text/xml': '.xml',
'application/zip': '.zip',
'application/x-rar': '.rar',
'image/jpeg': '.jpeg',
'image/png': '.png',
'image/gif': '.gif',
@ -38,6 +51,11 @@ EXTENSOES = {
'image/tiff': '.tiff',
'application/tiff': '.tiff',
'audio/x-wav': '.wav',
'video/mp4': '.mp4',
'image/x-icon': '.ico',
'image/x-ms-bmp': '.bmp',
'video/x-ms-asf': '.asf',
'audio/mpeg': '.mp3',
# TODO rever...
'text/richtext': '.rtf',
@ -45,7 +63,9 @@ EXTENSOES = {
# sem extensao
'application/octet-stream': '', # binário
'inode/x-empty': '', # vazio
'text/x-unknown-content-type': '',
'application/x-empty': '', # vazio
'text/x-unknown-content-type': '', # desconhecido
'application/CDFV2-unknown': '', # desconhecido
}
@ -56,29 +76,26 @@ def br(obj):
return obj
def guess_extension(caminho):
mime = magic.from_file(caminho, mime=True)
try:
return EXTENSOES[mime]
except KeyError as e:
msg = '\n'.join([
'Extensão não conhecida para o arquivo:',
caminho,
'E mimetype:',
mime,
' Algumas possibilidades são:', ] +
def guess_extension(fullname, buffer):
mime = magic.from_buffer(buffer, mime=True)
extensao = EXTENSOES.get(mime)
if extensao is not None:
return extensao
else:
possibilidades = '\n'.join(
[" '{}': '{}',".format(mime, ext)
for ext in mimetypes.guess_all_extensions(mime)] +
['Atualize o código do dicionário EXTENSOES!']
for ext in mimetypes.guess_all_extensions(mime)])
print('''Extensão não conhecida para o arquivo: {}
e mimetype: {}
Algumas possibilidades são:
{}
Atualize o código do dicionário EXTENSOES!
'''.format(fullname, mime, possibilidades)
)
print(msg)
raise Exception(msg, e)
return '.DESCONHECIDO.{}'.format(mime.replace('/', '__'))
def dump_file(doc, path):
name = doc['__name__']
fullname = os.path.join(path, name)
def get_conteudo_file(doc):
# A partir daqui usamos dict.pop('...') nos __Broken_state__
# para contornar um "vazamento" de memória que ocorre
# ao percorrer a árvore de objetos
@ -95,25 +112,28 @@ def dump_file(doc, path):
doc['data'] = pdata
pdata = doc
with open(fullname, 'w') as arq:
output = cStringIO.StringIO()
while pdata:
arq.write(pdata.pop('data'))
output.write(pdata.pop('data'))
pdata = br(pdata.pop('next', None))
base, original_extension = os.path.splitext(fullname)
extension = guess_extension(fullname)
if extension == '.xml' and original_extension in ['.xsl', '.xslt']:
# não trocamos as extensões XSL e XSLT
final_name = fullname
else:
# trocamos a extensão pela adivinhada
final_name = base + extension
os.rename(fullname, final_name)
print(final_name)
return output.getvalue()
def dump_file(doc, path, salvar, get_conteudo=get_conteudo_file):
name = doc['__name__']
fullname = os.path.join(path, name)
conteudo = get_conteudo(doc)
if conteudo:
# pula arquivos vazios
salvar(fullname, conteudo)
return name
def get_conteudo_dtml_method(doc):
return doc['raw']
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]
@ -135,7 +155,11 @@ def enumerate_btree(folder):
obj, meta_type = br(obj), type(obj).__name__
yield id, obj, meta_type
# verificação de consistência
assert contagem_esperada == contagem_real
if contagem_esperada != contagem_real:
print('ATENÇÃO: contagens diferentes na btree: '
'{} esperada: {} real: {}'.format(folder,
contagem_esperada,
contagem_real))
nao_identificados = defaultdict(list)
@ -148,14 +172,14 @@ def logando_nao_identificados():
if nao_identificados:
print('#' * 80)
print('#' * 80)
print(u'FORAM ENCONTRADOS ARQUIVOS DE FORMATO NÃO IDENTIFICADO!!!')
print(u'REFAÇA A EXPORTAÇÃO\n')
print('FORAM ENCONTRADOS ARQUIVOS DE FORMATO NÃO IDENTIFICADO!!!')
print('REFAÇA A EXPORTAÇÃO\n')
print(nao_identificados)
print('#' * 80)
print('#' * 80)
def dump_folder(folder, path='', enum=enumerate_folder):
def dump_folder(folder, path, salvar, enum=enumerate_folder):
name = folder['id']
path = os.path.join(path, name)
if not os.path.exists(path):
@ -165,7 +189,7 @@ def dump_folder(folder, path='', enum=enumerate_folder):
if dump == '?':
nao_identificados[meta_type].append(path + '/' + id)
elif dump:
id_interno = dump(obj, path)
id_interno = dump(obj, path, salvar)
assert id == id_interno
return name
@ -201,24 +225,24 @@ def read_sde(element):
return data
def save_as_yaml(path, name, obj):
def save_as_yaml(path, name, obj, salvar):
fullname = os.path.join(path, name)
with open(fullname, 'w') as arquivo:
yaml.safe_dump(obj, arquivo, allow_unicode=True)
print(fullname)
return fullname
conteudo = yaml.safe_dump(obj, allow_unicode=True)
salvar(fullname, conteudo)
def dump_sde(strdoc, path, tipo):
def dump_sde(strdoc, path, salvar, tipo):
id = strdoc['id']
sde = read_sde(strdoc)
save_as_yaml(path, '{}.{}.yaml'.format(id, tipo), sde)
save_as_yaml(path, '{}.{}.yaml'.format(id, tipo), sde, salvar)
return id
DUMP_FUNCTIONS = {
'File': dump_file,
'Image': dump_file,
'DTML Method': partial(dump_file,
get_conteudo=get_conteudo_dtml_method),
'Folder': partial(dump_folder, enum=enumerate_folder),
'BTreeFolder2': partial(dump_folder, enum=enumerate_btree),
'SDE-Document': partial(dump_sde, tipo='sde.document'),
@ -233,7 +257,7 @@ DUMP_FUNCTIONS = {
def get_app(data_fs_path):
storage = ZODB.FileStorage.FileStorage(data_fs_path)
storage = ZODB.FileStorage.FileStorage(data_fs_path, read_only=True)
db = ZODB.DB(storage)
connection = db.open()
root = connection.root()
@ -255,42 +279,115 @@ def find_sapl(app):
return sapl
def dump_propriedades(docs, path, encoding='iso-8859-1'):
def dump_propriedades(docs, path, salvar, encoding='iso-8859-1'):
props_sapl = br(docs['props_sapl'])
ids = [p['id'] for p in props_sapl['_properties']]
props = {id: props_sapl[id] for id in ids}
props = {id: p.decode(encoding) if isinstance(p, str) else p
for id, p in props.items()}
save_as_yaml(path, 'sapl_documentos/propriedades.yaml', props)
save_as_yaml(path, 'sapl_documentos/propriedades.yaml', props, salvar)
def dump_usuarios(sapl, path):
def dump_usuarios(sapl, path, salvar):
users = br(br(sapl['acl_users'])['data'])
users = {k: br(v) for k, v in users['data'].items()}
save_as_yaml(path, 'usuarios.yaml', users)
save_as_yaml(path, 'usuarios.yaml', users, salvar)
def dump_sapl(data_fs_path, destino='../../../../media'):
def _dump_sapl(data_fs_path, destino, salvar):
assert Path(data_fs_path).exists()
app, close_db = get_app(data_fs_path)
try:
sapl = find_sapl(app)
# extrai folhas XSLT
dump_folder(br(sapl['XSLT']), destino)
dump_folder(br(sapl['XSLT']), destino, salvar)
# extrai usuários com suas senhas e perfis
dump_usuarios(sapl, destino)
dump_usuarios(sapl, destino, salvar)
# extrai documentos
docs = br(sapl['sapl_documentos'])
with logando_nao_identificados():
dump_folder(docs, destino)
dump_propriedades(docs, destino)
dump_folder(docs, destino, salvar)
dump_propriedades(docs, destino, salvar)
finally:
close_db()
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
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)
return salvar
def dump_sapl(sigla):
sigla = sigla[-3:] # ignora prefixo (por ex. 'sapl_cm_')
data_fs_path = DIR_DADOS_MIGRACAO.child('datafs',
'Data_cm_{}.fs'.format(sigla))
assert data_fs_path.exists(), 'Origem não existe: {}'.format(data_fs_path)
nome_banco_legado = 'sapl_cm_{}'.format(sigla)
destino = DIR_DADOS_MIGRACAO.child('repos', nome_banco_legado)
destino.mkdir(parents=True)
repo = git.Repo.init(destino)
if TAG_ZOPE in repo.tags:
print('A exportação de documentos já está feita -- abortando')
return
repo_execute(repo, 'git annex init')
repo_execute(repo, 'git config annex.thin true')
salvar = build_salvar(repo)
try:
finalizado = False
_dump_sapl(data_fs_path, destino, salvar)
finalizado = True
finally:
# grava mundaças
repo_execute(repo, 'git annex add sapl_documentos')
repo.git.add(A=True)
if 'master' not in repo.heads or repo.index.diff('HEAD'):
# se de fato existe mudança
status = 'completa' if finalizado else 'parcial'
repo.index.commit(u'Exportação do zope {}'.format(status))
if finalizado:
repo.git.execute('git tag -f'.split() + [TAG_ZOPE])
if __name__ == "__main__":
if len(sys.argv) == 2:
data_fs_path = sys.argv[1]
dump_sapl(data_fs_path)
sigla = sys.argv[1]
dump_sapl(sigla)
else:
print('Uso: python exporta_zope <caminho p Data.fs>')
print('Uso: python exporta_zope <sigla>')

7
sapl/legacy/scripts/exporta_zope/requirements.txt

@ -1,3 +1,8 @@
# ZODB version 3.7.4
PyYAML==3.12
ZODB==5.3.0
PyYAML
Unipath
GitPython
pyaml
python-magic
ipython

4
sapl/legacy/scripts/exporta_zope/variaveis_comuns.py

@ -0,0 +1,4 @@
from unipath import Path
DIR_DADOS_MIGRACAO = Path('~/migracao_sapl/').expand()
TAG_ZOPE = 'zope'

26
sapl/legacy/scripts/migra_um_db.sh

@ -1,10 +1,7 @@
#!/bin/bash
# rodar esse script na raiz do projeto
if [ $# -ge 2 ]; then
# proteje pasta com dumps de alterações acidentais
# chmod -R -w ~/migracao_sapl/sapl_dumps
if [ $# -eq 1 ]; then
DIR_MIGRACAO=~/migracao_sapl
@ -20,28 +17,11 @@ if [ $# -ge 2 ]; then
echo "########################################" | tee -a $LOG
echo >> $LOG
if [ $3 ]; then
# se há senha do mysql
mysql -u$2 -p"$3" -N -s -e "DROP DATABASE IF EXISTS $1; CREATE DATABASE $1;"
mysql -u$2 -p"$3" < $DIR_MIGRACAO/dumps_mysql/$1.sql
else
# se não há senha do mysql
mysql -u$2 -N -s -e "DROP DATABASE IF EXISTS $1; CREATE DATABASE $1;"
mysql -u$2 < $DIR_MIGRACAO/dumps_mysql/$1.sql
fi;
echo "O banco legado foi restaurado" |& tee -a $LOG
echo >> $LOG
echo "--- DJANGO MIGRATE ---" | tee -a $LOG
echo >> $LOG
DATABASE_NAME=$1 ./manage.py migrate --settings sapl.legacy_migration_settings
echo >> $LOG
echo "--- MIGRACAO ---" | tee -a $LOG
echo >> $LOG
DATABASE_NAME=$1 ./manage.py migracao_25_31 --force --dados --settings sapl.legacy_migration_settings 2>&1 | tee -a $LOG
DATABASE_NAME=$1 ./manage.py migracao_25_31 --settings sapl.legacy_migration_settings 2>&1 | tee -a $LOG
echo >> $LOG
else
echo "USO:"
echo " $0 <nome_database> <usuário mysql> [senha mysql]"
echo " $0 <nome_database>"
fi;

40
sapl/legacy/scripts/normaliza_dump_mysql.py

@ -0,0 +1,40 @@
#!/usr/bin/env python
import re
import sys
from unipath import Path
cabecalho = '''
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
/*!40000 DROP DATABASE IF EXISTS `{banco}`*/;
CREATE DATABASE /*!32312 IF NOT EXISTS*/ `{banco}` /*!40100 DEFAULT CHARACTER SET latin1 */;
USE `{banco}`;
'''
def normaliza_dump_mysql(nome_arquivo):
arquivo = Path(nome_arquivo).expand()
banco = arquivo.stem
conteudo = arquivo.read_file()
inicio = re.finditer('--\n-- Table structure for table .*\n--\n', conteudo)
inicio = next(inicio).start()
conteudo = cabecalho.format(banco=banco) + conteudo[inicio:]
arquivo.write_file(conteudo)
if __name__ == "__main__":
nome_aquivo = sys.argv[1]
normaliza_dump_mysql(nome_aquivo)

28
sapl/legacy/scripts/normaliza_dump_mysql.sh

@ -1,28 +0,0 @@
#!/bin/bash
ARQUIVO=$1
BANCO=`basename $1 | cut -f1 -d.`
TMP=__tmp.sql
cat << EOF > $TMP
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
/*!40000 DROP DATABASE IF EXISTS \`$BANCO\`*/;
CREATE DATABASE /*!32312 IF NOT EXISTS*/ \`$BANCO\` /*!40100 DEFAULT CHARACTER SET latin1 */;
USE \`$BANCO\`;
EOF
sed 1,`grep -n '^USE ' $ARQUIVO |cut -f1 -d:`d $ARQUIVO >> $TMP
mv $TMP $ARQUIVO

5
sapl/legacy/scripts/recria_um_db_postgres.sh

@ -4,3 +4,8 @@
echo "Database $1"
sudo -u postgres psql -c "drop DATABASE if exists $1"
sudo -u postgres psql -c "CREATE DATABASE $1 WITH OWNER = sapl ENCODING = 'UTF8' TABLESPACE = pg_default LC_COLLATE = 'pt_BR.UTF-8' LC_CTYPE = 'pt_BR.UTF-8' CONNECTION LIMIT = -1 TEMPLATE template0;"
echo "--- DJANGO MIGRATE ---" | tee -a $LOG
DATABASE_NAME=$1 ./manage.py migrate --settings sapl.legacy_migration_settings

10
sapl/legacy_migration_settings.py

@ -3,6 +3,9 @@ import os
from decouple import Config, RepositoryEnv
from dj_database_url import parse as db_url
from sapl.legacy.scripts.exporta_zope.variaveis_comuns import \
DIR_DADOS_MIGRACAO
from .settings import * # flake8: noqa
config = Config(RepositoryEnv(BASE_DIR.child('legacy', '.env')))
@ -33,3 +36,10 @@ DEBUG = True
# delisga indexação fulltext em tempo real
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.BaseSignalProcessor'
SHELL_PLUS_DONT_LOAD = ['legacy']
NOME_BANCO_LEGADO = DATABASES['legacy']['NAME']
DIR_REPO = Path(DIR_DADOS_MIGRACAO, 'repos', NOME_BANCO_LEGADO)
MEDIA_ROOT = DIR_REPO

12
sapl/materia/forms.py

@ -582,7 +582,17 @@ class AnexadaForm(ModelForm):
msg = _('A matéria a ser anexada não existe no cadastro'
' de matérias legislativas.')
raise ValidationError(msg)
else:
materia_principal = self.instance.materia_principal
if materia_principal == materia_anexada:
raise ValidationError(_('Matéria não pode ser anexada a si mesma'))
is_anexada = Anexada.objects.filter(materia_principal=materia_principal,
materia_anexada=materia_anexada
).exists()
if is_anexada:
raise ValidationError(_('Materia já se encontra anexada'))
cleaned_data['materia_anexada'] = materia_anexada
return cleaned_data

20
sapl/materia/migrations/0028_auto_20180418_1629.py

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.13 on 2018-04-18 19:29
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('materia', '0027_auto_20180409_1443'),
]
operations = [
migrations.AlterField(
model_name='materialegislativa',
name='numero_origem_externa',
field=models.CharField(blank=True, max_length=10, verbose_name='Número'),
),
]

16
sapl/materia/models.py

@ -167,7 +167,7 @@ class MateriaLegislativa(models.Model):
on_delete=models.PROTECT,
verbose_name=_('Tipo'))
numero_origem_externa = models.CharField(
max_length=5, blank=True, verbose_name=_('Número'))
max_length=10, blank=True, verbose_name=_('Número'))
ano_origem_externa = models.PositiveSmallIntegerField(
blank=True, null=True, verbose_name=_('Ano'), choices=RANGE_ANOS)
data_origem_externa = models.DateField(
@ -325,9 +325,17 @@ class AcompanhamentoMateria(models.Model):
verbose_name_plural = _('Acompanhamentos de Matéria')
def __str__(self):
# FIXME str should be human readable, using hash is very strange
return _('%(materia)s - #%(hash)s') % {
'materia': self.materia, 'hash': self.hash}
if self.data_cadastro is None:
return _('%(materia)s - %(email)s') % {
'materia': self.materia,
'email': self.email
}
else:
return _('%(materia)s - %(email)s - Registrado em: %(data)s') % {
'materia': self.materia,
'email': self.email,
'data': str(self.data_cadastro.strftime('%d/%m/%Y'))
}
@reversion.register()

3
sapl/norma/views.py

@ -40,10 +40,9 @@ class NormaRelacionadaCrud(MasterDetailCrud):
model = NormaRelacionada
parent_field = 'norma_principal'
help_topic = 'norma_juridica'
public = [RP_LIST, RP_DETAIL]
class BaseMixin(MasterDetailCrud.BaseMixin):
list_field_names = ['norma_relacionada']
list_field_names = ['norma_relacionada', 'tipo_vinculo']
class CreateView(MasterDetailCrud.CreateView):
form_class = NormaRelacionadaForm

2
sapl/parlamentares/views.py

@ -192,7 +192,7 @@ class ColigacaoCrud(CrudAux):
help_topic = 'coligacao'
class ListView(CrudAux.ListView):
ordering = ('-numero_votos', 'nome')
ordering = ('legislatura', '-nome')
def get_context_data(self, **kwargs):
context = super(ColigacaoCrud.ListView, self).get_context_data(

31
sapl/protocoloadm/forms.py

@ -155,6 +155,7 @@ class DocumentoAdministrativoFilterSet(django_filters.FilterSet):
fields = ['tipo',
'numero',
'protocolo__numero',
'numero_externo',
'data',
'tramitacaoadministrativo__unidade_tramitacao_destino',
'tramitacaoadministrativo__status']
@ -173,7 +174,8 @@ class DocumentoAdministrativoFilterSet(django_filters.FilterSet):
row2 = to_row(
[('ano', 4),
('protocolo__numero', 4),
('protocolo__numero', 2),
('numero_externo', 2),
('data', 4)])
row3 = to_row(
@ -645,6 +647,7 @@ class DocumentoAdministrativoForm(ModelForm):
'tramitacao',
'dias_prazo',
'data_fim_prazo',
'numero_externo',
'observacao',
'texto_integral',
'protocolo',
@ -655,9 +658,6 @@ class DocumentoAdministrativoForm(ModelForm):
def clean(self):
super(DocumentoAdministrativoForm, self).clean()
if not self.is_valid():
return self.cleaned_data
cleaned_data = self.cleaned_data
if not self.is_valid():
@ -665,14 +665,21 @@ class DocumentoAdministrativoForm(ModelForm):
numero_protocolo = self.data['numero_protocolo']
ano_protocolo = self.data['ano_protocolo']
numero_documento = self.cleaned_data['numero']
tipo_documento = self.data['tipo']
if not self.instance.pk:
documento = DocumentoAdministrativo.objects.filter(numero=numero_documento,
numero_documento = int(self.cleaned_data['numero'])
tipo_documento = int(self.data['tipo'])
ano_documento = int(self.data['ano'])
# não permite atualizar para numero/ano/tipo existente
if self.instance.pk:
mudanca_doc = numero_documento != self.instance.numero \
or ano_documento != self.instance.ano \
or tipo_documento != self.instance.tipo.pk
if not self.instance.pk or mudanca_doc:
doc_exists = DocumentoAdministrativo.objects.filter(numero=numero_documento,
tipo=tipo_documento,
ano=ano_protocolo)
if documento:
ano=ano_protocolo).exists()
if doc_exists:
raise ValidationError('Documento já existente')
# campos opcionais, mas que se informados devem ser válidos
@ -721,7 +728,7 @@ class DocumentoAdministrativoForm(ModelForm):
[('texto_integral', 12)])
row6 = to_row(
[('dias_prazo', 6), ('data_fim_prazo', 6)])
[('numero_externo', 4), ('dias_prazo', 6), ('data_fim_prazo', 2)])
row7 = to_row(
[('observacao', 12)])

20
sapl/protocoloadm/migrations/0004_documentoadministrativo_numero_externo.py

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.11 on 2018-04-25 18:40
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('protocoloadm', '0003_auto_20180103_1343'),
]
operations = [
migrations.AddField(
model_name='documentoadministrativo',
name='numero_externo',
field=models.PositiveIntegerField(blank=True, null=True, verbose_name='Número Externo'),
),
]

4
sapl/protocoloadm/models.py

@ -133,6 +133,10 @@ class DocumentoAdministrativo(models.Model):
verbose_name=_('Em Tramitação?'),
choices=YES_NO_CHOICES)
assunto = models.TextField(verbose_name=_('Assunto'))
numero_externo = models.PositiveIntegerField(
blank=True,
null=True,
verbose_name=_('Número Externo'))
observacao = models.TextField(
blank=True, verbose_name=_('Observação'))
texto_integral = models.FileField(

2
sapl/sessao/forms.py

@ -452,7 +452,7 @@ class OradorForm(ModelForm):
sessao_plenaria_id=id_sessao)]
self.fields['parlamentar'].queryset = Parlamentar.objects.filter(
id__in=ids).order_by('nome_completo')
id__in=ids).order_by('nome_parlamentar')
class Meta:
model = Orador

78
sapl/sessao/views.py

@ -30,12 +30,11 @@ from sapl.materia.forms import filtra_tramitacao_status
from sapl.materia.models import (Autoria, DocumentoAcessorio,
TipoMateriaLegislativa, Tramitacao)
from sapl.materia.views import MateriaLegislativaPesquisaView
from sapl.norma.models import NormaJuridica
from sapl.parlamentares.models import (Filiacao, Legislatura, Mandato,
Parlamentar, SessaoLegislativa)
from sapl.sessao.apps import AppConfig
from sapl.sessao.forms import ExpedienteMateriaForm, OrdemDiaForm
from sapl.utils import show_results_filter_set
from sapl.utils import show_results_filter_set, remover_acentos
from .forms import (AdicionarVariasMateriasFilterSet, BancadaForm, BlocoForm,
ExpedienteForm, ListMateriaForm, MesaForm,
@ -393,6 +392,8 @@ def get_presencas_generic(model, sessao, legislatura):
presentes = [p.parlamentar for p in presencas]
presentes = sorted(presentes, key=lambda x: remover_acentos(x.nome_parlamentar))
mandato = Mandato.objects.filter(
legislatura=legislatura).order_by('parlamentar__nome_parlamentar')
@ -1112,15 +1113,13 @@ def remove_parlamentar_composicao(request):
if 'composicao_mesa' in request.POST:
try:
composicao = IntegranteMesa.objects.get(
id=int(request.POST['composicao_mesa']))
IntegranteMesa.objects.get(
id=int(request.POST['composicao_mesa'])).delete()
except ObjectDoesNotExist:
return JsonResponse(
{'msg': (
'Composição da Mesa não pôde ser removida!', 0)})
composicao.delete()
return JsonResponse(
{'msg': (
'Parlamentar excluido com sucesso!', 1)})
@ -1449,6 +1448,11 @@ class ExpedienteView(FormMixin, DetailView):
self.object = self.get_object()
form = ExpedienteForm(request.POST)
if 'apagar-expediente' in request.POST:
ExpedienteSessao.objects.filter(
sessao_plenaria_id=self.object.id).delete()
return self.form_valid(form)
if form.is_valid():
list_tipo = request.POST.getlist('tipo')
list_conteudo = request.POST.getlist('conteudo')
@ -1524,8 +1528,7 @@ class VotacaoEditView(SessaoPermissionMixin):
ordem_id = kwargs['oid']
if(int(request.POST['anular_votacao']) == 1):
for r in RegistroVotacao.objects.filter(ordem_id=ordem_id):
r.delete()
RegistroVotacao.objects.filter(ordem_id=ordem_id).delete()
ordem = OrdemDia.objects.get(
sessao_plenaria_id=self.object.id,
@ -1556,8 +1559,7 @@ class VotacaoEditView(SessaoPermissionMixin):
materia = {'materia': ordem.materia, 'ementa': ordem.materia.ementa}
context.update({'materia': materia})
votacao = RegistroVotacao.objects.filter(
materia_id=materia_id,
votacao = RegistroVotacao.objects.filter(materia_id=materia_id,
ordem_id=ordem_id).last()
votacao_existente = {'observacao': sub(
'&nbsp;', ' ', strip_tags(votacao.observacao)),
@ -1705,8 +1707,7 @@ def fechar_votacao_materia(materia):
VotoParlamentar.objects.filter(ordem=materia).delete()
elif type(materia) == ExpedienteMateria:
RegistroVotacao.objects.filter(
expediente=materia).delete()
RegistroVotacao.objects.filter(expediente=materia).delete()
VotoParlamentar.objects.filter(expediente=materia).delete()
if materia.resultado:
@ -1853,8 +1854,7 @@ class VotacaoNominalAbstract(SessaoPermissionMixin):
return self.form_invalid(form)
# Remove todas as votação desta matéria, caso existam
if self.ordem:
RegistroVotacao.objects.filter(
ordem_id=ordem_id).delete()
RegistroVotacao.objects.filter(ordem_id=ordem_id).delete()
elif self.expediente:
RegistroVotacao.objects.filter(
expediente_id=expediente_id).delete()
@ -1982,11 +1982,10 @@ class VotacaoNominalEditAbstract(SessaoPermissionMixin):
if self.ordem:
ordem_id = kwargs['oid']
try:
ordem = OrdemDia.objects.get(id=ordem_id)
votacao = RegistroVotacao.objects.get(
ordem_id=ordem_id)
except ObjectDoesNotExist:
ordem = OrdemDia.objects.filter(id=ordem_id).last()
votacao = RegistroVotacao.objects.filter(ordem_id=ordem_id).last()
if not ordem or not votacao:
raise Http404()
materia = ordem.materia
@ -1995,11 +1994,10 @@ class VotacaoNominalEditAbstract(SessaoPermissionMixin):
elif self.expediente:
expediente_id = kwargs['oid']
try:
expediente = ExpedienteMateria.objects.get(id=expediente_id)
votacao = RegistroVotacao.objects.get(
expediente_id=expediente_id)
except ObjectDoesNotExist:
expediente = ExpedienteMateria.objects.filter(id=expediente_id).last()
votacao = RegistroVotacao.objects.filter(expediente_id=expediente_id).last()
if not expediente or not votacao:
raise Http404()
materia = expediente.materia
@ -2116,9 +2114,9 @@ class VotacaoNominalTransparenciaDetailView(TemplateView):
materia_votacao = self.request.GET.get('materia', None)
if materia_votacao == 'ordem':
votacao = RegistroVotacao.objects.get(ordem=self.kwargs['oid'])
votacao = RegistroVotacao.objects.filter(ordem=self.kwargs['oid']).last()
elif materia_votacao == 'expediente':
votacao = RegistroVotacao.objects.get(expediente=self.kwargs['oid'])
votacao = RegistroVotacao.objects.filter(expediente=self.kwargs['oid']).last()
else:
raise Http404()
@ -2152,10 +2150,9 @@ class VotacaoNominalExpedienteDetailView(DetailView):
materia_id = kwargs['mid']
expediente_id = kwargs['oid']
votacao = RegistroVotacao.objects.get(
materia_id=materia_id,
expediente_id=expediente_id)
expediente = ExpedienteMateria.objects.get(id=expediente_id)
votacao = RegistroVotacao.objects.filter(materia_id=materia_id,
expediente_id=expediente_id).last()
expediente = ExpedienteMateria.objects.filter(id=expediente_id).last()
votos = VotoParlamentar.objects.filter(votacao_id=votacao.id)
list_votos = []
@ -2200,9 +2197,9 @@ class VotacaoSimbolicaTransparenciaDetailView(TemplateView):
materia_votacao = self.request.GET.get('materia', None)
if materia_votacao == 'ordem':
votacao = RegistroVotacao.objects.get(ordem=self.kwargs['oid'])
votacao = RegistroVotacao.objects.filter(ordem=self.kwargs['oid']).last()
elif materia_votacao == 'expediente':
votacao = RegistroVotacao.objects.get(expediente=self.kwargs['oid'])
votacao = RegistroVotacao.objects.filter(expediente=self.kwargs['oid']).last()
else:
raise Http404()
@ -2388,14 +2385,9 @@ class VotacaoExpedienteEditView(SessaoPermissionMixin):
'ementa': expediente.materia.ementa}
context.update({'materia': materia})
try:
votacao = RegistroVotacao.objects.get(
materia_id=materia_id,
expediente_id=expediente_id)
except MultipleObjectsReturned:
votacao = RegistroVotacao.objects.filter(
materia_id=materia_id,
expediente_id=expediente_id).last()
votacao = RegistroVotacao.objects.filter(materia_id=materia_id,
expediente_id=expediente_id
).last()
votacao_existente = {'observacao': sub(
'&nbsp;', ' ', strip_tags(votacao.observacao)),
'resultado': votacao.tipo_resultado_votacao.nome,
@ -2414,10 +2406,8 @@ class VotacaoExpedienteEditView(SessaoPermissionMixin):
materia_id = kwargs['mid']
expediente_id = kwargs['oid']
if(int(request.POST['anular_votacao']) == 1):
for r in RegistroVotacao.objects.filter(
expediente_id=expediente_id):
r.delete()
if int(request.POST['anular_votacao']) == 1:
RegistroVotacao.objects.filter(expediente_id=expediente_id).delete()
expediente = ExpedienteMateria.objects.get(
sessao_plenaria_id=self.object.id,

1
sapl/settings.py

@ -129,6 +129,7 @@ if DEBUG:
INSTALLED_APPS += ('debug_toolbar', 'rest_framework_docs',)
MIDDLEWARE += ['debug_toolbar.middleware.DebugToolbarMiddleware', ]
CACHES = {
'default': {
'BACKEND': 'speedinfo.backends.proxy_cache',

11
sapl/static/XSLT/HTML/.objects

@ -1,11 +0,0 @@
estilo.css:DTML Method
indicacao.xsl:File
mocao.xsl:File
mocao2.xsl:File
parecer.xsl:File
pedido.xsl:File
pedido2.xsl:File
pl.xsl:File
pl2.xsl:File
requerimento.xsl:File
requerimento2.xsl:File

95
sapl/static/XSLT/HTML/estilo.css

@ -1,95 +0,0 @@
body {
font-family: Times;
text-align: justify;
font-size: 12 pt;
margin: 5px 1cm 20px 2cm;
}
p,
.p{
font-family: Times;
text-align: justify;
font-size: 12pt;
text-indent: 1.5cm;
margin: 40px 0 20px 0;
}
.pequeno {
font-family: Times;
text-align: left;
font-size: 13pt;
margin: 0px 0 0px 0;
}
.cabecalho {
font-family: Times;
font-weight:bold;
text-align: left;
font-size: 15pt;
margin: 10px 0 0px 0;
}
.texto {
font-family: Times;
text-align: justify;
font-size: 12pt;
margin: 0px 0px 0px 0px;
}
.data {
text-align: right;
}
.autor {
text-align: center;
}
.center {
text-align: center;
}
.semrecuo {
text-indent: 0;
}
.ementa {
text-align: justify;
margin-left: 50%;
text-indent: 0;
}
.titulos1 {
text-align: center;
margin: 10px 0 0px 0;
}
.titulos2 {
text-align: center;
margin: 0px 0 0px 0;
}
p.artigo {
text-align: justify;
text-indent: 1cm;
margin: 10px 0 0px 0;
}
#imagem {
float:left;
}
#autores
{
-moz-column-count:3; /* Firefox */
-webkit-column-count:3; /* Safari and Chrome */
width:50px;
}
#col1 { width: 33%; float: left; center: 10px; }
#col2 { width: 33%; float: left; center: 10px; }
#col3 { width: 33%; float: left; center: 10px; }

51
sapl/static/XSLT/HTML/indicacao.xsl

@ -1,51 +0,0 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ind="/XSD/Indicacao">
<xsl:template match="/">
<html>
<head>
<link href="/XSLT/HTML/estilo.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<xsl:apply-templates />
</body>
</html>
</xsl:template>
<xsl:template match="ind:ementa_text">
<div>
<div id="imagem">
<img border="0" src="http://sapl.agudo.rs.leg.br/generico/sapl_documentos/props_sapl/logo_casa"/><br></br>
</div><br></br>
<p class ="cabecalho">Câmara Municipal de Agudo</p>
<p class ="pequeno"> Estado do Rio Grande do Sul <br></br><br></br><br></br></p>
</div>
<p class="autor"><strong><xsl:value-of select="text()" /></strong></p>
</xsl:template>
<xsl:template match="ind:autoria_text">
<p class="semrecuo"><xsl:value-of select="text()" /></p>
</xsl:template>
<xsl:template match="ind:destinatario_text">
<p class="semrecuo"><xsl:value-of select="text()" /></p>
</xsl:template>
<xsl:template match="ind:indicacao_text">
<p><xsl:value-of select="text()" /></p>
</xsl:template>
<xsl:template match="ind:data_text">
<p><xsl:value-of select="text()" /></p>
</xsl:template>
<xsl:template match="ind:autor_text">
<p class="autor"><xsl:value-of select="text()" /></p>
</xsl:template>
</xsl:stylesheet>

41
sapl/static/XSLT/HTML/mocao.xsl

@ -1,41 +0,0 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:moc="/XSD/Mocao">
<xsl:template match="/">
<html>
<body>
<table>
<xsl:apply-templates />
</table>
</body>
</html>
</xsl:template>
<xsl:template match="moc:ementa_text">
<tr>
<td width="50%"></td>
<td width="50%" align="left"><xsl:value-of select="text()" /></td>
</tr>
</xsl:template>
<xsl:template match="moc:mocao_text">
<tr>
<td colspan="2" align="left"><xsl:value-of select="text()" /></td>
</tr>
</xsl:template>
<xsl:template match="moc:data_text">
<tr>
<td colspan="2" align="left"><xsl:value-of select="text()" /></td>
</tr>
</xsl:template>
<xsl:template match="moc:autor_text">
<tr>
<td colspan="2" align="left"><xsl:value-of select="text()" /></td>
</tr>
</xsl:template>
</xsl:stylesheet>

45
sapl/static/XSLT/HTML/mocao2.xsl

@ -1,45 +0,0 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ind="/XSD/Mocao">
<xsl:template match="/">
<html>
<head>
<link href="/sapl/XSLT/HTML/estilo.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<div>
<div id="imagem">
<img border="0" src="/sapl/sapl_documentos/props_sapl/logo_casa"/>
</div><br></br>
<p class ="cabecalho">Câmara Municipal de Agudo</p>
<p class ="pequeno">Estado do Rio Grande do Sul<br></br><br></br><br></br> </p>
</div>
<xsl:apply-templates />
</body>
</html>
</xsl:template>
<xsl:template match="ind:ementa_text">
<p class ="autor"><strong><xsl:value-of select="text()" /></strong></p>
</xsl:template>
<xsl:template match="ind:mocao_text">
<p class ="texto"><xsl:value-of select="text()" /></p>
</xsl:template>
<xsl:template match="ind:justificativa_text">
<p class ="texto"><xsl:value-of select="text()" /></p>
</xsl:template>
<xsl:template match="ind:data_text">
<p><xsl:value-of select="text()" /></p>
</xsl:template>
<xsl:template match="ind:autor_text">
<p class="autor"><xsl:value-of select="text()" /></p>
</xsl:template>
</xsl:stylesheet>

41
sapl/static/XSLT/HTML/parecer.xsl

@ -1,41 +0,0 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:par="/XSD/Parecer">
<xsl:template match="/">
<html>
<body>
<table>
<xsl:apply-templates />
</table>
</body>
</html>
</xsl:template>
<xsl:template match="par:ementa_text">
<tr>
<td width="50%"></td>
<td width="50%" align="left"><xsl:value-of select="text()" /></td>
</tr>
</xsl:template>
<xsl:template match="par:parecer_text">
<tr>
<td colspan="2" align="left"><xsl:value-of select="text()" /></td>
</tr>
</xsl:template>
<xsl:template match="par:data_text">
<tr>
<td colspan="2" align="left"><xsl:value-of select="text()" /></td>
</tr>
</xsl:template>
<xsl:template match="par:autor_text">
<tr>
<td colspan="2" align="left"><xsl:value-of select="text()" /></td>
</tr>
</xsl:template>
</xsl:stylesheet>

47
sapl/static/XSLT/HTML/pedido.xsl

@ -1,47 +0,0 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:req="/XSD/Requerimento">
<xsl:template match="/">
<html>
<body>
<table>
<xsl:apply-templates />
</table>
</body>
</html>
</xsl:template>
<xsl:template match="req:ementa_text">
<tr>
<td width="50%"></td>
<td width="50%" align="left"><xsl:value-of select="text()" /></td>
</tr>
</xsl:template>
<xsl:template match="req:requisicao_text">
<tr>
<td colspan="2" align="left"><xsl:value-of select="text()" /></td>
</tr>
</xsl:template>
<xsl:template match="req:justificativa_text">
<tr>
<td colspan="2" align="left"><xsl:value-of select="text()" /></td>
</tr>
</xsl:template>
<xsl:template match="req:data_text">
<tr>
<td colspan="2" align="left"><xsl:value-of select="text()" /></td>
</tr>
</xsl:template>
<xsl:template match="req:autor_text">
<tr>
<td colspan="2" align="left"><xsl:value-of select="text()" /></td>
</tr>
</xsl:template>
</xsl:stylesheet>

53
sapl/static/XSLT/HTML/pedido2.xsl

@ -1,53 +0,0 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ind="/XSD/Pedido">
<xsl:template match="/">
<html>
<head>
<link href="/sapl/XSLT/HTML/estilo.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<div>
<div id="imagem">
<img border="0" src="/sapl/sapl_documentos/props_sapl/logo_casa"/>
</div><br></br>
<p class ="cabecalho">Câmara Municipal de Agudo</p>
<p class ="pequeno">Estado do Rio Grande do Sul<br></br><br></br><br></br> </p>
</div>
<xsl:apply-templates />
</body>
</html>
</xsl:template>
<xsl:template match="ind:ementa_text">
<p class ="autor"><strong><xsl:value-of select="text()" /></strong></p>
</xsl:template>
<xsl:template match="ind:autoria_text">
<p class="semrecuo"><xsl:value-of select="text()" /></p>
</xsl:template>
<xsl:template match="ind:destinatario_text">
<p class="semrecuo"><xsl:value-of select="text()" /></p>
</xsl:template>
<xsl:template match="ind:solicitacao_text">
<p class="texto"><xsl:value-of select="text()" /></p>
</xsl:template>
<xsl:template match="ind:alinea_text">
<p class="texto"><xsl:value-of select="concat(../@Rotulo,' ',text())"/></p>
</xsl:template>
<xsl:template match="ind:data_text">
<p><xsl:value-of select="text()" /></p>
</xsl:template>
<xsl:template match="ind:autor_text">
<p class="autor"><xsl:value-of select="text()" /></p>
</xsl:template>
</xsl:stylesheet>

105
sapl/static/XSLT/HTML/pl.xsl

@ -1,105 +0,0 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:pl="/XSD/ProjLei">
<xsl:output encoding="ISO-8859-1"/>
<xsl:template match="/pl:pl">
<html>
<head>
<title>
<xsl:value-of select="@id"/>
</title>
<style type="text/css">
body {margin-left: 2cm; margin-right: 1cm;}
p {font-family: Times; font-size: 12pt;}
p.epigrafe {text-align: center; text-transform: uppercase;}
p.ementa {text-align: justify; margin-left: 50%;}
p.preambulo {text-transform: uppercase; text-indent: 1cm;}
p.artigo {text-align: justify; text-indent: 1cm;}
p.paragrafo {text-align: justify; text-indent: 1cm;}
p.inciso {text-align: justify; text-indent: 1cm;}
p.alinea {text-align: justify; text-indent: 1cm;}
p.item {text-align: justify; text-indent: 1cm;}
p.justificativa {text-align: justify; text-indent: 1cm;}
p.mensagem {text-align: justify;}
p.data_apresentacao {text-align: justify; text-indent: 1cm;}
p.autor {text-align: center; text-transform: uppercase;}
h3.cab_secao {text-align: center; font-size: 12pt;}
</style>
</head>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="pl:proposicao">
<hr/>
<h3 class="cab_secao">PROPOSIÇÃO</h3>
<hr/>
<xsl:apply-templates select="./*"/>
</xsl:template>
<xsl:template match="pl:epigrafe">
<p class="epigrafe">
<xsl:value-of select="pl:epigrafe_text"/>
</p>
</xsl:template>
<xsl:template match="pl:ementa">
<p class="ementa">
<xsl:value-of select="pl:ementa_text"/>
</p>
</xsl:template>
<xsl:template match="pl:preambulo">
<p class="preambulo">
<xsl:value-of select="pl:preambulo_text"/>
</p>
</xsl:template>
<xsl:template match="pl:artigo_text">
<p class="artigo">
<xsl:value-of select="concat(../@Rotulo,' ',text())"/>
</p>
</xsl:template>
<xsl:template match="pl:paragrafo_text">
<p class="paragrafo">
<xsl:value-of select="concat(../@Rotulo,' ',text())"/>
</p>
</xsl:template>
<xsl:template match="pl:inciso_text">
<p class="inciso">
<xsl:value-of select="concat(../@Rotulo,' - ',text())"/>
</p>
</xsl:template>
<xsl:template match="pl:alinea_text">
<p class="alinea">
<xsl:value-of select="concat(../@Rotulo,' ',text())"/>
</p>
</xsl:template>
<xsl:template match="pl:item_text">
<p class="item">
<xsl:value-of select="concat(../@Rotulo,' ',text())"/>
</p>
</xsl:template>
<xsl:template match="pl:data_apresentacao_text">
<p class="data_apresentacao">
<xsl:value-of select="text()"/>
</p>
</xsl:template>
<xsl:template match="pl:autor_text">
<p class="autor">
<xsl:value-of select="text()"/>
</p>
</xsl:template>
<xsl:template match="pl:justificativa">
<hr/>
<h3 class="cab_secao">JUSTIFICATIVA</h3>
<hr/>
<p class="justificativa">
<xsl:value-of select="pl:justificativa_text"/>
</p>
</xsl:template>
<xsl:template match="pl:mensagem">
<hr/>
<h3 class="cab_secao">MENSAGEM</h3>
<hr/>
<p class="mensagem">
<xsl:value-of select="pl:mensagem_text"/>
</p>
</xsl:template>
</xsl:stylesheet>

100
sapl/static/XSLT/HTML/pl2.xsl

@ -1,100 +0,0 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ind="/XSD/ProjLei">
<xsl:template match="/">
<html>
<head>
<link href="/sapl/XSLT/HTML/estilo.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<div>
<div id="imagem">
<img border="0" src="/sapl/sapl_documentos/props_sapl/logo_casa"/>
</div><br></br>
<p class ="cabecalho">Câmara Municipal de Agudo</p>
<p class ="pequeno">Estado do Rio Grande do Sul<br></br><br></br><br></br> </p>
</div>
<xsl:apply-templates />
</body>
</html>
</xsl:template>
<xsl:template match="ind:epigrafe_text">
<p class ="autor"><strong><xsl:value-of select="text()" /></strong></p>
</xsl:template>
<xsl:template match="ind:ementa_text">
<p class ="ementa"><xsl:value-of select="text()" /></p>
</xsl:template>
<xsl:template match="ind:preambulo_text">
<p class ="artigo"><xsl:value-of select="text()" /></p>
</xsl:template>
<xsl:template match="ind:parte_text">
<p class ="titulos1"><xsl:value-of select="concat(../@Rotulo,' ')"/></p>
<p class ="titulos2"><xsl:value-of select="text()"/></p>
</xsl:template>
<xsl:template match="ind:livro_text">
<p class ="titulos1"><xsl:value-of select="concat(../@Rotulo,' ')"/></p>
<p class ="titulos2"><xsl:value-of select="text()"/></p>
</xsl:template>
<xsl:template match="ind:titulo_text">
<p class ="titulos1"><xsl:value-of select="concat(../@Rotulo,' ')"/></p>
<p class ="titulos2"><xsl:value-of select="text()"/></p>
</xsl:template>
<xsl:template match="ind:capitulo_text">
<p class ="titulos1"><xsl:value-of select="concat(../@Rotulo,' ')"/></p>
<p class ="titulos2"><xsl:value-of select="text()"/></p>
</xsl:template>
<xsl:template match="ind:secao_text">
<p class ="titulos1"><xsl:value-of select="concat(../@Rotulo,' ')"/></p>
<p class ="titulos2"><xsl:value-of select="text()"/></p>
</xsl:template>
<xsl:template match="ind:subsecao_text">
<p class ="titulos1"><xsl:value-of select="concat(../@Rotulo,' ')"/></p>
<p class ="titulos2"><xsl:value-of select="text()"/></p>
</xsl:template>
<xsl:template match="ind:artigo_text">
<p class="artigo"><xsl:value-of select="concat(../@Rotulo,' ',text())"/></p>
</xsl:template>
<xsl:template match="ind:paragrafo_text">
<p class="artigo"><xsl:value-of select="concat(../@Rotulo,' ',text())"/></p>
</xsl:template>
<xsl:template match="ind:inciso_text">
<p class="artigo"><xsl:value-of select="concat(../@Rotulo,' ',text())"/></p>
</xsl:template>
<xsl:template match="ind:alinea_text">
<p class="artigo"><xsl:value-of select="concat(../@Rotulo,' ',text())"/></p>
</xsl:template>
<xsl:template match="ind:item_text">
<p class="artigo"><xsl:value-of select="concat(../@Rotulo,' ',text())"/></p>
</xsl:template>
<xsl:template match="ind:data_apresentacao_text">
<p class="artigo"><xsl:value-of select="concat(../@Rotulo,' ',text())"/></p>
</xsl:template>
<xsl:template match="ind:autor_text">
<p class="artigo"><xsl:value-of select="concat(../@Rotulo,' ',text())"/></p>
</xsl:template>
<xsl:template match="ind:justificativa_text">
<p class="artigo"><xsl:value-of select="text()" /></p>
</xsl:template>
</xsl:stylesheet>

52
sapl/static/XSLT/HTML/requerimento.xsl

@ -1,52 +0,0 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:req="/XSD/Requerimento">
<xsl:template match="/">
<html>
<body>
<table>
<xsl:apply-templates />
</table>
</body>
</html>
</xsl:template>
<xsl:template match="req:destinatario_text">
<tr>
<td colspan="2" align="left"><xsl:value-of select="text()" /></td>
</tr>
</xsl:template>
<xsl:template match="req:ementa_text">
<tr>
<td width="50%"></td>
<td width="50%" align="left"><xsl:value-of select="text()" /></td>
</tr>
</xsl:template>
<xsl:template match="req:requisicao_text">
<tr>
<td colspan="2" align="left"><xsl:value-of select="text()" /></td>
</tr>
</xsl:template>
<xsl:template match="req:justificativa_text">
<tr>
<td colspan="2" align="left"><xsl:value-of select="text()" /></td>
</tr>
</xsl:template>
<xsl:template match="req:data_text">
<tr>
<td colspan="2" align="left"><xsl:value-of select="text()" /></td>
</tr>
</xsl:template>
<xsl:template match="req:autor_text">
<tr>
<td colspan="2" align="left"><xsl:value-of select="text()" /></td>
</tr>
</xsl:template>
</xsl:stylesheet>

57
sapl/static/XSLT/HTML/requerimento2.xsl

@ -1,57 +0,0 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ind="/XSD/Requerimento">
<xsl:template match="/">
<html>
<head>
<link href="/sapl/XSLT/HTML/estilo.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<div>
<div id="imagem">
<img border="0" src="/sapl/sapl_documentos/props_sapl/logo_casa"/>
</div><br></br>
<p class ="cabecalho">Câmara Municipal de Agudo</p>
<p class ="pequeno">Estado do Rio Grande do Sul<br></br><br></br><br></br> </p>
</div>
<xsl:apply-templates />
</body>
</html>
</xsl:template>
<xsl:template match="ind:ementa_text">
<p class="autor"><strong><xsl:value-of select="text()" /></strong></p>
</xsl:template>
<xsl:template match="ind:formatratamento_text">
<p class="semrecuo"><xsl:value-of select="text()"/> </p>
</xsl:template>
<xsl:template match="ind:parlamentar_text">
<p class="semrecuo"><xsl:value-of select="text()" /></p>
</xsl:template>
<xsl:template match="ind:cargofuncao_text">
<p class="semrecuo"><xsl:value-of select="text()" /></p>
</xsl:template>
<xsl:template match="ind:requisicao_text">
<p class="texto"><xsl:value-of select="text()" /></p>
</xsl:template>
<xsl:template match="ind:justificativa_text">
<p class="texto"><xsl:value-of select="text()" /></p>
</xsl:template>
<xsl:template match="ind:data_text">
<p><xsl:value-of select="text()" /></p>
</xsl:template>
<xsl:template match="ind:autor_text">
<p class="autor"><xsl:value-of select="text()" /></p>
</xsl:template>
</xsl:stylesheet>

2
sapl/templates/norma/normajuridica_detail.html

@ -35,7 +35,7 @@
<hr />
<div class="row-fluid">
<div class="col-sm-12">
<p class="control-label">Relacionamentos</p>
<p class="control-label">Normas Relacionadas</p>
{% if object.get_normas_relacionadas.0|length > 0 %}
{% for p in object.get_normas_relacionadas.0 %}

3
sapl/templates/norma/subnav.yaml

@ -2,8 +2,9 @@
- title: {% trans 'Início' %}
url: normajuridica_detail
- title: {% trans 'Normas Relacionadas' %}
- title: {% trans 'Alterações em Outras Normas' %}
url: normarelacionada_list
check_permission: norma.normarelacionada_list
# Opção adicionada para chamar o TextoArticulado da norma.
# para integração foram necessárias apenas criar a url norma_ta em urls.py

1
sapl/templates/protocoloadm/layouts.yaml

@ -11,6 +11,7 @@ DocumentoAdministrativo:
- interessado tramitacao
- texto_integral
{% trans 'Outras Informações' %}:
- numero_externo
- dias_prazo data_fim_prazo
- observacao

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

@ -6,7 +6,7 @@
<tr>
<td>
<b>{{e.tipo}}: </b> <br /><br />
<div contenteditable="false" style="border:0.5px solid #BAB4B1; border-radius: 10px; background-color: rgba(225, 225, 225, .8);">
<div style="border:0.5px solid #BAB4B1; border-radius: 10px; background-color: rgba(225, 225, 225, .8);">
<p>{{e.conteudo|safe}}</p>
</div>
</td>

1
sapl/templates/sessao/expediente.html

@ -28,6 +28,7 @@
<br />
<input type="submit" value="Salvar" class="btn btn-primary"/>
<input type="submit" id="apagar-expediente" name="apagar-expediente" value="Apagar" class="btn btn-danger" />
</form>
{% endif %}

4
sapl/test_urls.py

@ -164,7 +164,9 @@ apps_url_patterns_prefixs_and_users = {
'/logout',
'/ajuda',
'/email',
'/recuperar-senha'
'/recuperar-senha',
'/sapl',
'/XSLT',
]},
'comissoes': {
'users': {'operador_geral': ['/sistema', '/comissao'],

5
sapl/urls.py

@ -21,6 +21,7 @@ from django.views.generic.base import RedirectView, TemplateView
from django.views.static import serve as view_static_server
import sapl.api.urls
import sapl.audiencia.urls
import sapl.base.urls
import sapl.comissoes.urls
import sapl.compilacao.urls
@ -33,7 +34,6 @@ import sapl.protocoloadm.urls
import sapl.redireciona_urls.urls
import sapl.relatorios.urls
import sapl.sessao.urls
import sapl.audiencia.urls
urlpatterns = [
url(r'^$', TemplateView.as_view(template_name='index.html'),
@ -62,9 +62,6 @@ urlpatterns = [
url(r'^favicon\.ico$', RedirectView.as_view(
url='/static/img/favicon.ico', permanent=True)),
# Folhas XSLT e extras referenciadas por documentos migrados do sapl 2.5
url(r'^XSLT/HTML/(?P<path>.*)$', RedirectView.as_view(
url='/static/XSLT/HTML/%(path)s', permanent=False)),
url(r'', include(sapl.redireciona_urls.urls)),
]

5
sapl/utils.py

@ -2,6 +2,7 @@ import hashlib
import logging
import os
import re
import unicodedata
from functools import wraps
from operator import itemgetter
from unicodedata import normalize as unicodedata_normalize
@ -732,3 +733,7 @@ def RemoveTag(texto):
i += 1
return textoSaida
def remover_acentos(string):
return ''.join([c for c in unicodedata.normalize('NFD', string)
if unicodedata.category(c) != 'Mn'])

7
scripts_docker/remove-all-containers.sh

@ -1,4 +1,5 @@
#!/bin/bash
sudo docker stop $(docker ps -a -q) # Parar containers
sudo docker rm $(sudo docker ps -a -q) # Remover containers
sudo docker rmi -f $( sudo docker images -q ) # Remover imagens
sudo docker stop $(docker ps -a -q) # Para containers
sudo docker rm $(sudo docker ps -a -q) # Remove containers
sudo docker rmi -f $( sudo docker images -q ) # Remove imagens
sudo docker volume rm $(sudo docker volume ls -q -f dangling=true) # Remove volumes

2
setup.py

@ -49,7 +49,7 @@ install_requires = [
]
setup(
name='interlegis-sapl',
version='3.1.71',
version='3.1.76',
packages=find_packages(),
include_package_data=True,
license='GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007',

Loading…
Cancel
Save