Исходный код assignments.views

"""
Представления (views) для системы проверки домашних заданий.

Содержит все представления для:
- Авторизации и регистрации пользователей
- Dashboard студента и преподавателя
- Управления домашними заданиями
- Отправки и проверки работ
"""

import os

from django.contrib import messages
from django.contrib.auth import authenticate, get_user_model, login, logout
from django.contrib.auth.decorators import login_required
from django.shortcuts import get_object_or_404, redirect, render
from django.utils import timezone

from .decorators import student_required, teacher_required
from .forms import GradeForm, HomeworkForm, RegisterForm, SubmissionForm
from .models import Course, CourseEnrollmentRequest, Homework, Submission

User = get_user_model()

# ============= Авторизация =============


[документация] def register_view(request): """Регистрация нового пользователя""" if request.user.is_authenticated: return redirect("dashboard") if request.method == "POST": form = RegisterForm(request.POST) if form.is_valid(): user = form.save() login(request, user) messages.success(request, f"Добро пожаловать, {user.first_name}!") return redirect("dashboard") else: form = RegisterForm() return render(request, "assignments/register.html", {"form": form})
[документация] def login_view(request): """Вход в систему""" if request.user.is_authenticated: return redirect("dashboard") if request.method == "POST": username = request.POST.get("username") password = request.POST.get("password") user = authenticate(request, username=username, password=password) if user is not None: login(request, user) messages.success(request, f"Добро пожаловать, {user.first_name}!") return redirect("dashboard") messages.error(request, "Неверный логин или пароль") return render(request, "assignments/login.html")
[документация] @login_required def logout_view(request): """Выход из системы""" logout(request) messages.info(request, "Вы вышли из системы") return redirect("login")
# ============= Dashboard =============
[документация] @login_required def dashboard_view(request): """Главная страница - перенаправление в зависимости от роли""" if request.user.profile.is_student: return redirect("student_dashboard") if request.user.profile.is_teacher: return redirect("teacher_dashboard") return redirect("login")
# ============= Студент =============
[документация] @login_required @student_required def student_dashboard(request): """Dashboard студента - список курсов""" courses = request.user.enrolled_courses.all() # Статистика по всем курсам all_homeworks = Homework.objects.filter(course__in=courses) student_submissions = Submission.objects.filter(student=request.user) context = { "courses": courses, "total_courses": courses.count(), "total_homeworks": all_homeworks.count(), "submitted_count": student_submissions.count(), "graded_count": student_submissions.filter(grade__isnull=False).count(), } return render(request, "assignments/student_dashboard.html", context)
[документация] @login_required @student_required def course_detail(request, pk): """Детальная страница курса для студента""" course = get_object_or_404(Course, pk=pk) # Проверка доступа - студент должен быть записан на курс if request.user not in course.students.all(): messages.error(request, "У вас нет доступа к этому курсу") return redirect("student_dashboard") homeworks = course.homeworks.all().order_by("-created_at") course_submissions = Submission.objects.filter(student=request.user, homework__course=course) # Добавляем информацию о том, сдал ли студент каждое ДЗ homework_status = [] for hw in homeworks: submission = course_submissions.filter(homework=hw).first() homework_status.append( { "homework": hw, "submission": submission, "is_overdue": hw.due_date < timezone.now(), } ) context = { "course": course, "homework_status": homework_status, "total_homeworks": homeworks.count(), "submitted_count": course_submissions.count(), } return render(request, "assignments/course_detail.html", context)
[документация] @login_required @student_required def homework_detail(request, pk): """Детальная страница задания для студента""" homework = get_object_or_404(Homework, pk=pk) # Проверка доступа - студент должен быть записан на курс if request.user not in homework.course.students.all(): messages.error(request, "У вас нет доступа к этому заданию") return redirect("student_dashboard") submission = Submission.objects.filter(homework=homework, student=request.user).first() if request.method == "POST": # Разрешаем переотправку работы (замену файла) if submission: # Обновляем существующую отправку form = SubmissionForm(request.POST, request.FILES, instance=submission) if form.is_valid(): # Удаляем старый файл перед сохранением нового if submission.solution_file and os.path.isfile(submission.solution_file.path): os.remove(submission.solution_file.path) submission = form.save(commit=False) # Сбрасываем оценку при переотправке submission.grade = None submission.feedback = "" submission.save() messages.success(request, "Работа успешно переотправлена! Оценка сброшена.") return redirect("course_detail", pk=homework.course.pk) else: # Создаём новую отправку form = SubmissionForm(request.POST, request.FILES) if form.is_valid(): submission = form.save(commit=False) submission.homework = homework submission.student = request.user submission.save() messages.success(request, "Работа успешно отправлена!") return redirect("course_detail", pk=homework.course.pk) else: form = SubmissionForm(instance=submission) if submission else SubmissionForm() context = { "homework": homework, "submission": submission, "form": form, "is_overdue": homework.due_date < timezone.now(), } return render(request, "assignments/homework_detail.html", context)
[документация] @login_required @student_required def my_submissions(request): """Список всех отправленных работ студента""" submissions = Submission.objects.filter(student=request.user).order_by("-submitted_at") context = { "submissions": submissions, } return render(request, "assignments/my_submissions.html", context)
[документация] @login_required @student_required def my_grades(request): """Таблица оценок студента по всем курсам""" courses = request.user.enrolled_courses.all() grades_data = [] for course in courses: homeworks = course.homeworks.all().order_by("due_date") course_grades = {"course": course, "homeworks": []} for hw in homeworks: submission = Submission.objects.filter(homework=hw, student=request.user).first() course_grades["homeworks"].append( { "homework": hw, "submission": submission, "grade": submission.grade if submission else None, "status": ("Оценено" if submission and submission.grade else "На проверке" if submission else "Не сдано"), } ) grades_data.append(course_grades) context = { "grades_data": grades_data, } return render(request, "assignments/my_grades.html", context)
[документация] @login_required @student_required def available_courses(request): """Список всех доступных курсов для студента""" # Курсы, на которые студент уже записан enrolled_courses = request.user.enrolled_courses.all() # Все курсы all_courses = Course.objects.all() # Заявки студента student_requests = CourseEnrollmentRequest.objects.filter(student=request.user) requests_dict = {req.course_id: req for req in student_requests} # Подготавливаем информацию о каждом курсе courses_info = [] for course in all_courses: is_enrolled = course in enrolled_courses request_status = None enrollment_request = requests_dict.get(course.id) if enrollment_request: request_status = enrollment_request.status courses_info.append( { "course": course, "is_enrolled": is_enrolled, "request_status": request_status, "request_id": enrollment_request.id if enrollment_request else None, } ) context = { "courses_info": courses_info, } return render(request, "assignments/available_courses.html", context)
[документация] @login_required @student_required def request_enrollment(request, course_pk): """Подать заявку на зачисление на курс""" course = get_object_or_404(Course, pk=course_pk) # Проверяем, не записан ли студент уже на курс if request.user in course.students.all(): messages.warning(request, "Вы уже записаны на этот курс") return redirect("available_courses") # Проверяем, нет ли уже активной заявки existing_request = CourseEnrollmentRequest.objects.filter(course=course, student=request.user, status="pending").first() if existing_request: messages.warning(request, "Вы уже подали заявку на этот курс") return redirect("available_courses") if request.method == "POST": message = request.POST.get("message", "") CourseEnrollmentRequest.objects.create(course=course, student=request.user, message=message) messages.success(request, f'Заявка на курс "{course.title}" успешно подана!') return redirect("available_courses") context = {"course": course} return render(request, "assignments/request_enrollment.html", context)
[документация] @login_required @student_required def cancel_enrollment_request(request, request_pk): """Отменить заявку на зачисление""" enrollment_request = get_object_or_404(CourseEnrollmentRequest, pk=request_pk) # Проверка доступа if enrollment_request.student != request.user: messages.error(request, "У вас нет доступа к этой заявке") return redirect("available_courses") # Можно отменить только заявки со статусом "pending" if enrollment_request.status != "pending": messages.error(request, "Эту заявку нельзя отменить") return redirect("available_courses") if request.method == "POST": course_title = enrollment_request.course.title enrollment_request.delete() messages.success(request, f'Заявка на курс "{course_title}" отменена') return redirect("available_courses") return redirect("available_courses")
# ============= Преподаватель =============
[документация] @login_required @teacher_required def teacher_dashboard(request): """Dashboard преподавателя - список его курсов""" courses = request.user.teaching_courses.all() # Статистика all_homeworks = Homework.objects.filter(course__in=courses) all_submissions = Submission.objects.filter(homework__in=all_homeworks) pending_submissions = all_submissions.filter(grade__isnull=True) context = { "courses": courses, "total_courses": courses.count(), "total_homeworks": all_homeworks.count(), "total_submissions": all_submissions.count(), "pending_count": pending_submissions.count(), } return render(request, "assignments/teacher_dashboard.html", context)
[документация] @login_required @teacher_required def teacher_course_detail(request, pk): """Детальная страница курса для преподавателя""" course = get_object_or_404(Course, pk=pk) # Проверка доступа if request.user not in course.teachers.all(): messages.error(request, "У вас нет доступа к этому курсу") return redirect("teacher_dashboard") homeworks = course.homeworks.all().order_by("-created_at") all_submissions = Submission.objects.filter(homework__course=course) context = { "course": course, "homeworks": homeworks, "students_count": course.students.count(), "total_submissions": all_submissions.count(), "pending_count": all_submissions.filter(grade__isnull=True).count(), } return render(request, "assignments/teacher_course_detail.html", context)
[документация] @login_required @teacher_required def create_course(request): """Создание нового курса""" if request.method == "POST": title = request.POST.get("title") description = request.POST.get("description") if title and description: course = Course.objects.create(title=title, description=description) course.teachers.add(request.user) messages.success(request, f'Курс "{course.title}" успешно создан!') return redirect("teacher_course_detail", pk=course.pk) messages.error(request, "Заполните все обязательные поля") return render(request, "assignments/create_course.html")
[документация] @login_required @teacher_required def edit_course(request, pk): """Редактирование курса""" course = get_object_or_404(Course, pk=pk) # Проверка доступа if request.user not in course.teachers.all(): messages.error(request, "У вас нет доступа к этому курсу") return redirect("teacher_dashboard") if request.method == "POST": course.title = request.POST.get("title", course.title) course.description = request.POST.get("description", course.description) course.save() messages.success(request, "Курс успешно обновлен!") return redirect("teacher_course_detail", pk=course.pk) context = {"course": course} return render(request, "assignments/edit_course.html", context)
[документация] @login_required @teacher_required def manage_students(request, pk): """Управление студентами курса - просмотр зачисленных студентов и заявок""" course = get_object_or_404(Course, pk=pk) # Проверка доступа if request.user not in course.teachers.all(): messages.error(request, "У вас нет доступа к этому курсу") return redirect("teacher_dashboard") # Текущие студенты current_students = course.students.all() # Заявки на зачисление pending_requests = CourseEnrollmentRequest.objects.filter(course=course, status="pending").order_by("-created_at") # История заявок (одобренные и отклоненные) processed_requests = CourseEnrollmentRequest.objects.filter(course=course, status__in=["approved", "rejected"]).order_by( "-processed_at" )[ :20 ] # Показываем последние 20 context = { "course": course, "current_students": current_students, "pending_requests": pending_requests, "processed_requests": processed_requests, "pending_count": pending_requests.count(), } return render(request, "assignments/manage_students.html", context)
[документация] @login_required @teacher_required def approve_enrollment_request(request, request_pk): """Одобрить заявку на зачисление""" enrollment_request = get_object_or_404(CourseEnrollmentRequest, pk=request_pk) # Проверка доступа if request.user not in enrollment_request.course.teachers.all(): messages.error(request, "У вас нет доступа к этой заявке") return redirect("teacher_dashboard") # Проверка статуса if enrollment_request.status != "pending": messages.warning(request, "Эта заявка уже обработана") return redirect("manage_students", pk=enrollment_request.course.pk) if request.method == "POST": # Обновляем статус заявки enrollment_request.status = "approved" enrollment_request.processed_at = timezone.now() enrollment_request.processed_by = request.user enrollment_request.save() # Добавляем студента на курс enrollment_request.course.students.add(enrollment_request.student) messages.success( request, f'Студент {enrollment_request.student.get_full_name()} зачислен на курс "{enrollment_request.course.title}"', ) return redirect("manage_students", pk=enrollment_request.course.pk) return redirect("manage_students", pk=enrollment_request.course.pk)
[документация] @login_required @teacher_required def reject_enrollment_request(request, request_pk): """Отклонить заявку на зачисление""" enrollment_request = get_object_or_404(CourseEnrollmentRequest, pk=request_pk) # Проверка доступа if request.user not in enrollment_request.course.teachers.all(): messages.error(request, "У вас нет доступа к этой заявке") return redirect("teacher_dashboard") # Проверка статуса if enrollment_request.status != "pending": messages.warning(request, "Эта заявка уже обработана") return redirect("manage_students", pk=enrollment_request.course.pk) if request.method == "POST": # Обновляем статус заявки enrollment_request.status = "rejected" enrollment_request.processed_at = timezone.now() enrollment_request.processed_by = request.user enrollment_request.save() messages.info(request, f"Заявка от {enrollment_request.student.get_full_name()} отклонена") return redirect("manage_students", pk=enrollment_request.course.pk) return redirect("manage_students", pk=enrollment_request.course.pk)
[документация] @login_required @teacher_required def remove_student_from_course(request, course_pk, student_pk): """Удалить студента с курса""" course = get_object_or_404(Course, pk=course_pk) # Проверка доступа if request.user not in course.teachers.all(): messages.error(request, "У вас нет доступа к этому курсу") return redirect("teacher_dashboard") student = get_object_or_404(User, pk=student_pk) if request.method == "POST": course.students.remove(student) messages.success(request, f"Студент {student.get_full_name()} удален с курса") return redirect("manage_students", pk=course.pk) return redirect("manage_students", pk=course.pk)
[документация] @login_required @teacher_required def teacher_create_homework(request, course_pk): """Создание домашнего задания""" course = get_object_or_404(Course, pk=course_pk) # Проверка доступа if request.user not in course.teachers.all(): messages.error(request, "У вас нет доступа к этому курсу") return redirect("teacher_dashboard") if request.method == "POST": form = HomeworkForm(request.POST) if form.is_valid(): homework = form.save(commit=False) homework.course = course homework.save() messages.success(request, f'Задание "{homework.title}" создано!') return redirect("teacher_course_detail", pk=course.pk) else: form = HomeworkForm() context = {"form": form, "course": course} return render(request, "assignments/teacher_create_homework.html", context)
[документация] @login_required @teacher_required def teacher_edit_homework(request, pk): """Редактирование домашнего задания""" homework = get_object_or_404(Homework, pk=pk) # Проверка доступа if request.user not in homework.course.teachers.all(): messages.error(request, "У вас нет доступа к этому заданию") return redirect("teacher_dashboard") if request.method == "POST": form = HomeworkForm(request.POST, instance=homework) if form.is_valid(): form.save() messages.success(request, "Задание успешно обновлено!") return redirect("teacher_course_detail", pk=homework.course.pk) else: form = HomeworkForm(instance=homework) context = {"form": form, "homework": homework} return render(request, "assignments/teacher_edit_homework.html", context)
[документация] @login_required @teacher_required def teacher_homework_submissions(request, pk): """Список отправок по домашнему заданию""" homework = get_object_or_404(Homework, pk=pk) # Проверка доступа if request.user not in homework.course.teachers.all(): messages.error(request, "У вас нет доступа к этому заданию") return redirect("teacher_dashboard") submissions = homework.submissions.all().order_by("-submitted_at") context = { "homework": homework, "submissions": submissions, "total_count": submissions.count(), "graded_count": submissions.filter(grade__isnull=False).count(), "pending_count": submissions.filter(grade__isnull=True).count(), } return render(request, "assignments/teacher_homework_submissions.html", context)
[документация] @login_required @teacher_required def teacher_grade_submission(request, pk): """Проверка и выставление оценки""" submission = get_object_or_404(Submission, pk=pk) # Проверка доступа if request.user not in submission.homework.course.teachers.all(): messages.error(request, "У вас нет доступа к этой работе") return redirect("teacher_dashboard") if request.method == "POST": form = GradeForm(request.POST, instance=submission) if form.is_valid(): form.save() messages.success(request, f"Оценка для {submission.student.get_full_name()} выставлена!") return redirect("teacher_homework_submissions", pk=submission.homework.pk) else: form = GradeForm(instance=submission) context = { "submission": submission, "form": form, } return render(request, "assignments/teacher_grade_submission.html", context)
[документация] @login_required @teacher_required def teacher_all_submissions(request): """Все отправки преподавателя""" courses = request.user.teaching_courses.all() submissions = Submission.objects.filter(homework__course__in=courses).order_by("-submitted_at") # Фильтрация status_filter = request.GET.get("status", "all") course_filter = request.GET.get("course", "all") if status_filter == "pending": submissions = submissions.filter(grade__isnull=True) elif status_filter == "graded": submissions = submissions.filter(grade__isnull=False) if course_filter != "all": submissions = submissions.filter(homework__course_id=course_filter) context = { "submissions": submissions, "status_filter": status_filter, "course_filter": course_filter, "courses": courses, } return render(request, "assignments/teacher_all_submissions.html", context)
[документация] @login_required @teacher_required def delete_course(request, pk): """Удаление курса""" course = get_object_or_404(Course, pk=pk) # Проверка доступа if request.user not in course.teachers.all(): messages.error(request, "У вас нет доступа к этому курсу") return redirect("teacher_dashboard") if request.method == "POST": course_title = course.title course.delete() messages.success(request, f'Курс "{course_title}" успешно удалён!') return redirect("teacher_dashboard") return redirect("teacher_course_detail", pk=pk)
[документация] @login_required @teacher_required def delete_homework(request, pk): """Удаление домашнего задания""" homework = get_object_or_404(Homework, pk=pk) # Проверка доступа if request.user not in homework.course.teachers.all(): messages.error(request, "У вас нет доступа к этому заданию") return redirect("teacher_dashboard") if request.method == "POST": course_pk = homework.course.pk homework_title = homework.title homework.delete() messages.success(request, f'Задание "{homework_title}" успешно удалено!') return redirect("teacher_course_detail", pk=course_pk) return redirect("teacher_course_detail", pk=homework.course.pk)
[документация] @login_required @teacher_required def teacher_grades_table(request, course_pk): """Сводная таблица оценок студентов по курсу""" course = get_object_or_404(Course, pk=course_pk) # Проверка доступа if request.user not in course.teachers.all(): messages.error(request, "У вас нет доступа к этому курсу") return redirect("teacher_dashboard") # Получаем всех студентов курса и все задания students = course.students.all().order_by("last_name", "first_name") homeworks = course.homeworks.all().order_by("due_date") # Формируем таблицу оценок grades_table = [] for student in students: student_row = { "student": student, "grades": [], "total": 0, "average": 0, "completed": 0, } total_grade = 0 graded_count = 0 for hw in homeworks: submission = Submission.objects.filter(homework=hw, student=student).first() grade_info = { "homework": hw, "submission": submission, "grade": submission.grade if submission else None, "status": ( "graded" if submission and submission.grade is not None else "submitted" if submission else "missing" ), } student_row["grades"].append(grade_info) if submission and submission.grade is not None: total_grade += submission.grade graded_count += 1 if graded_count > 0: student_row["average"] = round(total_grade / graded_count, 1) student_row["completed"] = graded_count student_row["total"] = total_grade grades_table.append(student_row) context = { "course": course, "homeworks": homeworks, "grades_table": grades_table, "students_count": students.count(), "homeworks_count": homeworks.count(), } return render(request, "assignments/teacher_grades_table.html", context)
# ============= Общие =============
[документация] def home_view(request): """Главная страница для неавторизованных пользователей""" if request.user.is_authenticated: return redirect("dashboard") return render(request, "assignments/home.html")