feat(lms): add deadlines and manual assignment closing logic

This commit is contained in:
Dmitry Belan
2026-09-20 17:46:56 +03:00
parent 2cf38ef27e
commit 4b1ea99a4d
5 changed files with 35 additions and 4 deletions
+2 -2
View File
@@ -18,9 +18,9 @@ class QuestionInline(StackedInline):
@admin.register(Assignment) @admin.register(Assignment)
class AssignmentAdmin(ModelAdmin): class AssignmentAdmin(ModelAdmin):
list_display = ('title', 'subject', 'is_published', 'created_at') list_display = ('title', 'subject', 'is_published', 'is_closed', 'deadline', 'created_at')
list_display_links = ('title',) list_display_links = ('title',)
list_filter = ('subject', 'is_published') list_filter = ('subject', 'is_published', 'is_closed')
search_fields = ('title', 'description') search_fields = ('title', 'description')
exclude = ('created_by',) exclude = ('created_by',)
filter_horizontal = ('assigned_students',) filter_horizontal = ('assigned_students',)
+2
View File
@@ -22,6 +22,8 @@ class Assignment(models.Model):
assigned_students = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='assigned_assignments', verbose_name='Назначенные ученики', blank=True, limit_choices_to={'role': 'STUDENT'}, help_text='Выберите учеников, если задание не для всех.') assigned_students = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='assigned_assignments', verbose_name='Назначенные ученики', blank=True, limit_choices_to={'role': 'STUDENT'}, help_text='Выберите учеников, если задание не для всех.')
created_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, limit_choices_to={'role__in': ['TEACHER', 'ADMIN']}, verbose_name='Кем создано') created_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, limit_choices_to={'role__in': ['TEACHER', 'ADMIN']}, verbose_name='Кем создано')
created_at = models.DateTimeField('Дата создания', auto_now_add=True) created_at = models.DateTimeField('Дата создания', auto_now_add=True)
deadline = models.DateTimeField('Дедлайн', blank=True, null=True, help_text='После этого времени ответы не принимаются.')
is_closed = models.BooleanField('Прием ответов закрыт', default=False, help_text='Включите, чтобы вручную закрыть прием ответов на задание.')
class Meta: class Meta:
verbose_name = 'Задание' verbose_name = 'Задание'
@@ -94,6 +94,11 @@
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
{% elif not can_submit %}
<div class="mb-6 p-4 rounded-lg border bg-red-50 text-red-800 border-red-200">
<h4 class="font-bold mb-2">Прием ответов завершен</h4>
<p>Вы больше не можете отправлять ответы, так как дедлайн истек или преподаватель закрыл задание.</p>
</div>
{% else %} {% else %}
<form method="post" enctype="multipart/form-data" class="space-y-6"> <form method="post" enctype="multipart/form-data" class="space-y-6">
{% csrf_token %} {% csrf_token %}
+12
View File
@@ -51,6 +51,18 @@
<span class="material-icons text-[14px]">event</span> <span class="material-icons text-[14px]">event</span>
Добавлено: {{ assignment.created_at|date:"d M Y" }} Добавлено: {{ assignment.created_at|date:"d M Y" }}
</span> </span>
{% if assignment.deadline %}
<span class="flex items-center gap-1 {% if assignment.is_closed or assignment.deadline < now %}text-red-500{% else %}text-gray-500{% endif %}">
<span class="material-icons text-[14px]">timer</span>
Дедлайн: {{ assignment.deadline|date:"d M Y, H:i" }}
</span>
{% endif %}
{% if assignment.is_closed %}
<span class="flex items-center gap-1 text-red-500 font-medium mt-1">
<span class="material-icons text-[14px]">lock</span>
Прием ответов закрыт
</span>
{% endif %}
</div> </div>
</div> </div>
<div class="bg-gray-50 p-4 border-t border-gray-100"> <div class="bg-gray-50 p-4 border-t border-gray-100">
+14 -2
View File
@@ -44,10 +44,12 @@ def dashboard(request):
submissions = Submission.objects.filter(student=request.user) submissions = Submission.objects.filter(student=request.user)
submitted_assignment_ids = submissions.values_list('assignment_id', flat=True) submitted_assignment_ids = submissions.values_list('assignment_id', flat=True)
from django.utils import timezone
return render(request, 'lms/dashboard.html', { return render(request, 'lms/dashboard.html', {
'assignments': assignments, 'assignments': assignments,
'submitted_assignment_ids': submitted_assignment_ids, 'submitted_assignment_ids': submitted_assignment_ids,
'submissions': {sub.assignment_id: sub for sub in submissions} 'submissions': {sub.assignment_id: sub for sub in submissions},
'now': timezone.now()
}) })
@login_required @login_required
@@ -58,6 +60,13 @@ def assignment_detail(request, pk):
# Try to get existing submission # Try to get existing submission
submission = Submission.objects.filter(assignment=assignment, student=request.user).first() submission = Submission.objects.filter(assignment=assignment, student=request.user).first()
from django.utils import timezone
can_submit = True
if assignment.is_closed:
can_submit = False
if assignment.deadline and timezone.now() > assignment.deadline:
can_submit = False
if submission: if submission:
if request.method == 'POST': if request.method == 'POST':
@@ -65,7 +74,9 @@ def assignment_detail(request, pk):
return redirect('lms:dashboard') return redirect('lms:dashboard')
form = None form = None
else: else:
if request.method == 'POST': if not can_submit:
form = None
elif request.method == 'POST':
form = AnswersForm(request.POST, request.FILES, questions=questions) form = AnswersForm(request.POST, request.FILES, questions=questions)
if form.is_valid(): if form.is_valid():
submission = Submission.objects.create(assignment=assignment, student=request.user) submission = Submission.objects.create(assignment=assignment, student=request.user)
@@ -153,4 +164,5 @@ def assignment_detail(request, pk):
'submission': submission, 'submission': submission,
'questions': questions, 'questions': questions,
'question_fields': question_fields, 'question_fields': question_fields,
'can_submit': can_submit,
}) })