From b235660881b1f24e8e931009062d91b0656b63c6 Mon Sep 17 00:00:00 2001 From: Jhony Lucas Date: Fri, 19 Jul 2024 14:51:00 -0300 Subject: [PATCH] feat: add TaskExecutionForm and TaskExecutionAdmin to core admin.py (#638) --- backend/apps/core/admin.py | 26 +++++++++++++++--- backend/apps/core/apps.py | 3 +++ ...0003_alter_taskexecution_execution_time.py | 20 ++++++++++++++ backend/apps/core/models.py | 3 ++- backend/apps/core/signals.py | 14 ++++++++++ backend/custom/task_decorators.py | 27 ++++++++++++------- 6 files changed, 79 insertions(+), 14 deletions(-) create mode 100644 backend/apps/core/migrations/0003_alter_taskexecution_execution_time.py create mode 100644 backend/apps/core/signals.py diff --git a/backend/apps/core/admin.py b/backend/apps/core/admin.py index 8ee230f0..ecf53cea 100644 --- a/backend/apps/core/admin.py +++ b/backend/apps/core/admin.py @@ -1,17 +1,35 @@ # -*- coding: utf-8 -*- from datetime import timedelta +from django import forms from django.contrib import admin from .models import TaskExecution +class TaskExecutionForm(forms.ModelForm): + TASK_NAME_CHOICES = [ + ("sync_database_with_prod", "Sincronizar banco de dados com produção"), + ] + + task_name = forms.ChoiceField(choices=TASK_NAME_CHOICES) + + class Meta: + model = TaskExecution + fields = ["task_name", "execution_time", "duration", "status", "result", "error"] + widgets = { + "execution_time": forms.DateTimeInput(attrs={"readonly": "readonly"}), + "duration": forms.NumberInput(attrs={"readonly": "readonly"}), + "status": forms.TextInput(attrs={"readonly": "readonly"}), + "result": forms.Textarea(attrs={"readonly": "readonly"}), + "error": forms.Textarea(attrs={"readonly": "readonly"}), + } + + class TaskExecutionAdmin(admin.ModelAdmin): + form = TaskExecutionForm list_display = ["task_name", "execution_time", "status", "formatted_duration"] - readonly_fields = ["task_name", "execution_time", "duration", "status", "result", "error"] - - def has_add_permission(self, request): - return False + readonly_fields = ["execution_time", "duration", "status", "result", "error"] def formatted_duration(self, obj): duration_seconds = obj.duration diff --git a/backend/apps/core/apps.py b/backend/apps/core/apps.py index 36f41102..bf2f2187 100644 --- a/backend/apps/core/apps.py +++ b/backend/apps/core/apps.py @@ -6,3 +6,6 @@ class CoreConfig(AppConfig): default_auto_field = "django.db.models.BigAutoField" name = "backend.apps.core" app_label = "core" + + def ready(self): + import backend.apps.core.signals # noqa diff --git a/backend/apps/core/migrations/0003_alter_taskexecution_execution_time.py b/backend/apps/core/migrations/0003_alter_taskexecution_execution_time.py new file mode 100644 index 00000000..0520f388 --- /dev/null +++ b/backend/apps/core/migrations/0003_alter_taskexecution_execution_time.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 4.2.14 on 2024-07-19 13:46 + +import datetime + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("core", "0002_taskexecution"), + ] + + operations = [ + migrations.AlterField( + model_name="taskexecution", + name="execution_time", + field=models.DateTimeField(default=datetime.datetime.now), + ), + ] diff --git a/backend/apps/core/models.py b/backend/apps/core/models.py index c61a4961..0a3bc24b 100644 --- a/backend/apps/core/models.py +++ b/backend/apps/core/models.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from datetime import datetime from uuid import uuid4 from django.db import models @@ -17,7 +18,7 @@ class Metadata(BaseModel): class TaskExecution(models.Model): task_name = models.CharField(max_length=255) - execution_time = models.DateTimeField() + execution_time = models.DateTimeField(default=datetime.now) duration = models.FloatField(default=0) status = models.CharField(max_length=255) result = models.TextField(null=True, blank=True) diff --git a/backend/apps/core/signals.py b/backend/apps/core/signals.py new file mode 100644 index 00000000..b8645724 --- /dev/null +++ b/backend/apps/core/signals.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +from django.db.models.signals import post_save +from django.dispatch import receiver + +from backend.custom.environment import is_prd + +from .models import TaskExecution +from .tasks import sync_database_with_prod + + +@receiver(post_save, sender=TaskExecution) +def task_execution_post_save(sender, instance, created, **kwargs): + if created and not is_prd(): + sync_database_with_prod() diff --git a/backend/custom/task_decorators.py b/backend/custom/task_decorators.py index 4d1f17c1..7e863b4c 100644 --- a/backend/custom/task_decorators.py +++ b/backend/custom/task_decorators.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +import traceback from datetime import datetime from functools import wraps @@ -14,23 +15,31 @@ def wrapper(*args, **kwargs): error = None start_time = datetime.now() + task_log = TaskExecution.objects.filter(status="running", task_name=task_name).first() + + if not task_log: + task_log = TaskExecution( + task_name=task_name, + status="running", + execution_time=start_time, + ) + + TaskExecution.objects.bulk_create([task_log]) + try: result = func(*args, **kwargs) except Exception as e: status = "failed" - error = str(e) + error = f"{str(e)}\n{traceback.format_exc()}" finally: end_time = datetime.now() duration = (end_time - start_time).total_seconds() - TaskExecution.objects.create( - task_name=task_name, - status=status, - result=result, - error=error, - execution_time=start_time, - duration=duration, - ) + task_log.status = status + task_log.result = result + task_log.error = error + task_log.duration = duration + task_log.save() return result return wrapper