Browse Source

Merge branch 'migracao' into 3.1.x

pull/2255/head
Marcio Mazza 6 years ago
parent
commit
03231abdb7
  1. 130
      sapl/legacy/migracao_dados.py
  2. 3
      sapl/legacy/scripts/.flake8
  3. 22
      sapl/legacy/scripts/exporta_zope/exporta_zope.py
  4. 137
      sapl/legacy/scripts/ressucita_dependencias.py
  5. 19
      sapl/legacy_migration_settings.py

130
sapl/legacy/migracao_dados.py

@ -33,7 +33,8 @@ from sapl.comissoes.models import Comissao, Composicao, Participacao, Reuniao
from sapl.legacy.models import NormaJuridica as OldNormaJuridica
from sapl.legacy.models import TipoNumeracaoProtocolo
from sapl.legacy_migration_settings import (DIR_DADOS_MIGRACAO, DIR_REPO,
NOME_BANCO_LEGADO)
NOME_BANCO_LEGADO, PYTZ_TIMEZONE,
SIGLA_CASA)
from sapl.materia.models import (AcompanhamentoMateria, DocumentoAcessorio,
MateriaLegislativa, Proposicao,
StatusTramitacao, TipoDocumento,
@ -50,7 +51,6 @@ from sapl.sessao.models import (ExpedienteMateria, ExpedienteSessao, OrdemDia,
from sapl.utils import normalize
from .scripts.normaliza_dump_mysql import normaliza_dump_mysql
from .timezonesbrasil import get_timezone
# YAML SETUP ###############################################################
@ -539,6 +539,12 @@ PROPAGACOES_DE_EXCLUSAO = [
('parlamentar', 'mandato', 'cod_parlamentar'),
('parlamentar', 'composicao_mesa', 'cod_parlamentar'),
('parlamentar', 'composicao_comissao', 'cod_parlamentar'),
# no 2.5 os parlamentares excluídos não são listados na presença da sessão
('parlamentar', 'sessao_plenaria_presenca', 'cod_parlamentar'),
# ... nem na presença da ordem do dia
('parlamentar', 'ordem_dia_presenca', 'cod_parlamentar'),
# ... nem na mesa da sessão
('parlamentar', 'mesa_sessao_plenaria', 'cod_parlamentar'),
# coligacao
('coligacao', 'composicao_coligacao', 'cod_coligacao'),
@ -553,6 +559,9 @@ PROPAGACOES_DE_EXCLUSAO = [
('sessao_plenaria', 'expediente_sessao_plenaria', 'cod_sessao_plen'),
('sessao_plenaria', 'sessao_plenaria_presenca', 'cod_sessao_plen'),
('sessao_plenaria', 'ordem_dia_presenca', 'cod_sessao_plen'),
('sessao_plenaria', 'mesa_sessao_plenaria', 'cod_sessao_plen'),
('sessao_plenaria', 'oradores', 'cod_sessao_plen'),
('sessao_plenaria', 'oradores_expediente', 'cod_sessao_plen'),
# as consultas no código do sapl 2.5
# votacao_ordem_dia_obter_zsql e votacao_expediente_materia_obter_zsql
@ -575,6 +584,8 @@ PROPAGACOES_DE_EXCLUSAO = [
('materia_legislativa', 'despacho_inicial', 'cod_materia'),
('materia_legislativa', 'legislacao_citada', 'cod_materia'),
('materia_legislativa', 'relatoria', 'cod_materia'),
('materia_legislativa', 'materia_assunto', 'cod_materia'),
# norma
('norma_juridica', 'vinculo_norma_juridica', 'cod_norma_referente'),
@ -705,6 +716,12 @@ sessao_plenaria_presenca | dat_sessao = NULL | dat_sessao = 0
anula_tipos_origem_externa_invalidos()
corrige_unidades_tramitacao_destino_vazia_como_anterior()
# matérias inexistentes não são mostradas em norma jurídica => apagamos
exec_legado('''update norma_juridica set cod_materia = NULL
where cod_materia not in (
select cod_materia from materia_legislativa
where ind_excluido <> 1);''')
class Record:
pass
@ -790,19 +807,6 @@ def reinicia_sequence(model, id):
REPO = git.Repo.init(DIR_REPO)
# configura timezone de migração
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]
if nome_timezone:
timezone = pytz.timezone(nome_timezone)
else:
timezone = get_timezone(municipio, uf)
def populate_renamed_fields(new, old):
renames = field_renames[type(new)]
@ -822,16 +826,17 @@ def populate_renamed_fields(new, old):
and value in [None, 'None']):
value = ''
# adiciona timezone faltante aos campos com tempo
# os campos TIMESTAMP do mysql são gravados em UTC
# os DATETIME e TIME não têm timezone
def campo_tempo_sem_timezone(tipo):
return (field_type == tipo
and value and not value.tzinfo)
if campo_tempo_sem_timezone('DateTimeField'):
value = timezone.localize(value)
if campo_tempo_sem_timezone('TimeField'):
value = value.replace(tzinfo=timezone)
# ajusta tempos segundo timezone
# os campos TIMESTAMP do mysql são gravados em UTC
# os DATETIME e TIME não têm timezone
if field_type == 'DateTimeField' and value:
# as datas armazenadas no legado na verdade são naive
sem_tz = value.replace(tzinfo=None)
value = PYTZ_TIMEZONE.localize(sem_tz).astimezone(pytz.utc)
if field_type == 'TimeField' and value:
value = value.replace(tzinfo=PYTZ_TIMEZONE)
setattr(new, field.name, value)
@ -843,7 +848,7 @@ def roda_comando_shell(cmd):
def get_arquivo_ajustes_pre_migracao():
return DIR_DADOS_MIGRACAO.child(
'ajustes_pre_migracao', '{}.sql'.format(sigla_casa))
'ajustes_pre_migracao', '{}.sql'.format(SIGLA_CASA))
def migrar_dados(apagar_do_legado=False):
@ -1026,65 +1031,34 @@ def adjust_acompanhamentomateria(new, old):
new.confirmado = True
NOTA_DOCADM = '''
## NOTA DE MIGRAÇÃO DE DADOS DO SAPL 2.5 ##
O número de protocolo original deste documento era [{num_protocolo}], ano {ano_original}.
'''.strip() # noqa
def adjust_documentoadministrativo(new, old):
if old.num_protocolo:
nota = None
ano_original = new.ano
protocolo = Protocolo.objects.filter(
numero=old.num_protocolo, ano=new.ano)
if not protocolo:
# tentamos encontrar o protocolo no ano seguinte
ano_novo = ano_original + 1
protocolo = Protocolo.objects.filter(numero=old.num_protocolo,
ano=ano_novo)
if protocolo:
nota = NOTA_DOCADM + '''
O protocolo vinculado é o de mesmo número, porém do ano seguinte ({ano_novo}),
pois não existe protocolo no sistema com este número no ano {ano_original}.
'''
nota = nota.strip().format(num_protocolo=old.num_protocolo,
ano_original=ano_original,
ano_novo=ano_novo)
msg = 'PROTOCOLO ENCONTRADO APENAS PARA O ANO SEGUINTE!!!!! '\
'DocumentoAdministrativo: {cod_documento}, '\
'numero_protocolo: {num_protocolo}, '\
'ano doc adm: {ano_original}'
warn('protocolo_ano_seguinte', msg,
{'cod_documento': old.cod_documento,
'num_protocolo': old.num_protocolo,
'ano_original': ano_original,
'nota': nota})
else:
# Se não achamos mesmo no ano anteriro
# colocamos no número externo
new.numero_externo = old.num_protocolo
numero, ano = old.num_protocolo, new.ano
# False < True => o primeiro será o protocolo não anulado
protocolos = Protocolo.objects.filter(
numero=numero, ano=ano).order_by('anulado')
if protocolos:
new.protocolo = protocolos[0]
else:
# Se não achamos o protocolo registramos no número externo
new.numero_externo = numero
nota = '''
## NOTA DE MIGRAÇÃO DE DADOS DO SAPL 2.5 ##
O número de protocolo original deste documento era [{numero}], ano [{ano}].
nota = NOTA_DOCADM + '''
Não existe no sistema nenhum protocolo com estes dados
e portanto nenhum protocolo foi vinculado a este documento.
Colocamos então o número de protocolo no campo "número externo".
'''
nota = nota.format(
num_protocolo=old.num_protocolo,
ano_original=ano_original)
msg = 'Protocolo {num_protocolo} faltando (referenciado ' \
'no documento administrativo {cod_documento})'
warn('protocolo_faltando', msg,
{'num_protocolo': old.num_protocolo,
'cod_documento': old.cod_documento,
'nota': nota})
if protocolo:
assert len(protocolo) == 1, 'mais de um protocolo encontrado'
[new.protocolo] = protocolo
# adiciona nota ao final da observação
if nota:
nota = nota.strip().format(numero=numero, ano=ano)
msg = 'Protocolo {numero} faltando (referenciado ' \
'no documento administrativo {cod_documento})'
warn('protocolo_faltando', msg,
{'numero': numero,
'cod_documento': old.cod_documento,
'nota': nota})
new.observacao += ('\n\n' if new.observacao else '') + nota
@ -1163,7 +1137,7 @@ def adjust_protocolo_antes_salvar(new, old):
def get_arquivo_resolve_registro_votacao():
return DIR_DADOS_MIGRACAO.child(
'ajustes_pre_migracao',
'{}_resolve_registro_votacao_ambiguo.yaml'.format(sigla_casa))
'{}_resolve_registro_votacao_ambiguo.yaml'.format(SIGLA_CASA))
def get_como_resolver_registro_votacao_ambiguo():

3
sapl/legacy/scripts/.flake8

@ -0,0 +1,3 @@
[flake8]
ignore = E501

22
sapl/legacy/scripts/exporta_zope/exporta_zope.py

@ -298,6 +298,8 @@ DUMP_FUNCTIONS = {
'Image': dump_file,
'DTML Method': partial(dump_file,
get_conteudo=get_conteudo_dtml_method),
'DTMLMethod': 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'),
@ -381,18 +383,30 @@ def _dump_sapl(data_fs_path, documentos_fs_path, destino, salvar, mtimes):
sapl = find_sapl(app)
# extrai usuários com suas senhas e perfis
dump_usuarios(sapl, destino, salvar)
# extrai folhas XSLT (primeira tentativa)
if 'XSLT' in sapl:
dump_folder(br(sapl['XSLT']), destino, salvar, mtimes)
finally:
close_db()
app, close_db = get_app(documentos_fs_path)
try:
sapl = find_sapl(app)
# extrai folhas XSLT
if 'XSLT' in sapl:
dump_folder(br(sapl['XSLT']), destino, salvar, mtimes)
if sapl == {'id': 'sapl'}:
# em algumas instalações sapl_documentos está direto na raiz
docs = br(app['sapl_documentos'])
else:
# caso mais comum
docs = br(sapl['sapl_documentos'])
# extrai folhas XSLT (segunda tentativa)
if 'XSLT' in sapl:
dump_folder(br(sapl['XSLT']), destino, salvar, mtimes)
# extrai documentos
docs = br(sapl['sapl_documentos'])
with logando_nao_identificados():
dump_folder(docs, destino, salvar, mtimes)
dump_propriedades(docs, destino, salvar)

137
sapl/legacy/scripts/ressucita_dependencias.py

@ -1,3 +1,4 @@
from collections import OrderedDict
from textwrap import dedent
import texttable
@ -19,6 +20,7 @@ def stripsplit(ll):
def _tab_legado(model):
return models_novos_para_antigos[model]._meta.db_table
fks_legado = {
(_tab_legado(m), campos_novos_para_antigos[f]): _tab_legado(f.related_model) # noqa
for m in models_novos_para_antigos
@ -34,29 +36,40 @@ for tabela_origem, campo, tabela_destino in [
urls = '''
autor /sistema/autor
cargo_comissao /sistema/comissao/cargo
legislatura /sistema/parlamentar/legislatura
materia_legislativa /materia
norma_juridica /norma
parlamentar /parlamentar
sessao_legislativa /sistema/mesa-diretora/sessao-legislativa
sessao_plenaria /sessao
status_tramitacao /sistema/materia/status-tramitacao
tipo_autor /sistema/autor/tipo
tipo_expediente /sistema/sessao-plenaria/tipo-expediente
tipo_proposicao /sistema/proposicao/tipo
tipo_resultado_votacao /sistema/sessao-plenaria/tipo-resultado-votacao
unidade_tramitacao /sistema/materia/unidade-tramitacao
tipo_documento /sistema/materia/tipo-documento
orgao /sistema/materia/orgao
tipo_sessao_plenaria /sistema/sessao-plenaria/tipo
cargo_mesa /sistema/mesa-diretora/cargo-mesa
documento_administrativo /docadm
tipo_materia_legislativa /sistema/materia/tipo
tipo_norma_juridica /sistema/norma/tipo
comissao /comissao
registro_votacao ?????????
autor /sistema/autor
cargo_comissao /sistema/comissao/cargo
legislatura /sistema/parlamentar/legislatura
materia_legislativa /materia
norma_juridica /norma
parlamentar /parlamentar
sessao_legislativa /sistema/mesa-diretora/sessao-legislativa
sessao_plenaria /sessao
status_tramitacao /sistema/materia/status-tramitacao
tipo_autor /sistema/autor/tipo
tipo_expediente /sistema/sessao-plenaria/tipo-expediente
tipo_proposicao /sistema/proposicao/tipo
tipo_resultado_votacao /sistema/sessao-plenaria/tipo-resultado-votacao
unidade_tramitacao /sistema/materia/unidade-tramitacao
tipo_documento /sistema/materia/tipo-documento
orgao /sistema/materia/orgao
tipo_sessao_plenaria /sistema/sessao-plenaria/tipo
cargo_mesa /sistema/mesa-diretora/cargo-mesa
documento_administrativo /docadm
tipo_materia_legislativa /sistema/materia/tipo
tipo_norma_juridica /sistema/norma/tipo
comissao /comissao
assunto_materia /sistema/assunto-materia
coligacao /sistema/coligacao
nivel_instrucao /sistema/parlamentar/nivel-instrucao
partido /sistema/parlamentar/partido
regime_tramitacao /sistema/materia/regime-tramitacao
tipo_comissao /sistema/comissao/tipo
tipo_documento_administrativo /sistema/tipo-documento-adm
registro_votacao /admin/sessao/registrovotacao
tipo_dependente /sistema/parlamentar/tipo-dependente
origem /sistema/materia/origem
documento_acessorio /materia/documentoacessorio
tipo_fim_relatoria /sistema/materia/tipo-fim-relatoria
'''
urls = dict(stripsplit(urls))
@ -77,6 +90,7 @@ CAMPOS_ORIGEM_PARA_ALVO = {
'cod_unid_tram_dest': 'cod_unid_tramitacao',
'cod_unid_tram_local': 'cod_unid_tramitacao',
'tip_id_basica': 'tip_materia',
'cod_local_origem_externa': 'cod_origem',
}
@ -214,6 +228,21 @@ def get_dependencias_a_ressucitar(slug):
return preambulo, desexcluir, criar
# deve ser idempotente pois é usada na criação de autor
# por isso o ON DUPLICATE KEY UPDATE
SQL_INSERT_TIPO_AUTOR = '''
insert into tipo_autor (tip_autor, des_tipo_autor, ind_excluido)
values ({}, "DESCONHECIDO", 0) ON DUPLICATE KEY UPDATE ind_excluido = 0;
'''
# deve ser idempotente pois é usada na criação de comissao
# por isso o ON DUPLICATE KEY UPDATE
SQL_INSERT_TIPO_COMISSAO = '''
insert into tipo_comissao (tip_comissao, nom_tipo_comissao, sgl_natureza_comissao, sgl_tipo_comissao, des_dispositivo_regimental, ind_excluido)
values ({}, "DESCONHECIDO", "P", "DESC", NULL, 0)
ON DUPLICATE KEY UPDATE ind_excluido = 0;
'''
SQLS_CRIACAO = [
('tipo_proposicao', '''
insert into tipo_materia_legislativa (
@ -232,14 +261,52 @@ SQLS_CRIACAO = [
tip_resultado_votacao, nom_resultado, ind_excluido)
values ({}, "DESCONHECIDO", 0);
'''),
('tipo_autor', '''
insert into tipo_autor (tip_autor, des_tipo_autor, ind_excluido)
values ({}, "DESCONHECIDO", 0);
'''),
('tipo_autor', SQL_INSERT_TIPO_AUTOR),
('unidade_tramitacao', '''
insert into unidade_tramitacao (cod_unid_tramitacao, cod_comissao, cod_orgao, cod_parlamentar, ind_excluido)
insert into unidade_tramitacao (
cod_unid_tramitacao, cod_comissao, cod_orgao, cod_parlamentar, ind_excluido)
values ({}, NULL, NULL, NULL, 0);
'''),
('autor', SQL_INSERT_TIPO_AUTOR.format(0) + '''
insert into autor (
cod_autor, cod_partido, cod_comissao, cod_parlamentar, tip_autor,
nom_autor, des_cargo, col_username, ind_excluido)
values ({}, 0, 0, 0, 0, "DESCONHECIDO", "DESCONHECIDO", NULL, 0);
'''),
('tipo_documento', '''
insert into tipo_documento (tip_documento, des_tipo_documento, ind_excluido)
values ({}, "DESCONHECIDO", 0);
'''),
('partido', '''
insert into partido (cod_partido, sgl_partido, nom_partido, dat_criacao, dat_extincao, ind_excluido)
values ({}, "DESC", "DESCONHECIDO", NULL, NULL, 0);
'''),
('legislatura', '''
insert into legislatura (num_legislatura, dat_inicio, dat_fim, dat_eleicao, ind_excluido)
values ({}, "1/1/1", "1/1/1", "1/1/1", 0);
'''),
('cargo_mesa', '''
insert into cargo_mesa (cod_cargo, des_cargo, ind_unico, ind_excluido)
values ({}, "DESCONHECIDO", 0, 0);
'''),
('orgao', '''
insert into orgao (cod_orgao, nom_orgao, sgl_orgao, ind_unid_deliberativa, end_orgao, num_tel_orgao, ind_excluido)
values ({}, "DESCONHECIDO", "DESC", 0, NULL, NULL, 0);
'''),
('origem', '''
insert into origem (cod_origem, sgl_origem, nom_origem, ind_excluido)
values ({}, "DESC", "DESCONHECIDO", 0);
'''),
('tipo_comissao', SQL_INSERT_TIPO_COMISSAO),
('comissao', SQL_INSERT_TIPO_COMISSAO.format(0) + '''
insert into comissao (cod_comissao, tip_comissao, nom_comissao, sgl_comissao, dat_criacao,
ind_unid_deliberativa, ind_excluido)
values ({}, 0, "DESCONHECIDO", "DESC", "1-1-1", 0, 0);
'''),
('parlamentar', '''
insert into parlamentar (cod_parlamentar, nom_completo, nom_parlamentar, sex_parlamentar, cod_casa, ind_ativo, ind_unid_deliberativa, ind_excluido)
values ({}, "DESCONHECIDO", "DESCONHECIDO", "M", 0, 0, 0, 0);
'''),
]
SQLS_CRIACAO = {k: (dedent(sql.strip()), extras)
for k, sql, *extras in SQLS_CRIACAO}
@ -307,6 +374,10 @@ def get_url(slug):
return 'sapl.{}.leg.br'.format(slug.replace('-', '.'))
def sem_repeticoes_mantendo_ordem(sequencia):
return OrderedDict.fromkeys(sequencia).keys()
def get_sqls_desexcluir_criar(preambulo, desexcluir, criar, slug):
sqls_links = [get_sql(*(args + (slug,)))
for itens, get_sql in ((desexcluir, get_sql_desexcluir),
@ -316,7 +387,15 @@ def get_sqls_desexcluir_criar(preambulo, desexcluir, criar, slug):
return ''
else:
sqls, links = zip(*sqls_links)
links = [l for ll in links for l in ll] # flatten
sqls = [dedent(s.strip()) + ';'
for sql in sqls
for s in sql.split(';') if s.strip()]
sqls = sem_repeticoes_mantendo_ordem(sqls)
links = (l for ll in links for l in ll) # flatten
links = sem_repeticoes_mantendo_ordem(links)
sqls, links = ['\n'.join(sorted(s)) for s in [sqls, links]]
return TEMPLATE_RESSUCITADOS.format(preambulo, links, sqls)

19
sapl/legacy_migration_settings.py

@ -1,10 +1,14 @@
import os
import re
import pytz
import yaml
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 sapl.legacy.timezonesbrasil import get_timezone
from .settings import * # flake8: noqa
@ -43,3 +47,18 @@ NOME_BANCO_LEGADO = DATABASES['legacy']['NAME']
DIR_REPO = Path(DIR_DADOS_MIGRACAO, 'repos', NOME_BANCO_LEGADO)
MEDIA_ROOT = DIR_REPO
# configura timezone de migração
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]
if nome_timezone:
PYTZ_TIMEZONE = pytz.timezone(nome_timezone)
else:
PYTZ_TIMEZONE = get_timezone(municipio, uf)
TIME_ZONE = PYTZ_TIMEZONE.zone

Loading…
Cancel
Save