Repository: interlegis/sapl

Issue:
Problema quando objeto não existe
A migração para sua execução quando um objeto que deveria existir não existe.
No tratamento da exceção há uma tentativa de salvar um stub com o mesmo valor de pk do objeto que deveria existir, mas por algum motivo não está salvando o stub criado.



Discussion:

Resolving patch:
diff --git a/sapl/legacy/migration.py b/sapl/legacy/migration.py
--- a/sapl/legacy/migration.py
+++ b/sapl/legacy/migration.py
@@ -6,8 +6,8 @@
 from django.apps.config import AppConfig
 from django.contrib.auth import get_user_model
 from django.core.exceptions import ObjectDoesNotExist
-from django.db import connections, models
-from django.db.models import CharField, TextField, ProtectedError
+from django.db import connections, models, OperationalError, ProgrammingError
+from django.db.models import CharField, TextField, ProtectedError, Max
 from django.db.models.base import ModelBase
 from model_mommy import mommy
 from model_mommy.mommy import foreign_key_required, make
@@ -17,9 +17,10 @@
 from sapl.materia.models import (Proposicao, StatusTramitacao, TipoDocumento,
                                  TipoMateriaLegislativa, TipoProposicao,
                                  Tramitacao)
+from sapl.legacy.models import Protocolo as ProtocoloLegado
 from sapl.norma.models import AssuntoNorma, NormaJuridica
 from sapl.parlamentares.models import Parlamentar
-from sapl.protocoloadm.models import StatusTramitacaoAdministrativo
+from sapl.protocoloadm.models import Protocolo, StatusTramitacaoAdministrativo
 from sapl.sessao.models import ExpedienteMateria, OrdemDia, SessaoPlenaria
 from sapl.utils import normalize
 
@@ -37,6 +38,7 @@
 
 unique_constraints = []
 one_to_one_constraints = []
+primeira_vez = []
 
 name_sets = [set(m.__name__ for m in ac.get_models()) for ac in appconfs]
 
@@ -115,8 +117,13 @@ def get_fk_related(field, value, label=None):
             if value == 0:
                 if not field.null:
                     fields_dict = get_fields_dict(field.related_model)
-                    value = mommy.make(field.related_model,
-                                       **fields_dict)
+                    # Cria stub ao final da tabela para evitar erros
+                    pk = 1
+                    if hasattr(field.related_model.objects.last(), 'pk'):
+                        pk = field.related_model.objects.last().pk
+                    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)
@@ -141,6 +148,15 @@ def get_field(model, fieldname):
     return model._meta.get_field(fieldname)
 
 
+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)))
+
+
 def exec_sql(sql, db='default'):
     cursor = connections[db].cursor()
     cursor.execute(sql)
@@ -217,16 +233,21 @@ def stub_desnecessario(obj):
     return desnecessario
 
 
+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
+
+
+def alter_sequence(model, id):
+    sequence_name = '%s_id_seq' % model._meta.db_table
+    exec_sql('ALTER SEQUENCE %s RESTART WITH %s;' % (sequence_name, id))
+
+
 def save_with_id(new, id):
-    sequence_name = '%s_id_seq' % type(new)._meta.db_table
-    cursor = exec_sql('SELECT last_value from %s;' % sequence_name)
-    (last_value,) = cursor.fetchone()
-    if last_value == 1 or id != last_value + 1:
-        # we explicitly set the next id if last_value == 1
-        # because last_value == 1 for a table containing either 0 or 1 records
-        # (we would have trouble for id == 2 and a missing id == 1)
-        exec_sql('ALTER SEQUENCE %s RESTART WITH %s;' % (sequence_name, id))
+    last_value = get_last_value(type(new))
+    alter_sequence(type(new), id)
     new.save()
+    alter_sequence(type(new), last_value + 1)
     assert new.id == id, 'New id is different from provided!'
 
 
@@ -241,7 +262,7 @@ def save_relation(obj, nome_campo='', problema='', descricao='',
 
 def make_stub(model, id):
     fields_dict = get_fields_dict(model)
-    new = mommy.prepare(model, **fields_dict)
+    new = mommy.prepare(model, **fields_dict, pk=id)
     save_with_id(new, id)
 
     return new
@@ -332,6 +353,7 @@ def migrate(self, obj=appconfs):
         # warning: model/app migration order is of utmost importance
         self.to_delete = []
         ProblemaMigracao.objects.all().delete()
+        exec_sql_file('sapl/legacy/scripts/fix_tables.sql', 'legacy')
         get_user_model().objects.exclude(is_superuser=True).delete()
 
         info('Começando migração: %s...' % obj)
@@ -355,7 +377,7 @@ def migrate(self, obj=appconfs):
         while self.delete_stubs():
             pass
         info('Recriando unique constraints...')
-        recreate_constraints()
+        # recreate_constraints()
 
     def _do_migrate(self, obj):
         if isinstance(obj, AppConfig):
@@ -485,6 +507,18 @@ def adjust_participacao(new, old):
     new.composicao = composicao
 
 
+def adjust_protocolo(new, old):
+    if new.numero is None and not primeira_vez:
+        p = ProtocoloLegado.objects.filter(
+            ano_protocolo=new.ano).aggregate(Max('num_protocolo'))
+        new.numero = p['num_protocolo__max'] + 1
+        primeira_vez.append(True)
+    if new.numero is None and primeira_vez:
+        p = Protocolo.objects.filter(
+            ano=new.ano).aggregate(Max('numero'))
+        new.numero = p['numero__max'] + 1
+
+
 def adjust_sessaoplenaria(new, old):
     assert not old.tip_expediente
 
@@ -533,6 +567,15 @@ def adjust_normajuridica_depois_salvar(new, old):
         new.assuntos.add(AssuntoNorma.objects.get(pk=pk_assunto))
 
 
+def adjust_protocolo_depois_salvar(new, old):
+    if old.num_protocolo is None:
+        problema = 'Número do protocolo de PK %s é nulo' % new.pk
+        descricao = 'Número do protocolo alterado para %s!' % new.numero
+        warn(problema + ' => ' + descricao)
+        save_relation(obj=new, problema=problema,
+                      descricao=descricao, eh_stub=False)
+
+
 def adjust_autor(new, old):
     if old.cod_parlamentar:
         new.autor_related = Parlamentar.objects.get(pk=old.cod_parlamentar)
@@ -557,6 +600,7 @@ def adjust_autor(new, old):
     OrdemDia: adjust_ordemdia,
     Parlamentar: adjust_parlamentar,
     Participacao: adjust_participacao,
+    Protocolo: adjust_protocolo,
     SessaoPlenaria: adjust_sessaoplenaria,
     TipoProposicao: adjust_tipoproposicao,
     StatusTramitacao: adjust_statustramitacao,
@@ -566,6 +610,7 @@ def adjust_autor(new, old):
 
 AJUSTE_DEPOIS_SALVAR = {
     NormaJuridica: adjust_normajuridica_depois_salvar,
+    Protocolo: adjust_protocolo_depois_salvar,
 }
 
 # CHECKS ####################################################################
@@ -586,6 +631,8 @@ def check_app_no_ind_excluido(app):
 
 
 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'
diff --git a/sapl/legacy_migration_settings.py b/sapl/legacy_migration_settings.py
--- a/sapl/legacy_migration_settings.py
+++ b/sapl/legacy_migration_settings.py
@@ -28,6 +28,8 @@
 
 DATABASE_ROUTERS = ['sapl.legacy.router.LegacyRouter', ]
 
+DEBUG = False
+
 MOMMY_CUSTOM_FIELDS_GEN = {
     'django.db.models.ForeignKey': 'sapl.legacy.migration.make_with_log'
 }
diff --git a/sapl/norma/forms.py b/sapl/norma/forms.py
--- a/sapl/norma/forms.py
+++ b/sapl/norma/forms.py
@@ -35,8 +35,6 @@ def get_esferas():
 
 class NormaFilterSet(django_filters.FilterSet):
 
-    RANGE_ANOS.insert(0, ('', 'Selecione'))
-
     filter_overrides = {models.DateField: {
         'filter_class': django_filters.DateFromToRangeFilter,
         'extra': lambda f: {


Test patch: