diff --git a/physics/templates/physics/exam_start.html b/physics/templates/physics/exam_start.html
index e113b5cc..96be8048 100755
--- a/physics/templates/physics/exam_start.html
+++ b/physics/templates/physics/exam_start.html
@@ -4,7 +4,7 @@
Start exam: {{exam.name}}
- {% if request.user.is_authenticated %}
+ {% if request.user.is_authenticated and valid_user %}
Start Exam
{% else %}
Enter your CID and passcode in the below boxes.
diff --git a/physics/tests/test_physics_exams.py b/physics/tests/test_physics_exams.py
index d0dcc2c1..dacd3b99 100644
--- a/physics/tests/test_physics_exams.py
+++ b/physics/tests/test_physics_exams.py
@@ -27,10 +27,6 @@ from django.contrib.auth.models import User
APP_NAME = "physics"
-
-
-
-
def create_exam(db):
exam: Exam = PhysicsExamViews.Exam.objects.create(
name="Cid Exam", exam_mode=True, active=True
@@ -43,7 +39,7 @@ def create_exam(db):
exam.valid_cid_users.add(*group1.ciduser_set.all())
- #exam.valid_user_users.add(user1)
+ # exam.valid_user_users.add(user1)
exam.valid_user_users.add(*usergroup1.users.all())
assert exam in group1.physics_cid_user_groups.all()
@@ -105,7 +101,11 @@ def test_exams(db, client):
create_category(db)
- generated_questions = [create_question(exam), create_question(exam), create_question(exam)]
+ generated_questions = [
+ create_question(exam),
+ create_question(exam),
+ create_question(exam),
+ ]
active_exams = client.get(reverse(f"{APP_NAME}:active_exams"))
assert active_exams.status_code == 200
@@ -116,12 +116,12 @@ def test_exams(db, client):
cid_user_2001 = CidUser.objects.get(cid=2001)
user1 = User.objects.get(username="user1")
-
+ user2 = User.objects.get(username="user2")
users_tested = 1
- for cid_user in [cid_user_1001, cid_user_1000, user1]:
-
+ # for cid_user in [cid_user_1001, cid_user_1000, user1, user2]:
+ for cid_user in [cid_user_1001, cid_user_1000, user1, user2]:
if isinstance(cid_user, CidUser):
testing_cid_user = True
else:
@@ -129,7 +129,6 @@ def test_exams(db, client):
current_user = cid_user
client.force_login(cid_user)
-
exam.active = True
exam.publish_results = False
exam.save()
@@ -137,10 +136,15 @@ def test_exams(db, client):
reverse(f"physics:exam_start", kwargs={"pk": exam.pk})
)
+ print(cid_user)
+
+ # Check user access to the start page
+ assert start_page_res.status_code == HTTPStatus.OK
start_page_soup = BeautifulSoup(start_page_res.content, "html.parser")
- if testing_cid_user:
+ print(start_page_soup)
+ if testing_cid_user or cid_user in [user2]:
assert "Enter your CID and passcode" in str(start_page_soup)
- else:
+ else:
assert "Start Exam" in str(start_page_soup)
for n, question in enumerate(generated_questions):
@@ -163,10 +167,15 @@ def test_exams(db, client):
},
)
+ cid_take_res = client.get(take_url)
+
+ # Check that users that have not been added cannot access the take pages
+ if cid_user in [user2]:
+ assert cid_take_res.status_code == HTTPStatus.NOT_FOUND
+ continue
+ else:
+ assert cid_take_res.status_code == HTTPStatus.OK
- cid_take_res = client.get(
- take_url
- )
cid_take_soup = BeautifulSoup(cid_take_res.content, "html.parser")
# Check correct user is displayed
@@ -174,10 +183,7 @@ def test_exams(db, client):
display_user = str(cid_user.cid)
else:
display_user = current_user.username
- assert (
- display_user
- in cid_take_soup.find("span", {"id": "user-id"}).string
- )
+ assert display_user in cid_take_soup.find("span", {"id": "user-id"}).string
# Check review mode alert not visible
assert cid_take_soup.find("div", {"class": "review-mode-alert"}) is None
@@ -216,7 +222,6 @@ def test_exams(db, client):
is None
)
-
if testing_cid_user:
take_overview_url = reverse(
f"physics:exam_take_overview",
@@ -234,27 +239,32 @@ def test_exams(db, client):
},
)
-
+ cid_take_overview_res = client.get(take_overview_url)
- cid_take_overview_res = client.get(
- take_overview_url
- )
- cid_take_overview_soup = BeautifulSoup(
- cid_take_overview_res.content, "html.parser"
- )
- assert (
- "0 out of 3 questions answered"
- in cid_take_overview_soup.find(
- "div", attrs={"class": "overview-text"}
- ).string
- )
+ # Check that users that have not been added cannot access the take pages
+ if cid_user in [user2]:
+ assert cid_take_overview_res.status_code == HTTPStatus.NOT_FOUND
+ else:
+ assert cid_take_overview_res.status_code == HTTPStatus.OK
- assert (
- len(
- cid_take_overview_soup.find_all("button", attrs={"class": "unanswered"})
+ cid_take_overview_soup = BeautifulSoup(
+ cid_take_overview_res.content, "html.parser"
+ )
+ assert (
+ "0 out of 3 questions answered"
+ in cid_take_overview_soup.find(
+ "div", attrs={"class": "overview-text"}
+ ).string
+ )
+
+ assert (
+ len(
+ cid_take_overview_soup.find_all(
+ "button", attrs={"class": "unanswered"}
+ )
+ )
+ == 3
)
- == 3
- )
# Loop through questions again to test submissions
# All physics questions have 5 parts (and require a csrf token)
@@ -280,6 +290,13 @@ def test_exams(db, client):
)
cid_take_res = client.get(question_url)
+
+ if cid_user in [user2]:
+ assert cid_take_res.status_code == HTTPStatus.NOT_FOUND
+ continue
+ else:
+ assert cid_take_res.status_code == HTTPStatus.OK
+
cid_take_soup = BeautifulSoup(cid_take_res.content, "html.parser")
# Extract csrftoken
@@ -331,7 +348,6 @@ def test_exams(db, client):
assert all(cid_user_answer.get_answers())
-
# Recheck the overview page (now that we have submitted answers)
if testing_cid_user:
take_overview_url = reverse(
@@ -350,86 +366,119 @@ def test_exams(db, client):
},
)
- cid_take_overview_res = client.get(
- take_overview_url
- )
- cid_take_overview_soup = BeautifulSoup(
- cid_take_overview_res.content, "html.parser"
- )
- assert (
- "3 out of 3 questions answered"
- in cid_take_overview_soup.find(
- "div", attrs={"class": "overview-text"}
- ).string
- )
+ cid_take_overview_res = client.get(take_overview_url)
- assert (
- len(
- cid_take_overview_soup.find_all("button", attrs={"class": "unanswered"})
- )
- == 0
- )
-
- if testing_cid_user:
- # Get overview scores page (invalid passcode)
- AssertNotFound(client,
- reverse(f"cid_scores", kwargs={"cid": cid_user.cid, "passcode": "AAAA"})
- )
-
- cid_scores_url = reverse(f"cid_scores", kwargs={"cid": cid_user.cid, "passcode": cid_user.passcode})
+ if cid_user in [user2]:
+ assert cid_take_overview_res.status_code == HTTPStatus.NOT_FOUND
else:
- cid_scores_url = reverse(f"user_scores")
+ assert cid_take_overview_res.status_code == HTTPStatus.OK
+
+ cid_take_overview_soup = BeautifulSoup(
+ cid_take_overview_res.content, "html.parser"
+ )
+ assert (
+ "3 out of 3 questions answered"
+ in cid_take_overview_soup.find(
+ "div", attrs={"class": "overview-text"}
+ ).string
+ )
+
+ assert (
+ len(
+ cid_take_overview_soup.find_all(
+ "button", attrs={"class": "unanswered"}
+ )
+ )
+ == 0
+ )
+
+ if testing_cid_user:
+ # Get overview scores page (invalid passcode)
+ AssertNotFound(
+ client,
+ reverse(
+ f"cid_scores", kwargs={"cid": cid_user.cid, "passcode": "AAAA"}
+ ),
+ )
+
+ cid_scores_url = reverse(
+ f"cid_scores",
+ kwargs={"cid": cid_user.cid, "passcode": cid_user.passcode},
+ )
+ else:
+ cid_scores_url = reverse(f"user_scores")
# Get overview scores page
- cid_scores_res = client.get(
- cid_scores_url
- )
+ cid_scores_res = client.get(cid_scores_url)
+
+ assert cid_scores_res.status_code == HTTPStatus.OK
cid_scores_soup = BeautifulSoup(cid_scores_res.content, "html.parser")
- search_exam = (
- cid_scores_soup.find("div", {"id": "exam-assigned"})
- .find("ul", {"class": exam.app_name.lower()})
- .find_all("li", attrs={"data-exam-id": exam.pk})
- )
- assert search_exam
- assert exam.name in str(search_exam)
- assert "Active" in str(search_exam)
- # assert "Active" in assigned_exams # check it is active
+ if cid_user in [user2]:
+ # page should load but we shouldn't be able to find the exam
+ with pytest.raises(AttributeError):
+ search_exam = (
+ cid_scores_soup.find("div", {"id": "exam-assigned"})
+ .find("ul", {"class": exam.app_name.lower()})
+ .find_all("li", attrs={"data-exam-id": exam.pk})
+ )
+ else:
+ search_exam = (
+ cid_scores_soup.find("div", {"id": "exam-assigned"})
+ .find("ul", {"class": exam.app_name.lower()})
+ .find_all("li", attrs={"data-exam-id": exam.pk})
+ )
+ assert search_exam
+ assert exam.name in str(search_exam)
+ assert "Active" in str(search_exam)
+ # assert "Active" in assigned_exams # check it is active
- invalid_exam = cid_scores_soup.find_all(
- "li", attrs={"data-exam-id": exam.pk + 5}
- )
- assert not invalid_exam # Check we can't find an invalid exam
-
- # Check that we have a results link
- results_exam = (
- cid_scores_soup.find("div", {"id": "exam-results"})
- .find("ul", {"class": exam.app_name})
- .find_all("li", attrs={"data-exam-id": exam.pk})
- )
- assert results_exam
- assert exam.name in str(results_exam)
- assert exam.name + " test" not in str(results_exam)
- assert "Results Published" not in str(
- results_exam
- ) # It should not be published yet
+ invalid_exam = cid_scores_soup.find_all(
+ "li", attrs={"data-exam-id": exam.pk + 5}
+ )
+ assert not invalid_exam # Check we can't find an invalid exam
+ # Check that we have a results link
+ results_exam = (
+ cid_scores_soup.find("div", {"id": "exam-results"})
+ .find("ul", {"class": exam.app_name})
+ .find_all("li", attrs={"data-exam-id": exam.pk})
+ )
+ assert results_exam
+ assert exam.name in str(results_exam)
+ assert exam.name + " test" not in str(results_exam)
+ assert "Results Published" not in str(
+ results_exam
+ ) # It should not be published yet
if testing_cid_user:
- AssertNotFound(client,
+ AssertNotFound(
+ client,
reverse(
f"{exam.app_name}:exam_scores_cid_user",
kwargs={"pk": exam.pk, "cid": cid_user.cid, "passcode": "AAAA"},
- )
+ ),
)
- AssertNotFound(client,
- reverse(f"{exam.app_name}:exam_scores_cid_user", kwargs={"pk": exam.pk, "cid": cid_user_2001.cid, "passcode": cid_user_2001.passcode})
+ AssertNotFound(
+ client,
+ reverse(
+ f"{exam.app_name}:exam_scores_cid_user",
+ kwargs={
+ "pk": exam.pk,
+ "cid": cid_user_2001.cid,
+ "passcode": cid_user_2001.passcode,
+ },
+ ),
)
exam_scores_url = reverse(
f"{exam.app_name}:exam_scores_cid_user",
- kwargs={"pk": exam.pk, "cid": cid_user.cid, "passcode": cid_user.passcode},
+ kwargs={
+ "pk": exam.pk,
+ "cid": cid_user.cid,
+ "passcode": cid_user.passcode,
+ },
)
else:
exam_scores_url = reverse(
@@ -437,11 +486,15 @@ def test_exams(db, client):
kwargs={"pk": exam.pk},
)
-
# Load the exam results page
- cid_exam_scores_res = client.get(
- exam_scores_url
- )
+ cid_exam_scores_res = client.get(exam_scores_url)
+
+ if cid_user in [user2]:
+ assert cid_exam_scores_res.status_code == HTTPStatus.NOT_FOUND
+ continue
+ else:
+ assert cid_exam_scores_res.status_code == HTTPStatus.OK
+
cid_exam_scores_soup = BeautifulSoup(cid_exam_scores_res.content, "html.parser")
# Check that we have an alert that exams are not published
@@ -468,9 +521,7 @@ def test_exams(db, client):
generated_questions.append(extra_question)
# Load the exam results page (again)
- cid_exam_scores_res = client.get(
- exam_scores_url
- )
+ cid_exam_scores_res = client.get(exam_scores_url)
cid_exam_scores_soup = BeautifulSoup(cid_exam_scores_res.content, "html.parser")
answer_lis = cid_exam_scores_soup.find_all("li", {"class": "question-part"})
@@ -493,9 +544,7 @@ def test_exams(db, client):
exam.save()
# Load the exam results page (again)
- cid_exam_scores_res = client.get(
- exam_scores_url
- )
+ cid_exam_scores_res = client.get(exam_scores_url)
cid_exam_scores_soup = BeautifulSoup(cid_exam_scores_res.content, "html.parser")
assert (
@@ -541,14 +590,30 @@ def test_exams(db, client):
# answer the final question
if testing_cid_user:
- cid_user_answer = UserAnswer(question=extra_question, a=True, b=False, c=True, d=False, e=True, cid=cid_user.cid, exam=exam)
+ cid_user_answer = UserAnswer(
+ question=extra_question,
+ a=True,
+ b=False,
+ c=True,
+ d=False,
+ e=True,
+ cid=cid_user.cid,
+ exam=exam,
+ )
else:
- cid_user_answer = UserAnswer(question=extra_question, a=True, b=False, c=True, d=False, e=True, user=current_user, exam=exam)
+ cid_user_answer = UserAnswer(
+ question=extra_question,
+ a=True,
+ b=False,
+ c=True,
+ d=False,
+ e=True,
+ user=current_user,
+ exam=exam,
+ )
cid_user_answer.save()
- cid_exam_scores_res = client.get(
- exam_scores_url
- )
+ cid_exam_scores_res = client.get(exam_scores_url)
cid_exam_scores_soup = BeautifulSoup(cid_exam_scores_res.content, "html.parser")
# As we have answered correctly the new score should be increased by 5
@@ -559,7 +624,6 @@ def test_exams(db, client):
== "14"
)
-
# Check what happens when exam is not active
exam.active = False
exam.save()
@@ -589,11 +653,9 @@ def test_exams(db, client):
"sk": 0,
},
)
- cid_take_res = client.get(
- cid_take_url
- )
+ cid_take_res = client.get(cid_take_url)
cid_take_soup = BeautifulSoup(cid_take_res.content, "html.parser")
assert "Exam is currently inactive" in cid_take_soup.text
generated_questions.pop(-1).delete()
- users_tested = users_tested + 1
\ No newline at end of file
+ users_tested = users_tested + 1
diff --git a/physics/urls.py b/physics/urls.py
index 37129187..3a11e6ef 100644
--- a/physics/urls.py
+++ b/physics/urls.py
@@ -9,13 +9,8 @@ urlpatterns = []
urlpatterns.extend(
[
- # path("question/", views.QuestionView.as_view(), name="question_view"),
path("question/", views.QuestionView.as_view(), name="question_view"),
path("question//", views.question_detail, name="question_detail"),
- # path("question/create/", views.QuestionCreate.as_view(), name="anatomy_question_create"),
- # path("question//update", views.QuestionUpdate.as_view(), name="anatomy_question_update"),
- # path("exam///mark", views.mark, name="mark"),
- # path("exam//mark", views.mark_overview, name="mark_overview"),
path("exam//take_old", views.exam_take_old, name="exam_take_old"),
path(
"exam/////take",
@@ -64,6 +59,7 @@ urlpatterns.extend(
views.UserAnswerDelete.as_view(),
name="user_answer_delete",
),
+ # TODO: consider merging with generic...
path(
"exam//scores///",
views.exam_scores_cid_user,
diff --git a/physics/views.py b/physics/views.py
index 13d64ac4..a2bef598 100644
--- a/physics/views.py
+++ b/physics/views.py
@@ -99,13 +99,15 @@ def active_exams(request):
active_exams.append(exam)
return render(request, "physics/available_exam_list.html", {"exams": active_exams})
+
def exam_scores_cid_user(request, pk, cid=None, passcode=None):
exam = get_object_or_404(Exam, pk=pk)
if not exam.exam_mode:
raise Http404("Packet not in exam mode")
- if cid is not None and not exam.check_cid_user(cid, passcode, request):
+ print(cid, passcode, request.user)
+ if not exam.check_cid_user(cid, passcode, request):
raise Http404("Error accessing exam")
questions = exam.exam_questions.all()
@@ -168,7 +170,6 @@ def exam_scores_cid_user(request, pk, cid=None, passcode=None):
def exam_take_old(request, pk):
-
exam = get_object_or_404(Exam, pk=pk)
if not exam.active:
@@ -197,6 +198,9 @@ def exam_start(request, pk):
"physics/exam_start.html",
{
"exam": exam,
+ # This may be overly comlicated but if the logged in user
+ # does not have access we give the option to take it as a cid user
+ "valid_user": exam.check_logged_in_user(request),
},
)
@@ -204,12 +208,11 @@ def exam_start(request, pk):
def exam_take_overview(request, pk, cid=None, passcode=None):
exam = get_object_or_404(Exam, pk=pk)
- if cid is not None and not exam.check_cid_user(cid, passcode):
- raise Http404("Error accessing exam")
-
if not exam.active:
return exam_inactive(request, context={"exam": exam})
+ if not exam.check_cid_user(cid, passcode, request):
+ raise Http404("Error accessing exam")
questions = exam.exam_questions.all()
@@ -253,14 +256,13 @@ def exam_take_overview(request, pk, cid=None, passcode=None):
)
-def exam_take(request, pk, sk, cid=None, passcode=None):
-
+def exam_take(request, pk: int, sk: int, cid: str = None, passcode: str = None):
exam = get_object_or_404(Exam, pk=pk)
if not exam.active:
return exam_inactive(request, context={"exam": exam})
- if cid is not None and not exam.check_cid_user(cid, passcode):
+ if not exam.check_cid_user(cid, passcode, request):
raise Http404("Error accessing exam")
if cid is not None:
@@ -329,7 +331,6 @@ def exam_take(request, pk, sk, cid=None, passcode=None):
else:
form = UserAnswerForm(instance=answer)
-
saved_answer = False
if answer is not None:
saved_answer = [answer.a, answer.b, answer.c, answer.d, answer.e]
@@ -346,6 +347,7 @@ def exam_take(request, pk, sk, cid=None, passcode=None):
"previous": previous,
"exam_length": exam_length,
"pos": pos,
+ "saved_answer": saved_answer,
"passcode": passcode,
"cid_user_exam": cid_user_exam,
},
@@ -413,6 +415,7 @@ GenericExamViews = ExamViews(Exam, Question, None, UserAnswer, "physics", "physi
GenericViews = GenericViewBase("physics", Question, UserAnswer, Exam)
+
class ExamCreate(ExamCreateBase):
model = Exam
form_class = ExamForm
@@ -427,7 +430,6 @@ class ExamDelete(AuthorOrCheckerRequiredMixin, ExamDeleteBase):
model = Exam
-
class ExamClone(AuthorOrCheckerRequiredMixin, ExamCloneMixin, ExamCreate):
"""Clone exam view"""
@@ -439,8 +441,6 @@ class ExamAuthorUpdate(
form_class = ExamAuthorForm
-
-
class QuestionView(
LoginRequiredMixin, SingleTableMixin, FilterView, AuthorOrCheckerRequiredMixin
):
@@ -456,7 +456,6 @@ class QuestionView(
return context
-
class UserAnswerView(AuthorOrCheckerRequiredMixin, LoginRequiredMixin, DetailView):
model = UserAnswer
diff --git a/rad/api.py b/rad/api.py
index e2c3d2f6..ef2dc9fa 100644
--- a/rad/api.py
+++ b/rad/api.py
@@ -3,6 +3,8 @@ from ninja import NinjaAPI
from rapids.api import router as rapids_router
from longs.api import router as longs_router
from anatomy.api import router as anatomy_router
+from sbas.api import router as sbas_router
+from physics.api import router as physics_router
api = NinjaAPI(csrf=True, version="1")
@@ -11,6 +13,8 @@ api = NinjaAPI(csrf=True, version="1")
api.add_router("rapid/", rapids_router)
api.add_router("anatomy/", anatomy_router)
api.add_router("longs/", longs_router)
+api.add_router("sbas/", sbas_router)
+api.add_router("physics/", physics_router)
@api.get("/hello")
def hello(request):
diff --git a/rad/test_helpers.py b/rad/test_helpers.py
index dce6a7a7..ccb29c69 100644
--- a/rad/test_helpers.py
+++ b/rad/test_helpers.py
@@ -22,6 +22,8 @@ def create_cid_user_and_groups(db):
# Create UserUsers
user1 = User.objects.create_user(username="user1", email="test@email.com", password="test1234")
+ user2 = User.objects.create_user(username="user2", email="test2@email.com", password="test1234")
usergroup1 = UserUserGroup.objects.create(name="Group1")
usergroup1.users.add(user1)
- usergroup2 = UserUserGroup.objects.create(name="Group2")
\ No newline at end of file
+ usergroup2 = UserUserGroup.objects.create(name="Group2")
+ usergroup2.users.add(user2)
\ No newline at end of file
diff --git a/sbas/templates/sbas/exam_start.html b/sbas/templates/sbas/exam_start.html
index 45cb8f5c..634140cc 100755
--- a/sbas/templates/sbas/exam_start.html
+++ b/sbas/templates/sbas/exam_start.html
@@ -4,6 +4,9 @@
Start exam: {{exam.name}}
+ {% if request.user.is_authenticated and valid_user %}
+ Start Exam
+ {% else %}
Enter your CID and passcode in the below boxes.
@@ -44,4 +47,5 @@
});
});
+ {% endif %}
{% endblock %}
\ No newline at end of file
diff --git a/sbas/urls.py b/sbas/urls.py
index 2fb30b1b..3d446317 100644
--- a/sbas/urls.py
+++ b/sbas/urls.py
@@ -32,6 +32,11 @@ urlpatterns = [
views.exam_take,
name="exam_take",
),
+ path(
+ "exam///take",
+ views.exam_take,
+ name="exam_take_user",
+ ),
path("exam//start", views.exam_start, name="exam_start"),
path(
"exam////finish",
diff --git a/sbas/views.py b/sbas/views.py
index 2a5cce07..cd914eab 100644
--- a/sbas/views.py
+++ b/sbas/views.py
@@ -24,7 +24,7 @@ from django.core.cache import cache
from django.urls import reverse_lazy, reverse
-from django.http import Http404, JsonResponse
+from django.http import Http404, HttpResponseBadRequest, JsonResponse
from django.http import HttpResponseRedirect, HttpResponse
@@ -41,7 +41,16 @@ import json
import statistics
import plotly.express as px
-from generic.views import AuthorRequiredMixin, ExamCloneMixin, ExamCreateBase, ExamDeleteBase, ExamUpdateBase, ExamViews, GenericViewBase, exam_inactive
+from generic.views import (
+ AuthorRequiredMixin,
+ ExamCloneMixin,
+ ExamCreateBase,
+ ExamDeleteBase,
+ ExamUpdateBase,
+ ExamViews,
+ GenericViewBase,
+ exam_inactive,
+)
from rest_framework import viewsets, permissions
from rest_framework.pagination import PageNumberPagination
@@ -156,29 +165,29 @@ def exam_start(request, pk):
if not exam.active:
return exam_inactive(request, context={"exam": exam})
- raise Http404("Exam not found")
return render(
request,
"sbas/exam_start.html",
- {
- "exam": exam,
- },
+ {"exam": exam, "valid_user": exam.check_logged_in_user(request)},
)
-def exam_take_overview(request, pk, cid, passcode):
+def exam_take_overview(request, pk, cid=None, passcode=None):
exam = get_object_or_404(Exam, pk=pk)
if not exam.active:
return exam_inactive(request, context={"exam": exam})
- if not exam.check_cid_user(cid, passcode):
+ if not exam.check_cid_user(cid, passcode, request):
raise Http404("Error accessing exam")
questions = exam.exam_questions.all()
- answers = UserAnswer.objects.filter(cid=cid, exam=exam)
+ if cid is not None:
+ answers = UserAnswer.objects.filter(cid=cid, exam=exam)
+ else:
+ answers = UserAnswer.objects.filter(user=request.user, exam=exam)
answer_question_map = {}
for ans in answers:
@@ -193,8 +202,11 @@ def exam_take_overview(request, pk, cid, passcode):
else:
question_answer_tuples.append((q, None))
- c = CidUser.objects.filter(cid=cid).first()
- cid_user_exam = exam.get_or_create_cid_user_exam(cid_user=c)
+ if cid is not None:
+ c = CidUser.objects.filter(cid=cid).first()
+ cid_user_exam = exam.get_or_create_cid_user_exam(cid_user=c)
+ else:
+ cid_user_exam = exam.get_or_create_cid_user_exam(user_user=request.user)
return render(
request,
@@ -211,18 +223,20 @@ def exam_take_overview(request, pk, cid, passcode):
)
-def exam_take(request, pk, sk, cid, passcode):
-
+def exam_take(request, pk: int, sk: int, cid: str = None, passcode: str = None):
exam = get_object_or_404(Exam, pk=pk)
if not exam.active:
raise Http404("Exam not found")
- if not exam.check_cid_user(cid, passcode):
+ if not exam.check_cid_user(cid, passcode, request):
raise Http404("Error accessing exam")
- c = CidUser.objects.filter(cid=cid).first()
- cid_user_exam = exam.get_or_create_cid_user_exam(cid_user=c)
+ if cid is not None:
+ c = CidUser.objects.filter(cid=cid).first()
+ cid_user_exam = exam.get_or_create_cid_user_exam(cid_user=c)
+ else:
+ cid_user_exam = exam.get_or_create_cid_user_exam(user_user=request.user)
question = exam.exam_questions.all()[sk]
@@ -230,7 +244,17 @@ def exam_take(request, pk, sk, cid, passcode):
pos = exam.get_question_index(question)
- answer = question.cid_user_answers.filter(cid=cid, exam=exam).first()
+ if cid is not None:
+ answer = question.cid_user_answers.filter(cid=cid, exam=exam).first()
+ else:
+ answer = question.cid_user_answers.filter(user=request.user, exam=exam).first()
+
+ previous = -1
+ if sk > 0:
+ previous = sk - 1
+ next = sk + 1
+ if sk == exam_length - 1:
+ next = False
if request.method == "POST":
if answer:
@@ -239,7 +263,11 @@ def exam_take(request, pk, sk, cid, passcode):
form = UserAnswerForm(request.POST)
if form.is_valid():
answer = form.save(commit=False)
- answer.cid = cid
+
+ if cid is not None:
+ answer.cid = cid
+ else:
+ answer.user = request.user
answer.question = question
answer.exam = exam
# answer.published_date = timezone.now()
@@ -248,28 +276,28 @@ def exam_take(request, pk, sk, cid, passcode):
cid_user_exam.end_time = timezone.now()
cid_user_exam.save()
- kwargs = {"pk": pk, "cid": cid, "passcode": passcode}
+ if cid is not None:
+ kwargs = {"pk": pk, "cid": cid, "passcode": passcode}
+ take_url = "sbas:exam_take"
+ finish_url = "sbas:exam_take_overview"
+ else:
+ kwargs = {"pk": pk}
+ take_url = "sbas:exam_take_user"
+ finish_url = "sbas:exam_take_overview_user"
if "next" in request.POST:
- return redirect("sbas:exam_take", sk=pos + 1, **kwargs)
+ if not next:
+ return HttpResponseBadRequest("Invalid request")
+ return redirect(take_url, sk=pos + 1, **kwargs)
elif "previous" in request.POST:
- return redirect("sbas:exam_take", sk=pos - 1, **kwargs)
+ return redirect(take_url, sk=pos - 1, **kwargs)
elif "finish" in request.POST:
- return redirect("sbas:exam_take_overview", **kwargs)
+ return redirect(finish_url, **kwargs)
elif "goto" in request.POST:
- return redirect(
- "sbas:exam_take", sk=int(request.POST.get("goto")), **kwargs
- )
+ return redirect(take_url, sk=int(request.POST.get("goto")), **kwargs)
else:
form = UserAnswerForm(instance=answer)
- previous = -1
- if sk > 0:
- previous = sk - 1
- next = sk + 1
- if sk == exam_length - 1:
- next = False
-
saved_answer = False
if answer is not None:
saved_answer = answer.answer
@@ -293,9 +321,7 @@ def exam_take(request, pk, sk, cid, passcode):
)
-GenericExamViews = ExamViews(
- Exam, Question, None, UserAnswer, "sbas", "sbas"
-)
+GenericExamViews = ExamViews(Exam, Question, None, UserAnswer, "sbas", "sbas")
class QuestionView(LoginRequiredMixin, SingleTableMixin, FilterView):
@@ -310,6 +336,7 @@ class QuestionView(LoginRequiredMixin, SingleTableMixin, FilterView):
context["app_name"] = "sbas"
return context
+
class QuestionCreateBase(RevisionMixin, LoginRequiredMixin, CreateView):
model = Question
form_class = QuestionForm
@@ -321,22 +348,20 @@ class QuestionCreateBase(RevisionMixin, LoginRequiredMixin, CreateView):
def get_context_data(self, **kwargs):
context = super(QuestionCreateBase, self).get_context_data(**kwargs)
- #if self.request.POST:
+ # if self.request.POST:
# context["answer_formset"] = AnswerFormSet(
# self.request.POST, self.request.FILES
# )
- #else:
+ # else:
# context["answer_formset"] = AnswerFormSet()
return context
def form_valid(self, form):
-
self.object = form.save(commit=False)
self.object.save()
form.instance.author.add(self.request.user.id)
-
response = super().form_valid(form)
# If the normal submit button is pressed we save as normal
if "submit" in self.request.POST:
@@ -347,7 +372,6 @@ class QuestionCreateBase(RevisionMixin, LoginRequiredMixin, CreateView):
class QuestionCreate(QuestionCreateBase):
-
# initial = {'laterality': AnatomyQuestion.NONE}
def get_initial(self):
@@ -359,6 +383,7 @@ class QuestionCreate(QuestionCreateBase):
return initial
+
class QuestionUpdate(RevisionMixin, AuthorOrCheckerRequiredMixin, UpdateView):
model = Question
form_class = QuestionForm
@@ -373,10 +398,9 @@ class QuestionUpdate(RevisionMixin, AuthorOrCheckerRequiredMixin, UpdateView):
return context
def form_valid(self, form):
-
# save exam orders (there must be a better way to do this)
- #exam_orders = {}
- #for exam in self.object.exams.all():
+ # exam_orders = {}
+ # for exam in self.object.exams.all():
# exam_orders[exam] = list(exam.exam_questions.all())
# print(exam_orders[exam])
@@ -389,6 +413,7 @@ class QuestionUpdate(RevisionMixin, AuthorOrCheckerRequiredMixin, UpdateView):
response = super().form_valid(form)
return response
+
class QuestionClone(QuestionCreateBase):
"""Clones a existing question"""
@@ -404,6 +429,7 @@ class QuestionClone(QuestionCreateBase):
return initial_data
+
class UserAnswerView(LoginRequiredMixin, DetailView):
model = UserAnswer
@@ -450,9 +476,11 @@ class ExamUpdate(ExamUpdateBase, AuthorOrCheckerRequiredMixin):
model = Exam
form_class = ExamForm
+
class ExamDelete(AuthorOrCheckerRequiredMixin, ExamDeleteBase):
model = Exam
+
# class ExamViewSet(RevisionMixin, viewsets.ModelViewSet):
# # queryset = Exam.objects.all().order_by('name')
# serializer_class = ExamSerializer
@@ -468,11 +496,13 @@ class ExamDelete(AuthorOrCheckerRequiredMixin, ExamDeleteBase):
#
# return Exam.objects.filter(author__id=user.id)
-class ExamAuthorUpdate(RevisionMixin, LoginRequiredMixin, AuthorRequiredMixin, UpdateView):
+
+class ExamAuthorUpdate(
+ RevisionMixin, LoginRequiredMixin, AuthorRequiredMixin, UpdateView
+):
model = Exam
form_class = ExamAuthorForm
-
class ExamClone(ExamCloneMixin, ExamCreate):
- """Clone exam view"""
\ No newline at end of file
+ """Clone exam view"""
diff --git a/templates/base.html b/templates/base.html
index a05a9a14..e0413561 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -128,7 +128,6 @@