from django.shortcuts import render, get_object_or_404, redirect from django.db.models import Q from django.contrib.auth.decorators import login_required, user_passes_test from django.utils.html import strip_tags from .models import Assignment, Submission, Answer import json from .forms import AnswersForm, StudentRegistrationForm from django.contrib.auth import login def register(request): if request.user.is_authenticated: return redirect('lms:dashboard') if request.method == 'POST': form = StudentRegistrationForm(request.POST) if form.is_valid(): user = form.save(commit=False) user.role = 'STUDENT' user.save() login(request, user) return redirect('lms:dashboard') else: form = StudentRegistrationForm() return render(request, 'lms/register.html', {'form': form}) def is_student(user): return user.is_authenticated and user.role == 'STUDENT' def home(request): if request.user.is_authenticated: if getattr(request.user, 'role', None) == 'STUDENT': return redirect('lms:dashboard') else: return redirect('/admin/') return render(request, 'lms/home.html') @login_required @user_passes_test(is_student, login_url='/login/') def dashboard(request): assignments = Assignment.objects.filter( Q(assign_to_all=True) | Q(assigned_students=request.user), is_published=True ).distinct().order_by('-created_at') submissions = Submission.objects.filter(student=request.user) submissions_dict = {sub.assignment_id: sub for sub in submissions} total_count = assignments.count() waiting_solution_count = sum(1 for a in assignments if a.id not in submissions_dict or submissions_dict[a.id].status == 'IN_PROGRESS') pending_count = sum(1 for a in assignments if a.id in submissions_dict and submissions_dict[a.id].status == 'PENDING') graded_count = sum(1 for a in assignments if a.id in submissions_dict and submissions_dict[a.id].status == 'GRADED') from django.utils import timezone return render(request, 'lms/dashboard.html', { 'assignments': assignments, 'submissions': submissions_dict, 'now': timezone.now(), 'total_count': total_count, 'waiting_solution_count': waiting_solution_count, 'pending_count': pending_count, 'graded_count': graded_count, }) @login_required @user_passes_test(is_student, login_url='/login/') def assignment_detail(request, pk): assignment = get_object_or_404(Assignment, pk=pk, is_published=True) questions = assignment.questions.all() # Try to get existing submission submission = Submission.objects.filter(assignment=assignment, student=request.user).first() from django.utils import timezone import datetime now = timezone.now() can_submit = True time_left_seconds = None needs_start = False is_late = False if assignment.deadline and now > assignment.deadline: is_late = True if not assignment.allow_late_submissions: can_submit = False if assignment.time_limit: if not submission: if can_submit: needs_start = True can_submit = False # Can't submit until started elif submission.status == 'IN_PROGRESS': if submission.started_at: elapsed = (now - submission.started_at).total_seconds() allowed = assignment.time_limit * 60 if elapsed > allowed + 60: # 1 minute grace period for network latency if request.method == 'POST': # Allow saving this final POST, but mark it as expired submission.is_time_expired = True else: can_submit = False submission.is_time_expired = True submission.status = 'PENDING' submission.save() else: time_left_seconds = int(allowed - elapsed) if time_left_seconds < 0: time_left_seconds = 0 else: can_submit = False # Already submitted if request.method == 'POST': if needs_start and 'action' in request.POST and request.POST['action'] == 'start': Submission.objects.create( assignment=assignment, student=request.user, status='IN_PROGRESS', started_at=now, is_late=is_late ) return redirect('lms:assignment_detail', pk=pk) if submission and submission.status != 'IN_PROGRESS': return redirect('lms:dashboard') if can_submit and not needs_start: form = AnswersForm(request.POST, request.FILES, questions=questions) if form.is_valid(): if not submission: submission = Submission.objects.create(assignment=assignment, student=request.user, is_late=is_late) all_auto = True total_points = 0 max_points = 0 for q in questions: answer_obj = Answer.objects.filter(submission=submission, question=q).first() if not answer_obj: answer_obj = Answer(submission=submission, question=q) if q.question_type == 'TEXT_AUTO': val = form.cleaned_data.get(f'answer_{q.id}', '') answer_obj.text_answer = val if q.correct_answer: correct = strip_tags(q.correct_answer).strip().lower() answer_obj.is_correct = (val.strip().lower() == correct) answer_obj.points = 1 if answer_obj.is_correct else 0 total_points += answer_obj.points else: all_auto = False max_points += 1 elif q.question_type == 'TABLE_AUTO': rows = getattr(q, 'table_rows', 2) or 2 table_data = [] for i in range(1, rows + 1): val1 = form.cleaned_data.get(f'answer_{q.id}_r{i}_c1', '') val2 = form.cleaned_data.get(f'answer_{q.id}_r{i}_c2', '') table_data.append([val1, val2]) answer_obj.text_answer = json.dumps(table_data) if q.correct_answer: correct_rows = q.correct_answer.split(';') is_correct = True if len(correct_rows) != rows: is_correct = False else: for i, row in enumerate(correct_rows): cols = row.split('|') if len(cols) == 2: if cols[0].strip().lower() != table_data[i][0].strip().lower() or cols[1].strip().lower() != table_data[i][1].strip().lower(): is_correct = False break else: is_correct = False break answer_obj.is_correct = is_correct answer_obj.points = 1 if is_correct else 0 total_points += answer_obj.points else: all_auto = False max_points += 1 else: all_auto = False val = form.cleaned_data.get(f'file_{q.id}') if val: answer_obj.file_answer = val answer_obj.save() if all_auto and max_points > 0: submission.status = 'GRADED' submission.grade = f"{total_points}/{max_points}" else: submission.status = 'PENDING' submission.save() return redirect('lms:dashboard') else: form = None else: if can_submit and not needs_start: form = AnswersForm(questions=questions) else: form = None question_fields = [] if form: for q in questions: if q.question_type == 'TEXT_AUTO': question_fields.append((q, 'text', form[f'answer_{q.id}'])) elif q.question_type == 'TABLE_AUTO': rows = getattr(q, 'table_rows', 2) or 2 table_fields = [] for i in range(1, rows + 1): table_fields.append([form[f'answer_{q.id}_r{i}_c1'], form[f'answer_{q.id}_r{i}_c2']]) question_fields.append((q, 'table', table_fields)) else: question_fields.append((q, 'file', form[f'file_{q.id}'])) return render(request, 'lms/assignment_detail.html', { 'assignment': assignment, 'form': form, 'submission': submission, 'questions': questions, 'question_fields': question_fields, 'can_submit': can_submit, 'needs_start': needs_start, 'time_left_seconds': time_left_seconds, 'is_late': is_late, })