diff --git a/requirements/migration-requirements.txt b/requirements/migration-requirements.txt index 25d226602..d5793dad8 100644 --- a/requirements/migration-requirements.txt +++ b/requirements/migration-requirements.txt @@ -1,2 +1,2 @@ -r dev-requirements.txt -mysqlclient +mysqlclient==1.3.12 diff --git a/sapl/legacy/management/commands/recria_constraints.py b/sapl/legacy/management/commands/recria_constraints.py index 9e999e5f6..d1d8d606d 100644 --- a/sapl/legacy/management/commands/recria_constraints.py +++ b/sapl/legacy/management/commands/recria_constraints.py @@ -1,7 +1,5 @@ from django.core.management.base import BaseCommand -from sapl.legacy.migration import recria_constraints - class Command(BaseCommand): @@ -9,4 +7,4 @@ class Command(BaseCommand): 'migração de dados') def handle(self, *args, **options): - recria_constraints() + pass diff --git a/sapl/legacy/migration.py b/sapl/legacy/migration.py index 7b31f00f9..d45b9ee75 100644 --- a/sapl/legacy/migration.py +++ b/sapl/legacy/migration.py @@ -1,5 +1,6 @@ import re from datetime import date +from functools import lru_cache from subprocess import PIPE, call 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.contenttypes.models import ContentType from django.core.exceptions import ObjectDoesNotExist -from django.db import OperationalError, ProgrammingError, connections, models -from django.db.models import CharField, Count, Max, ProtectedError, TextField +from django.db import connections, transaction +from django.db.models import Count, Max 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.materia.models import (AcompanhamentoMateria, Proposicao, StatusTramitacao, TipoDocumento, @@ -112,80 +111,36 @@ def warn(msg): print('CUIDADO! ' + msg) -def erro(msg): - print('ERRO: ' + msg) +class ForeignKeyFaltando(ObjectDoesNotExist): + 'Uma FK aponta para um registro inexistente' + pass -def get_fk_related(field, value, label=None): - if value is None and field.null is False: - value = 0 - if value is not None: - 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 +@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): + if value is None and field.null: + return None -def get_field(model, fieldname): - return model._meta.get_field(fieldname) + # if field.related_model.objects.filter(id=value).exists(): + 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'): - cursor = connections[db].cursor() - for line in open(path): - try: - cursor.execute(line) - except (OperationalError, ProgrammingError) as e: - print("Args: '%s'" % (str(e.args))) + with open(path) as arq: + sql = arq.read() + with connections[db].cursor() as cursor: + cursor.execute(sql) def exec_sql(sql, db='default'): @@ -204,135 +159,10 @@ def iter_sql_records(sql, db): record.__dict__.update(zip(fieldnames, row)) 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): 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): @@ -357,24 +187,6 @@ def save_relation(obj, nome_campo='', problema='', descricao='', 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(): lista = [('A', 'Altera o(a)', 'Alterado(a) pelo(a)'), @@ -449,52 +261,46 @@ class DataMigrator: for field in new._meta.fields: old_field_name = renames.get(field.name) 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: old_value = getattr(old, old_field_name) - if isinstance(field, models.ForeignKey): - old_type = type(old) # not necessarily a model - if hasattr(old_type, '_meta') and \ - old_type._meta.pk.name != 'id': + + if field_type == 'ForeignKey': + # not necessarily a model + if hasattr(old, '_meta') and old._meta.pk.name != 'id': label = old.pk else: label = '-- SEM PK --' + fk_field_name = '{}_id'.format(field.name) value = get_fk_related(field, old_value, label) + setattr(new, fk_field_name, value) else: value = getattr(old, old_field_name) - if field_type == 'DateField' and \ - not field.null and value is None: - descricao = 'A data 1111-11-11 foi colocada no lugar' - problema = 'O valor da data era nulo ou inválido' - warn(msg + - ' => ' + descricao) - value = date(1111, 11, 11) - self.data_mudada['obj'] = new - self.data_mudada['descricao'] = descricao - self.data_mudada['problema'] = problema - self.data_mudada.setdefault('nome_campo', []).\ - append(field.name) - if field_type == 'CharField' or field_type == 'TextField': - if value is None or value == 'None': + # TODO rever esse DateField após as mudança para datas com + # timezone + if field_type == 'DateField' and \ + not field.null and value is None: + # TODO REVER ISSO + descricao = 'A data 1111-11-11 foi colocada no lugar' + problema = 'O valor da data era nulo ou inválido' + warn("O valor do campo %s (%s) do model %s " + "era inválido => %s" % ( + field.name, field_type, + field.model.__name__, descricao)) + value = date(1111, 11, 11) + self.data_mudada['obj'] = new + 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 = '' - 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) + setattr(new, field.name, value) def migrate(self, obj=appconfs, interativo=True): # warning: model/app migration order is of utmost importance exec_sql_file(PROJECT_DIR.child( 'sapl', 'legacy', 'scripts', 'fix_tables.sql'), 'legacy') - self.to_delete = [] # excluindo database antigo. if interativo: @@ -516,29 +322,9 @@ class DataMigrator: info('Começando migração: %s...' % 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...') excluir_registrovotacao_duplicados() - info('Deletando stubs desnecessários...') - while self.delete_stubs(): - pass - - info('Recriando constraints...') - recria_constraints() - def _do_migrate(self, obj): if isinstance(obj, AppConfig): 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_pk_name = legacy_model._meta.pk.name - delete_constraints(model) - # setup migration strategy for tables with or without a pk if legacy_pk_name == 'id': # There is no pk in the legacy table @@ -589,58 +373,37 @@ class DataMigrator: ajuste_depois_salvar = AJUSTE_DEPOIS_SALVAR.get(model) # convert old records to new ones - for old in old_records: - new = model() - self.populate_renamed_fields(new, old) - if ajuste_antes_salvar: - ajuste_antes_salvar(new, old) - 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') - if getattr(old, 'ind_excluido', False): - self.to_delete.append(new) + with transaction.atomic(): + for old in old_records: + if getattr(old, 'ind_excluido', False): + # não migramos registros marcados como excluídos + continue + new = model() + try: + self.populate_renamed_fields(new, old) + if ajuste_antes_salvar: + 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) + 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 ultimo_valor = get_last_value(model) 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): dm = DataMigrator() @@ -655,27 +418,22 @@ def adjust_acompanhamentomateria(new, old): def adjust_documentoadministrativo(new, old): if new.numero_protocolo: - try: - protocolo = Protocolo.objects.get(numero=new.numero_protocolo, - ano=new.ano) - new.protocolo = protocolo - except Exception: - try: - protocolo = Protocolo.objects.get(numero=new.numero_protocolo, - ano=new.ano + 1) - new.protocolo = protocolo - except Exception: - protocolo = mommy.make(Protocolo, numero=new.numero_protocolo, - ano=new.ano) - with reversion.create_revision(): - problema = 'Protocolo Vinculado [numero_protocolo=%s, '\ - 'ano=%s] não existe' % (new.numero_protocolo, - new.ano) - 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.') + protocolo = Protocolo.objects.filter( + numero=new.numero_protocolo, ano=new.ano) + if not protocolo: + protocolo = Protocolo.objects.filter( + numero=new.numero_protocolo, ano=new.ano + 1) + print('PROTOCOLO ENCONTRADO APENAS PARA O ANO SEGUINTE!!!!! ' + 'DocumentoAdministrativo: {}, numero_protocolo: {}, ' + 'ano doc adm: {}'.format( + old.cod_documento, new.numero_protocolo, new.ano)) + if not protocolo: + raise ForeignKeyFaltando( + 'Protocolo {} faltando ' + '(referenciado no documento administrativo {}'.format( + new.numero_protocolo, old.cod_documento)) + assert len(protocolo) == 1 + new.protocolo = protocolo[0] def adjust_mandato(new, old): @@ -707,7 +465,6 @@ def adjust_ordemdia_depois_salvar(new, old): save_relation(obj=new, problema=problema, descricao=descricao, eh_stub=False) reversion.set_comment('OrdemDia sem número da ordem.') - pass def adjust_parlamentar(new, old): @@ -723,7 +480,7 @@ def adjust_parlamentar(new, old): def adjust_participacao(new, old): composicao = Composicao() - composicao.comissao, composicao.periodo = [ + composicao.comissao_id, composicao.periodo_id = [ get_fk_related(Composicao._meta.get_field(name), value) for name, value in (('comissao', old.cod_comissao), ('periodo', old.cod_periodo_comp))] @@ -840,6 +597,14 @@ def adjust_tramitacao(new, old): 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): # Ajusta choice de esfera_federacao # 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): # Ajusta relação M2M - lista_pks_assunto = old.cod_assunto.split(',') - # list(filter(..)) usado para retirar strings vazias da lista - for pk_assunto in list(filter(None, lista_pks_assunto)): - new.assuntos.add(AssuntoNorma.objects.get(pk=pk_assunto)) + if not old.cod_assunto: # it can be null or empty + 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)) + except ObjectDoesNotExist: + pass # ignora assuntos inexistentes def adjust_autor(new, old): if old.cod_parlamentar: try: new.autor_related = Parlamentar.objects.get(pk=old.cod_parlamentar) - except Exception: - with reversion.create_revision(): - msg = 'Um parlamentar relacionado de PK [%s] não existia' \ - % old.cod_parlamentar - 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 + except ObjectDoesNotExist: + # ignoramos o autor órfão + raise ForeignKeyFaltando('Parlamentar inexiste para autor') + else: + new.nome = new.autor_related.nome_parlamentar elif old.cod_comissao: - new.autor_related = Comissao.objects.get(pk=old.cod_comissao) - new.nome = new.autor_related.nome + try: + 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 not get_user_model().objects.filter( - username=old.col_username).exists(): - user = get_user_model()(username=old.col_username) + user_model = get_user_model() + if not user_model.objects.filter(username=old.col_username).exists(): + # cria um novo ususaŕio para o autor + user = user_model(username=old.col_username) user.set_password(12345) with reversion.create_revision(): 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") user.groups.add(grupo_autor) @@ -905,6 +677,7 @@ def adjust_comissao(new, old): AJUSTE_ANTES_SALVAR = { Autor: adjust_autor, + TipoAutor: adjust_tipo_autor, AcompanhamentoMateria: adjust_acompanhamentomateria, Comissao: adjust_comissao, DocumentoAdministrativo: adjust_documentoadministrativo, @@ -935,31 +708,13 @@ AJUSTE_DEPOIS_SALVAR = { # CHECKS #################################################################### -def get_ind_excluido(obj): - legacy_model = legacy_app.get_model(type(obj).__name__) - return getattr(legacy_model.objects.get( - **{legacy_model._meta.pk.name: obj.id}), 'ind_excluido', False) +def get_ind_excluido(new): + legacy_model = legacy_app.get_model(type(new).__name__) + old = legacy_model.objects.get(**{legacy_model._meta.pk.name: new.id}) + return getattr(old, 'ind_excluido', False) def check_app_no_ind_excluido(app): 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!') - -# 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 diff --git a/sapl/legacy/scripts/fix_tables.sql b/sapl/legacy/scripts/fix_tables.sql index 1f8cdb63b..d8a71bca4 100644 --- a/sapl/legacy/scripts/fix_tables.sql +++ b/sapl/legacy/scripts/fix_tables.sql @@ -1,5 +1,6 @@ -- 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 DROP PROCEDURE IF EXISTS verifica_campos_proposicao; 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_tipo_situacao_militar; 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 -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 -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 -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 -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 -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 '' -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 CALL verifica_campos_proposicao; 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_tipo_situacao_militar; 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; diff --git a/sapl/legacy/scripts/migra_um_db.sh b/sapl/legacy/scripts/migra_um_db.sh index f55dfb53a..26240d8d4 100755 --- a/sapl/legacy/scripts/migra_um_db.sh +++ b/sapl/legacy/scripts/migra_um_db.sh @@ -23,9 +23,3 @@ echo "--- MIGRACAO DE DADOS ---" | tee -a $LOG echo >> $LOG DATABASE_NAME=$1 ./manage.py migracao_25_31 -f --settings sapl.legacy_migration_settings |& tee -a $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 diff --git a/sapl/legacy_migration_settings.py b/sapl/legacy_migration_settings.py index b9d2cefc2..2501a44dc 100644 --- a/sapl/legacy_migration_settings.py +++ b/sapl/legacy_migration_settings.py @@ -28,3 +28,6 @@ DEBUG = True MOMMY_CUSTOM_FIELDS_GEN = { 'django.db.models.ForeignKey': 'sapl.legacy.migration.make_with_log' } + +# delisga indexação fulltext em tempo real +HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.BaseSignalProcessor' diff --git a/sapl/materia/migrations/0012_auto_20170815_1238.py b/sapl/materia/migrations/0012_auto_20170815_1238.py new file mode 100644 index 000000000..5f62c1670 --- /dev/null +++ b/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'), + ), + ] diff --git a/sapl/materia/migrations/0013_auto_20170816_1136.py b/sapl/materia/migrations/0013_auto_20170816_1136.py new file mode 100644 index 000000000..e6804e8e1 --- /dev/null +++ b/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'), + ), + ] diff --git a/sapl/materia/migrations/0016_merge.py b/sapl/materia/migrations/0016_merge.py new file mode 100644 index 000000000..472cefbd4 --- /dev/null +++ b/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 = [ + ] diff --git a/sapl/materia/models.py b/sapl/materia/models.py index fda53d192..8f8bdca31 100644 --- a/sapl/materia/models.py +++ b/sapl/materia/models.py @@ -620,6 +620,10 @@ class Proposicao(models.Model): blank=True, 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')) # 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, + # TODO PÓS MIGRACAO INICIAL (vide #1381) + # não nulo quando todas as + # bases tiverem sido corrigidas + null=True, verbose_name=_('Status')) materia = models.ForeignKey(MateriaLegislativa, on_delete=models.PROTECT) 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')) unidade_tramitacao_destino = models.ForeignKey( UnidadeTramitacao, + # TODO PÓS MIGRACAO INICIAL (vide #1381) + # não nulo quando todas as + # bases tiverem sido corrigidas + null=True, related_name='tramitacoes_destino', on_delete=models.PROTECT, verbose_name=_('Unidade Destino'))