add (correct) support for logged in users to sba and physics exams
This commit is contained in:
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
<h2>Start exam: {{exam.name}}</h2>
|
<h2>Start exam: {{exam.name}}</h2>
|
||||||
|
|
||||||
{% if request.user.is_authenticated %}
|
{% if request.user.is_authenticated and valid_user %}
|
||||||
<a href="{% url 'physics:exam_take_user' pk=exam.pk sk=0 %}">Start Exam</a>
|
<a href="{% url 'physics:exam_take_user' pk=exam.pk sk=0 %}">Start Exam</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
Enter your CID and passcode in the below boxes.<br />
|
Enter your CID and passcode in the below boxes.<br />
|
||||||
|
|||||||
+179
-117
@@ -27,10 +27,6 @@ from django.contrib.auth.models import User
|
|||||||
APP_NAME = "physics"
|
APP_NAME = "physics"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def create_exam(db):
|
def create_exam(db):
|
||||||
exam: Exam = PhysicsExamViews.Exam.objects.create(
|
exam: Exam = PhysicsExamViews.Exam.objects.create(
|
||||||
name="Cid Exam", exam_mode=True, active=True
|
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_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())
|
exam.valid_user_users.add(*usergroup1.users.all())
|
||||||
|
|
||||||
assert exam in group1.physics_cid_user_groups.all()
|
assert exam in group1.physics_cid_user_groups.all()
|
||||||
@@ -105,7 +101,11 @@ def test_exams(db, client):
|
|||||||
|
|
||||||
create_category(db)
|
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"))
|
active_exams = client.get(reverse(f"{APP_NAME}:active_exams"))
|
||||||
assert active_exams.status_code == 200
|
assert active_exams.status_code == 200
|
||||||
@@ -116,12 +116,12 @@ def test_exams(db, client):
|
|||||||
cid_user_2001 = CidUser.objects.get(cid=2001)
|
cid_user_2001 = CidUser.objects.get(cid=2001)
|
||||||
|
|
||||||
user1 = User.objects.get(username="user1")
|
user1 = User.objects.get(username="user1")
|
||||||
|
user2 = User.objects.get(username="user2")
|
||||||
|
|
||||||
users_tested = 1
|
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):
|
if isinstance(cid_user, CidUser):
|
||||||
testing_cid_user = True
|
testing_cid_user = True
|
||||||
else:
|
else:
|
||||||
@@ -129,7 +129,6 @@ def test_exams(db, client):
|
|||||||
current_user = cid_user
|
current_user = cid_user
|
||||||
client.force_login(cid_user)
|
client.force_login(cid_user)
|
||||||
|
|
||||||
|
|
||||||
exam.active = True
|
exam.active = True
|
||||||
exam.publish_results = False
|
exam.publish_results = False
|
||||||
exam.save()
|
exam.save()
|
||||||
@@ -137,8 +136,13 @@ def test_exams(db, client):
|
|||||||
reverse(f"physics:exam_start", kwargs={"pk": exam.pk})
|
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")
|
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)
|
assert "Enter your CID and passcode" in str(start_page_soup)
|
||||||
else:
|
else:
|
||||||
assert "Start Exam" in str(start_page_soup)
|
assert "Start Exam" in str(start_page_soup)
|
||||||
@@ -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")
|
cid_take_soup = BeautifulSoup(cid_take_res.content, "html.parser")
|
||||||
|
|
||||||
# Check correct user is displayed
|
# Check correct user is displayed
|
||||||
@@ -174,10 +183,7 @@ def test_exams(db, client):
|
|||||||
display_user = str(cid_user.cid)
|
display_user = str(cid_user.cid)
|
||||||
else:
|
else:
|
||||||
display_user = current_user.username
|
display_user = current_user.username
|
||||||
assert (
|
assert display_user in cid_take_soup.find("span", {"id": "user-id"}).string
|
||||||
display_user
|
|
||||||
in cid_take_soup.find("span", {"id": "user-id"}).string
|
|
||||||
)
|
|
||||||
|
|
||||||
# Check review mode alert not visible
|
# Check review mode alert not visible
|
||||||
assert cid_take_soup.find("div", {"class": "review-mode-alert"}) is None
|
assert cid_take_soup.find("div", {"class": "review-mode-alert"}) is None
|
||||||
@@ -216,7 +222,6 @@ def test_exams(db, client):
|
|||||||
is None
|
is None
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
if testing_cid_user:
|
if testing_cid_user:
|
||||||
take_overview_url = reverse(
|
take_overview_url = reverse(
|
||||||
f"physics:exam_take_overview",
|
f"physics:exam_take_overview",
|
||||||
@@ -234,27 +239,32 @@ def test_exams(db, client):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cid_take_overview_res = client.get(take_overview_url)
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
cid_take_overview_res = client.get(
|
cid_take_overview_soup = BeautifulSoup(
|
||||||
take_overview_url
|
cid_take_overview_res.content, "html.parser"
|
||||||
)
|
)
|
||||||
cid_take_overview_soup = BeautifulSoup(
|
assert (
|
||||||
cid_take_overview_res.content, "html.parser"
|
"0 out of 3 questions answered"
|
||||||
)
|
in cid_take_overview_soup.find(
|
||||||
assert (
|
"div", attrs={"class": "overview-text"}
|
||||||
"0 out of 3 questions answered"
|
).string
|
||||||
in cid_take_overview_soup.find(
|
)
|
||||||
"div", attrs={"class": "overview-text"}
|
|
||||||
).string
|
|
||||||
)
|
|
||||||
|
|
||||||
assert (
|
assert (
|
||||||
len(
|
len(
|
||||||
cid_take_overview_soup.find_all("button", attrs={"class": "unanswered"})
|
cid_take_overview_soup.find_all(
|
||||||
|
"button", attrs={"class": "unanswered"}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
== 3
|
||||||
)
|
)
|
||||||
== 3
|
|
||||||
)
|
|
||||||
|
|
||||||
# Loop through questions again to test submissions
|
# Loop through questions again to test submissions
|
||||||
# All physics questions have 5 parts (and require a csrf token)
|
# 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)
|
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")
|
cid_take_soup = BeautifulSoup(cid_take_res.content, "html.parser")
|
||||||
|
|
||||||
# Extract csrftoken
|
# Extract csrftoken
|
||||||
@@ -331,7 +348,6 @@ def test_exams(db, client):
|
|||||||
|
|
||||||
assert all(cid_user_answer.get_answers())
|
assert all(cid_user_answer.get_answers())
|
||||||
|
|
||||||
|
|
||||||
# Recheck the overview page (now that we have submitted answers)
|
# Recheck the overview page (now that we have submitted answers)
|
||||||
if testing_cid_user:
|
if testing_cid_user:
|
||||||
take_overview_url = reverse(
|
take_overview_url = reverse(
|
||||||
@@ -350,86 +366,119 @@ def test_exams(db, client):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
cid_take_overview_res = client.get(
|
cid_take_overview_res = client.get(take_overview_url)
|
||||||
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
|
|
||||||
)
|
|
||||||
|
|
||||||
assert (
|
if cid_user in [user2]:
|
||||||
len(
|
assert cid_take_overview_res.status_code == HTTPStatus.NOT_FOUND
|
||||||
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:
|
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
|
# Get overview scores page
|
||||||
cid_scores_res = client.get(
|
cid_scores_res = client.get(cid_scores_url)
|
||||||
cid_scores_url
|
|
||||||
)
|
assert cid_scores_res.status_code == HTTPStatus.OK
|
||||||
|
|
||||||
cid_scores_soup = BeautifulSoup(cid_scores_res.content, "html.parser")
|
cid_scores_soup = BeautifulSoup(cid_scores_res.content, "html.parser")
|
||||||
|
|
||||||
search_exam = (
|
if cid_user in [user2]:
|
||||||
cid_scores_soup.find("div", {"id": "exam-assigned"})
|
# page should load but we shouldn't be able to find the exam
|
||||||
.find("ul", {"class": exam.app_name.lower()})
|
with pytest.raises(AttributeError):
|
||||||
.find_all("li", attrs={"data-exam-id": exam.pk})
|
search_exam = (
|
||||||
)
|
cid_scores_soup.find("div", {"id": "exam-assigned"})
|
||||||
assert search_exam
|
.find("ul", {"class": exam.app_name.lower()})
|
||||||
assert exam.name in str(search_exam)
|
.find_all("li", attrs={"data-exam-id": exam.pk})
|
||||||
assert "Active" in str(search_exam)
|
)
|
||||||
# assert "Active" in assigned_exams # check it is active
|
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(
|
invalid_exam = cid_scores_soup.find_all(
|
||||||
"li", attrs={"data-exam-id": exam.pk + 5}
|
"li", attrs={"data-exam-id": exam.pk + 5}
|
||||||
)
|
)
|
||||||
assert not invalid_exam # Check we can't find an invalid exam
|
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
|
|
||||||
|
|
||||||
|
# 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:
|
if testing_cid_user:
|
||||||
AssertNotFound(client,
|
AssertNotFound(
|
||||||
|
client,
|
||||||
reverse(
|
reverse(
|
||||||
f"{exam.app_name}:exam_scores_cid_user",
|
f"{exam.app_name}:exam_scores_cid_user",
|
||||||
kwargs={"pk": exam.pk, "cid": cid_user.cid, "passcode": "AAAA"},
|
kwargs={"pk": exam.pk, "cid": cid_user.cid, "passcode": "AAAA"},
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
AssertNotFound(client,
|
AssertNotFound(
|
||||||
reverse(f"{exam.app_name}:exam_scores_cid_user", kwargs={"pk": exam.pk, "cid": cid_user_2001.cid, "passcode": cid_user_2001.passcode})
|
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(
|
exam_scores_url = reverse(
|
||||||
f"{exam.app_name}:exam_scores_cid_user",
|
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:
|
else:
|
||||||
exam_scores_url = reverse(
|
exam_scores_url = reverse(
|
||||||
@@ -437,11 +486,15 @@ def test_exams(db, client):
|
|||||||
kwargs={"pk": exam.pk},
|
kwargs={"pk": exam.pk},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# Load the exam results page
|
# Load the exam results page
|
||||||
cid_exam_scores_res = client.get(
|
cid_exam_scores_res = client.get(exam_scores_url)
|
||||||
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")
|
cid_exam_scores_soup = BeautifulSoup(cid_exam_scores_res.content, "html.parser")
|
||||||
|
|
||||||
# Check that we have an alert that exams are not published
|
# 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)
|
generated_questions.append(extra_question)
|
||||||
|
|
||||||
# Load the exam results page (again)
|
# Load the exam results page (again)
|
||||||
cid_exam_scores_res = client.get(
|
cid_exam_scores_res = client.get(exam_scores_url)
|
||||||
exam_scores_url
|
|
||||||
)
|
|
||||||
cid_exam_scores_soup = BeautifulSoup(cid_exam_scores_res.content, "html.parser")
|
cid_exam_scores_soup = BeautifulSoup(cid_exam_scores_res.content, "html.parser")
|
||||||
|
|
||||||
answer_lis = cid_exam_scores_soup.find_all("li", {"class": "question-part"})
|
answer_lis = cid_exam_scores_soup.find_all("li", {"class": "question-part"})
|
||||||
@@ -493,9 +544,7 @@ def test_exams(db, client):
|
|||||||
exam.save()
|
exam.save()
|
||||||
|
|
||||||
# Load the exam results page (again)
|
# Load the exam results page (again)
|
||||||
cid_exam_scores_res = client.get(
|
cid_exam_scores_res = client.get(exam_scores_url)
|
||||||
exam_scores_url
|
|
||||||
)
|
|
||||||
cid_exam_scores_soup = BeautifulSoup(cid_exam_scores_res.content, "html.parser")
|
cid_exam_scores_soup = BeautifulSoup(cid_exam_scores_res.content, "html.parser")
|
||||||
|
|
||||||
assert (
|
assert (
|
||||||
@@ -541,14 +590,30 @@ def test_exams(db, client):
|
|||||||
|
|
||||||
# answer the final question
|
# answer the final question
|
||||||
if testing_cid_user:
|
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:
|
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_user_answer.save()
|
||||||
|
|
||||||
cid_exam_scores_res = client.get(
|
cid_exam_scores_res = client.get(exam_scores_url)
|
||||||
exam_scores_url
|
|
||||||
)
|
|
||||||
cid_exam_scores_soup = BeautifulSoup(cid_exam_scores_res.content, "html.parser")
|
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
|
# As we have answered correctly the new score should be increased by 5
|
||||||
@@ -559,7 +624,6 @@ def test_exams(db, client):
|
|||||||
== "14"
|
== "14"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# Check what happens when exam is not active
|
# Check what happens when exam is not active
|
||||||
exam.active = False
|
exam.active = False
|
||||||
exam.save()
|
exam.save()
|
||||||
@@ -589,9 +653,7 @@ def test_exams(db, client):
|
|||||||
"sk": 0,
|
"sk": 0,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
cid_take_res = client.get(
|
cid_take_res = client.get(cid_take_url)
|
||||||
cid_take_url
|
|
||||||
)
|
|
||||||
cid_take_soup = BeautifulSoup(cid_take_res.content, "html.parser")
|
cid_take_soup = BeautifulSoup(cid_take_res.content, "html.parser")
|
||||||
assert "Exam is currently inactive" in cid_take_soup.text
|
assert "Exam is currently inactive" in cid_take_soup.text
|
||||||
|
|
||||||
|
|||||||
+1
-5
@@ -9,13 +9,8 @@ urlpatterns = []
|
|||||||
|
|
||||||
urlpatterns.extend(
|
urlpatterns.extend(
|
||||||
[
|
[
|
||||||
# path("question/", views.QuestionView.as_view(), name="question_view"),
|
|
||||||
path("question/", views.QuestionView.as_view(), name="question_view"),
|
path("question/", views.QuestionView.as_view(), name="question_view"),
|
||||||
path("question/<int:pk>/", views.question_detail, name="question_detail"),
|
path("question/<int:pk>/", views.question_detail, name="question_detail"),
|
||||||
# path("question/create/", views.QuestionCreate.as_view(), name="anatomy_question_create"),
|
|
||||||
# path("question/<int:pk>/update", views.QuestionUpdate.as_view(), name="anatomy_question_update"),
|
|
||||||
# path("exam/<int:pk>/<int:sk>/mark", views.mark, name="mark"),
|
|
||||||
# path("exam/<int:pk>/mark", views.mark_overview, name="mark_overview"),
|
|
||||||
path("exam/<int:pk>/take_old", views.exam_take_old, name="exam_take_old"),
|
path("exam/<int:pk>/take_old", views.exam_take_old, name="exam_take_old"),
|
||||||
path(
|
path(
|
||||||
"exam/<int:pk>/<int:sk>/<str:cid>/<str:passcode>/take",
|
"exam/<int:pk>/<int:sk>/<str:cid>/<str:passcode>/take",
|
||||||
@@ -64,6 +59,7 @@ urlpatterns.extend(
|
|||||||
views.UserAnswerDelete.as_view(),
|
views.UserAnswerDelete.as_view(),
|
||||||
name="user_answer_delete",
|
name="user_answer_delete",
|
||||||
),
|
),
|
||||||
|
# TODO: consider merging with generic...
|
||||||
path(
|
path(
|
||||||
"exam/<int:pk>/scores/<int:cid>/<str:passcode>/",
|
"exam/<int:pk>/scores/<int:cid>/<str:passcode>/",
|
||||||
views.exam_scores_cid_user,
|
views.exam_scores_cid_user,
|
||||||
|
|||||||
+12
-13
@@ -99,13 +99,15 @@ def active_exams(request):
|
|||||||
active_exams.append(exam)
|
active_exams.append(exam)
|
||||||
return render(request, "physics/available_exam_list.html", {"exams": active_exams})
|
return render(request, "physics/available_exam_list.html", {"exams": active_exams})
|
||||||
|
|
||||||
|
|
||||||
def exam_scores_cid_user(request, pk, cid=None, passcode=None):
|
def exam_scores_cid_user(request, pk, cid=None, passcode=None):
|
||||||
exam = get_object_or_404(Exam, pk=pk)
|
exam = get_object_or_404(Exam, pk=pk)
|
||||||
|
|
||||||
if not exam.exam_mode:
|
if not exam.exam_mode:
|
||||||
raise Http404("Packet not in 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")
|
raise Http404("Error accessing exam")
|
||||||
|
|
||||||
questions = exam.exam_questions.all()
|
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):
|
def exam_take_old(request, pk):
|
||||||
|
|
||||||
exam = get_object_or_404(Exam, pk=pk)
|
exam = get_object_or_404(Exam, pk=pk)
|
||||||
|
|
||||||
if not exam.active:
|
if not exam.active:
|
||||||
@@ -197,6 +198,9 @@ def exam_start(request, pk):
|
|||||||
"physics/exam_start.html",
|
"physics/exam_start.html",
|
||||||
{
|
{
|
||||||
"exam": exam,
|
"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):
|
def exam_take_overview(request, pk, cid=None, passcode=None):
|
||||||
exam = get_object_or_404(Exam, pk=pk)
|
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:
|
if not exam.active:
|
||||||
return exam_inactive(request, context={"exam": exam})
|
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()
|
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)
|
exam = get_object_or_404(Exam, pk=pk)
|
||||||
|
|
||||||
if not exam.active:
|
if not exam.active:
|
||||||
return exam_inactive(request, context={"exam": exam})
|
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")
|
raise Http404("Error accessing exam")
|
||||||
|
|
||||||
if cid is not None:
|
if cid is not None:
|
||||||
@@ -329,7 +331,6 @@ def exam_take(request, pk, sk, cid=None, passcode=None):
|
|||||||
else:
|
else:
|
||||||
form = UserAnswerForm(instance=answer)
|
form = UserAnswerForm(instance=answer)
|
||||||
|
|
||||||
|
|
||||||
saved_answer = False
|
saved_answer = False
|
||||||
if answer is not None:
|
if answer is not None:
|
||||||
saved_answer = [answer.a, answer.b, answer.c, answer.d, answer.e]
|
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,
|
"previous": previous,
|
||||||
"exam_length": exam_length,
|
"exam_length": exam_length,
|
||||||
"pos": pos,
|
"pos": pos,
|
||||||
|
"saved_answer": saved_answer,
|
||||||
"passcode": passcode,
|
"passcode": passcode,
|
||||||
"cid_user_exam": cid_user_exam,
|
"cid_user_exam": cid_user_exam,
|
||||||
},
|
},
|
||||||
@@ -413,6 +415,7 @@ GenericExamViews = ExamViews(Exam, Question, None, UserAnswer, "physics", "physi
|
|||||||
|
|
||||||
GenericViews = GenericViewBase("physics", Question, UserAnswer, Exam)
|
GenericViews = GenericViewBase("physics", Question, UserAnswer, Exam)
|
||||||
|
|
||||||
|
|
||||||
class ExamCreate(ExamCreateBase):
|
class ExamCreate(ExamCreateBase):
|
||||||
model = Exam
|
model = Exam
|
||||||
form_class = ExamForm
|
form_class = ExamForm
|
||||||
@@ -427,7 +430,6 @@ class ExamDelete(AuthorOrCheckerRequiredMixin, ExamDeleteBase):
|
|||||||
model = Exam
|
model = Exam
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ExamClone(AuthorOrCheckerRequiredMixin, ExamCloneMixin, ExamCreate):
|
class ExamClone(AuthorOrCheckerRequiredMixin, ExamCloneMixin, ExamCreate):
|
||||||
"""Clone exam view"""
|
"""Clone exam view"""
|
||||||
|
|
||||||
@@ -439,8 +441,6 @@ class ExamAuthorUpdate(
|
|||||||
form_class = ExamAuthorForm
|
form_class = ExamAuthorForm
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class QuestionView(
|
class QuestionView(
|
||||||
LoginRequiredMixin, SingleTableMixin, FilterView, AuthorOrCheckerRequiredMixin
|
LoginRequiredMixin, SingleTableMixin, FilterView, AuthorOrCheckerRequiredMixin
|
||||||
):
|
):
|
||||||
@@ -456,7 +456,6 @@ class QuestionView(
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class UserAnswerView(AuthorOrCheckerRequiredMixin, LoginRequiredMixin, DetailView):
|
class UserAnswerView(AuthorOrCheckerRequiredMixin, LoginRequiredMixin, DetailView):
|
||||||
model = UserAnswer
|
model = UserAnswer
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ from ninja import NinjaAPI
|
|||||||
from rapids.api import router as rapids_router
|
from rapids.api import router as rapids_router
|
||||||
from longs.api import router as longs_router
|
from longs.api import router as longs_router
|
||||||
from anatomy.api import router as anatomy_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")
|
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("rapid/", rapids_router)
|
||||||
api.add_router("anatomy/", anatomy_router)
|
api.add_router("anatomy/", anatomy_router)
|
||||||
api.add_router("longs/", longs_router)
|
api.add_router("longs/", longs_router)
|
||||||
|
api.add_router("sbas/", sbas_router)
|
||||||
|
api.add_router("physics/", physics_router)
|
||||||
|
|
||||||
@api.get("/hello")
|
@api.get("/hello")
|
||||||
def hello(request):
|
def hello(request):
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ def create_cid_user_and_groups(db):
|
|||||||
|
|
||||||
# Create UserUsers
|
# Create UserUsers
|
||||||
user1 = User.objects.create_user(username="user1", email="test@email.com", password="test1234")
|
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 = UserUserGroup.objects.create(name="Group1")
|
||||||
usergroup1.users.add(user1)
|
usergroup1.users.add(user1)
|
||||||
usergroup2 = UserUserGroup.objects.create(name="Group2")
|
usergroup2 = UserUserGroup.objects.create(name="Group2")
|
||||||
|
usergroup2.users.add(user2)
|
||||||
@@ -4,6 +4,9 @@
|
|||||||
|
|
||||||
<h2>Start exam: {{exam.name}}</h2>
|
<h2>Start exam: {{exam.name}}</h2>
|
||||||
|
|
||||||
|
{% if request.user.is_authenticated and valid_user %}
|
||||||
|
<a href="{% url 'sbas:exam_take_user' pk=exam.pk sk=0 %}">Start Exam</a>
|
||||||
|
{% else %}
|
||||||
Enter your CID and passcode in the below boxes.<br />
|
Enter your CID and passcode in the below boxes.<br />
|
||||||
<p><input id="cid-box" type="text" value="Candidate ID"></p>
|
<p><input id="cid-box" type="text" value="Candidate ID"></p>
|
||||||
<p><input id="passcode-box" type="text" value="Passcode"></p>
|
<p><input id="passcode-box" type="text" value="Passcode"></p>
|
||||||
@@ -44,4 +47,5 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -32,6 +32,11 @@ urlpatterns = [
|
|||||||
views.exam_take,
|
views.exam_take,
|
||||||
name="exam_take",
|
name="exam_take",
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"exam/<int:pk>/<int:sk>/take",
|
||||||
|
views.exam_take,
|
||||||
|
name="exam_take_user",
|
||||||
|
),
|
||||||
path("exam/<int:pk>/start", views.exam_start, name="exam_start"),
|
path("exam/<int:pk>/start", views.exam_start, name="exam_start"),
|
||||||
path(
|
path(
|
||||||
"exam/<int:pk>/<str:cid>/<str:passcode>/finish",
|
"exam/<int:pk>/<str:cid>/<str:passcode>/finish",
|
||||||
|
|||||||
+75
-45
@@ -24,7 +24,7 @@ from django.core.cache import cache
|
|||||||
|
|
||||||
from django.urls import reverse_lazy, reverse
|
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
|
from django.http import HttpResponseRedirect, HttpResponse
|
||||||
|
|
||||||
@@ -41,7 +41,16 @@ import json
|
|||||||
import statistics
|
import statistics
|
||||||
import plotly.express as px
|
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 import viewsets, permissions
|
||||||
from rest_framework.pagination import PageNumberPagination
|
from rest_framework.pagination import PageNumberPagination
|
||||||
@@ -156,29 +165,29 @@ def exam_start(request, pk):
|
|||||||
|
|
||||||
if not exam.active:
|
if not exam.active:
|
||||||
return exam_inactive(request, context={"exam": exam})
|
return exam_inactive(request, context={"exam": exam})
|
||||||
raise Http404("Exam not found")
|
|
||||||
|
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
"sbas/exam_start.html",
|
"sbas/exam_start.html",
|
||||||
{
|
{"exam": exam, "valid_user": exam.check_logged_in_user(request)},
|
||||||
"exam": exam,
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
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)
|
exam = get_object_or_404(Exam, pk=pk)
|
||||||
|
|
||||||
if not exam.active:
|
if not exam.active:
|
||||||
return exam_inactive(request, context={"exam": exam})
|
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")
|
raise Http404("Error accessing exam")
|
||||||
|
|
||||||
questions = exam.exam_questions.all()
|
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 = {}
|
answer_question_map = {}
|
||||||
for ans in answers:
|
for ans in answers:
|
||||||
@@ -193,8 +202,11 @@ def exam_take_overview(request, pk, cid, passcode):
|
|||||||
else:
|
else:
|
||||||
question_answer_tuples.append((q, None))
|
question_answer_tuples.append((q, None))
|
||||||
|
|
||||||
c = CidUser.objects.filter(cid=cid).first()
|
if cid is not None:
|
||||||
cid_user_exam = exam.get_or_create_cid_user_exam(cid_user=c)
|
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(
|
return render(
|
||||||
request,
|
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)
|
exam = get_object_or_404(Exam, pk=pk)
|
||||||
|
|
||||||
if not exam.active:
|
if not exam.active:
|
||||||
raise Http404("Exam not found")
|
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")
|
raise Http404("Error accessing exam")
|
||||||
|
|
||||||
c = CidUser.objects.filter(cid=cid).first()
|
if cid is not None:
|
||||||
cid_user_exam = exam.get_or_create_cid_user_exam(cid_user=c)
|
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]
|
question = exam.exam_questions.all()[sk]
|
||||||
|
|
||||||
@@ -230,7 +244,17 @@ def exam_take(request, pk, sk, cid, passcode):
|
|||||||
|
|
||||||
pos = exam.get_question_index(question)
|
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 request.method == "POST":
|
||||||
if answer:
|
if answer:
|
||||||
@@ -239,7 +263,11 @@ def exam_take(request, pk, sk, cid, passcode):
|
|||||||
form = UserAnswerForm(request.POST)
|
form = UserAnswerForm(request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
answer = form.save(commit=False)
|
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.question = question
|
||||||
answer.exam = exam
|
answer.exam = exam
|
||||||
# answer.published_date = timezone.now()
|
# 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.end_time = timezone.now()
|
||||||
cid_user_exam.save()
|
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:
|
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:
|
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:
|
elif "finish" in request.POST:
|
||||||
return redirect("sbas:exam_take_overview", **kwargs)
|
return redirect(finish_url, **kwargs)
|
||||||
elif "goto" in request.POST:
|
elif "goto" in request.POST:
|
||||||
return redirect(
|
return redirect(take_url, sk=int(request.POST.get("goto")), **kwargs)
|
||||||
"sbas:exam_take", sk=int(request.POST.get("goto")), **kwargs
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
form = UserAnswerForm(instance=answer)
|
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
|
saved_answer = False
|
||||||
if answer is not None:
|
if answer is not None:
|
||||||
saved_answer = answer.answer
|
saved_answer = answer.answer
|
||||||
@@ -293,9 +321,7 @@ def exam_take(request, pk, sk, cid, passcode):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
GenericExamViews = ExamViews(
|
GenericExamViews = ExamViews(Exam, Question, None, UserAnswer, "sbas", "sbas")
|
||||||
Exam, Question, None, UserAnswer, "sbas", "sbas"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class QuestionView(LoginRequiredMixin, SingleTableMixin, FilterView):
|
class QuestionView(LoginRequiredMixin, SingleTableMixin, FilterView):
|
||||||
@@ -310,6 +336,7 @@ class QuestionView(LoginRequiredMixin, SingleTableMixin, FilterView):
|
|||||||
context["app_name"] = "sbas"
|
context["app_name"] = "sbas"
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class QuestionCreateBase(RevisionMixin, LoginRequiredMixin, CreateView):
|
class QuestionCreateBase(RevisionMixin, LoginRequiredMixin, CreateView):
|
||||||
model = Question
|
model = Question
|
||||||
form_class = QuestionForm
|
form_class = QuestionForm
|
||||||
@@ -321,22 +348,20 @@ class QuestionCreateBase(RevisionMixin, LoginRequiredMixin, CreateView):
|
|||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(QuestionCreateBase, self).get_context_data(**kwargs)
|
context = super(QuestionCreateBase, self).get_context_data(**kwargs)
|
||||||
#if self.request.POST:
|
# if self.request.POST:
|
||||||
# context["answer_formset"] = AnswerFormSet(
|
# context["answer_formset"] = AnswerFormSet(
|
||||||
# self.request.POST, self.request.FILES
|
# self.request.POST, self.request.FILES
|
||||||
# )
|
# )
|
||||||
#else:
|
# else:
|
||||||
# context["answer_formset"] = AnswerFormSet()
|
# context["answer_formset"] = AnswerFormSet()
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
|
|
||||||
self.object = form.save(commit=False)
|
self.object = form.save(commit=False)
|
||||||
self.object.save()
|
self.object.save()
|
||||||
|
|
||||||
form.instance.author.add(self.request.user.id)
|
form.instance.author.add(self.request.user.id)
|
||||||
|
|
||||||
|
|
||||||
response = super().form_valid(form)
|
response = super().form_valid(form)
|
||||||
# If the normal submit button is pressed we save as normal
|
# If the normal submit button is pressed we save as normal
|
||||||
if "submit" in self.request.POST:
|
if "submit" in self.request.POST:
|
||||||
@@ -347,7 +372,6 @@ class QuestionCreateBase(RevisionMixin, LoginRequiredMixin, CreateView):
|
|||||||
|
|
||||||
|
|
||||||
class QuestionCreate(QuestionCreateBase):
|
class QuestionCreate(QuestionCreateBase):
|
||||||
|
|
||||||
# initial = {'laterality': AnatomyQuestion.NONE}
|
# initial = {'laterality': AnatomyQuestion.NONE}
|
||||||
|
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
@@ -359,6 +383,7 @@ class QuestionCreate(QuestionCreateBase):
|
|||||||
|
|
||||||
return initial
|
return initial
|
||||||
|
|
||||||
|
|
||||||
class QuestionUpdate(RevisionMixin, AuthorOrCheckerRequiredMixin, UpdateView):
|
class QuestionUpdate(RevisionMixin, AuthorOrCheckerRequiredMixin, UpdateView):
|
||||||
model = Question
|
model = Question
|
||||||
form_class = QuestionForm
|
form_class = QuestionForm
|
||||||
@@ -373,10 +398,9 @@ class QuestionUpdate(RevisionMixin, AuthorOrCheckerRequiredMixin, UpdateView):
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
|
|
||||||
# save exam orders (there must be a better way to do this)
|
# save exam orders (there must be a better way to do this)
|
||||||
#exam_orders = {}
|
# exam_orders = {}
|
||||||
#for exam in self.object.exams.all():
|
# for exam in self.object.exams.all():
|
||||||
# exam_orders[exam] = list(exam.exam_questions.all())
|
# exam_orders[exam] = list(exam.exam_questions.all())
|
||||||
# print(exam_orders[exam])
|
# print(exam_orders[exam])
|
||||||
|
|
||||||
@@ -389,6 +413,7 @@ class QuestionUpdate(RevisionMixin, AuthorOrCheckerRequiredMixin, UpdateView):
|
|||||||
response = super().form_valid(form)
|
response = super().form_valid(form)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
class QuestionClone(QuestionCreateBase):
|
class QuestionClone(QuestionCreateBase):
|
||||||
"""Clones a existing question"""
|
"""Clones a existing question"""
|
||||||
|
|
||||||
@@ -404,6 +429,7 @@ class QuestionClone(QuestionCreateBase):
|
|||||||
|
|
||||||
return initial_data
|
return initial_data
|
||||||
|
|
||||||
|
|
||||||
class UserAnswerView(LoginRequiredMixin, DetailView):
|
class UserAnswerView(LoginRequiredMixin, DetailView):
|
||||||
model = UserAnswer
|
model = UserAnswer
|
||||||
|
|
||||||
@@ -450,9 +476,11 @@ class ExamUpdate(ExamUpdateBase, AuthorOrCheckerRequiredMixin):
|
|||||||
model = Exam
|
model = Exam
|
||||||
form_class = ExamForm
|
form_class = ExamForm
|
||||||
|
|
||||||
|
|
||||||
class ExamDelete(AuthorOrCheckerRequiredMixin, ExamDeleteBase):
|
class ExamDelete(AuthorOrCheckerRequiredMixin, ExamDeleteBase):
|
||||||
model = Exam
|
model = Exam
|
||||||
|
|
||||||
|
|
||||||
# class ExamViewSet(RevisionMixin, viewsets.ModelViewSet):
|
# class ExamViewSet(RevisionMixin, viewsets.ModelViewSet):
|
||||||
# # queryset = Exam.objects.all().order_by('name')
|
# # queryset = Exam.objects.all().order_by('name')
|
||||||
# serializer_class = ExamSerializer
|
# serializer_class = ExamSerializer
|
||||||
@@ -468,11 +496,13 @@ class ExamDelete(AuthorOrCheckerRequiredMixin, ExamDeleteBase):
|
|||||||
#
|
#
|
||||||
# return Exam.objects.filter(author__id=user.id)
|
# return Exam.objects.filter(author__id=user.id)
|
||||||
|
|
||||||
class ExamAuthorUpdate(RevisionMixin, LoginRequiredMixin, AuthorRequiredMixin, UpdateView):
|
|
||||||
|
class ExamAuthorUpdate(
|
||||||
|
RevisionMixin, LoginRequiredMixin, AuthorRequiredMixin, UpdateView
|
||||||
|
):
|
||||||
model = Exam
|
model = Exam
|
||||||
form_class = ExamAuthorForm
|
form_class = ExamAuthorForm
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ExamClone(ExamCloneMixin, ExamCreate):
|
class ExamClone(ExamCloneMixin, ExamCreate):
|
||||||
"""Clone exam view"""
|
"""Clone exam view"""
|
||||||
@@ -128,7 +128,6 @@
|
|||||||
<ul class="nav col-md-4 justify-content-end list-unstyled d-flex">
|
<ul class="nav col-md-4 justify-content-end list-unstyled d-flex">
|
||||||
<li class="ms-3"><a class="text-muted" href="{% url 'privacy' %}">Privacy</a></li>
|
<li class="ms-3"><a class="text-muted" href="{% url 'privacy' %}">Privacy</a></li>
|
||||||
<li class="ms-3"><a class="text-muted" href="{% url 'cookie_consent_cookie_group_list' %}">Cookies</a></li>
|
<li class="ms-3"><a class="text-muted" href="{% url 'cookie_consent_cookie_group_list' %}">Cookies</a></li>
|
||||||
<li class="ms-3"><a class="text-muted" href="#"><svg class="bi" width="24" height="24"><use xlink:href="#facebook"></use></svg></a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</footer>
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
Reference in New Issue
Block a user