Add `LoggedCommand` to JobLogs in django admin interface
This commit is contained in:
parent
6badbc480c
commit
5790fac78f
|
|
@ -2,9 +2,29 @@ from django.contrib import admin
|
||||||
from django.contrib.auth import admin as auth_admin, get_user_model
|
from django.contrib.auth import admin as auth_admin, get_user_model
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from vbv_lernwelt.core.models import JobLog
|
||||||
|
from vbv_lernwelt.core.utils import pretty_print_json
|
||||||
|
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
|
class LogAdmin(admin.ModelAdmin):
|
||||||
|
def has_add_permission(self, request):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def has_delete_permission(self, request, obj=None):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_readonly_fields(self, request, obj=None):
|
||||||
|
# set all fields read only
|
||||||
|
return [field.name for field in self.opts.local_fields] + [
|
||||||
|
field.name for field in self.opts.local_many_to_many
|
||||||
|
]
|
||||||
|
|
||||||
|
def pretty_print_json(self, json_string):
|
||||||
|
return pretty_print_json(json_string)
|
||||||
|
|
||||||
|
|
||||||
@admin.register(User)
|
@admin.register(User)
|
||||||
class UserAdmin(auth_admin.UserAdmin):
|
class UserAdmin(auth_admin.UserAdmin):
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
|
|
@ -34,3 +54,27 @@ class UserAdmin(auth_admin.UserAdmin):
|
||||||
"sso_id",
|
"sso_id",
|
||||||
]
|
]
|
||||||
search_fields = ["first_name", "last_name", "email", "username", "sso_id"]
|
search_fields = ["first_name", "last_name", "email", "username", "sso_id"]
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(JobLog)
|
||||||
|
class JobLogAdmin(LogAdmin):
|
||||||
|
date_hierarchy = "started"
|
||||||
|
list_display = (
|
||||||
|
"job_name",
|
||||||
|
"started",
|
||||||
|
"ended",
|
||||||
|
"runtime",
|
||||||
|
"success",
|
||||||
|
)
|
||||||
|
list_filter = ["job_name", "success"]
|
||||||
|
|
||||||
|
def json_data_pretty(self, instance):
|
||||||
|
return self.pretty_print_json(instance.json_data)
|
||||||
|
|
||||||
|
def get_readonly_fields(self, request, obj=None):
|
||||||
|
return super().get_readonly_fields(request, obj) + ["json_data_pretty"]
|
||||||
|
|
||||||
|
def runtime(self, obj):
|
||||||
|
if obj.ended:
|
||||||
|
return (obj.ended - obj.started).seconds // 60
|
||||||
|
return None
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
import sys
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
import structlog
|
||||||
|
from django.core.management.base import BaseCommand
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
|
from vbv_lernwelt.core.models import JobLog
|
||||||
|
|
||||||
|
logger = structlog.get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=abstract-method
|
||||||
|
class LoggedCommand(BaseCommand):
|
||||||
|
def execute(self, *args, **options):
|
||||||
|
name = getattr(self, "name", "")
|
||||||
|
if not name:
|
||||||
|
name = sys.argv[1]
|
||||||
|
|
||||||
|
logger.info("start LoggedCommand", job_name=name, label="job_log")
|
||||||
|
|
||||||
|
self.job_log = JobLog.objects.create(job_name=name)
|
||||||
|
try:
|
||||||
|
super(LoggedCommand, self).execute(*args, **options)
|
||||||
|
self.job_log.refresh_from_db()
|
||||||
|
self.job_log.ended = timezone.now()
|
||||||
|
self.job_log.success = True
|
||||||
|
self.job_log.save()
|
||||||
|
logger.info(
|
||||||
|
"LoggedCommand successfully ended", job_name=name, label="job_log"
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
self.job_log.refresh_from_db()
|
||||||
|
self.job_log.error_message = str(e)
|
||||||
|
self.job_log.stack_trace = traceback.format_exc()
|
||||||
|
self.job_log.save()
|
||||||
|
logger.error("LoggedCommand failed", job_name=name, label="job_log")
|
||||||
|
raise e
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
# Generated by Django 3.2.20 on 2023-08-25 15:23
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("core", "0001_initial"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="JobLog",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.BigAutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("started", models.DateTimeField(auto_now_add=True)),
|
||||||
|
("ended", models.DateTimeField(blank=True, null=True)),
|
||||||
|
("job_name", models.CharField(max_length=255)),
|
||||||
|
("success", models.BooleanField(default=False)),
|
||||||
|
("error_message", models.TextField(blank=True, default="")),
|
||||||
|
("stack_trace", models.TextField(blank=True, default="")),
|
||||||
|
("json_data", models.JSONField(default=dict)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -41,3 +41,16 @@ class SecurityRequestResponseLog(models.Model):
|
||||||
response_status_code = models.CharField(max_length=255, blank=True, default="")
|
response_status_code = models.CharField(max_length=255, blank=True, default="")
|
||||||
|
|
||||||
additional_json_data = JSONField(default=dict, blank=True)
|
additional_json_data = JSONField(default=dict, blank=True)
|
||||||
|
|
||||||
|
|
||||||
|
class JobLog(models.Model):
|
||||||
|
started = models.DateTimeField(auto_now_add=True)
|
||||||
|
ended = models.DateTimeField(blank=True, null=True)
|
||||||
|
job_name = models.CharField(max_length=255)
|
||||||
|
success = models.BooleanField(default=False)
|
||||||
|
error_message = models.TextField(blank=True, default="")
|
||||||
|
stack_trace = models.TextField(blank=True, default="")
|
||||||
|
json_data = models.JSONField(default=dict)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "{job_name} {started:%H:%M %d.%m.%Y}".format(**self.__dict__)
|
||||||
|
|
|
||||||
|
|
@ -38,3 +38,16 @@ def replace_whitespace(text, replacement=" "):
|
||||||
|
|
||||||
def get_django_content_type(obj):
|
def get_django_content_type(obj):
|
||||||
return obj._meta.app_label + "." + type(obj).__name__
|
return obj._meta.app_label + "." + type(obj).__name__
|
||||||
|
|
||||||
|
|
||||||
|
def pretty_print_json(json_string):
|
||||||
|
try:
|
||||||
|
parsed = json_string
|
||||||
|
if isinstance(json_string, str):
|
||||||
|
parsed = json.loads(json_string)
|
||||||
|
return mark_safe(
|
||||||
|
"<pre>{}</pre>".format(json.dumps(parsed, indent=4, sort_keys=True))
|
||||||
|
)
|
||||||
|
# pylint: disable=broad-except
|
||||||
|
except Exception:
|
||||||
|
return json_string
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
# Generated by Django 3.2.20 on 2023-08-25 15:23
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("course_session", "0004_coursesessionedoniqtest"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name="coursesessionassignment",
|
||||||
|
options={"ordering": ["course_session", "submission_deadline__start"]},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name="coursesessionattendancecourse",
|
||||||
|
options={"ordering": ["course_session", "due_date__start"]},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name="coursesessionedoniqtest",
|
||||||
|
options={"ordering": ["course_session", "deadline__start"]},
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
import djclick as click
|
|
||||||
import structlog
|
import structlog
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from vbv_lernwelt.core.base import LoggedCommand
|
||||||
from vbv_lernwelt.core.models import User
|
from vbv_lernwelt.core.models import User
|
||||||
from vbv_lernwelt.course.models import CourseSessionUser
|
from vbv_lernwelt.course.models import CourseSessionUser
|
||||||
from vbv_lernwelt.course_session.models import CourseSessionAttendanceCourse
|
from vbv_lernwelt.course_session.models import CourseSessionAttendanceCourse
|
||||||
|
|
@ -55,6 +55,8 @@ def attendance_course_reminder_notification_job():
|
||||||
logger.info("No attendance courses found")
|
logger.info("No attendance courses found")
|
||||||
|
|
||||||
|
|
||||||
@click.command()
|
class Command(LoggedCommand):
|
||||||
def command():
|
help = "Sends attendance course reminder notifications to participants"
|
||||||
attendance_course_reminder_notification_job()
|
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
attendance_course_reminder_notification_job()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue