Browse Source

fix #1106 - dados duplicados constraint (#1276)

* Adiciona exclusão de RegistroVotacao duplicados.

Signed-off-by: Luciano Almeida <lucianoalmeida@interlegis.leg.br>

* Recria constraints e marca problemas caso exista

Signed-off-by: Luciano Almeida <lucianoalmeida@interlegis.leg.br>
pull/1279/head
Luciano Henrique Nunes de Almeida 8 years ago
committed by Edward
parent
commit
123d9d0916
  1. 24
      sapl/base/migrations/0004_auto_20170714_1838.py
  2. 4
      sapl/base/models.py
  3. 122
      sapl/legacy/migration.py

24
sapl/base/migrations/0004_auto_20170714_1838.py

@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.12 on 2017-07-14 18:38
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('base', '0003_auto_20170519_1106'),
]
operations = [
migrations.RemoveField(
model_name='problemamigracao',
name='eh_importante',
),
migrations.AddField(
model_name='problemamigracao',
name='critico',
field=models.BooleanField(default=False, verbose_name='Crítico'),
),
]

4
sapl/base/models.py

@ -65,8 +65,8 @@ class ProblemaMigracao(models.Model):
problema = models.CharField(max_length=300, verbose_name=_('Problema')) problema = models.CharField(max_length=300, verbose_name=_('Problema'))
descricao = models.CharField(max_length=300, verbose_name=_('Descrição')) descricao = models.CharField(max_length=300, verbose_name=_('Descrição'))
eh_stub = models.BooleanField(verbose_name=_('É stub?')) eh_stub = models.BooleanField(verbose_name=_('É stub?'))
eh_importante = models.BooleanField( critico = models.BooleanField(
default=False, verbose_name=_('É importante?')) default=False, verbose_name=_('Crítico'))
class Meta: class Meta:
verbose_name = _('Problema na Migração') verbose_name = _('Problema na Migração')

122
sapl/legacy/migration.py

@ -12,7 +12,7 @@ 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 OperationalError, ProgrammingError, connections, models
from django.db.models import CharField, Max, ProtectedError, TextField from django.db.models import CharField, Max, ProtectedError, TextField, Count
from django.db.models.base import ModelBase from django.db.models.base import ModelBase
from django.db.models.signals import post_delete, post_save from django.db.models.signals import post_delete, post_save
from model_mommy import mommy from model_mommy import mommy
@ -34,7 +34,7 @@ from sapl.protocoloadm.models import (DocumentoAdministrativo,Protocolo,
StatusTramitacaoAdministrativo) StatusTramitacaoAdministrativo)
from sapl.sessao.models import ExpedienteMateria, OrdemDia, RegistroVotacao from sapl.sessao.models import ExpedienteMateria, OrdemDia, RegistroVotacao
from sapl.settings import PROJECT_DIR from sapl.settings import PROJECT_DIR
from sapl.utils import delete_texto, normalize, save_texto from sapl.utils import normalize
# BASE ###################################################################### # BASE ######################################################################
# apps to be migrated, in app dependency order (very important) # apps to be migrated, in app dependency order (very important)
@ -161,6 +161,8 @@ def get_fk_related(field, value, label=None):
ct = ContentType.objects.get(pk=13) ct = ContentType.objects.get(pk=13)
value = TipoProposicao.objects.create( value = TipoProposicao.objects.create(
id=value, descricao='Erro', content_type=ct) id=value, descricao='Erro', content_type=ct)
ultimo_valor = get_last_value(type(value))
alter_sequence(type(value), ultimo_valor+1)
else: else:
value = tipo[0] value = tipo[0]
else: else:
@ -244,6 +246,25 @@ def delete_constraints(model):
(table, r[0])) (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(): def recria_constraints():
constraints = Constraint.objects.all() constraints = Constraint.objects.all()
for con in constraints: for con in constraints:
@ -253,6 +274,8 @@ def recria_constraints():
args = [a.argumento for a in con.argumento_set.all()] args = [a.argumento for a in con.argumento_set.all()]
args_string = '' args_string = ''
args_string = "(" + "_".join(map(str, args[2:-1])) + ")" args_string = "(" + "_".join(map(str, args[2:-1])) + ")"
model = ContentType.objects.filter(
model=con.nome_model.lower())[0].model_class()
try: try:
exec_sql("ALTER TABLE %s ADD CONSTRAINT %s UNIQUE %s;" % exec_sql("ALTER TABLE %s ADD CONSTRAINT %s UNIQUE %s;" %
(nome_tabela, nome_constraint, args_string)) (nome_tabela, nome_constraint, args_string))
@ -272,6 +295,18 @@ def recria_constraints():
args[i] = args[i] + '_id' args[i] = args[i] + '_id'
args_string = '' args_string = ''
args_string += "(" + ', '.join(map(str, args)) + ")" 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: try:
exec_sql("ALTER TABLE %s ADD CONSTRAINT %s UNIQUE %s;" % exec_sql("ALTER TABLE %s ADD CONSTRAINT %s UNIQUE %s;" %
(nome_tabela, nome_constraint, args_string)) (nome_tabela, nome_constraint, args_string))
@ -279,8 +314,8 @@ def recria_constraints():
info('A constraint %s já foi recriada!' % nome_constraint) info('A constraint %s já foi recriada!' % nome_constraint)
except Exception as err: except Exception as err:
problema = re.findall('\(.*?\)', err.args[0]) problema = re.findall('\(.*?\)', err.args[0])
erro('A constraint [%s] da tabela [%s] não pode ser recriada' % erro('A constraint [%s] da tabela [%s] não pode ser" \
(nome_constraint, nome_tabela)) recriada' % (nome_constraint, nome_tabela))
erro('Os dados %s = %s estão duplicados. ' erro('Os dados %s = %s estão duplicados. '
'Arrume antes de recriar as constraints!' % 'Arrume antes de recriar as constraints!' %
(problema[0], problema[1])) (problema[0], problema[1]))
@ -315,10 +350,10 @@ def save_with_id(new, id):
def save_relation(obj, nome_campo='', problema='', descricao='', def save_relation(obj, nome_campo='', problema='', descricao='',
eh_stub=False): eh_stub=False, critico=False):
link = ProblemaMigracao( link = ProblemaMigracao(
content_object=obj, nome_campo=nome_campo, problema=problema, content_object=obj, nome_campo=nome_campo, problema=problema,
descricao=descricao, eh_stub=eh_stub,) descricao=descricao, eh_stub=eh_stub, critico=critico)
link.save() link.save()
@ -373,6 +408,34 @@ def fill_vinculo_norma_juridica():
TipoVinculoNormaJuridica.objects.bulk_create(lista_objs) TipoVinculoNormaJuridica.objects.bulk_create(lista_objs)
# Uma anomalia no sapl 2.5 causa a duplicação de registros de votação.
# Essa duplicação deve ser eliminada para que não haja erro no sapl 3.1
def excluir_registrovotacao_duplicados():
duplicatas_ids = RegistroVotacao.objects.values(
'materia', 'ordem', 'expediente').annotate(
Count('id')).order_by().filter(id__count__gt=1)
duplicatas_queryset = RegistroVotacao.objects.filter(
materia__in=[item['materia'] for item in duplicatas_ids])
for dup in duplicatas_queryset:
lista_dups = duplicatas_queryset.filter(
materia=dup.materia, expediente=dup.expediente, ordem=dup.ordem)
primeiro_registro = lista_dups[0]
lista_dups = lista_dups.exclude(pk=primeiro_registro.pk)
for objeto in lista_dups:
if (objeto.pk > primeiro_registro.pk):
try:
objeto.delete()
except:
assert 0
else:
try:
primeiro_registro.delete()
primeiro_registro = objeto
except:
assert 0
class DataMigrator: class DataMigrator:
def __init__(self): def __init__(self):
@ -449,8 +512,6 @@ class DataMigrator:
call([PROJECT_DIR.child('manage.py'), 'flush', call([PROJECT_DIR.child('manage.py'), 'flush',
'--database=default', '--no-input'], stdout=PIPE) '--database=default', '--no-input'], stdout=PIPE)
desconecta_sinais_indexacao()
fill_vinculo_norma_juridica() fill_vinculo_norma_juridica()
info('Começando migração: %s...' % obj) info('Começando migração: %s...' % obj)
self._do_migrate(obj) self._do_migrate(obj)
@ -468,11 +529,15 @@ class DataMigrator:
save_relation(obj=obj, problema=msg, save_relation(obj=obj, problema=msg,
descricao=descricao, eh_stub=False) descricao=descricao, eh_stub=False)
info('Excluindo possíveis duplicações em RegistroVotacao...')
excluir_registrovotacao_duplicados()
info('Deletando stubs desnecessários...') info('Deletando stubs desnecessários...')
while self.delete_stubs(): while self.delete_stubs():
pass pass
conecta_sinais_indexacao() info('Recriando constraints...')
recria_constraints()
def _do_migrate(self, obj): def _do_migrate(self, obj):
if isinstance(obj, AppConfig): if isinstance(obj, AppConfig):
@ -655,11 +720,25 @@ def adjust_participacao(new, old):
new.composicao = composicao new.composicao = composicao
def adjust_proposicao(new, old): def adjust_proposicao_antes_salvar(new, old):
if new.data_envio: if new.data_envio:
new.ano = new.data_envio.year new.ano = new.data_envio.year
def adjust_proposicao_depois_salvar(new, old):
if not hasattr(old.dat_envio, 'year') or old.dat_envio.year == 1800:
msg = "O valor do campo data_envio (DateField) da model Proposicao"
"era inválido"
descricao = 'A data 1111-11-11 foi colocada no lugar'
problema = 'O valor da data era nulo ou inválido'
warn(msg + ' => ' + descricao)
new.data_envio = date(1111, 11, 11)
with reversion.create_revision():
save_relation(obj=new, problema=problema,
descricao=descricao, eh_stub=False)
reversion.set_comment('Ajuste de data pela migração')
def adjust_normarelacionada(new, old): def adjust_normarelacionada(new, old):
tipo = TipoVinculoNormaJuridica.objects.filter(sigla=old.tip_vinculo) tipo = TipoVinculoNormaJuridica.objects.filter(sigla=old.tip_vinculo)
assert len(tipo) == 1 assert len(tipo) == 1
@ -817,7 +896,7 @@ AJUSTE_ANTES_SALVAR = {
OrdemDia: adjust_ordemdia_antes_salvar, OrdemDia: adjust_ordemdia_antes_salvar,
Parlamentar: adjust_parlamentar, Parlamentar: adjust_parlamentar,
Participacao: adjust_participacao, Participacao: adjust_participacao,
Proposicao: adjust_proposicao, Proposicao: adjust_proposicao_antes_salvar,
Protocolo: adjust_protocolo, Protocolo: adjust_protocolo,
RegistroVotacao: adjust_registrovotacao_antes_salvar, RegistroVotacao: adjust_registrovotacao_antes_salvar,
TipoAfastamento: adjust_tipoafastamento, TipoAfastamento: adjust_tipoafastamento,
@ -830,6 +909,7 @@ AJUSTE_ANTES_SALVAR = {
AJUSTE_DEPOIS_SALVAR = { AJUSTE_DEPOIS_SALVAR = {
NormaJuridica: adjust_normajuridica_depois_salvar, NormaJuridica: adjust_normajuridica_depois_salvar,
OrdemDia: adjust_ordemdia_depois_salvar, OrdemDia: adjust_ordemdia_depois_salvar,
Proposicao: adjust_proposicao_depois_salvar,
Protocolo: adjust_protocolo_depois_salvar, Protocolo: adjust_protocolo_depois_salvar,
RegistroVotacao: adjust_registrovotacao_depois_salvar, RegistroVotacao: adjust_registrovotacao_depois_salvar,
} }
@ -865,23 +945,3 @@ def make_with_log(model, _quantity=None, make_m2m=False, **attrs):
return stub return stub
make_with_log.required = foreign_key_required make_with_log.required = foreign_key_required
# DISCONNECT SIGNAL ########################################################
def desconecta_sinais_indexacao():
post_save.disconnect(save_texto, NormaJuridica)
post_save.disconnect(save_texto, DocumentoAcessorio)
post_save.disconnect(save_texto, MateriaLegislativa)
post_delete.disconnect(delete_texto, NormaJuridica)
post_delete.disconnect(delete_texto, DocumentoAcessorio)
post_delete.disconnect(delete_texto, MateriaLegislativa)
def conecta_sinais_indexacao():
post_save.connect(save_texto, NormaJuridica)
post_save.connect(save_texto, DocumentoAcessorio)
post_save.connect(save_texto, MateriaLegislativa)
post_delete.connect(delete_texto, NormaJuridica)
post_delete.connect(delete_texto, DocumentoAcessorio)
post_delete.connect(delete_texto, MateriaLegislativa)

Loading…
Cancel
Save