Files
training-software/quizzes/tasks.py
Paperclip CTO 6d7cc5ed16
Some checks failed
CI / Tests (Python 3.12) (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / OpenAPI Schema (push) Has been cancelled
feat(TRA-240): attempt policy enforcement — limits, timers, auto-expiry
- max_attempts enforcement in start_attempt: raises AttemptLimitError when
  quota of finalized attempts is exhausted (in_progress not counted)
- Timer enforcement: start_attempt sets expired_at = now + time_limit_seconds;
  check_attempt_not_expired auto-finalizes and raises AttemptExpiredError on deadline breach
- finalize_timed_out_attempt: grades partial responses, sets TIMED_OUT status
- expire_timed_out_attempts Celery task: idempotent sweep of overdue in-progress attempts
- Views: 409 on limit breach (start), 410 on expired response submit; timed_out result on submit
- 14 unit + integration tests covering policy boundary conditions and API responses

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-05-07 09:17:25 +02:00

25 lines
717 B
Python

from celery import shared_task
from django.utils.timezone import now
@shared_task(name="quizzes.expire_timed_out_attempts")
def expire_timed_out_attempts():
"""
Finalize all in-progress attempts whose deadline has passed.
Scheduled by django-celery-beat; safe to run multiple times (idempotent per attempt).
"""
from .models import AttemptStatus, QuizAttempt
from .services import finalize_timed_out_attempt
expired = QuizAttempt.objects.filter(
status=AttemptStatus.IN_PROGRESS,
expired_at__lt=now(),
).select_related("quiz")
count = 0
for attempt in expired:
finalize_timed_out_attempt(attempt)
count += 1
return {"finalized": count}