diff --git a/lmsege/lms/admin.py b/lmsege/lms/admin.py index 923e4d5..04f79af 100644 --- a/lmsege/lms/admin.py +++ b/lmsege/lms/admin.py @@ -125,23 +125,12 @@ class SubmissionAdmin(ModelAdmin): @admin.display(description='Статистика (сохраните оценку в формате X/Y, чтобы обновить)') def statistics(self, obj): - if not obj or not obj.grade: + stats = obj.get_statistics() if obj else None + + if not stats: 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 - percentage_str = f"{percentage:.1f}" - - 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( '''
@@ -173,17 +162,16 @@ class SubmissionAdmin(ModelAdmin):
''', grade=obj.grade, - percentage_str=percentage_str, - threshold_text=threshold_text, - color=color, - total_int=total_int, - threshold_points=threshold_points + percentage_str=stats['percentage_str'], + threshold_text=stats['threshold_text'], + color=stats['color'], + total_int=stats['total'], + threshold_points=stats['threshold_points'] ) except Exception as e: return format_html( - '
Оценка имеет неверный формат. Ошибка: {error}
Значение: "{val}"
', - error=str(e), - val=obj.grade + '
Ошибка при отрисовке статистики: {error}
', + error=str(e) ) def get_queryset(self, request): diff --git a/lmsege/lms/models.py b/lmsege/lms/models.py index 804631f..4442d74 100644 --- a/lmsege/lms/models.py +++ b/lmsege/lms/models.py @@ -26,6 +26,7 @@ class Assignment(models.Model): deadline = models.DateTimeField('Дедлайн', blank=True, null=True, help_text='После этого времени ответы не принимаются.') time_limit = models.PositiveIntegerField('Лимит времени (минуты)', blank=True, null=True, help_text='Например, 235 для 3ч 55м. Оставьте пустым, если без таймера.') allow_late_submissions = models.BooleanField('Разрешить дорешивание', default=True, help_text='Если включено, ученики смогут сдавать задание после дедлайна с пометкой "После дедлайна".') + passing_threshold = models.PositiveIntegerField('Порог прохождения (%)', default=40, help_text='Процент выполнения, необходимый для прохождения. Например, 40.') class Meta: verbose_name = 'Задание' @@ -80,6 +81,36 @@ class Submission(models.Model): def __str__(self): return f"Ответ от {self.student.username} на {self.assignment.title}" + def get_statistics(self): + if not self.grade: + return None + try: + parts = self.grade.split('/') + earned = float(parts[0].strip()) + total = float(parts[1].strip()) + percentage = (earned / total) * 100 if total > 0 else 0 + + threshold_percent = self.assignment.passing_threshold + is_passed = percentage >= threshold_percent + + total_int = int(total) if total.is_integer() else total + earned_int = int(earned) if earned.is_integer() else earned + threshold_points = int(total * (threshold_percent / 100.0)) + + return { + 'earned': earned_int, + 'total': total_int, + 'percentage': percentage, + 'percentage_str': f"{percentage:.1f}", + 'is_passed': is_passed, + 'threshold_percent': threshold_percent, + 'threshold_points': threshold_points, + 'threshold_text': 'Выше порога' if is_passed else f'Ниже порога (Мин: {threshold_percent}%)', + 'color': '#2ea043' if is_passed else '#f85149' + } + except Exception: + return None + class Answer(models.Model): submission = models.ForeignKey(Submission, on_delete=models.CASCADE, related_name='answers', verbose_name='Решение (Submission)') question = models.ForeignKey(Question, on_delete=models.CASCADE, related_name='answers', verbose_name='Задача') diff --git a/lmsege/lms/templates/lms/assignment_detail.html b/lmsege/lms/templates/lms/assignment_detail.html index 3bd36c2..898f8e6 100644 --- a/lmsege/lms/templates/lms/assignment_detail.html +++ b/lmsege/lms/templates/lms/assignment_detail.html @@ -37,28 +37,74 @@

Ваш ответ

{% if submission and submission.status != 'IN_PROGRESS' %} -
- {% if submission.status == 'GRADED' %} -

- - - - Оценка: {{ submission.grade|default:"Не выставлена" }} -

- {% else %} -

- - Ваша работа отправлена и ожидает проверки. -

- {% endif %} - - {% if submission.teacher_feedback %} -
-

Общий комментарий преподавателя:

-

{{ submission.teacher_feedback }}

+ {% with stats=submission.get_statistics %} + {% if stats %} +
+
+
+
+
Итоговая оценка
+
+
+ {{ submission.grade }} +
+ первичных баллов +
+
+
+ +
+
+ Выполнение: {{ stats.percentage_str }}% + + {{ stats.threshold_text }} + +
+
+
+
+
+ 0 б + Порог: {{ stats.threshold_points }} б + Максимум: {{ stats.total }} б +
+
- {% endif %} -
+ + {% if submission.teacher_feedback %} +
+

Комментарий преподавателя

+

+ {{ submission.teacher_feedback }} +

+
+ {% endif %} +
+ {% else %} +
+ {% if submission.status == 'GRADED' %} +

+ + + + Оценка: {{ submission.grade|default:"Не выставлена" }} +

+ {% else %} +

+ + Ваша работа отправлена и ожидает проверки. +

+ {% endif %} + + {% if submission.teacher_feedback %} +
+

Общий комментарий преподавателя:

+

{{ submission.teacher_feedback }}

+
+ {% endif %} +
+ {% endif %} + {% endwith %}

Ваши ответы на задачи:

diff --git a/lmsege/lmsege/__pycache__/settings.cpython-314.pyc b/lmsege/lmsege/__pycache__/settings.cpython-314.pyc index 19f8bad..36748e3 100644 Binary files a/lmsege/lmsege/__pycache__/settings.cpython-314.pyc and b/lmsege/lmsege/__pycache__/settings.cpython-314.pyc differ