start adding exam dates
This commit is contained in:
+55
-16
@@ -2,9 +2,10 @@ from collections import defaultdict
|
||||
import datetime
|
||||
import json
|
||||
import os
|
||||
from typing import Self, Tuple
|
||||
from typing import Optional, Self, Tuple
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.db import models
|
||||
from django.forms import ValidationError
|
||||
from django.http import Http404, HttpRequest
|
||||
from django.utils import timezone
|
||||
from django.core.validators import MaxValueValidator, MinValueValidator
|
||||
@@ -553,6 +554,10 @@ class ExamOrCollectionGenericBase(models.Model, AuthorMixin):
|
||||
|
||||
name = models.CharField(max_length=200, help_text="Name of the exam/collection")
|
||||
|
||||
start_date = models.DateTimeField(help_text="Date (and time) the exam/collection starts", null=True, blank=True)
|
||||
end_date = models.DateTimeField(help_text="Date (and time) the exam/collection ends", null=True, blank=True)
|
||||
restrict_to_dates = models.BooleanField(default=False, help_text="If the exam/collection should only be taken between the start and end date times")
|
||||
|
||||
active = models.BooleanField(
|
||||
help_text="If an exam/collection should be available to take", default=False
|
||||
)
|
||||
@@ -583,6 +588,22 @@ class ExamOrCollectionGenericBase(models.Model, AuthorMixin):
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
self.full_clean()
|
||||
return super().save(*args, **kwargs)
|
||||
|
||||
def clean(self, *args, **kwargs):
|
||||
if self.restrict_to_dates:
|
||||
if self.start_date is None:
|
||||
raise ValidationError("If restrict to dates is set, a start date must be set")
|
||||
|
||||
if self.end_date is not None and self.end_date <= self.start_date:
|
||||
print(f"{self.start_date=}")
|
||||
print(f"{self.end_date=}")
|
||||
raise ValidationError("End date must be after start date")
|
||||
|
||||
return super().clean(*args, **kwargs)
|
||||
|
||||
def get_app_name(self):
|
||||
raise NotImplementedError("You must implement get_app_name in the derived class")
|
||||
|
||||
@@ -603,15 +624,15 @@ class ExamOrCollectionGenericBase(models.Model, AuthorMixin):
|
||||
|
||||
def check_logged_in_user(self, request: HttpRequest):
|
||||
"""Helper to check if the logged in user can access the exam"""
|
||||
return self.check_cid_user(request=request, user_id=request.user.id)
|
||||
return self.check_cid_user(user=request.user, user_id=request.user.id)
|
||||
|
||||
def check_user_can_review(
|
||||
self, cid, passcode, request: HttpRequest, active_only=False
|
||||
):
|
||||
return self.check_user_can_take(cid, passcode, request, active_only)
|
||||
return self.check_user_can_take(cid, passcode, request.user, active_only)
|
||||
|
||||
def check_user_can_take(
|
||||
self, cid, passcode, request: HttpRequest, active_only=True
|
||||
self, cid, passcode, user = None, active_only=True
|
||||
):
|
||||
"""
|
||||
Helper to check if a user is allowed to access an exam/collection
|
||||
@@ -624,33 +645,40 @@ class ExamOrCollectionGenericBase(models.Model, AuthorMixin):
|
||||
Raises:
|
||||
Http404: If user does not have access
|
||||
"""
|
||||
# If we are limiting by dates check that the current date is within the range
|
||||
if self.restrict_to_dates:
|
||||
if self.start_date >= timezone.now() or (self.end_date is not None and self.end_date <= timezone.now()):
|
||||
raise Http404("Exam not found")
|
||||
# If it is not active no one can take
|
||||
print(0)
|
||||
if active_only and not self.active:
|
||||
elif active_only and not self.active:
|
||||
raise Http404("Exam not found")
|
||||
|
||||
print(1)
|
||||
# If candidates only check they have access
|
||||
if self.candidates_only:
|
||||
if not self.check_cid_user(cid, passcode, request):
|
||||
if not self.check_cid_user(cid, passcode, user):
|
||||
raise Http404("Error accessing exam")
|
||||
return
|
||||
|
||||
print(2)
|
||||
# Otherwise check that they are a valid user.
|
||||
if not request.user.is_active:
|
||||
if user is None:
|
||||
raise Http404("Error accessing exam")
|
||||
|
||||
if not user.is_active:
|
||||
raise Http404("Error accessing exam")
|
||||
|
||||
return
|
||||
|
||||
def check_cid_user(
|
||||
self,
|
||||
cid: int | None = None,
|
||||
passcode: str | None = None,
|
||||
request: HttpRequest | None = None,
|
||||
user: User | None = None,
|
||||
#request: HttpRequest | None = None,
|
||||
user_id: int | None = None,
|
||||
allow_authors: bool = True,
|
||||
):
|
||||
print(f"Check cid user: {cid=}, {passcode=}, exam_id={self.pk}")
|
||||
if request is not None and request.user.is_superuser:
|
||||
if user is not None and user.is_superuser:
|
||||
return True
|
||||
|
||||
if user_id is not None:
|
||||
@@ -662,7 +690,10 @@ class ExamOrCollectionGenericBase(models.Model, AuthorMixin):
|
||||
return False
|
||||
|
||||
if cid is None and user_id is None:
|
||||
user_id = request.user.id
|
||||
if user is not None:
|
||||
user_id = user.id
|
||||
else:
|
||||
return False
|
||||
|
||||
# Start by checking if the logged in user can access
|
||||
if user_id is not None:
|
||||
@@ -739,7 +770,7 @@ class ExamOrCollectionGenericBase(models.Model, AuthorMixin):
|
||||
raise NotImplementedError
|
||||
|
||||
def get_cid_user_exams(
|
||||
self, cid_user: "CidUser" = None, user_user: User = None
|
||||
self, cid_user: Optional["CidUser"] = None, user_user: User = None
|
||||
) -> "CidUserExam":
|
||||
content_type = ContentType.objects.get_for_model(self)
|
||||
if cid_user is None and user_user is None:
|
||||
@@ -833,16 +864,22 @@ class ExamBase(ExamOrCollectionGenericBase):
|
||||
|
||||
stats_graph = models.TextField(default=0)
|
||||
|
||||
user_scores = models.JSONField(default=dict)
|
||||
user_scores = models.JSONField(default=dict, blank=True)
|
||||
|
||||
exam_results_emailed = models.DateTimeField(default=None, null=True)
|
||||
exam_results_emailed = models.DateTimeField(default=None, null=True, blank=True)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def save(self, *args, recreate_json=True, **kwargs):
|
||||
self.recreate_json = recreate_json
|
||||
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
#def clean(self, *args, **kwargs):
|
||||
# if self.user_scores is None:
|
||||
# self.user_scores = {}
|
||||
# return super().clean(*args, **kwargs)
|
||||
|
||||
def get_questions(self):
|
||||
"""Returns a list of questions in the exam in the correct order"""
|
||||
@@ -900,6 +937,8 @@ class ExamBase(ExamOrCollectionGenericBase):
|
||||
|
||||
def get_time_limit(self):
|
||||
"""Returns a human readable time limit"""
|
||||
if self.time_limit is None:
|
||||
return "No time limit set"
|
||||
h, m, s = str(datetime.timedelta(seconds=self.time_limit)).split(":")
|
||||
|
||||
time_limit = ""
|
||||
|
||||
Reference in New Issue
Block a user