|
@ -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 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@lru_cache() |
|
|
|
|
|
def _get_all_ids_from_model(model): |
|
|
|
|
|
# esta função para uso apenas em get_fk_related |
|
|
|
|
|
return set(model.objects.values_list('id', flat=True)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_fk_related(field, value, label=None): |
|
|
def get_fk_related(field, value, label=None): |
|
|
if value is None and field.null is False: |
|
|
if value is None and field.null: |
|
|
value = 0 |
|
|
return None |
|
|
if value is not None: |
|
|
|
|
|
try: |
|
|
# if field.related_model.objects.filter(id=value).exists(): |
|
|
value = field.related_model.objects.get(id=value) |
|
|
if value in _get_all_ids_from_model(field.related_model): |
|
|
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 |
|
|
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: |
|
|
else: |
|
|
assert value |
|
|
msg = 'FK [%s] não encontrada para o valor %s (em %s %s)' % ( |
|
|
return value |
|
|
field.name, value, field.model.__name__, label or '---') |
|
|
|
|
|
warn(msg) |
|
|
|
|
|
raise ForeignKeyFaltando(msg) |
|
|
def get_field(model, fieldname): |
|
|
|
|
|
return model._meta.get_field(fieldname) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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) |
|
|
|
|
|
# TODO rever esse DateField após as mudança para datas com |
|
|
|
|
|
# timezone |
|
|
if field_type == 'DateField' and \ |
|
|
if field_type == 'DateField' and \ |
|
|
not field.null and value is None: |
|
|
not field.null and value is None: |
|
|
|
|
|
# TODO REVER ISSO |
|
|
descricao = 'A data 1111-11-11 foi colocada no lugar' |
|
|
descricao = 'A data 1111-11-11 foi colocada no lugar' |
|
|
problema = 'O valor da data era nulo ou inválido' |
|
|
problema = 'O valor da data era nulo ou inválido' |
|
|
warn(msg + |
|
|
warn("O valor do campo %s (%s) do model %s " |
|
|
' => ' + descricao) |
|
|
"era inválido => %s" % ( |
|
|
|
|
|
field.name, field_type, |
|
|
|
|
|
field.model.__name__, descricao)) |
|
|
value = date(1111, 11, 11) |
|
|
value = date(1111, 11, 11) |
|
|
self.data_mudada['obj'] = new |
|
|
self.data_mudada['obj'] = new |
|
|
self.data_mudada['descricao'] = descricao |
|
|
self.data_mudada['descricao'] = descricao |
|
|
self.data_mudada['problema'] = problema |
|
|
self.data_mudada['problema'] = problema |
|
|
self.data_mudada.setdefault('nome_campo', []).\ |
|
|
self.data_mudada.setdefault('nome_campo', []).\ |
|
|
append(field.name) |
|
|
append(field.name) |
|
|
if field_type == 'CharField' or field_type == 'TextField': |
|
|
if (field_type in ['CharField', 'TextField'] |
|
|
if value is None or value == 'None': |
|
|
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 |
|
|
|
|
|
with transaction.atomic(): |
|
|
for old in old_records: |
|
|
for old in old_records: |
|
|
|
|
|
if getattr(old, 'ind_excluido', False): |
|
|
|
|
|
# não migramos registros marcados como excluídos |
|
|
|
|
|
continue |
|
|
new = model() |
|
|
new = model() |
|
|
|
|
|
try: |
|
|
self.populate_renamed_fields(new, old) |
|
|
self.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: |
|
|
|
|
|
# tentamos preencher uma FK e o ojeto relacionado |
|
|
|
|
|
# não existe |
|
|
|
|
|
# então este é um objeo órfão: simplesmente ignoramos |
|
|
|
|
|
continue |
|
|
|
|
|
else: |
|
|
save(new, old) |
|
|
save(new, old) |
|
|
if ajuste_depois_salvar: |
|
|
if ajuste_depois_salvar: |
|
|
ajuste_depois_salvar(new, old) |
|
|
ajuste_depois_salvar(new, old) |
|
|
|
|
|
|
|
|
if self.data_mudada: |
|
|
if self.data_mudada: |
|
|
with reversion.create_revision(): |
|
|
with reversion.create_revision(): |
|
|
save_relation(**self.data_mudada) |
|
|
save_relation(**self.data_mudada) |
|
|
self.data_mudada.clear() |
|
|
self.data_mudada.clear() |
|
|
reversion.set_comment('Ajuste de data pela migração') |
|
|
reversion.set_comment( |
|
|
if getattr(old, 'ind_excluido', False): |
|
|
'Ajuste de data pela migração') |
|
|
self.to_delete.append(new) |
|
|
|
|
|
|
|
|
|
|
|
# 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 |
|
|
|
|
|
|
|
|
|
|
|
# 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)) |
|
|
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') |
|
|
|
|
|
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 |
|
|
new.nome = new.autor_related.nome_parlamentar |
|
|
|
|
|
|
|
|
elif old.cod_comissao: |
|
|
elif old.cod_comissao: |
|
|
|
|
|
try: |
|
|
new.autor_related = Comissao.objects.get(pk=old.cod_comissao) |
|
|
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 |
|
|
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 |
|
|
|
|
|