Browse Source

Revisa e adiciona campos faltantes à migração

pull/1847/head
Marcio Mazza 7 years ago
parent
commit
87c8c24c91
  1. 1
      sapl/base/legacy.yaml
  2. 1
      sapl/comissoes/legacy.yaml
  3. 139
      sapl/legacy/migracao_dados.py
  4. 96
      sapl/legacy/test_renames.py
  5. 9
      sapl/materia/legacy.yaml
  6. 6
      sapl/materia/models.py
  7. 1
      sapl/norma/legacy.yaml
  8. 4
      sapl/parlamentares/legacy.yaml
  9. 1
      sapl/protocoloadm/legacy.yaml
  10. 1
      sapl/sessao/legacy.yaml

1
sapl/base/legacy.yaml

@ -5,4 +5,3 @@ Autor:
nome: nom_autor
cargo: des_cargo
tipo: tip_autor
username: col_username

1
sapl/comissoes/legacy.yaml

@ -25,7 +25,6 @@ Comissao:
telefone_secretaria: num_tel_secretaria
tipo: tip_comissao
unidade_deliberativa: ind_unid_deliberativa
ativa:
Periodo (PeriodoCompComissao):
data_fim: dat_fim_periodo

139
sapl/legacy/migracao_dados.py

@ -4,6 +4,7 @@ from collections import OrderedDict, defaultdict
from datetime import date
from functools import lru_cache, partial
from itertools import groupby
from operator import xor
from subprocess import PIPE, call
import pkg_resources
@ -70,8 +71,6 @@ for a1, s1 in name_sets:
else:
assert not s1.intersection(s2)
legacy_app = apps.get_app_config('legacy')
# RENAMES ###################################################################
@ -110,6 +109,21 @@ def get_renames():
return field_renames, model_renames
field_renames, model_renames = get_renames()
legacy_app = apps.get_app_config('legacy')
models_novos_para_antigos = {
model: legacy_app.get_model(model_renames.get(model, model.__name__))
for model in field_renames}
models_novos_para_antigos[Composicao] = models_novos_para_antigos[Participacao]
campos_novos_para_antigos = {
model._meta.get_field(nome_novo): nome_antigo
for model, renames in field_renames.items()
for nome_novo, nome_antigo in renames.items()}
# MIGRATION #################################################################
@ -124,22 +138,40 @@ def warn(tipo, msg, dados):
print('CUIDADO! ' + msg.format(**dados))
@lru_cache()
def get_pk_legado(tabela):
res = exec_legado(
'show index from {} WHERE Key_name = "PRIMARY"'.format(tabela))
return [r[4] for r in res]
@lru_cache()
def get_estrutura_legado(model):
model_legado = models_novos_para_antigos[model]
tabela_legado = model_legado._meta.db_table
campos_pk_legado = get_pk_legado(tabela_legado)
return model_legado, tabela_legado, campos_pk_legado
class ForeignKeyFaltando(ObjectDoesNotExist):
'Uma FK aponta para um registro inexistente'
def __init__(self, field, value, label):
def __init__(self, field, valor, old):
self.field = field
self.value = value
self.label = label
self.valor = valor
self.old = old
msg = 'FK [{field}] não encontrada para o valor {value} (em {model} / {label})' # noqa
msg = 'FK não encontrada para [{campo} = {valor}] (em {tabela} / pk = {pk})' # noqa
@property
def dados(self):
return OrderedDict((('field', self.field.name),
('value', self.value),
('model', self.field.model.__name__),
('label', self.label)))
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}
return OrderedDict((('campo', campo),
('valor', self.valor),
('tabela', tabela),
('pk', pk)))
@lru_cache()
@ -148,18 +180,17 @@ def _get_all_ids_from_model(model):
return set(model.objects.values_list('id', flat=True))
def get_fk_related(field, value, label={}):
if value is None and field.null:
def get_fk_related(field, old, old_field_name):
valor = getattr(old, old_field_name)
if valor is None and field.null:
return None
# if field.related_model.objects.filter(id=value).exists():
if value in _get_all_ids_from_model(field.related_model):
return value
elif value == 0 and field.null:
if valor in _get_all_ids_from_model(field.related_model):
return valor
elif valor == 0 and field.null:
# consideramos zeros como nulos, se não está entre os ids anteriores
return None
else:
raise ForeignKeyFaltando(field=field, value=value, label=label)
raise ForeignKeyFaltando(field=field, valor=valor, old=old)
def exec_sql(sql, db='default'):
@ -559,9 +590,16 @@ class Record:
def iter_sql_records(tabela):
if tabela == 'despacho_inicial':
sql = ''' select cod_materia, cod_comissao from despacho_inicial
where ind_excluido <> 1
group by cod_materia, cod_comissao
order by cod_materia, min(num_ordem)
'''
else:
sql = 'select * from ' + tabela
if existe_coluna_no_legado(tabela, 'ind_excluido'):
sql += ' where ind_excluido != 1'
sql += ' where ind_excluido <> 1'
cursor = exec_legado(sql)
fieldnames = [name[0] for name in cursor.description]
for row in cursor.fetchall():
@ -631,12 +669,6 @@ def reinicia_sequence(model, id):
sequence_name, id))
def get_pk_legado(tabela):
res = exec_legado(
'show index from {} WHERE Key_name = "PRIMARY"'.format(tabela))
return [r[4] for r in res]
DIR_DADOS_MIGRACAO = Path('~/migracao_sapl/').expand()
PATH_TABELA_TIMEZONES = DIR_DADOS_MIGRACAO.child('tabela_timezones.yaml')
DIR_RESULTADOS = DIR_DADOS_MIGRACAO.child('resultados')
@ -650,7 +682,6 @@ yaml.add_representer(OrderedDict, dict_representer)
class DataMigrator:
def __init__(self):
self.field_renames, self.model_renames = get_renames()
self.choice_valida = {}
# configura timezone de migração
@ -665,22 +696,20 @@ class DataMigrator:
else:
self.timezone = get_timezone(municipio, uf)
def populate_renamed_fields(self, new, old, campos_pk_legado):
renames = self.field_renames[type(new)]
def populate_renamed_fields(self, new, old):
renames = field_renames[type(new)]
for field in new._meta.fields:
old_field_name = renames.get(field.name)
field_type = field.get_internal_type()
if old_field_name:
old_value = getattr(old, old_field_name)
field_type = field.get_internal_type()
if field_type == 'ForeignKey':
label = {c: getattr(old, c) for c in campos_pk_legado}
fk_field_name = '{}_id'.format(field.name)
value = get_fk_related(field, old_value, label)
value = get_fk_related(field, old, old_field_name)
setattr(new, fk_field_name, value)
else:
value = old_value
value = getattr(old, old_field_name)
if (field_type in ['CharField', 'TextField']
and value in [None, 'None']):
@ -733,6 +762,7 @@ class DataMigrator:
self._do_migrate(obj)
except Exception as e:
ocorrencias['traceback'] = str(traceback.format_exc())
raise e
finally:
# grava ocorrências
arq_ocorrencias = dir_ocorrencias.child(
@ -748,7 +778,7 @@ class DataMigrator:
def _do_migrate(self, obj):
if isinstance(obj, AppConfig):
models = [model for model in obj.models.values()
if model in self.field_renames]
if model in field_renames]
if obj.label == 'materia':
# Devido à referência TipoProposicao.tipo_conteudo_related
@ -775,10 +805,8 @@ class DataMigrator:
def migrate_model(self, model):
print('Migrando %s...' % model.__name__)
nome_model = self.model_renames.get(model, model.__name__)
model_legado = legacy_app.get_model(nome_model)
tabela_legado = model_legado._meta.db_table
campos_pk_legado = get_pk_legado(tabela_legado)
model_legado, tabela_legado, campos_pk_legado = \
get_estrutura_legado(model)
if len(campos_pk_legado) == 1:
# a pk no legado tem um único campo
@ -808,7 +836,7 @@ class DataMigrator:
for old in old_records:
new = model()
try:
self.populate_renamed_fields(new, old, campos_pk_legado)
self.populate_renamed_fields(new, old)
if ajuste_antes_salvar:
ajuste_antes_salvar(new, old)
except ForeignKeyFaltando as e:
@ -833,7 +861,9 @@ class DataMigrator:
for campo in campos_pk_legado))
# salva novos registros
with reversion.create_revision():
model.objects.bulk_create(novos)
reversion.set_comment('Objetos criados pela migração')
if ajuste_depois_salvar:
ajuste_depois_salvar()
@ -962,22 +992,13 @@ def adjust_parlamentar(new, old):
def adjust_participacao(new, old):
composicao = Composicao()
composicao.comissao_id, composicao.periodo_id = [
get_fk_related(Composicao._meta.get_field(name),
value,
{'composicao_comissao.cod_comp_comissao': old.pk})
for name, value in (('comissao', old.cod_comissao),
('periodo', old.cod_periodo_comp))]
# check if there is already an "equal" one in the db
already_created = Composicao.objects.filter(
comissao=composicao.comissao, periodo=composicao.periodo)
if already_created:
assert len(already_created) == 1 # we must never have made 2 copies
[composicao] = already_created
else:
comissao_id, periodo_id = [
get_fk_related(Composicao._meta.get_field(name), old, old_field_name)
for name, old_field_name in (('comissao', 'cod_comissao'),
('periodo', 'cod_periodo_comp'))]
with reversion.create_revision():
composicao.save()
composicao, _ = Composicao.objects.get_or_create(
comissao_id=comissao_id, periodo_id=periodo_id)
reversion.set_comment('Objeto criado pela migração')
new.composicao = composicao
@ -988,9 +1009,8 @@ def adjust_proposicao_antes_salvar(new, old):
def adjust_normarelacionada(new, old):
tipo = TipoVinculoNormaJuridica.objects.filter(sigla=old.tip_vinculo)
assert len(tipo) == 1
new.tipo_vinculo = tipo[0]
new.tipo_vinculo = TipoVinculoNormaJuridica.objects.get(
sigla=old.tip_vinculo)
def adjust_protocolo_antes_salvar(new, old):
@ -1015,8 +1035,11 @@ def adjust_registrovotacao_antes_salvar(new, old):
def adjust_tipoafastamento(new, old):
if old.ind_afastamento == 1:
assert xor(old.ind_afastamento, old.ind_fim_mandato)
if old.ind_afastamento:
new.indicador = 'A'
elif old.ind_fim_mandato:
new.indicador = 'F'
MODEL_TIPO_MATERIA_OU_DOCUMENTO = {'M': TipoMateriaLegislativa,

96
sapl/legacy/test_renames.py

@ -1,29 +1,76 @@
import sapl.comissoes
import sapl.materia
import sapl.norma
import sapl.sessao
from django.contrib.contenttypes.fields import GenericForeignKey
from sapl.base.models import AppConfig, Autor, CasaLegislativa, TipoAutor
from sapl.comissoes.models import \
DocumentoAcessorio as DocumentoAcessorioComissoes
from sapl.comissoes.models import Comissao, Composicao, Participacao, Reuniao
from sapl.materia.models import (AcompanhamentoMateria, DocumentoAcessorio,
MateriaLegislativa, Proposicao,
TipoMateriaLegislativa, TipoProposicao,
Tramitacao)
from sapl.norma.models import (NormaJuridica, NormaRelacionada,
TipoVinculoNormaJuridica)
from sapl.parlamentares.models import (Frente, Mandato, Parlamentar, Partido,
TipoAfastamento, Votante)
from sapl.protocoloadm.models import DocumentoAdministrativo
from sapl.sessao.models import (Bancada, Bloco, CargoBancada,
ExpedienteMateria, Orador, OradorExpediente,
OrdemDia, RegistroVotacao, ResumoOrdenacao,
SessaoPlenaria, TipoResultadoVotacao,
VotoParlamentar)
from .migracao_dados import appconfs, get_renames, legacy_app
RENAMING_IGNORED_MODELS = [
sapl.comissoes.models.Composicao,
sapl.norma.models.AssuntoNormaRelationship,
Votante, Frente, Bancada, CargoBancada, Bloco, # parlamentares
Composicao, Reuniao, DocumentoAcessorioComissoes, # commissoes
AppConfig, CasaLegislativa, # base
ResumoOrdenacao, # sessao
TipoVinculoNormaJuridica, # norma
# FIXME retirar daqui depois que a issue #218 for resolvida!!!!!!!
sapl.sessao.models.AcompanharMateria,
]
RENAMING_IGNORED_FIELDS = [
(sapl.comissoes.models.Participacao, {'composicao'}),
(sapl.materia.models.Proposicao, {'documento'}),
(sapl.materia.models.TipoProposicao, {'tipo_documento'}),
(sapl.materia.models.Tramitacao, {'ultima'}),
(sapl.sessao.models.SessaoPlenaria, {'finalizada',
'upload_pauta',
(TipoAfastamento, {'indicador'}),
(Participacao, {'composicao'}),
(Proposicao, {
'ano', 'content_type', 'object_id', 'conteudo_gerado_related',
'status', 'hash_code', 'texto_original'}),
(TipoProposicao, {
'object_id', 'content_type', 'tipo_conteudo_related', 'perfis',
# não estou entendendo como esses campos são enumerados,
# mas eles não fazem parte da migração
# 'tipomaterialegislativa_set', 'tipodocumento_set',
}),
(Tramitacao, {'ultima'}),
(SessaoPlenaria, {'finalizada', 'iniciada', 'painel_aberto', 'interativa',
'upload_ata',
'iniciada'}),
(sapl.sessao.models.ExpedienteMateria, {'votacao_aberta'}),
(sapl.sessao.models.OrdemDia, {'votacao_aberta'}),
'upload_anexo',
'upload_pauta'}),
(ExpedienteMateria, {'votacao_aberta'}),
(OrdemDia, {'votacao_aberta'}),
(NormaJuridica, {'texto_integral', 'data_ultima_atualizacao', 'assuntos'}),
(Parlamentar, {
'uf_residencia', 'municipio_residencia', 'cropping', 'fotografia'}),
(Partido, {'logo_partido'}),
(MateriaLegislativa, {
'autores', 'anexadas', 'data_ultima_atualizacao', 'texto_original'}),
(DocumentoAdministrativo, {'protocolo', 'texto_integral'}),
(Mandato, {'titular', 'data_fim_mandato', 'data_inicio_mandato'}),
(TipoMateriaLegislativa, {'sequencia_numeracao'}),
(TipoAutor, {'content_type'}),
(TipoResultadoVotacao, {'natureza'}),
(RegistroVotacao, {'ordem', 'expediente'}),
(DocumentoAcessorio, {'arquivo', 'data_ultima_atualizacao'}),
(OradorExpediente, {'upload_anexo', 'observacao'}),
(Orador, {'upload_anexo', 'observacao'}),
(VotoParlamentar, {'user', 'ip', 'expediente', 'data_hora', 'ordem'}),
(NormaRelacionada, {'tipo_vinculo'}),
(AcompanhamentoMateria, {'confirmado', 'data_cadastro', 'usuario'}),
(Autor, {'user', 'content_type', 'object_id', 'autor_related'}),
(Comissao, {'ativa'}),
]
@ -31,7 +78,9 @@ def test_get_renames():
field_renames, model_renames = get_renames()
all_models = {m for ac in appconfs for m in ac.get_models()}
for model in all_models:
field_names = {f.name for f in model._meta.fields if f.name != 'id'}
field_names = {f.name for f in model._meta.get_fields()
if f.name != 'id'
and (f.concrete or isinstance(f, GenericForeignKey))}
if model not in field_renames:
# check ignored models in renaming
assert model in RENAMING_IGNORED_MODELS
@ -46,11 +95,12 @@ def test_get_renames():
match_msg_template % ('new', 'current')
# ignored fields are explicitly listed
missing_in_renames = field_names - renamed
if missing_in_renames:
assert (model, missing_in_renames) in \
RENAMING_IGNORED_FIELDS, \
'Field(s) missing in renames but not explicitly listed'
missing = field_names - renamed
if missing:
assert (model, missing) in RENAMING_IGNORED_FIELDS, \
'Campos faltando na renomeação,' \
'mas não listados explicitamente: ({}, {})'.format(
model.__name__, missing)
# all old names correspond to a legacy field
legacy_model = legacy_app.get_model(

9
sapl/materia/legacy.yaml

@ -58,9 +58,8 @@ AssuntoMateria:
dispositivo: des_dispositivo
DespachoInicial:
comissao: cod_comissao
materia: cod_materia
numero_ordem: num_ordem
comissao: cod_comissao
TipoDocumento:
descricao: des_tipo_documento
@ -112,10 +111,6 @@ Parecer:
TipoProposicao:
descricao: des_tipo_proposicao
materia_ou_documento: ind_mat_ou_doc
modelo: nom_modelo
tipo_documento:
tipo_materia:
Proposicao:
autor: cod_autor
@ -124,7 +119,7 @@ Proposicao:
data_recebimento: dat_recebimento
descricao: txt_descricao
justificativa_devolucao: txt_justif_devolucao
materia: cod_mat_ou_doc
materia_de_vinculo: cod_materia
numero_proposicao: num_proposicao
tipo: tip_proposicao

6
sapl/materia/models.py

@ -40,9 +40,6 @@ class TipoProposicao(models.Model):
error_messages={
'unique': _('Já existe um Tipo de Proposição com esta descrição.')
})
# FIXME - para a rotina de migração - estes campos mudaram
# retire o comentário quando resolver
content_type = models.ForeignKey(ContentType, default=None,
on_delete=models.PROTECT,
verbose_name=_('Definição de Tipo'))
@ -378,9 +375,6 @@ class AssuntoMateria(models.Model):
@reversion.register()
class DespachoInicial(models.Model):
# TODO M2M?
# TODO Despachos não são necessáriamente comissoes, podem ser outros
# órgãos, ex: procuradorias
materia = models.ForeignKey(MateriaLegislativa, on_delete=models.CASCADE)
comissao = models.ForeignKey(Comissao, on_delete=models.CASCADE)

1
sapl/norma/legacy.yaml

@ -9,7 +9,6 @@ TipoNormaJuridica:
NormaJuridica:
ano: ano_norma
assunto: cod_assunto
complemento: ind_complemento
data: dat_norma
data_publicacao: dat_publicacao

4
sapl/parlamentares/legacy.yaml

@ -38,7 +38,6 @@ Parlamentar:
ativo: ind_ativo
biografia: txt_biografia
cep_residencia: num_cep_resid
cod_casa: cod_casa
cpf: num_cpf
data_nascimento: dat_nascimento
email: end_email
@ -58,7 +57,6 @@ Parlamentar:
telefone: num_tel_parlamentar
telefone_residencia: num_tel_resid
titulo_eleitor: num_tit_eleitor
unidade_deliberativa: ind_unid_deliberativa
TipoDependente:
descricao: des_tipo_dependente
@ -80,10 +78,8 @@ Filiacao:
partido: cod_partido
TipoAfastamento:
afastamento: ind_afastamento
descricao: des_afastamento
dispositivo: des_dispositivo
fim_mandato: ind_fim_mandato
Mandato:
coligacao: cod_coligacao

1
sapl/protocoloadm/legacy.yaml

@ -11,7 +11,6 @@ DocumentoAdministrativo:
dias_prazo: num_dias_prazo
interessado: txt_interessado
numero: num_documento
numero_protocolo: num_protocolo
observacao: txt_observacao
tipo: tip_documento
tramitacao: ind_tramitacao

1
sapl/sessao/legacy.yaml

@ -52,7 +52,6 @@ OradorExpediente (OradoresExpediente): {}
OrdemDia: {}
PresencaOrdemDia (OrdemDiaPresenca):
data_ordem: dat_ordem
parlamentar: cod_parlamentar
sessao_plenaria: cod_sessao_plen

Loading…
Cancel
Save