From 08eba4f9d7993665a00be0e0a667b5a4969c46cb Mon Sep 17 00:00:00 2001 From: Dmitry Belan Date: Mon, 28 Sep 2026 22:46:32 +0300 Subject: [PATCH] Add statistics block to SubmissionAdmin --- lmsege/lms/admin.py | 73 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 71 insertions(+), 2 deletions(-) diff --git a/lmsege/lms/admin.py b/lmsege/lms/admin.py index 9c74992..8fbe1d6 100644 --- a/lmsege/lms/admin.py +++ b/lmsege/lms/admin.py @@ -99,6 +99,8 @@ class AnswerInline(StackedInline): def has_add_permission(self, request, obj=None): return False +from django.utils.html import format_html + @admin.register(Submission) class SubmissionAdmin(ModelAdmin): change_list_template = "admin/lms/submission/change_list.html" @@ -107,10 +109,77 @@ class SubmissionAdmin(ModelAdmin): search_fields = ('student__username', 'assignment__title') inlines = [AnswerInline] + fieldsets = ( + ('Параметры проверки и оценка', { + 'fields': ('statistics', ('assignment', 'student'), ('grade', 'status'), ('is_time_expired', 'is_late'), 'teacher_feedback') + }), + ('Временные метки', { + 'fields': (('started_at', 'submitted_at'),) + }), + ) + def get_readonly_fields(self, request, obj=None): if obj: - return ('assignment', 'student', 'is_time_expired', 'is_late', 'started_at', 'submitted_at') - return ('is_time_expired', 'is_late', 'started_at', 'submitted_at') + return ('statistics', 'assignment', 'student', 'is_time_expired', 'is_late', 'started_at', 'submitted_at') + return ('statistics', 'is_time_expired', 'is_late', 'started_at', 'submitted_at') + + @admin.display(description='Статистика (сохраните оценку в формате X/Y, чтобы обновить)') + def statistics(self, obj): + if not obj or not obj.grade: + return format_html('
Оценка не выставлена или имеет неверный формат. Укажите оценку в формате "X/Y" (например, "3/27").
') + + try: + parts = obj.grade.split('/') + earned = float(parts[0].strip()) + total = float(parts[1].strip()) + percentage = (earned / total) * 100 if total > 0 else 0 + + is_passed = percentage >= 40 + threshold_text = "Выше порога" if is_passed else "Ниже порога (Мин: 40%)" + color = "#2ea043" if is_passed else "#f85149" + + total_int = int(total) if total.is_integer() else total + threshold_points = int(total * 0.4) + + return format_html( + ''' +
+
+
+
Итоговая оценка
+
+
+ {grade} +
+ первичных баллов +
+
+
+
+ Выполнение: {percentage:.1f}% + {threshold_text} +
+
+
+
+
+ 0 б + Порог: {threshold_points} б + Максимум: {total_int} б +
+
+
+
+ ''', + grade=obj.grade, + percentage=percentage, + threshold_text=threshold_text, + color=color, + total_int=total_int, + threshold_points=threshold_points + ) + except Exception: + return format_html('
Оценка имеет неверный формат. Используйте формат "X/Y" (например, "3/27").
') def get_queryset(self, request): qs = super().get_queryset(request)