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. 142
      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'))
descricao = models.CharField(max_length=300, verbose_name=_('Descrição'))
eh_stub = models.BooleanField(verbose_name=_('É stub?'))
eh_importante = models.BooleanField(
default=False, verbose_name=_('É importante?'))
critico = models.BooleanField(
default=False, verbose_name=_('Crítico'))
class Meta:
verbose_name = _('Problema na Migração')

142
sapl/legacy/migration.py

@ -12,7 +12,7 @@ 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, Max, ProtectedError, TextField
from django.db.models import CharField, Max, ProtectedError, TextField, Count
from django.db.models.base import ModelBase
from django.db.models.signals import post_delete, post_save
from model_mommy import mommy
@ -34,7 +34,7 @@ from sapl.protocoloadm.models import (DocumentoAdministrativo,Protocolo,
StatusTramitacaoAdministrativo)
from sapl.sessao.models import ExpedienteMateria, OrdemDia, RegistroVotacao
from sapl.settings import PROJECT_DIR
from sapl.utils import delete_texto, normalize, save_texto
from sapl.utils import normalize
# BASE ######################################################################
# 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)
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:
@ -244,6 +246,25 @@ def delete_constraints(model):
(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:
@ -253,6 +274,8 @@ def recria_constraints():
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))
@ -272,18 +295,30 @@ def recria_constraints():
args[i] = args[i] + '_id'
args_string = ''
args_string += "(" + ', '.join(map(str, args)) + ")"
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]))
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):
@ -315,10 +350,10 @@ def save_with_id(new, id):
def save_relation(obj, nome_campo='', problema='', descricao='',
eh_stub=False):
eh_stub=False, critico=False):
link = ProblemaMigracao(
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()
@ -373,6 +408,34 @@ def fill_vinculo_norma_juridica():
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:
def __init__(self):
@ -449,8 +512,6 @@ class DataMigrator:
call([PROJECT_DIR.child('manage.py'), 'flush',
'--database=default', '--no-input'], stdout=PIPE)
desconecta_sinais_indexacao()
fill_vinculo_norma_juridica()
info('Começando migração: %s...' % obj)
self._do_migrate(obj)
@ -468,11 +529,15 @@ class DataMigrator:
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
conecta_sinais_indexacao()
info('Recriando constraints...')
recria_constraints()
def _do_migrate(self, obj):
if isinstance(obj, AppConfig):
@ -655,11 +720,25 @@ def adjust_participacao(new, old):
new.composicao = composicao
def adjust_proposicao(new, old):
def adjust_proposicao_antes_salvar(new, old):
if new.data_envio:
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):
tipo = TipoVinculoNormaJuridica.objects.filter(sigla=old.tip_vinculo)
assert len(tipo) == 1
@ -817,7 +896,7 @@ AJUSTE_ANTES_SALVAR = {
OrdemDia: adjust_ordemdia_antes_salvar,
Parlamentar: adjust_parlamentar,
Participacao: adjust_participacao,
Proposicao: adjust_proposicao,
Proposicao: adjust_proposicao_antes_salvar,
Protocolo: adjust_protocolo,
RegistroVotacao: adjust_registrovotacao_antes_salvar,
TipoAfastamento: adjust_tipoafastamento,
@ -830,6 +909,7 @@ AJUSTE_ANTES_SALVAR = {
AJUSTE_DEPOIS_SALVAR = {
NormaJuridica: adjust_normajuridica_depois_salvar,
OrdemDia: adjust_ordemdia_depois_salvar,
Proposicao: adjust_proposicao_depois_salvar,
Protocolo: adjust_protocolo_depois_salvar,
RegistroVotacao: adjust_registrovotacao_depois_salvar,
}
@ -865,23 +945,3 @@ def make_with_log(model, _quantity=None, make_m2m=False, **attrs):
return stub
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