start adding exam dates
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
# Generated by Django 5.0.2 on 2024-05-13 10:41
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('anatomy', '0016_alter_exam_authors_only'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='exam',
|
||||
name='end_date',
|
||||
field=models.DateTimeField(help_text='Date (and time) the exam/collection ends', null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='exam',
|
||||
name='restrict_to_dates',
|
||||
field=models.BooleanField(default=False, help_text='If the exam/collection should only be taken between the start and end date times'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='exam',
|
||||
name='start_date',
|
||||
field=models.DateTimeField(help_text='Date (and time) the exam/collection starts', null=True),
|
||||
),
|
||||
]
|
||||
+1
-1
@@ -413,7 +413,7 @@ def exam_scores_cid_user(request, pk, cid, passcode):
|
||||
if not exam.exam_mode:
|
||||
raise Http404("Packet not in exam mode")
|
||||
|
||||
if not exam.check_cid_user(cid, passcode, request):
|
||||
if not exam.check_cid_user(cid, passcode, request.user):
|
||||
raise Http404("Error accessing exam")
|
||||
|
||||
questions = exam.exam_questions.all()
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
# Generated by Django 5.0.2 on 2024-05-13 10:41
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('atlas', '0050_rename_questions_casedetail_question_schema'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='casecollection',
|
||||
name='end_date',
|
||||
field=models.DateTimeField(help_text='Date (and time) the exam/collection ends', null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='casecollection',
|
||||
name='restrict_to_dates',
|
||||
field=models.BooleanField(default=False, help_text='If the exam/collection should only be taken between the start and end date times'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='casecollection',
|
||||
name='start_date',
|
||||
field=models.DateTimeField(help_text='Date (and time) the exam/collection starts', null=True),
|
||||
),
|
||||
]
|
||||
+2
-2
@@ -1736,7 +1736,7 @@ def collection_take_overview(
|
||||
"""
|
||||
collection = get_object_or_404(CaseCollection, pk=pk)
|
||||
|
||||
collection.check_user_can_take(cid, passcode, request)
|
||||
collection.check_user_can_take(cid, passcode, request.user)
|
||||
|
||||
cid_user_exam = collection.get_or_create_cid_user_exam(
|
||||
cid=cid, user_user=request.user
|
||||
@@ -1819,7 +1819,7 @@ def collection_case_view_take(
|
||||
form = None
|
||||
answer: None | CidReportAnswer | UserReportAnswer = None
|
||||
|
||||
collection.check_user_can_take(cid, passcode, request)
|
||||
collection.check_user_can_take(cid, passcode, request.user)
|
||||
|
||||
case, case_count = collection.get_case_by_index(case_number, case_count=True)
|
||||
|
||||
|
||||
+31
-3
@@ -8,6 +8,9 @@ from django.forms import (
|
||||
HiddenInput,
|
||||
EmailField,
|
||||
IntegerField,
|
||||
DateField,
|
||||
SplitDateTimeField,
|
||||
SplitDateTimeWidget,
|
||||
)
|
||||
from django.forms import inlineformset_factory
|
||||
from atlas.models import CaseCollection
|
||||
@@ -79,6 +82,24 @@ class ExamFormMixin:
|
||||
required=False,
|
||||
queryset=user_user_group_queryset,
|
||||
)
|
||||
self.fields["start_date"] = SplitDateTimeField(
|
||||
widget=SplitDateTimeWidget(
|
||||
date_attrs={"type": "date", "class": "datepicker"},
|
||||
time_attrs={"type": "time", "class": "timepicker"},
|
||||
date_format="%Y-%m-%d",
|
||||
time_format="%H:%M",
|
||||
),
|
||||
required=False,
|
||||
)
|
||||
self.fields["end_date"] = SplitDateTimeField(
|
||||
widget=SplitDateTimeWidget(
|
||||
date_attrs={"type": "date", "class": "datepicker"},
|
||||
time_attrs={"type": "time", "class": "timepicker"},
|
||||
date_format="%Y-%m-%d",
|
||||
time_format="%H:%M",
|
||||
),
|
||||
required=False,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
fields = [
|
||||
@@ -88,6 +109,9 @@ class ExamFormMixin:
|
||||
"authors_only",
|
||||
"exam_mode",
|
||||
"include_history",
|
||||
"start_date",
|
||||
"end_date",
|
||||
"restrict_to_dates",
|
||||
"active",
|
||||
"publish_results",
|
||||
"archive",
|
||||
@@ -96,6 +120,11 @@ class ExamFormMixin:
|
||||
# "author",
|
||||
]
|
||||
|
||||
# widgets = {
|
||||
# "start_date": TextInput(attrs={"type": "date"}),
|
||||
# "end_date": TextInput(attrs={"type": "date"}),
|
||||
# }
|
||||
|
||||
def save(self, commit=True):
|
||||
instance = ModelForm.save(self, False)
|
||||
instance.save()
|
||||
@@ -588,9 +617,7 @@ class ExamCollectionForm(ModelForm):
|
||||
self.fields[reverse_prop] = ModelMultipleChoiceField(
|
||||
required=False,
|
||||
queryset=exam_obj.objects.filter(archive=False),
|
||||
widget=FilteredSelectMultiple(
|
||||
verbose_name=name, is_stacked=False
|
||||
),
|
||||
widget=FilteredSelectMultiple(verbose_name=name, is_stacked=False),
|
||||
)
|
||||
|
||||
def save(self, commit=True):
|
||||
@@ -619,6 +646,7 @@ class ExamCollectionForm(ModelForm):
|
||||
|
||||
return instance
|
||||
|
||||
|
||||
class ExamCollectionCloneForm(ModelForm):
|
||||
class Meta:
|
||||
model = ExamCollection
|
||||
|
||||
+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 = ""
|
||||
|
||||
+6
-6
@@ -1487,7 +1487,7 @@ class ExamViews(View, LoginRequiredMixin):
|
||||
f"{exam.app_name=}, {exam.name=}, {cid=}, {passcode=}, {request.user=}"
|
||||
)
|
||||
if exam.exam_mode and not exam.check_cid_user(
|
||||
cid, passcode, request=request, user_id=request.user.pk
|
||||
cid, passcode, user=request.user, user_id=request.user.pk
|
||||
):
|
||||
print(exam.name, "fail")
|
||||
continue
|
||||
@@ -1736,7 +1736,7 @@ class ExamViews(View, LoginRequiredMixin):
|
||||
user_id = None
|
||||
else:
|
||||
user_id = request.user.pk
|
||||
if exam.exam_mode and not exam.check_cid_user(cid, passcode, request, user_id):
|
||||
if exam.exam_mode and not exam.check_cid_user(cid, passcode, request.user, user_id):
|
||||
raise Http404("No available exam")
|
||||
|
||||
# exam_json_cache = cache.get("{}_exam_json_{}".format(self.app_name, pk))
|
||||
@@ -1811,7 +1811,7 @@ class ExamViews(View, LoginRequiredMixin):
|
||||
user_id = None
|
||||
else:
|
||||
user_id = request.user.pk
|
||||
if not exam.check_cid_user(cid, passcode, request, user_id):
|
||||
if not exam.check_cid_user(cid, passcode, request.user, user_id):
|
||||
raise Http404("No available exam")
|
||||
|
||||
time = timezone.now()
|
||||
@@ -1850,7 +1850,7 @@ class ExamViews(View, LoginRequiredMixin):
|
||||
user_id = None
|
||||
else:
|
||||
user_id = request.user.pk
|
||||
if not exam.check_cid_user(cid, passcode, request, user_id):
|
||||
if not exam.check_cid_user(cid, passcode, request.user, user_id):
|
||||
raise Http404("No available exam")
|
||||
|
||||
if request.GET.get("_"):
|
||||
@@ -1883,7 +1883,7 @@ class ExamViews(View, LoginRequiredMixin):
|
||||
|
||||
print("CID", cid)
|
||||
|
||||
if not exam.check_cid_user(cid, passcode, request, user_id):
|
||||
if not exam.check_cid_user(cid, passcode, request.user, user_id):
|
||||
raise Http404("No available exam")
|
||||
|
||||
return redirect("{}:question_json_unbased".format(self.app_name), pk=sk)
|
||||
@@ -1921,7 +1921,7 @@ class ExamViews(View, LoginRequiredMixin):
|
||||
if not exam.exam_mode:
|
||||
raise Http404("Packet not in exam mode")
|
||||
|
||||
if not exam.check_cid_user(cid, passcode, request):
|
||||
if not exam.check_cid_user(cid, passcode, request.user):
|
||||
raise Http404("Error accessing exam")
|
||||
|
||||
if user is not None:
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
# Generated by Django 5.0.2 on 2024-05-13 10:41
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('longs', '0022_alter_exam_authors_only'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='exam',
|
||||
name='end_date',
|
||||
field=models.DateTimeField(help_text='Date (and time) the exam/collection ends', null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='exam',
|
||||
name='restrict_to_dates',
|
||||
field=models.BooleanField(default=False, help_text='If the exam/collection should only be taken between the start and end date times'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='exam',
|
||||
name='start_date',
|
||||
field=models.DateTimeField(help_text='Date (and time) the exam/collection starts', null=True),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,28 @@
|
||||
# Generated by Django 5.0.2 on 2024-05-13 10:41
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('physics', '0009_alter_exam_authors_only'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='exam',
|
||||
name='end_date',
|
||||
field=models.DateTimeField(help_text='Date (and time) the exam/collection ends', null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='exam',
|
||||
name='restrict_to_dates',
|
||||
field=models.BooleanField(default=False, help_text='If the exam/collection should only be taken between the start and end date times'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='exam',
|
||||
name='start_date',
|
||||
field=models.DateTimeField(help_text='Date (and time) the exam/collection starts', null=True),
|
||||
),
|
||||
]
|
||||
+2
-2
@@ -139,7 +139,7 @@ def exam_start(request, pk):
|
||||
def exam_take_overview(request, pk, cid=None, passcode=None):
|
||||
exam = get_object_or_404(Exam, pk=pk)
|
||||
|
||||
exam.check_user_can_take(cid, passcode, request)
|
||||
exam.check_user_can_take(cid, passcode, request.user)
|
||||
|
||||
questions = exam.exam_questions.all()
|
||||
|
||||
@@ -185,7 +185,7 @@ def exam_take(request, pk: int, sk: int, cid: str| None = None, passcode: str| N
|
||||
if not exam.active:
|
||||
return exam_inactive(request, context={"exam": exam})
|
||||
|
||||
exam.check_user_can_take(cid, passcode, request)
|
||||
exam.check_user_can_take(cid, passcode, request.user)
|
||||
|
||||
cid_user_exam = exam.get_or_create_cid_user_exam(cid=cid, user_user=request.user)
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
# Generated by Django 5.0.2 on 2024-05-13 10:41
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('rapids', '0008_alter_exam_authors_only'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='exam',
|
||||
name='end_date',
|
||||
field=models.DateTimeField(help_text='Date (and time) the exam/collection ends', null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='exam',
|
||||
name='restrict_to_dates',
|
||||
field=models.BooleanField(default=False, help_text='If the exam/collection should only be taken between the start and end date times'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='exam',
|
||||
name='start_date',
|
||||
field=models.DateTimeField(help_text='Date (and time) the exam/collection starts', null=True),
|
||||
),
|
||||
]
|
||||
@@ -1,7 +1,12 @@
|
||||
import json
|
||||
import os
|
||||
from re import A
|
||||
import time
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.http import Http404
|
||||
from django.utils import timezone
|
||||
|
||||
import pytest
|
||||
|
||||
import tempfile
|
||||
@@ -508,3 +513,53 @@ def test_exams(db, client):
|
||||
|
||||
generated_questions.pop(-1).delete()
|
||||
users_tested = users_tested + 1
|
||||
|
||||
|
||||
def test_exams_with_dates(db, client):
|
||||
create_cid_user_and_groups(db)
|
||||
exam = create_exam(db)
|
||||
|
||||
|
||||
active_exams = client.get(reverse(f"{APP_NAME}:active_exams"))
|
||||
assert active_exams.status_code == 200
|
||||
assert len(json.loads(active_exams.content)["exams"]) == 0
|
||||
|
||||
cid_user_1000 = CidUser.objects.get(cid=1000)
|
||||
user1 = User.objects.get(username="user1")
|
||||
exam.check_user_can_take(None, None, user1)
|
||||
exam.check_user_can_take(cid_user_1000.cid, cid_user_1000.passcode)
|
||||
|
||||
exam.active = False
|
||||
exam.save()
|
||||
|
||||
with pytest.raises(Http404):
|
||||
exam.check_user_can_take(None, None, user1)
|
||||
|
||||
with pytest.raises(Http404):
|
||||
exam.check_user_can_take(cid_user_1000.cid, cid_user_1000.passcode)
|
||||
|
||||
with pytest.raises(ValidationError):
|
||||
exam.restrict_to_dates = True
|
||||
exam.save()
|
||||
with pytest.raises(ValidationError):
|
||||
exam.restrict_to_dates = True
|
||||
exam.end_date = timezone.now()
|
||||
exam.start_date = timezone.now() + timezone.timedelta(days=1)
|
||||
exam.save()
|
||||
|
||||
exam.start_date = timezone.now()
|
||||
exam.end_date = None # The previously set value seems to remain...
|
||||
|
||||
exam.restrict_to_dates = True
|
||||
exam.save()
|
||||
exam.check_user_can_take(None, None, user1)
|
||||
exam.check_user_can_take(cid_user_1000.cid, cid_user_1000.passcode)
|
||||
|
||||
exam.end_date = timezone.now()
|
||||
exam.save()
|
||||
|
||||
with pytest.raises(Http404):
|
||||
exam.check_user_can_take(None, None, user1)
|
||||
|
||||
with pytest.raises(Http404):
|
||||
exam.check_user_can_take(cid_user_1000.cid, cid_user_1000.passcode)
|
||||
@@ -0,0 +1,28 @@
|
||||
# Generated by Django 5.0.2 on 2024-05-13 10:41
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('sbas', '0009_alter_exam_authors_only'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='exam',
|
||||
name='end_date',
|
||||
field=models.DateTimeField(help_text='Date (and time) the exam/collection ends', null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='exam',
|
||||
name='restrict_to_dates',
|
||||
field=models.BooleanField(default=False, help_text='If the exam/collection should only be taken between the start and end date times'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='exam',
|
||||
name='start_date',
|
||||
field=models.DateTimeField(help_text='Date (and time) the exam/collection starts', null=True),
|
||||
),
|
||||
]
|
||||
+2
-2
@@ -119,7 +119,7 @@ def exam_take_overview(request, pk, cid=None, passcode=None):
|
||||
if not exam.active:
|
||||
return exam_inactive(request, context={"exam": exam})
|
||||
|
||||
exam.check_user_can_take(cid, passcode, request)
|
||||
exam.check_user_can_take(cid, passcode, request.user)
|
||||
|
||||
questions = exam.exam_questions.all()
|
||||
|
||||
@@ -164,7 +164,7 @@ def exam_take(request, pk: int, sk: int, cid: int = None, passcode: str = None):
|
||||
if not exam.active:
|
||||
return exam_inactive(request, context={"exam": exam})
|
||||
|
||||
exam.check_user_can_take(cid, passcode, request)
|
||||
exam.check_user_can_take(cid, passcode, request.user)
|
||||
|
||||
cid_user_exam = exam.get_or_create_cid_user_exam(cid = cid, user_user=request.user)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user