Browse Source

Migra conteudo_gerado_related de Proposicao

pull/1847/head
Marcio Mazza 7 years ago
parent
commit
901c0ec043
  1. 140
      sapl/legacy/migracao_dados.py
  2. 4
      sapl/materia/models.py

140
sapl/legacy/migracao_dados.py

@ -1,6 +1,6 @@
import re import re
import traceback import traceback
from collections import OrderedDict, defaultdict from collections import OrderedDict, defaultdict, namedtuple
from datetime import date from datetime import date
from functools import lru_cache, partial from functools import lru_cache, partial
from itertools import groupby from itertools import groupby
@ -8,18 +8,16 @@ from operator import xor
from subprocess import PIPE, call from subprocess import PIPE, call
import pkg_resources import pkg_resources
import pytz
import reversion import reversion
import yaml import yaml
from django.apps import apps from django.apps import apps
from django.apps.config import AppConfig
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group from django.contrib.auth.models import Group
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.db import connections, transaction from django.db import connections, transaction
from django.db.models import Max, Q from django.db.models import Max, Q
from django.db.models.base import ModelBase
from pytz import timezone
from unipath import Path from unipath import Path
from sapl.base.models import AppConfig as AppConf from sapl.base.models import AppConfig as AppConf
@ -27,8 +25,8 @@ from sapl.base.models import Autor, TipoAutor, cria_models_tipo_autor
from sapl.comissoes.models import Comissao, Composicao, Participacao from sapl.comissoes.models import Comissao, Composicao, Participacao
from sapl.legacy.models import NormaJuridica as OldNormaJuridica from sapl.legacy.models import NormaJuridica as OldNormaJuridica
from sapl.legacy.models import TipoNumeracaoProtocolo from sapl.legacy.models import TipoNumeracaoProtocolo
from sapl.materia.models import (AcompanhamentoMateria, Proposicao, from sapl.materia.models import (AcompanhamentoMateria, MateriaLegislativa,
StatusTramitacao, TipoDocumento, Proposicao, StatusTramitacao, TipoDocumento,
TipoMateriaLegislativa, TipoProposicao, TipoMateriaLegislativa, TipoProposicao,
Tramitacao) Tramitacao)
from sapl.norma.models import (AssuntoNorma, NormaJuridica, NormaRelacionada, from sapl.norma.models import (AssuntoNorma, NormaJuridica, NormaRelacionada,
@ -117,12 +115,31 @@ models_novos_para_antigos = {
for model in field_renames} for model in field_renames}
models_novos_para_antigos[Composicao] = models_novos_para_antigos[Participacao] models_novos_para_antigos[Composicao] = models_novos_para_antigos[Participacao]
content_types = {model: ContentType.objects.get(
app_label=model._meta.app_label, model=model._meta.model_name)
for model in field_renames}
campos_novos_para_antigos = { campos_novos_para_antigos = {
model._meta.get_field(nome_novo): nome_antigo model._meta.get_field(nome_novo): nome_antigo
for model, renames in field_renames.items() for model, renames in field_renames.items()
for nome_novo, nome_antigo in renames.items()} for nome_novo, nome_antigo in renames.items()}
# campos de Composicao (de Comissao)
for nome_novo, nome_antigo in (('comissao', 'cod_comissao'),
('periodo', 'cod_periodo_comp')):
campos_novos_para_antigos[
Composicao._meta.get_field(nome_novo)] = nome_antigo
# campos virtuais de Proposicao para funcionarem com get_fk_related
CampoFalso = namedtuple('CampoFalso', ['model', 'related_model'])
CAMPOS_FALSOS_PROPOSICAO = {
TipoMateriaLegislativa: CampoFalso(Proposicao, MateriaLegislativa),
TipoDocumento: CampoFalso(Proposicao, DocumentoAdministrativo)
}
for campo_falso in CAMPOS_FALSOS_PROPOSICAO.values():
campos_novos_para_antigos[campo_falso] = 'cod_mat_ou_doc'
# MIGRATION ################################################################# # MIGRATION #################################################################
@ -168,10 +185,14 @@ class ForeignKeyFaltando(ObjectDoesNotExist):
campo = campos_novos_para_antigos[self.field] campo = campos_novos_para_antigos[self.field]
_, tabela, campos_pk = get_estrutura_legado(self.field.model) _, tabela, campos_pk = get_estrutura_legado(self.field.model)
pk = {c: getattr(self.old, c) for c in campos_pk} pk = {c: getattr(self.old, c) for c in campos_pk}
sql = 'select * from {} where {}'.format(
tabela,
' and '.join(['{} = {}'.format(k, v) for k, v in pk.items()]))
return OrderedDict((('campo', campo), return OrderedDict((('campo', campo),
('valor', self.valor), ('valor', self.valor),
('tabela', tabela), ('tabela', tabela),
('pk', pk))) ('pk', pk),
('sql', sql)))
@lru_cache() @lru_cache()
@ -180,8 +201,8 @@ def _get_all_ids_from_model(model):
return set(model.objects.values_list('id', flat=True)) return set(model.objects.values_list('id', flat=True))
def get_fk_related(field, old, old_field_name): def get_fk_related(field, old):
valor = getattr(old, old_field_name) valor = getattr(old, campos_novos_para_antigos[field])
if valor is None and field.null: if valor is None and field.null:
return None return None
if valor in _get_all_ids_from_model(field.related_model): if valor in _get_all_ids_from_model(field.related_model):
@ -679,24 +700,20 @@ def dict_representer(dumper, data):
yaml.add_representer(OrderedDict, dict_representer) yaml.add_representer(OrderedDict, dict_representer)
class DataMigrator:
def __init__(self):
self.choice_valida = {}
# configura timezone de migração # configura timezone de migração
self.nome_banco_legado = DATABASES['legacy']['NAME'] nome_banco_legado = DATABASES['legacy']['NAME']
match = re.match('sapl_cm_(.*)', self.nome_banco_legado) match = re.match('sapl_cm_(.*)', nome_banco_legado)
sigla_casa = match.group(1) sigla_casa = match.group(1)
with open(PATH_TABELA_TIMEZONES, 'r') as arq: with open(PATH_TABELA_TIMEZONES, 'r') as arq:
tabela_timezones = yaml.load(arq) tabela_timezones = yaml.load(arq)
municipio, uf, nome_timezone = tabela_timezones[sigla_casa] municipio, uf, nome_timezone = tabela_timezones[sigla_casa]
if nome_timezone: if nome_timezone:
self.timezone = timezone(nome_timezone) timezone = pytz.timezone(nome_timezone)
else: else:
self.timezone = get_timezone(municipio, uf) timezone = get_timezone(municipio, uf)
def populate_renamed_fields(self, new, old):
def populate_renamed_fields(new, old):
renames = field_renames[type(new)] renames = field_renames[type(new)]
for field in new._meta.fields: for field in new._meta.fields:
@ -706,7 +723,7 @@ class DataMigrator:
if field_type == 'ForeignKey': if field_type == 'ForeignKey':
fk_field_name = '{}_id'.format(field.name) fk_field_name = '{}_id'.format(field.name)
value = get_fk_related(field, old, old_field_name) value = get_fk_related(field, old)
setattr(new, fk_field_name, value) setattr(new, fk_field_name, value)
else: else:
value = getattr(old, old_field_name) value = getattr(old, old_field_name)
@ -722,15 +739,14 @@ class DataMigrator:
return (field_type == tipo return (field_type == tipo
and value and not value.tzinfo) and value and not value.tzinfo)
if campo_tempo_sem_timezone('DateTimeField'): if campo_tempo_sem_timezone('DateTimeField'):
value = self.timezone.localize(value) value = timezone.localize(value)
if campo_tempo_sem_timezone('TimeField'): if campo_tempo_sem_timezone('TimeField'):
value = value.replace(tzinfo=self.timezone) value = value.replace(tzinfo=timezone)
setattr(new, field.name, value) setattr(new, field.name, value)
def migrar(self, obj=appconfs, interativo=True):
# warning: model/app migration order is of utmost importance
def migrar_dados(interativo=True):
uniformiza_banco() uniformiza_banco()
# excluindo database antigo. # excluindo database antigo.
@ -754,19 +770,19 @@ class DataMigrator:
fill_vinculo_norma_juridica() fill_vinculo_norma_juridica()
fill_dados_basicos() fill_dados_basicos()
info('Começando migração: %s...' % obj) info('Começando migração: ...')
try: try:
ocorrencias.clear() ocorrencias.clear()
dir_ocorrencias = DIR_RESULTADOS.child(date.today().isoformat()) dir_ocorrencias = DIR_RESULTADOS.child(date.today().isoformat())
dir_ocorrencias.mkdir(parents=True) dir_ocorrencias.mkdir(parents=True)
self._do_migrate(obj) migrar_todos_os_models()
except Exception as e: except Exception as e:
ocorrencias['traceback'] = str(traceback.format_exc()) ocorrencias['traceback'] = str(traceback.format_exc())
raise e raise e
finally: finally:
# grava ocorrências # grava ocorrências
arq_ocorrencias = dir_ocorrencias.child( arq_ocorrencias = dir_ocorrencias.child(
self.nome_banco_legado + '.yaml') nome_banco_legado + '.yaml')
with open(arq_ocorrencias, 'w') as arq: with open(arq_ocorrencias, 'w') as arq:
dump = yaml.dump(dict(ocorrencias), allow_unicode=True) dump = yaml.dump(dict(ocorrencias), allow_unicode=True)
arq.write(dump.replace('\n- ', '\n\n- ')) arq.write(dump.replace('\n- ', '\n\n- '))
@ -775,34 +791,33 @@ class DataMigrator:
# recria tipos de autor padrão que não foram criados pela migração # recria tipos de autor padrão que não foram criados pela migração
cria_models_tipo_autor() cria_models_tipo_autor()
def _do_migrate(self, obj):
if isinstance(obj, AppConfig):
models = [model for model in obj.models.values()
if model in field_renames]
if obj.label == 'materia': def move_para_depois_de(lista, movido, referencias):
indice_inicial = lista.index(movido)
lista.remove(movido)
indice_apos_refs = max(lista.index(r) for r in referencias) + 1
lista.insert(max(indice_inicial, indice_apos_refs), movido)
return lista
def migrar_todos_os_models():
models = [model for app in appconfs for model in app.models.values()
if model in field_renames]
# Devido à referência TipoProposicao.tipo_conteudo_related # Devido à referência TipoProposicao.tipo_conteudo_related
# a migração de TipoProposicao precisa ser feita # a migração de TipoProposicao precisa ser feita
# após TipoMateriaLegislativa e TipoDocumento # após TipoMateriaLegislativa e TipoDocumento
# (porém antes de Proposicao) # (porém antes de Proposicao)
models.remove(TipoProposicao) move_para_depois_de(models, TipoProposicao,
pos_tipo_proposicao = max( [TipoMateriaLegislativa, TipoDocumento])
models.index(TipoMateriaLegislativa),
models.index(TipoDocumento)) + 1
models.insert(pos_tipo_proposicao, TipoProposicao)
assert models.index(TipoProposicao) < models.index(Proposicao) assert models.index(TipoProposicao) < models.index(Proposicao)
move_para_depois_de(models, Proposicao,
[MateriaLegislativa, DocumentoAdministrativo])
self._do_migrate(models) for model in models:
elif isinstance(obj, ModelBase): migrar_model(model)
self.migrate_model(obj)
elif hasattr(obj, '__iter__'):
for item in obj:
self._do_migrate(item)
else:
raise TypeError(
'Parameter must be a Model, AppConfig or a sequence of them')
def migrate_model(self, model):
def migrar_model(model):
print('Migrando %s...' % model.__name__) print('Migrando %s...' % model.__name__)
model_legado, tabela_legado, campos_pk_legado = \ model_legado, tabela_legado, campos_pk_legado = \
@ -836,7 +851,7 @@ class DataMigrator:
for old in old_records: for old in old_records:
new = model() new = model()
try: try:
self.populate_renamed_fields(new, old) populate_renamed_fields(new, old)
if ajuste_antes_salvar: if ajuste_antes_salvar:
ajuste_antes_salvar(new, old) ajuste_antes_salvar(new, old)
except ForeignKeyFaltando as e: except ForeignKeyFaltando as e:
@ -878,11 +893,6 @@ class DataMigrator:
exec_legado(sql_delete_legado) exec_legado(sql_delete_legado)
def migrar_dados(obj=appconfs, interativo=True):
dm = DataMigrator()
dm.migrar(obj, interativo)
# MIGRATION_ADJUSTMENTS ##################################################### # MIGRATION_ADJUSTMENTS #####################################################
def adjust_acompanhamentomateria(new, old): def adjust_acompanhamentomateria(new, old):
@ -993,9 +1003,8 @@ def adjust_parlamentar(new, old):
def adjust_participacao(new, old): def adjust_participacao(new, old):
comissao_id, periodo_id = [ comissao_id, periodo_id = [
get_fk_related(Composicao._meta.get_field(name), old, old_field_name) get_fk_related(Composicao._meta.get_field(name), old)
for name, old_field_name in (('comissao', 'cod_comissao'), for name in ('comissao', 'periodo')]
('periodo', 'cod_periodo_comp'))]
with reversion.create_revision(): with reversion.create_revision():
composicao, _ = Composicao.objects.get_or_create( composicao, _ = Composicao.objects.get_or_create(
comissao_id=comissao_id, periodo_id=periodo_id) comissao_id=comissao_id, periodo_id=periodo_id)
@ -1003,11 +1012,6 @@ def adjust_participacao(new, old):
new.composicao = composicao new.composicao = composicao
def adjust_proposicao_antes_salvar(new, old):
if new.data_envio:
new.ano = new.data_envio.year
def adjust_normarelacionada(new, old): def adjust_normarelacionada(new, old):
new.tipo_vinculo = TipoVinculoNormaJuridica.objects.get( new.tipo_vinculo = TipoVinculoNormaJuridica.objects.get(
sigla=old.tip_vinculo) sigla=old.tip_vinculo)
@ -1042,14 +1046,14 @@ def adjust_tipoafastamento(new, old):
new.indicador = 'F' new.indicador = 'F'
MODEL_TIPO_MATERIA_OU_DOCUMENTO = {'M': TipoMateriaLegislativa, TIPO_MATERIA_OU_TIPO_DOCUMENTO = {'M': TipoMateriaLegislativa,
'D': TipoDocumento} 'D': TipoDocumento}
def adjust_tipoproposicao(new, old): def adjust_tipoproposicao(new, old):
"Aponta para o tipo relacionado de matéria ou documento" "Aponta para o tipo relacionado de matéria ou documento"
value = old.tip_mat_ou_doc value = old.tip_mat_ou_doc
model_tipo = MODEL_TIPO_MATERIA_OU_DOCUMENTO[old.ind_mat_ou_doc] model_tipo = TIPO_MATERIA_OU_TIPO_DOCUMENTO[old.ind_mat_ou_doc]
tipo = model_tipo.objects.filter(pk=value) tipo = model_tipo.objects.filter(pk=value)
if tipo: if tipo:
new.tipo_conteudo_related = tipo[0] new.tipo_conteudo_related = tipo[0]
@ -1060,6 +1064,16 @@ def adjust_tipoproposicao(new, old):
label={'ind_mat_ou_doc': old.ind_mat_ou_doc}) label={'ind_mat_ou_doc': old.ind_mat_ou_doc})
def adjust_proposicao_antes_salvar(new, old):
if new.data_envio:
new.ano = new.data_envio.year
if old.cod_mat_ou_doc:
tipo_mat_ou_doc = type(new.tipo.tipo_conteudo_related)
campo_falso = CAMPOS_FALSOS_PROPOSICAO[tipo_mat_ou_doc]
new.content_type = content_types[campo_falso.related_model]
new.object_id = get_fk_related(campo_falso, old)
def adjust_statustramitacao(new, old): def adjust_statustramitacao(new, old):
if old.ind_fim_tramitacao: if old.ind_fim_tramitacao:
new.indicador = 'F' new.indicador = 'F'

4
sapl/materia/models.py

@ -686,16 +686,12 @@ class Proposicao(models.Model):
texto_articulado = GenericRelation( texto_articulado = GenericRelation(
TextoArticulado, related_query_name='texto_articulado') TextoArticulado, related_query_name='texto_articulado')
# FIXME - para a rotina de migração - este campo mudou
# retire o comentário quando resolver
materia_de_vinculo = models.ForeignKey( materia_de_vinculo = models.ForeignKey(
MateriaLegislativa, blank=True, null=True, MateriaLegislativa, blank=True, null=True,
on_delete=models.CASCADE, on_delete=models.CASCADE,
verbose_name=_('Matéria anexadora'), verbose_name=_('Matéria anexadora'),
related_name=_('proposicao_set')) related_name=_('proposicao_set'))
# FIXME - para a rotina de migração - estes campos mudaram
# retire o comentário quando resolver
content_type = models.ForeignKey( content_type = models.ForeignKey(
ContentType, default=None, blank=True, null=True, ContentType, default=None, blank=True, null=True,
verbose_name=_('Tipo de Material Gerado')) verbose_name=_('Tipo de Material Gerado'))

Loading…
Cancel
Save