Browse Source

Merge branch '1376-simplifica-migracao'

pull/1477/head
Marcio Mazza 7 years ago
parent
commit
5f92926ef2
  1. 2
      requirements/migration-requirements.txt
  2. 4
      sapl/legacy/management/commands/recria_constraints.py
  3. 527
      sapl/legacy/migration.py
  4. 130
      sapl/legacy/scripts/fix_tables.sql
  5. 6
      sapl/legacy/scripts/migra_um_db.sh
  6. 3
      sapl/legacy_migration_settings.py
  7. 26
      sapl/materia/migrations/0012_auto_20170815_1238.py
  8. 21
      sapl/materia/migrations/0013_auto_20170816_1136.py
  9. 16
      sapl/materia/migrations/0016_merge.py
  10. 12
      sapl/materia/models.py

2
requirements/migration-requirements.txt

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

4
sapl/legacy/management/commands/recria_constraints.py

@ -1,7 +1,5 @@
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from sapl.legacy.migration import recria_constraints
class Command(BaseCommand): class Command(BaseCommand):
@ -9,4 +7,4 @@ class Command(BaseCommand):
'migração de dados') 'migração de dados')
def handle(self, *args, **options): def handle(self, *args, **options):
recria_constraints() pass

527
sapl/legacy/migration.py

@ -1,5 +1,6 @@
import re import re
from datetime import date from datetime import date
from functools import lru_cache
from subprocess import PIPE, call from subprocess import PIPE, call
import pkg_resources import pkg_resources
@ -11,13 +12,11 @@ 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 OperationalError, ProgrammingError, connections, models from django.db import connections, transaction
from django.db.models import CharField, Count, Max, ProtectedError, TextField from django.db.models import Count, Max
from django.db.models.base import ModelBase from django.db.models.base import ModelBase
from model_mommy import mommy
from model_mommy.mommy import foreign_key_required, make
from sapl.base.models import Argumento, Autor, Constraint, ProblemaMigracao from sapl.base.models import Autor, ProblemaMigracao, TipoAutor
from sapl.comissoes.models import Comissao, Composicao, Participacao from sapl.comissoes.models import Comissao, Composicao, Participacao
from sapl.materia.models import (AcompanhamentoMateria, Proposicao, from sapl.materia.models import (AcompanhamentoMateria, Proposicao,
StatusTramitacao, TipoDocumento, StatusTramitacao, TipoDocumento,
@ -112,80 +111,36 @@ def warn(msg):
print('CUIDADO! ' + msg) print('CUIDADO! ' + msg)
def erro(msg): class ForeignKeyFaltando(ObjectDoesNotExist):
print('ERRO: ' + msg) 'Uma FK aponta para um registro inexistente'
pass
def get_fk_related(field, value, label=None): @lru_cache()
if value is None and field.null is False: def _get_all_ids_from_model(model):
value = 0 # esta função para uso apenas em get_fk_related
if value is not None: return set(model.objects.values_list('id', flat=True))
try:
value = field.related_model.objects.get(id=value)
except ObjectDoesNotExist:
msg = 'FK [%s] não encontrada para valor %s ' \
'(em %s %s)' % (
field.name, value,
field.model.__name__, label or '---')
if value == 0:
if not field.null:
fields_dict = get_fields_dict(field.related_model)
# Cria stub ao final da tabela para evitar erros
pk = get_last_value(field.related_model)
with reversion.create_revision():
reversion.set_comment('Stub criado pela migração')
value = mommy.make(
field.related_model, **fields_dict,
pk=(pk + 1 or 1))
descricao = 'stub criado para campos não nuláveis!'
save_relation(value, [field.name], msg, descricao,
eh_stub=True)
warn(msg + ' => ' + descricao)
else:
value = None
else:
if field.model._meta.label == 'sessao.RegistroVotacao' and \
field.name == 'ordem':
return value
# Caso TipoProposicao não exista, um objeto será criado então
# com content_type=13 (ProblemaMigracao)
if field.related_model.__name__ == 'TipoProposicao':
tipo = TipoProposicao.objects.filter(descricao='Erro')
if not tipo:
with reversion.create_revision():
reversion.set_comment(
'TipoProposicao "Erro" criado')
ct = ContentType.objects.get(pk=13)
value = TipoProposicao.objects.create(
id=value, descricao='Erro', content_type=ct)
ultimo_valor = get_last_value(type(value))
alter_sequence(type(value), ultimo_valor + 1)
else:
value = tipo[0]
else:
with reversion.create_revision():
reversion.set_comment('Stub criado pela migração')
value = make_stub(field.related_model, value)
descricao = 'stub criado para entrada orfã!'
warn(msg + ' => ' + descricao)
save_relation(value, [field.name], msg, descricao,
eh_stub=True)
else:
assert value
return value
def get_fk_related(field, value, label=None):
if value is None and field.null:
return None
def get_field(model, fieldname): # if field.related_model.objects.filter(id=value).exists():
return model._meta.get_field(fieldname) if value in _get_all_ids_from_model(field.related_model):
return value
else:
msg = 'FK [%s] não encontrada para o valor %s (em %s %s)' % (
field.name, value, field.model.__name__, label or '---')
warn(msg)
raise ForeignKeyFaltando(msg)
def exec_sql_file(path, db='default'): def exec_sql_file(path, db='default'):
cursor = connections[db].cursor() with open(path) as arq:
for line in open(path): sql = arq.read()
try: with connections[db].cursor() as cursor:
cursor.execute(line) cursor.execute(sql)
except (OperationalError, ProgrammingError) as e:
print("Args: '%s'" % (str(e.args)))
def exec_sql(sql, db='default'): def exec_sql(sql, db='default'):
@ -204,135 +159,10 @@ def iter_sql_records(sql, db):
record.__dict__.update(zip(fieldnames, row)) record.__dict__.update(zip(fieldnames, row))
yield record yield record
# Todos os models têm no máximo uma constraint unique together
# Isso é necessário para que o método delete_constraints funcione corretamente
assert all(len(model._meta.unique_together) <= 1
for app in appconfs
for model in app.models.values())
def delete_constraints(model):
# pega nome da unique constraint dado o nome da tabela
table = model._meta.db_table
cursor = exec_sql("SELECT conname FROM pg_constraint WHERE conrelid = "
"(SELECT oid FROM pg_class WHERE relname LIKE "
"'%s') and contype = 'u';" % (table))
result = ()
result = cursor.fetchall()
# se existir um resultado, unique constraint será deletado
for r in result:
if r[0].endswith('key'):
words_list = r[0].split('_')
constraint = Constraint.objects.create(
nome_tabela=table, nome_constraint=r[0],
nome_model=model.__name__, tipo_constraint='one_to_one')
for w in words_list:
Argumento.objects.create(constraint=constraint, argumento=w)
else:
if model._meta.unique_together:
args_list = model._meta.unique_together[0]
constraint = Constraint.objects.create(
nome_tabela=table, nome_constraint=r[0],
nome_model=model.__name__,
tipo_constraint='unique_together')
for a in args_list:
Argumento.objects.create(constraint=constraint,
argumento=a)
warn('Excluindo unique constraint de nome %s' % r[0])
exec_sql("ALTER TABLE %s DROP CONSTRAINT %s;" %
(table, r[0]))
def problema_duplicatas(model, lista_duplicatas, argumentos):
for obj in lista_duplicatas:
pks = []
string_pks = ""
problema = "%s de PK %s não é único." % (model.__name__, obj.pk)
args_dict = {k: obj.__dict__[k]
for k in set(argumentos) & set(obj.__dict__.keys())}
for dup in model.objects.filter(**args_dict):
pks.append(dup.pk)
string_pks = "(" + ", ".join(map(str, pks)) + ")"
descricao = "As entradas de PK %s são idênticas, mas " \
"apenas uma deve existir" % string_pks
with reversion.create_revision():
warn(problema + ' => ' + descricao)
save_relation(obj=obj, problema=problema,
descricao=descricao, eh_stub=False, critico=True)
reversion.set_comment('%s não é único.' % model.__name__)
def recria_constraints():
constraints = Constraint.objects.all()
for con in constraints:
if con.tipo_constraint == 'one_to_one':
nome_tabela = con.nome_tabela
nome_constraint = con.nome_constraint
args = [a.argumento for a in con.argumento_set.all()]
args_string = ''
args_string = "(" + "_".join(map(str, args[2:-1])) + ")"
model = ContentType.objects.filter(
model=con.nome_model.lower())[0].model_class()
try:
exec_sql("ALTER TABLE %s ADD CONSTRAINT %s UNIQUE %s;" %
(nome_tabela, nome_constraint, args_string))
except ProgrammingError:
info('A constraint %s já foi recriada!' % nome_constraint)
if con.tipo_constraint == 'unique_together':
nome_tabela = con.nome_tabela
nome_constraint = con.nome_constraint
# Pegando explicitamente o primeiro valor do filter,
# pois pode ser que haja mais de uma ocorrência
model = ContentType.objects.filter(
model=con.nome_model.lower())[0].model_class()
args = [a.argumento for a in con.argumento_set.all()]
for i in range(len(args)):
if isinstance(model._meta.get_field(args[i]),
models.ForeignKey):
args[i] = args[i] + '_id'
args_string = ''
args_string += "(" + ', '.join(map(str, args)) + ")"
distintos = model.objects.distinct(*args)
todos = model.objects.all()
if hasattr(model, "content_type"):
distintos = distintos.exclude(content_type_id=None,
object_id=None)
todos = todos.exclude(content_type_id=None, object_id=None)
lista_duplicatas = list(set(todos).difference(set(distintos)))
if lista_duplicatas:
problema_duplicatas(model, lista_duplicatas, args)
else:
try:
exec_sql("ALTER TABLE %s ADD CONSTRAINT %s UNIQUE %s;" %
(nome_tabela, nome_constraint, args_string))
except ProgrammingError:
info('A constraint %s já foi recriada!' % nome_constraint)
except Exception as err:
problema = re.findall('\(.*?\)', err.args[0])
erro('A constraint [%s] da tabela [%s] não pode ser" \
recriada' % (nome_constraint, nome_tabela))
erro('Os dados %s = %s estão duplicados. '
'Arrume antes de recriar as constraints!' %
(problema[0], problema[1]))
def obj_desnecessario(obj):
relacoes = [
f for f in obj._meta.get_fields()
if (f.one_to_many or f.one_to_one) and f.auto_created]
sem_referencia = not any(rr.related_model.objects.filter(
**{rr.field.name: obj}).exists() for rr in relacoes)
if type(obj).__name__ == 'Parlamentar' and sem_referencia and \
obj.autor.all():
sem_referencia = False
return sem_referencia
def get_last_value(model): def get_last_value(model):
last_value = model.objects.all().aggregate(Max('pk')) last_value = model.objects.all().aggregate(Max('pk'))
return last_value['pk__max'] if last_value['pk__max'] else 0 return last_value['pk__max'] or 0
def alter_sequence(model, id): def alter_sequence(model, id):
@ -357,24 +187,6 @@ def save_relation(obj, nome_campo='', problema='', descricao='',
link.save() link.save()
def make_stub(model, id):
fields_dict = get_fields_dict(model)
new = mommy.prepare(model, **fields_dict, pk=id)
save_with_id(new, id)
return new
def get_fields_dict(model):
all_fields = model._meta.get_fields()
fields_dict = {}
fields_dict = {f.name: '????????????'[:f.max_length]
for f in all_fields
if isinstance(f, (CharField, TextField)) and
not f.choices and not f.blank}
return fields_dict
def fill_vinculo_norma_juridica(): def fill_vinculo_norma_juridica():
lista = [('A', 'Altera o(a)', lista = [('A', 'Altera o(a)',
'Alterado(a) pelo(a)'), 'Alterado(a) pelo(a)'),
@ -449,52 +261,46 @@ class DataMigrator:
for field in new._meta.fields: for field in new._meta.fields:
old_field_name = renames.get(field.name) old_field_name = renames.get(field.name)
field_type = field.get_internal_type() field_type = field.get_internal_type()
msg = ("O valor do campo %s (%s) da model %s era inválido" %
(field.name, field_type, field.model.__name__))
if old_field_name: if old_field_name:
old_value = getattr(old, old_field_name) old_value = getattr(old, old_field_name)
if isinstance(field, models.ForeignKey):
old_type = type(old) # not necessarily a model if field_type == 'ForeignKey':
if hasattr(old_type, '_meta') and \ # not necessarily a model
old_type._meta.pk.name != 'id': if hasattr(old, '_meta') and old._meta.pk.name != 'id':
label = old.pk label = old.pk
else: else:
label = '-- SEM PK --' label = '-- SEM PK --'
fk_field_name = '{}_id'.format(field.name)
value = get_fk_related(field, old_value, label) value = get_fk_related(field, old_value, label)
setattr(new, fk_field_name, value)
else: else:
value = getattr(old, old_field_name) value = getattr(old, old_field_name)
if field_type == 'DateField' and \ # TODO rever esse DateField após as mudança para datas com
not field.null and value is None: # timezone
descricao = 'A data 1111-11-11 foi colocada no lugar' if field_type == 'DateField' and \
problema = 'O valor da data era nulo ou inválido' not field.null and value is None:
warn(msg + # TODO REVER ISSO
' => ' + descricao) descricao = 'A data 1111-11-11 foi colocada no lugar'
value = date(1111, 11, 11) problema = 'O valor da data era nulo ou inválido'
self.data_mudada['obj'] = new warn("O valor do campo %s (%s) do model %s "
self.data_mudada['descricao'] = descricao "era inválido => %s" % (
self.data_mudada['problema'] = problema field.name, field_type,
self.data_mudada.setdefault('nome_campo', []).\ field.model.__name__, descricao))
append(field.name) value = date(1111, 11, 11)
if field_type == 'CharField' or field_type == 'TextField': self.data_mudada['obj'] = new
if value is None or value == 'None': self.data_mudada['descricao'] = descricao
self.data_mudada['problema'] = problema
self.data_mudada.setdefault('nome_campo', []).\
append(field.name)
if (field_type in ['CharField', 'TextField']
and value in [None, 'None']):
value = '' value = ''
setattr(new, field.name, value) setattr(new, field.name, value)
elif field.model.__name__ == 'TipoAutor' and \
field.name == 'content_type':
model = normalize(new.descricao.lower()).replace(' ', '')
content_types = field.related_model.objects.filter(
model=model).exclude(app_label='legacy')
assert len(content_types) <= 1
value = content_types[0] if content_types else None
setattr(new, field.name, value)
def migrate(self, obj=appconfs, interativo=True): def migrate(self, obj=appconfs, interativo=True):
# warning: model/app migration order is of utmost importance # warning: model/app migration order is of utmost importance
exec_sql_file(PROJECT_DIR.child( exec_sql_file(PROJECT_DIR.child(
'sapl', 'legacy', 'scripts', 'fix_tables.sql'), 'legacy') 'sapl', 'legacy', 'scripts', 'fix_tables.sql'), 'legacy')
self.to_delete = []
# excluindo database antigo. # excluindo database antigo.
if interativo: if interativo:
@ -516,29 +322,9 @@ class DataMigrator:
info('Começando migração: %s...' % obj) info('Começando migração: %s...' % obj)
self._do_migrate(obj) self._do_migrate(obj)
# Itera várias vezes na lista excluindo o que for possível
info('Deletando models com ind_excluido...')
while self.delete_ind_excluido():
pass
# Salva o que não pôde ser excluido da lista no problema da migração
for obj in self.to_delete:
msg = 'A entrada de PK %s da model %s não pode ser ' \
'excluida' % (obj.pk, obj._meta.model_name)
descricao = 'Um ou mais objetos protegidos'
warn(msg + ' => ' + descricao)
save_relation(obj=obj, problema=msg,
descricao=descricao, eh_stub=False)
info('Excluindo possíveis duplicações em RegistroVotacao...') info('Excluindo possíveis duplicações em RegistroVotacao...')
excluir_registrovotacao_duplicados() excluir_registrovotacao_duplicados()
info('Deletando stubs desnecessários...')
while self.delete_stubs():
pass
info('Recriando constraints...')
recria_constraints()
def _do_migrate(self, obj): def _do_migrate(self, obj):
if isinstance(obj, AppConfig): if isinstance(obj, AppConfig):
models_to_migrate = (model for model in obj.models.values() models_to_migrate = (model for model in obj.models.values()
@ -566,8 +352,6 @@ class DataMigrator:
legacy_model = legacy_app.get_model(legacy_model_name) legacy_model = legacy_app.get_model(legacy_model_name)
legacy_pk_name = legacy_model._meta.pk.name legacy_pk_name = legacy_model._meta.pk.name
delete_constraints(model)
# setup migration strategy for tables with or without a pk # setup migration strategy for tables with or without a pk
if legacy_pk_name == 'id': if legacy_pk_name == 'id':
# There is no pk in the legacy table # There is no pk in the legacy table
@ -589,58 +373,37 @@ class DataMigrator:
ajuste_depois_salvar = AJUSTE_DEPOIS_SALVAR.get(model) ajuste_depois_salvar = AJUSTE_DEPOIS_SALVAR.get(model)
# convert old records to new ones # convert old records to new ones
for old in old_records: with transaction.atomic():
new = model() for old in old_records:
self.populate_renamed_fields(new, old) if getattr(old, 'ind_excluido', False):
if ajuste_antes_salvar: # não migramos registros marcados como excluídos
ajuste_antes_salvar(new, old) continue
save(new, old) new = model()
if ajuste_depois_salvar: try:
ajuste_depois_salvar(new, old) self.populate_renamed_fields(new, old)
if self.data_mudada: if ajuste_antes_salvar:
with reversion.create_revision(): ajuste_antes_salvar(new, old)
save_relation(**self.data_mudada) except ForeignKeyFaltando:
self.data_mudada.clear() # tentamos preencher uma FK e o ojeto relacionado
reversion.set_comment('Ajuste de data pela migração') # não existe
if getattr(old, 'ind_excluido', False): # então este é um objeo órfão: simplesmente ignoramos
self.to_delete.append(new) continue
else:
save(new, old)
if ajuste_depois_salvar:
ajuste_depois_salvar(new, old)
if self.data_mudada:
with reversion.create_revision():
save_relation(**self.data_mudada)
self.data_mudada.clear()
reversion.set_comment(
'Ajuste de data pela migração')
# necessário para ajustar sequence da tabela para o ultimo valor de id # necessário para ajustar sequence da tabela para o ultimo valor de id
ultimo_valor = get_last_value(model) ultimo_valor = get_last_value(model)
alter_sequence(model, ultimo_valor + 1) alter_sequence(model, ultimo_valor + 1)
def delete_ind_excluido(self):
excluidos = 0
for obj in self.to_delete:
if obj_desnecessario(obj):
try:
obj.delete()
except ProtectedError:
pass
else:
self.to_delete.remove(obj)
excluidos += 1
return excluidos
def delete_stubs(self):
excluidos = 0
for obj in ProblemaMigracao.objects.all():
if obj.content_object and obj.eh_stub:
original = obj.content_type.get_all_objects_for_this_type(
id=obj.object_id)
if obj_desnecessario(original[0]):
qtd_exclusoes, *_ = original.delete()
assert qtd_exclusoes == 1
qtd_exclusoes, *_ = obj.delete()
assert qtd_exclusoes == 1
excluidos = excluidos + 1
elif not obj.content_object and not obj.eh_stub:
qtd_exclusoes, *_ = obj.delete()
assert qtd_exclusoes == 1
excluidos = excluidos + 1
return excluidos
def migrate(obj=appconfs, interativo=True): def migrate(obj=appconfs, interativo=True):
dm = DataMigrator() dm = DataMigrator()
@ -655,27 +418,22 @@ def adjust_acompanhamentomateria(new, old):
def adjust_documentoadministrativo(new, old): def adjust_documentoadministrativo(new, old):
if new.numero_protocolo: if new.numero_protocolo:
try: protocolo = Protocolo.objects.filter(
protocolo = Protocolo.objects.get(numero=new.numero_protocolo, numero=new.numero_protocolo, ano=new.ano)
ano=new.ano) if not protocolo:
new.protocolo = protocolo protocolo = Protocolo.objects.filter(
except Exception: numero=new.numero_protocolo, ano=new.ano + 1)
try: print('PROTOCOLO ENCONTRADO APENAS PARA O ANO SEGUINTE!!!!! '
protocolo = Protocolo.objects.get(numero=new.numero_protocolo, 'DocumentoAdministrativo: {}, numero_protocolo: {}, '
ano=new.ano + 1) 'ano doc adm: {}'.format(
new.protocolo = protocolo old.cod_documento, new.numero_protocolo, new.ano))
except Exception: if not protocolo:
protocolo = mommy.make(Protocolo, numero=new.numero_protocolo, raise ForeignKeyFaltando(
ano=new.ano) 'Protocolo {} faltando '
with reversion.create_revision(): '(referenciado no documento administrativo {}'.format(
problema = 'Protocolo Vinculado [numero_protocolo=%s, '\ new.numero_protocolo, old.cod_documento))
'ano=%s] não existe' % (new.numero_protocolo, assert len(protocolo) == 1
new.ano) new.protocolo = protocolo[0]
descricao = 'O protocolo inexistente foi criado'
warn(problema + ' => ' + descricao)
save_relation(obj=protocolo, problema=problema,
descricao=descricao, eh_stub=True)
reversion.set_comment('Protocolo não existia.')
def adjust_mandato(new, old): def adjust_mandato(new, old):
@ -707,7 +465,6 @@ def adjust_ordemdia_depois_salvar(new, old):
save_relation(obj=new, problema=problema, save_relation(obj=new, problema=problema,
descricao=descricao, eh_stub=False) descricao=descricao, eh_stub=False)
reversion.set_comment('OrdemDia sem número da ordem.') reversion.set_comment('OrdemDia sem número da ordem.')
pass
def adjust_parlamentar(new, old): def adjust_parlamentar(new, old):
@ -723,7 +480,7 @@ def adjust_parlamentar(new, old):
def adjust_participacao(new, old): def adjust_participacao(new, old):
composicao = Composicao() composicao = Composicao()
composicao.comissao, composicao.periodo = [ composicao.comissao_id, composicao.periodo_id = [
get_fk_related(Composicao._meta.get_field(name), value) get_fk_related(Composicao._meta.get_field(name), value)
for name, value in (('comissao', old.cod_comissao), for name, value in (('comissao', old.cod_comissao),
('periodo', old.cod_periodo_comp))] ('periodo', old.cod_periodo_comp))]
@ -840,6 +597,14 @@ def adjust_tramitacao(new, old):
new.turno = 'U' new.turno = 'U'
def adjust_tipo_autor(new, old):
model_apontado = normalize(new.descricao.lower()).replace(' ', '')
content_types = ContentType.objects.filter(
model=model_apontado).exclude(app_label='legacy')
assert len(content_types) <= 1
new.content_type = content_types[0] if content_types else None
def adjust_normajuridica_antes_salvar(new, old): def adjust_normajuridica_antes_salvar(new, old):
# Ajusta choice de esfera_federacao # Ajusta choice de esfera_federacao
# O 'S' vem de 'Selecionar'. Na versão antiga do SAPL, quando uma opção do # O 'S' vem de 'Selecionar'. Na versão antiga do SAPL, quando uma opção do
@ -852,43 +617,50 @@ def adjust_normajuridica_antes_salvar(new, old):
def adjust_normajuridica_depois_salvar(new, old): def adjust_normajuridica_depois_salvar(new, old):
# Ajusta relação M2M # Ajusta relação M2M
lista_pks_assunto = old.cod_assunto.split(',')
# list(filter(..)) usado para retirar strings vazias da lista if not old.cod_assunto: # it can be null or empty
for pk_assunto in list(filter(None, lista_pks_assunto)): return
new.assuntos.add(AssuntoNorma.objects.get(pk=pk_assunto))
# lista de pks separadas por vírgulas (ignorando strings vazias)
lista_pks_assunto = [int(pk) for pk in old.cod_assunto.split(',') if pk]
for pk_assunto in lista_pks_assunto:
try:
new.assuntos.add(AssuntoNorma.objects.get(pk=pk_assunto))
except ObjectDoesNotExist:
pass # ignora assuntos inexistentes
def adjust_autor(new, old): def adjust_autor(new, old):
if old.cod_parlamentar: if old.cod_parlamentar:
try: try:
new.autor_related = Parlamentar.objects.get(pk=old.cod_parlamentar) new.autor_related = Parlamentar.objects.get(pk=old.cod_parlamentar)
except Exception: except ObjectDoesNotExist:
with reversion.create_revision(): # ignoramos o autor órfão
msg = 'Um parlamentar relacionado de PK [%s] não existia' \ raise ForeignKeyFaltando('Parlamentar inexiste para autor')
% old.cod_parlamentar else:
reversion.set_comment('Stub criado pela migração') new.nome = new.autor_related.nome_parlamentar
value = make_stub(Parlamentar, old.cod_parlamentar)
descricao = 'stub criado para entrada orfã!'
warn(msg + ' => ' + descricao)
save_relation(value, [], msg, descricao,
eh_stub=True)
new.autor_related = value
new.nome = new.autor_related.nome_parlamentar
elif old.cod_comissao: elif old.cod_comissao:
new.autor_related = Comissao.objects.get(pk=old.cod_comissao) try:
new.nome = new.autor_related.nome new.autor_related = Comissao.objects.get(pk=old.cod_comissao)
except ObjectDoesNotExist:
# ignoramos o autor órfão
raise ForeignKeyFaltando('Comissao inexiste para autor')
else:
new.nome = new.autor_related.nome
if old.col_username: if old.col_username:
if not get_user_model().objects.filter( user_model = get_user_model()
username=old.col_username).exists(): if not user_model.objects.filter(username=old.col_username).exists():
user = get_user_model()(username=old.col_username) # cria um novo ususaŕio para o autor
user = user_model(username=old.col_username)
user.set_password(12345) user.set_password(12345)
with reversion.create_revision(): with reversion.create_revision():
user.save() user.save()
reversion.set_comment('Objeto criado pela migração') reversion.set_comment(
'Usuário criado pela migração para o autor {}'.format(
old.cod_autor))
grupo_autor = Group.objects.get(name="Autor") grupo_autor = Group.objects.get(name="Autor")
user.groups.add(grupo_autor) user.groups.add(grupo_autor)
@ -905,6 +677,7 @@ def adjust_comissao(new, old):
AJUSTE_ANTES_SALVAR = { AJUSTE_ANTES_SALVAR = {
Autor: adjust_autor, Autor: adjust_autor,
TipoAutor: adjust_tipo_autor,
AcompanhamentoMateria: adjust_acompanhamentomateria, AcompanhamentoMateria: adjust_acompanhamentomateria,
Comissao: adjust_comissao, Comissao: adjust_comissao,
DocumentoAdministrativo: adjust_documentoadministrativo, DocumentoAdministrativo: adjust_documentoadministrativo,
@ -935,31 +708,13 @@ AJUSTE_DEPOIS_SALVAR = {
# CHECKS #################################################################### # CHECKS ####################################################################
def get_ind_excluido(obj): def get_ind_excluido(new):
legacy_model = legacy_app.get_model(type(obj).__name__) legacy_model = legacy_app.get_model(type(new).__name__)
return getattr(legacy_model.objects.get( old = legacy_model.objects.get(**{legacy_model._meta.pk.name: new.id})
**{legacy_model._meta.pk.name: obj.id}), 'ind_excluido', False) return getattr(old, 'ind_excluido', False)
def check_app_no_ind_excluido(app): def check_app_no_ind_excluido(app):
for model in app.models.values(): for model in app.models.values():
assert not any(get_ind_excluido(obj) for obj in model.objects.all()) assert not any(get_ind_excluido(new) for new in model.objects.all())
print('OK!') print('OK!')
# MOMMY MAKE WITH LOG ######################################################
def make_with_log(model, _quantity=None, make_m2m=False, **attrs):
last_value = get_last_value(model)
alter_sequence(model, last_value + 1)
fields_dict = get_fields_dict(model)
stub = make(model, _quantity, make_m2m, **fields_dict)
problema = 'Um stub foi necessário durante a criação de um outro stub'
descricao = 'Essa entrada é necessária para um dos stubs criados'
' anteriormente'
warn(problema)
save_relation(obj=stub, problema=problema,
descricao=descricao, eh_stub=True)
return stub
make_with_log.required = foreign_key_required

130
sapl/legacy/scripts/fix_tables.sql

@ -1,5 +1,6 @@
-- Apaga as restrições somente para essa sessão -- Apaga as restrições somente para essa sessão
SELECT REPLACE(@@sql_mode,'STRICT_TRANS_TABLES,','ALLOW_INVALID_DATES'); SELECT replace(@@sql_mode,'STRICT_TRANS_TABLES,','ALLOW_INVALID_DATES');
-- Exclui procedures caso já existam -- Exclui procedures caso já existam
DROP PROCEDURE IF EXISTS verifica_campos_proposicao; DROP PROCEDURE IF EXISTS verifica_campos_proposicao;
DROP PROCEDURE IF EXISTS verifica_campos_tipo_materia_legislativa; DROP PROCEDURE IF EXISTS verifica_campos_tipo_materia_legislativa;
@ -7,18 +8,119 @@ DROP PROCEDURE IF EXISTS verifica_campos_sessao_plenaria_presenca;
DROP PROCEDURE IF EXISTS cria_lexml_registro_provedor_e_publicador; DROP PROCEDURE IF EXISTS cria_lexml_registro_provedor_e_publicador;
DROP PROCEDURE IF EXISTS cria_tipo_situacao_militar; DROP PROCEDURE IF EXISTS cria_tipo_situacao_militar;
DROP PROCEDURE IF EXISTS muda_vinculo_norma_juridica_ind_excluido; DROP PROCEDURE IF EXISTS muda_vinculo_norma_juridica_ind_excluido;
DROP PROCEDURE IF EXISTS muda_unidade_tramitacao_cod_parlamentar;
-- Procedure para criar campo num_proposicao em proposicao -- Procedure para criar campo num_proposicao em proposicao
CREATE PROCEDURE verifica_campos_proposicao() BEGIN IF NOT EXISTS (SELECT * FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='proposicao' AND column_name='num_proposicao') THEN UPDATE proposicao SET dat_envio = '1800-01-01' WHERE CAST(dat_envio AS CHAR(20)) = '0000-00-00 00:00:00'; ALTER TABLE proposicao ADD COLUMN num_proposicao INT(11) NULL after txt_justif_devolucao; END IF; END; CREATE PROCEDURE verifica_campos_proposicao() BEGIN IF NOT EXISTS
(SELECT *
FROM information_schema.columns
WHERE table_schema=database()
AND TABLE_NAME='proposicao'
AND COLUMN_NAME='num_proposicao') THEN
UPDATE proposicao
SET dat_envio = '1800-01-01'
WHERE cast(dat_envio AS char(20)) = '0000-00-00 00:00:00';
ALTER TABLE proposicao ADD COLUMN num_proposicao int(11) NULL AFTER txt_justif_devolucao;
END IF; END;
-- Procedure para criar campo iind_num_automatica em tipo_materia_legislativa -- Procedure para criar campo iind_num_automatica em tipo_materia_legislativa
CREATE PROCEDURE verifica_campos_tipo_materia_legislativa() BEGIN IF NOT EXISTS (SELECT * FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='tipo_materia_legislativa' AND column_name='ind_num_automatica') THEN ALTER TABLE tipo_materia_legislativa ADD COLUMN ind_num_automatica BOOLEAN NULL DEFAULT FALSE after des_tipo_materia; END IF; IF NOT EXISTS (SELECT * FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='tipo_materia_legislativa' AND column_name='quorum_minimo_votacao') THEN ALTER TABLE tipo_materia_legislativa ADD COLUMN quorum_minimo_votacao INT(11) NULL after ind_num_automatica; END IF; END; CREATE PROCEDURE verifica_campos_tipo_materia_legislativa()
BEGIN IF NOT EXISTS
(SELECT *
FROM information_schema.columns
WHERE table_schema=database()
AND TABLE_NAME='tipo_materia_legislativa'
AND COLUMN_NAME='ind_num_automatica') THEN
ALTER TABLE tipo_materia_legislativa ADD COLUMN ind_num_automatica BOOLEAN NULL DEFAULT FALSE AFTER des_tipo_materia;
END IF;
IF NOT EXISTS
(SELECT *
FROM information_schema.columns
WHERE table_schema=database()
AND TABLE_NAME='tipo_materia_legislativa'
AND COLUMN_NAME='quorum_minimo_votacao') THEN
ALTER TABLE tipo_materia_legislativa ADD COLUMN quorum_minimo_votacao int(11) NULL AFTER ind_num_automatica;
END IF; END;
-- Procedure para criar campos cod_presenca_sessao (sendo a nova PK da tabela) e dat_sessao em sessao_plenaria_presenca -- Procedure para criar campos cod_presenca_sessao (sendo a nova PK da tabela) e dat_sessao em sessao_plenaria_presenca
CREATE PROCEDURE verifica_campos_sessao_plenaria_presenca() BEGIN IF NOT EXISTS (SELECT * FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='sessao_plenaria_presenca' AND column_name='cod_presenca_sessao') THEN ALTER TABLE sessao_plenaria_presenca DROP PRIMARY KEY, ADD cod_presenca_sessao INT AUTO_INCREMENT PRIMARY KEY FIRST; END IF; IF NOT EXISTS (SELECT * FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='sessao_plenaria_presenca' AND column_name='dat_sessao') THEN ALTER TABLE sessao_plenaria_presenca ADD COLUMN dat_sessao DATE NULL after cod_parlamentar; END IF; END; CREATE PROCEDURE verifica_campos_sessao_plenaria_presenca() BEGIN IF NOT EXISTS
(SELECT *
FROM information_schema.columns
WHERE table_schema=database()
AND TABLE_NAME='sessao_plenaria_presenca'
AND COLUMN_NAME='cod_presenca_sessao') THEN
ALTER TABLE sessao_plenaria_presenca
DROP PRIMARY KEY,
ADD cod_presenca_sessao INT auto_increment PRIMARY KEY FIRST;
END IF;
IF NOT EXISTS
(SELECT *
FROM information_schema.columns
WHERE table_schema=database()
AND TABLE_NAME='sessao_plenaria_presenca'
AND COLUMN_NAME='dat_sessao') THEN
ALTER TABLE sessao_plenaria_presenca ADD COLUMN dat_sessao DATE NULL AFTER cod_parlamentar;
END IF; END;
-- Procedure para criar tabela lexml_registro_provedor e lexml_registro_publicador -- Procedure para criar tabela lexml_registro_provedor e lexml_registro_publicador
CREATE PROCEDURE cria_lexml_registro_provedor_e_publicador() BEGIN IF NOT EXISTS (SELECT * FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='lexml_registro_publicador') THEN CREATE TABLE lexml_registro_publicador (cod_publicador INT AUTO_INCREMENT NOT NULL, id_publicador INT, nom_publicador VARCHAR(255), adm_email VARCHAR(50), sigla VARCHAR(255), nom_responsavel VARCHAR(255), tipo VARCHAR(50), id_responsavel INT, PRIMARY KEY (cod_publicador)); END IF; IF NOT EXISTS (SELECT * FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='lexml_registro_provedor') THEN CREATE TABLE lexml_registro_provedor (cod_provedor INT AUTO_INCREMENT NOT NULL, id_provedor INT, nom_provedor VARCHAR(255), sgl_provedor VARCHAR(15), adm_email VARCHAR(50), nom_responsavel VARCHAR(255), tipo VARCHAR(50), id_responsavel INT, xml_provedor LONGTEXT, PRIMARY KEY (cod_provedor)); END IF; END; CREATE PROCEDURE cria_lexml_registro_provedor_e_publicador()
BEGIN IF NOT EXISTS
(SELECT *
FROM information_schema.columns
WHERE table_schema=database()
AND TABLE_NAME='lexml_registro_publicador') THEN
CREATE TABLE lexml_registro_publicador (
cod_publicador INT auto_increment NOT NULL,
id_publicador INT, nom_publicador varchar(255),
adm_email varchar(50),
sigla varchar(255),
nom_responsavel varchar(255),
tipo varchar(50),
id_responsavel INT, PRIMARY KEY (cod_publicador));
END IF;
IF NOT EXISTS
(SELECT *
FROM information_schema.columns
WHERE table_schema=database()
AND TABLE_NAME='lexml_registro_provedor') THEN
CREATE TABLE lexml_registro_provedor (
cod_provedor INT auto_increment NOT NULL,
id_provedor INT, nom_provedor varchar(255),
sgl_provedor varchar(15),
adm_email varchar(50),
nom_responsavel varchar(255),
tipo varchar(50),
id_responsavel INT, xml_provedor longtext,
PRIMARY KEY (cod_provedor));
END IF; END;
-- Procedure para criar tabela tipo_situacao_militar -- Procedure para criar tabela tipo_situacao_militar
CREATE PROCEDURE cria_tipo_situacao_militar() BEGIN IF NOT EXISTS (SELECT * FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='tipo_situacao_militar') THEN CREATE TABLE tipo_situacao_militar (tip_situacao_militar INT AUTO_INCREMENT NOT NULL, des_tipo_situacao VARCHAR(50), ind_excluido INT, PRIMARY KEY (tip_situacao_militar)); END IF; END; CREATE PROCEDURE cria_tipo_situacao_militar() BEGIN IF NOT EXISTS
(SELECT *
FROM information_schema.columns
WHERE table_schema=database()
AND TABLE_NAME='tipo_situacao_militar') THEN
CREATE TABLE tipo_situacao_militar (
tip_situacao_militar INT auto_increment NOT NULL,
des_tipo_situacao varchar(50),
ind_excluido INT, PRIMARY KEY (tip_situacao_militar));
END IF; END;
-- Procedure para mudar valor do campo ind_excluido da tabela vinculo_norma_juridica de 0 para string vazia '' -- Procedure para mudar valor do campo ind_excluido da tabela vinculo_norma_juridica de 0 para string vazia ''
CREATE PROCEDURE muda_vinculo_norma_juridica_ind_excluido() BEGIN UPDATE vinculo_norma_juridica SET ind_excluido = '' WHERE trim(ind_excluido) = '0'; END; CREATE PROCEDURE muda_vinculo_norma_juridica_ind_excluido() BEGIN
UPDATE vinculo_norma_juridica
SET ind_excluido = ''
WHERE trim(ind_excluido) = '0';
END;
-- Procedure para mudar valor do campo cod_parlamentar da tabela unidade_tramitacao de 0 para string vazia NULL
CREATE PROCEDURE muda_unidade_tramitacao_cod_parlamentar() BEGIN
UPDATE unidade_tramitacao
SET cod_parlamentar = NULL
WHERE cod_parlamentar = 0;
END;
-- Executa as procedures criadas acima -- Executa as procedures criadas acima
CALL verifica_campos_proposicao; CALL verifica_campos_proposicao;
CALL verifica_campos_tipo_materia_legislativa; CALL verifica_campos_tipo_materia_legislativa;
@ -26,3 +128,17 @@ CALL verifica_campos_sessao_plenaria_presenca;
CALL cria_lexml_registro_provedor_e_publicador; CALL cria_lexml_registro_provedor_e_publicador;
CALL cria_tipo_situacao_militar; CALL cria_tipo_situacao_militar;
CALL muda_vinculo_norma_juridica_ind_excluido; CALL muda_vinculo_norma_juridica_ind_excluido;
CALL muda_unidade_tramitacao_cod_parlamentar;
-- Corrige cod_parlamentar igual a zero em unidade de tramitação
update unidade_tramitacao set cod_parlamentar = NULL where cod_parlamentar = 0;
-- Corrige cod_nivel_instrucao e tip_situacao_militar zero em parlamentar
update parlamentar set cod_nivel_instrucao = NULL where cod_nivel_instrucao = 0;
update parlamentar set tip_situacao_militar = NULL where tip_situacao_militar = 0;
-- Corrige tip_afastamento igual a zero em mandato
update mandato set tip_afastamento = NULL where tip_afastamento = 0;
-- Corrige tip_fim_relatoria igual a zero em relatoria
update relatoria set tip_fim_relatoria = NULL where tip_fim_relatoria = 0;

6
sapl/legacy/scripts/migra_um_db.sh

@ -23,9 +23,3 @@ echo "--- MIGRACAO DE DADOS ---" | tee -a $LOG
echo >> $LOG echo >> $LOG
DATABASE_NAME=$1 ./manage.py migracao_25_31 -f --settings sapl.legacy_migration_settings |& tee -a $LOG DATABASE_NAME=$1 ./manage.py migracao_25_31 -f --settings sapl.legacy_migration_settings |& tee -a $LOG
echo >> $LOG echo >> $LOG
echo "--- RECRIANDO CONSTRAINTS ---" | tee -a $LOG
echo >> $LOG
DATABASE_NAME=$1 ./manage.py recria_constraints --settings sapl.legacy_migration_settings |& tee -a $LOG
echo >> $LOG

3
sapl/legacy_migration_settings.py

@ -28,3 +28,6 @@ DEBUG = True
MOMMY_CUSTOM_FIELDS_GEN = { MOMMY_CUSTOM_FIELDS_GEN = {
'django.db.models.ForeignKey': 'sapl.legacy.migration.make_with_log' 'django.db.models.ForeignKey': 'sapl.legacy.migration.make_with_log'
} }
# delisga indexação fulltext em tempo real
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.BaseSignalProcessor'

26
sapl/materia/migrations/0012_auto_20170815_1238.py

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.12 on 2017-08-15 12:38
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('materia', '0011_auto_20170808_1034'),
]
operations = [
migrations.AlterField(
model_name='proposicao',
name='tipo',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='materia.TipoProposicao', verbose_name='Tipo'),
),
migrations.AlterField(
model_name='tramitacao',
name='status',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='materia.StatusTramitacao', verbose_name='Status'),
),
]

21
sapl/materia/migrations/0013_auto_20170816_1136.py

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.12 on 2017-08-16 11:36
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('materia', '0012_auto_20170815_1238'),
]
operations = [
migrations.AlterField(
model_name='tramitacao',
name='unidade_tramitacao_destino',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='tramitacoes_destino', to='materia.UnidadeTramitacao', verbose_name='Unidade Destino'),
),
]

16
sapl/materia/migrations/0016_merge.py

@ -0,0 +1,16 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.13 on 2017-09-08 11:57
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('materia', '0015_auto_20170908_1024'),
('materia', '0013_auto_20170816_1136'),
]
operations = [
]

12
sapl/materia/models.py

@ -620,6 +620,10 @@ class Proposicao(models.Model):
blank=True, blank=True,
on_delete=models.PROTECT) on_delete=models.PROTECT)
tipo = models.ForeignKey(TipoProposicao, on_delete=models.PROTECT, tipo = models.ForeignKey(TipoProposicao, on_delete=models.PROTECT,
# TODO PÓS MIGRACAO INICIAL (vide #1381)
# não nulo quando todas as
# bases tiverem sido corrigidas
null=True,
verbose_name=_('Tipo')) verbose_name=_('Tipo'))
# XXX data_envio was not null, but actual data said otherwise!!! # XXX data_envio was not null, but actual data said otherwise!!!
@ -844,6 +848,10 @@ class Tramitacao(models.Model):
) )
status = models.ForeignKey(StatusTramitacao, on_delete=models.PROTECT, status = models.ForeignKey(StatusTramitacao, on_delete=models.PROTECT,
# TODO PÓS MIGRACAO INICIAL (vide #1381)
# não nulo quando todas as
# bases tiverem sido corrigidas
null=True,
verbose_name=_('Status')) verbose_name=_('Status'))
materia = models.ForeignKey(MateriaLegislativa, on_delete=models.PROTECT) materia = models.ForeignKey(MateriaLegislativa, on_delete=models.PROTECT)
data_tramitacao = models.DateField(verbose_name=_('Data Tramitação')) data_tramitacao = models.DateField(verbose_name=_('Data Tramitação'))
@ -856,6 +864,10 @@ class Tramitacao(models.Model):
blank=True, null=True, verbose_name=_('Data Encaminhamento')) blank=True, null=True, verbose_name=_('Data Encaminhamento'))
unidade_tramitacao_destino = models.ForeignKey( unidade_tramitacao_destino = models.ForeignKey(
UnidadeTramitacao, UnidadeTramitacao,
# TODO PÓS MIGRACAO INICIAL (vide #1381)
# não nulo quando todas as
# bases tiverem sido corrigidas
null=True,
related_name='tramitacoes_destino', related_name='tramitacoes_destino',
on_delete=models.PROTECT, on_delete=models.PROTECT,
verbose_name=_('Unidade Destino')) verbose_name=_('Unidade Destino'))

Loading…
Cancel
Save