From 0db2886dc510629710f63120e140d89e2a1e7c82 Mon Sep 17 00:00:00 2001 From: Roberta Takenaka Date: Fri, 24 Jul 2026 15:27:37 -0300 Subject: [PATCH 1/3] =?UTF-8?q?fix(tracker):=20corrige=20duplica=C3=A7?= =?UTF-8?q?=C3=A3o=20e=20sobrescrita=20de=20UnexpectedEvent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Propósito: Evitar múltiplos registros de UnexpectedEvent para o mesmo par item/action (incluindo None/None), atualizando o registro existente mais recente em vez de sempre criar um novo. Solução técnica: - Adiciona campo 'updated' (auto_now) para rastrear a última atualização do evento. - Reduz exception_type/exception_msg de TextField para CharField (100/400 chars), padronizando com o restante do modelo. - Adiciona ordering por '-updated', '-created'. - Adiciona _get() para buscar registro existente por item/action, usando o mais recente em caso de múltiplos. - Corrige _update() para ser chamado como método de instância, sem argumento duplicado de obj (bug do refactor anterior que causava TypeError). - Corrige _create() para retornar o objeto após chamar _update() (bug do refactor anterior que fazia create() receber None e quebrar em obj.save() com AttributeError). - Remove imports não utilizados (ContentFile, ParentalKey, ClusterableModel, painéis do Wagtail, CoreAdminModelForm, CommonControlField, tracker.choices) e a exceção EventReportCreateError, que não é mais usada neste módulo. --- tracker/models.py | 83 +++++++++++++++++++++++++++++++---------------- 1 file changed, 55 insertions(+), 28 deletions(-) diff --git a/tracker/models.py b/tracker/models.py index 03143bb51..899cdbf1b 100644 --- a/tracker/models.py +++ b/tracker/models.py @@ -4,18 +4,8 @@ import uuid from datetime import datetime -from django.core.files.base import ContentFile from django.db import models from django.utils.translation import gettext_lazy as _ -from modelcluster.fields import ParentalKey -from modelcluster.models import ClusterableModel -from wagtail.admin.panels import FieldPanel, InlinePanel, ObjectList, TabbedInterface -from wagtail.models import Orderable -from wagtailautocomplete.edit_handlers import AutocompletePanel - -from core.forms import CoreAdminModelForm -from core.models import CommonControlField -from tracker import choices class ProcEventCreateError(Exception): ... @@ -27,9 +17,6 @@ class UnexpectedEventCreateError(Exception): ... class EventCreateError(Exception): ... -class EventReportCreateError(Exception): ... - - class EventReportSaveFileError(Exception): ... @@ -97,8 +84,9 @@ def finish(self, completed=None, detail=None, errors=None, exceptions=None): class UnexpectedEvent(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) created = models.DateTimeField(verbose_name=_("Creation date"), auto_now_add=True) - exception_type = models.TextField(_("Exception Type"), null=True, blank=True) - exception_msg = models.TextField(_("Exception Msg"), null=True, blank=True) + updated = models.DateTimeField(verbose_name=_("Last update date"), auto_now=True) + exception_type = models.CharField(_("Exception Type"), max_length=100, null=True, blank=True) + exception_msg = models.CharField(_("Exception Msg"), max_length=400, null=True, blank=True) traceback = models.JSONField(null=True, blank=True) detail = models.JSONField(null=True, blank=True) item = models.CharField( @@ -120,7 +108,7 @@ class Meta: models.Index(fields=["item"]), models.Index(fields=["action"]), ] - ordering = ["-created"] + ordering = ["-updated", "-created"] def __str__(self): if self.item or self.action: @@ -148,23 +136,22 @@ def create( action=None, detail=None, ): + """ + Cria um novo UnexpectedEvent ou atualiza um já existente + (mesmo item + action, incluindo o par None/None), usando o + mais recente em caso de múltiplos registros. + """ try: if exception: logging.exception(exception) - obj = cls() - obj.item = item - obj.action = action - obj.exception_msg = str(exception) - obj.exception_type = str(type(exception)) - try: - json.dumps(detail) - obj.detail = detail - except Exception as e: - obj.detail = str(detail) + obj = cls._get(item, action) + + if obj is not None: + obj._update(exception, exc_traceback, item, action, detail) + else: + obj = cls._create(exception, exc_traceback, item, action, detail) - if exc_traceback: - obj.traceback = traceback.format_tb(exc_traceback) obj.save() return obj except Exception as exc: @@ -172,6 +159,46 @@ def create( f"Unable to create unexpected event ({exception} {exc_traceback}). EXCEPTION {exc}" ) + # ------------------------------------------------------------------ + # Métodos auxiliares + # ------------------------------------------------------------------ + + @classmethod + def _get(cls, item, action): + """ + Busca um registro existente com o mesmo item e action + (incluindo o par None, None). Se houver múltiplos, retorna + o mais recente considerando updated e, em seguida, created. + """ + qs = cls.objects.filter(item=item, action=action).order_by( + "-updated", "-created" + ) + return qs.first() + + def _update(self, exception, exc_traceback, item, action, detail): + """ + Preenche/atualiza os campos do objeto (usado tanto na criação + quanto na atualização). + """ + self.item = item + self.action = action + self.exception_msg = str(exception) + self.exception_type = str(type(exception)) + try: + json.dumps(detail) + self.detail = detail + except Exception: + self.detail = str(detail) + + if exc_traceback: + self.traceback = traceback.format_tb(exc_traceback) + + @classmethod + def _create(cls, exception, exc_traceback, item, action, detail): + obj = cls() + obj._update(exception, exc_traceback, item, action, detail) + return obj + def tracker_file_directory_path(instance, filename): # file will be uploaded to MEDIA_ROOT/user_/ From 6375277f74dcefbaf94f776ebafb636818c0cd7c Mon Sep 17 00:00:00 2001 From: Roberta Takenaka Date: Fri, 24 Jul 2026 15:27:47 -0300 Subject: [PATCH 2/3] feat(tracker): exibe campo 'updated' na listagem de UnexpectedEvent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Propósito: Permitir visualizar, na listagem administrativa do Wagtail, quando cada UnexpectedEvent foi atualizado pela última vez — informação relevante agora que o mesmo registro pode ser reaproveitado em vez de duplicado (ver commit anterior em tracker/models.py). Solução técnica: - Adiciona 'updated' à tupla list_display do UnexpectedEventModelAdmin. - Remove import não utilizado de HttpResponseRedirect. --- tracker/wagtail_hooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tracker/wagtail_hooks.py b/tracker/wagtail_hooks.py index eec299f4e..177460bd4 100644 --- a/tracker/wagtail_hooks.py +++ b/tracker/wagtail_hooks.py @@ -1,4 +1,3 @@ -from django.http import HttpResponseRedirect from django.utils.translation import gettext_lazy as _ from wagtail.snippets.models import register_snippet from wagtail.snippets.views.snippets import SnippetViewSet, SnippetViewSetGroup @@ -23,6 +22,7 @@ class UnexpectedEventModelAdmin(SnippetViewSet): "exception_type", "exception_msg", "created", + "updated" ) list_filter = ("action", "exception_type", ) search_fields = ( From 0cd852a41a7939d221de186dd1123e9162cc958c Mon Sep 17 00:00:00 2001 From: Roberta Takenaka Date: Fri, 24 Jul 2026 15:28:32 -0300 Subject: [PATCH 3/3] =?UTF-8?q?chore(tracker):=20migration=20para=20novo?= =?UTF-8?q?=20campo=20'updated'=20e=20altera=C3=A7=C3=B5es=20de=20Unexpect?= =?UTF-8?q?edEvent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Propósito: Aplicar no banco de dados as mudanças de schema decorrentes do refactor de UnexpectedEvent (commit anterior em tracker/models.py). Solução técnica: - Adiciona campo 'updated' (DateTimeField, auto_now). - Altera exception_type/exception_msg de TextField para CharField (max_length 100 e 400, respectivamente). - Atualiza Meta.ordering para ['-updated', '-created']. --- ..._alter_unexpectedevent_options_and_more.py | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 tracker/migrations/0005_alter_unexpectedevent_options_and_more.py diff --git a/tracker/migrations/0005_alter_unexpectedevent_options_and_more.py b/tracker/migrations/0005_alter_unexpectedevent_options_and_more.py new file mode 100644 index 000000000..6d0996b45 --- /dev/null +++ b/tracker/migrations/0005_alter_unexpectedevent_options_and_more.py @@ -0,0 +1,36 @@ +# Generated by Django 5.2.7 on 2026-07-24 18:20 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("tracker", "0004_alter_hello_options"), + ] + + operations = [ + migrations.AlterModelOptions( + name="unexpectedevent", + options={"ordering": ["-updated", "-created"]}, + ), + migrations.AddField( + model_name="unexpectedevent", + name="updated", + field=models.DateTimeField(auto_now=True, verbose_name="Last update date"), + ), + migrations.AlterField( + model_name="unexpectedevent", + name="exception_msg", + field=models.CharField( + blank=True, max_length=400, null=True, verbose_name="Exception Msg" + ), + ), + migrations.AlterField( + model_name="unexpectedevent", + name="exception_type", + field=models.CharField( + blank=True, max_length=100, null=True, verbose_name="Exception Type" + ), + ), + ]