update sba testing (and fix others)
This commit is contained in:
@@ -1889,6 +1889,32 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
print("ans", ans)
|
print("ans", ans)
|
||||||
print("ans score", answer_score)
|
print("ans score", answer_score)
|
||||||
|
|
||||||
|
match self.app_name:
|
||||||
|
case "sbas":
|
||||||
|
correct_answer = q.get_correct_answer()
|
||||||
|
|
||||||
|
if not exam.publish_results and not view_all_results:
|
||||||
|
correct_answer = "*****"
|
||||||
|
answer_score = 0
|
||||||
|
answers.append(ans)
|
||||||
|
answers_marks.append(answer_score)
|
||||||
|
|
||||||
|
merged_ans = (ans, answer_score, correct_answer)
|
||||||
|
chosen_answer = q.get_answer_by_choice(ans)
|
||||||
|
answers_and_marks.append((q, *merged_ans, chosen_answer))
|
||||||
|
case "physics":
|
||||||
|
correct_answer = q.get_answers()
|
||||||
|
|
||||||
|
if not exam.publish_results and not view_all_results:
|
||||||
|
correct_answer = ("*****", "*****", "*****", "*****", "*****")
|
||||||
|
answer_score = (0, 0, 0, 0, 0)
|
||||||
|
answers.append(ans)
|
||||||
|
answers_marks.append(answer_score)
|
||||||
|
|
||||||
|
print(q.get_questions(), ans, answer_score, correct_answer)
|
||||||
|
merged_ans = zip(q.get_questions(), ans, answer_score, correct_answer)
|
||||||
|
answers_and_marks.append((q, merged_ans))
|
||||||
|
case _:
|
||||||
correct_answer = q.get_primary_answer()
|
correct_answer = q.get_primary_answer()
|
||||||
|
|
||||||
if not exam.publish_results and not view_all_results:
|
if not exam.publish_results and not view_all_results:
|
||||||
@@ -1900,6 +1926,10 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
|
|
||||||
print(answers_marks)
|
print(answers_marks)
|
||||||
|
|
||||||
|
match self.app_name:
|
||||||
|
case "physics":
|
||||||
|
total_score = sum(sum(i) for i in answers_marks)
|
||||||
|
case _:
|
||||||
if "unmarked" in answers_marks:
|
if "unmarked" in answers_marks:
|
||||||
answered = [i for i in answers_marks if type(i) == int]
|
answered = [i for i in answers_marks if type(i) == int]
|
||||||
total_score = sum(answered)
|
total_score = sum(answered)
|
||||||
@@ -1913,6 +1943,8 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
max_score = len(questions) * 2
|
max_score = len(questions) * 2
|
||||||
case "longs":
|
case "longs":
|
||||||
max_score = len(questions) * 8
|
max_score = len(questions) * 8
|
||||||
|
case "physics":
|
||||||
|
max_score = len(questions) * 5
|
||||||
case _:
|
case _:
|
||||||
max_score = len(questions)
|
max_score = len(questions)
|
||||||
|
|
||||||
|
|||||||
@@ -74,19 +74,19 @@ def create_long_series(question, image_number=5):
|
|||||||
series.save()
|
series.save()
|
||||||
|
|
||||||
for n in range(image_number):
|
for n in range(image_number):
|
||||||
create_long_series_image(series)
|
create_long_series_image(series, n)
|
||||||
|
|
||||||
return series
|
return series
|
||||||
|
|
||||||
|
|
||||||
def create_long_series_image(series):
|
def create_long_series_image(series, n=0):
|
||||||
image = tempfile.NamedTemporaryFile(
|
image = tempfile.NamedTemporaryFile(
|
||||||
dir=settings.MEDIA_ROOT, suffix=".jpg", delete=False
|
dir=settings.MEDIA_ROOT, suffix="{n}.jpg", delete=False
|
||||||
)
|
)
|
||||||
|
|
||||||
long_series_image = LongSeriesImage.objects.create(image=image.name, series=series)
|
long_series_image = LongSeriesImage(image=image.name, series=series)
|
||||||
|
|
||||||
# long_series_image.save()
|
long_series_image.save()
|
||||||
|
|
||||||
|
|
||||||
def create_question(exam, answer=None, normal=False):
|
def create_question(exam, answer=None, normal=False):
|
||||||
|
|||||||
@@ -128,6 +128,13 @@ class Question(QuestionBase):
|
|||||||
def get_questions(self):
|
def get_questions(self):
|
||||||
return [self.a, self.b, self.c, self.d, self.e]
|
return [self.a, self.b, self.c, self.d, self.e]
|
||||||
|
|
||||||
|
def get_unanswered_mark_and_text(self) -> tuple[int, tuple[str, str, str, str, str]]:
|
||||||
|
"""
|
||||||
|
|
||||||
|
returns score, tuple[answers]
|
||||||
|
"""
|
||||||
|
return ((0,) * 5, ("Not answered",) * 5)
|
||||||
|
|
||||||
|
|
||||||
#@reversion.register
|
#@reversion.register
|
||||||
class Exam(ExamBase):
|
class Exam(ExamBase):
|
||||||
@@ -209,6 +216,9 @@ class UserAnswer(UserAnswerBase):
|
|||||||
def get_answers(self):
|
def get_answers(self):
|
||||||
return (self.a, self.b, self.c, self.d, self.e)
|
return (self.a, self.b, self.c, self.d, self.e)
|
||||||
|
|
||||||
|
def get_answer(self):
|
||||||
|
return self.get_answers()
|
||||||
|
|
||||||
def get_answer_string(self):
|
def get_answer_string(self):
|
||||||
return [str(i) for i in (self.a, self.b, self.c, self.d, self.e)]
|
return [str(i) for i in (self.a, self.b, self.c, self.d, self.e)]
|
||||||
|
|
||||||
|
|||||||
+10
-10
@@ -60,16 +60,16 @@ urlpatterns.extend(
|
|||||||
name="user_answer_delete",
|
name="user_answer_delete",
|
||||||
),
|
),
|
||||||
# TODO: consider merging with generic...
|
# 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,
|
||||||
name="exam_scores_cid_user",
|
# name="exam_scores_cid_user",
|
||||||
),
|
#),
|
||||||
path(
|
#path(
|
||||||
"exam/<int:pk>/scores/",
|
# "exam/<int:pk>/scores/",
|
||||||
views.exam_scores_cid_user,
|
# views.exam_scores_cid_user,
|
||||||
name="exam_scores_user",
|
# name="exam_scores_user",
|
||||||
),
|
#),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+62
-62
@@ -100,68 +100,68 @@ def active_exams(request):
|
|||||||
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)
|
||||||
|
#
|
||||||
exam.check_user_can_review(cid, passcode, request)
|
# exam.check_user_can_review(cid, passcode, request)
|
||||||
|
#
|
||||||
questions = exam.exam_questions.all()
|
# questions = exam.exam_questions.all()
|
||||||
|
#
|
||||||
answers_and_marks = []
|
# answers_and_marks = []
|
||||||
answers_marks = []
|
# answers_marks = []
|
||||||
answers = []
|
# answers = []
|
||||||
|
#
|
||||||
view_all_results = False
|
# view_all_results = False
|
||||||
if request.user.groups.filter(name="view_all_results").exists():
|
# if request.user.groups.filter(name="view_all_results").exists():
|
||||||
view_all_results = True
|
# view_all_results = True
|
||||||
|
#
|
||||||
for q in questions:
|
# for q in questions:
|
||||||
# Get user answer
|
# # Get user answer
|
||||||
if cid is not None:
|
# if cid is not None:
|
||||||
user_answer = q.cid_user_answers.filter(cid=cid, exam__id=pk).first()
|
# user_answer = q.cid_user_answers.filter(cid=cid, exam__id=pk).first()
|
||||||
else:
|
# else:
|
||||||
user_answer = q.cid_user_answers.filter(
|
# user_answer = q.cid_user_answers.filter(
|
||||||
user=request.user, exam__id=pk
|
# user=request.user, exam__id=pk
|
||||||
).first()
|
# ).first()
|
||||||
|
#
|
||||||
if user_answer is None:
|
# if user_answer is None:
|
||||||
answer_scores = (0, 0, 0, 0, 0)
|
# answer_scores = (0, 0, 0, 0, 0)
|
||||||
ans = ("", "", "", "", "")
|
# ans = ("", "", "", "", "")
|
||||||
else:
|
# else:
|
||||||
ans = user_answer.get_answers()
|
# ans = user_answer.get_answers()
|
||||||
answer_scores = user_answer.get_answer_score()
|
# answer_scores = user_answer.get_answer_score()
|
||||||
|
#
|
||||||
correct_answers = q.get_answers()
|
# correct_answers = q.get_answers()
|
||||||
|
#
|
||||||
if not exam.publish_results and not view_all_results:
|
# if not exam.publish_results and not view_all_results:
|
||||||
correct_answers = ("*****", "*****", "*****", "*****", "*****")
|
# correct_answers = ("*****", "*****", "*****", "*****", "*****")
|
||||||
answer_scores = (0, 0, 0, 0, 0)
|
# answer_scores = (0, 0, 0, 0, 0)
|
||||||
answers.append(ans)
|
# answers.append(ans)
|
||||||
answers_marks.append(answer_scores)
|
# answers_marks.append(answer_scores)
|
||||||
|
#
|
||||||
merged_ans = zip(q.get_questions(), ans, answer_scores, correct_answers)
|
# merged_ans = zip(q.get_questions(), ans, answer_scores, correct_answers)
|
||||||
answers_and_marks.append((q, merged_ans))
|
# answers_and_marks.append((q, merged_ans))
|
||||||
|
#
|
||||||
total_score = sum(sum(i) for i in answers_marks)
|
# total_score = sum(sum(i) for i in answers_marks)
|
||||||
|
#
|
||||||
max_score = len(questions) * 5
|
# max_score = len(questions) * 5
|
||||||
|
#
|
||||||
return render(
|
# return render(
|
||||||
request,
|
# request,
|
||||||
"physics/exam_scores_user.html",
|
# "physics/exam_scores_user.html",
|
||||||
{
|
# {
|
||||||
"exam": exam,
|
# "exam": exam,
|
||||||
"cid": cid,
|
# "cid": cid,
|
||||||
"passcode": passcode,
|
# "passcode": passcode,
|
||||||
"questions": questions,
|
# "questions": questions,
|
||||||
"answers": answers,
|
# "answers": answers,
|
||||||
"answers_marks": answers_marks,
|
# "answers_marks": answers_marks,
|
||||||
"total_score": total_score,
|
# "total_score": total_score,
|
||||||
"max_score": max_score,
|
# "max_score": max_score,
|
||||||
"answers_and_marks": answers_and_marks,
|
# "answers_and_marks": answers_and_marks,
|
||||||
"view_all_results": view_all_results,
|
# "view_all_results": view_all_results,
|
||||||
},
|
# },
|
||||||
)
|
# )
|
||||||
|
|
||||||
|
|
||||||
def exam_take_old(request, pk):
|
def exam_take_old(request, pk):
|
||||||
|
|||||||
+105
-31
@@ -11,6 +11,7 @@ from django.contrib.auth.models import User
|
|||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from rich.pretty import pprint
|
from rich.pretty import pprint
|
||||||
|
|
||||||
|
|
||||||
def AssertNotFound(client, url):
|
def AssertNotFound(client, url):
|
||||||
"""Helper to quickly test for urls that should return a 404 (NOT FOUND) error"""
|
"""Helper to quickly test for urls that should return a 404 (NOT FOUND) error"""
|
||||||
response = client.get(url)
|
response = client.get(url)
|
||||||
@@ -74,9 +75,11 @@ class ExamTester:
|
|||||||
|
|
||||||
cidgroup1: CidUserGroup = CidUserGroup.objects.get(name="Group1")
|
cidgroup1: CidUserGroup = CidUserGroup.objects.get(name="Group1")
|
||||||
|
|
||||||
|
self.answers_per_question = 1
|
||||||
match app_name:
|
match app_name:
|
||||||
case "physics":
|
case "physics":
|
||||||
assert exam in cidgroup1.physics_cid_user_groups.all()
|
assert exam in cidgroup1.physics_cid_user_groups.all()
|
||||||
|
self.answers_per_question = 5
|
||||||
case "sbas":
|
case "sbas":
|
||||||
assert exam in cidgroup1.sba_cid_user_groups.all()
|
assert exam in cidgroup1.sba_cid_user_groups.all()
|
||||||
|
|
||||||
@@ -123,7 +126,9 @@ class ExamTester:
|
|||||||
def check_active_exams(self, n):
|
def check_active_exams(self, n):
|
||||||
active_exams = self.client.get(reverse(f"{self.app_name}:active_exams"))
|
active_exams = self.client.get(reverse(f"{self.app_name}:active_exams"))
|
||||||
assert active_exams.status_code == 200
|
assert active_exams.status_code == 200
|
||||||
assert len(json.loads(active_exams.content)["exams"]) == n, f"Current user: {self.current_user}"
|
assert (
|
||||||
|
len(json.loads(active_exams.content)["exams"]) == n
|
||||||
|
), f"Current user: {self.current_user}"
|
||||||
|
|
||||||
def check_onsite_start_page(self):
|
def check_onsite_start_page(self):
|
||||||
start_page_res = self.client.get(
|
start_page_res = self.client.get(
|
||||||
@@ -169,7 +174,9 @@ class ExamTester:
|
|||||||
assert cid_take_res.status_code == HTTPStatus.NOT_FOUND
|
assert cid_take_res.status_code == HTTPStatus.NOT_FOUND
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
assert cid_take_res.status_code == HTTPStatus.OK, f"Current user: {self.current_user}"
|
assert (
|
||||||
|
cid_take_res.status_code == HTTPStatus.OK
|
||||||
|
), f"Current user: {self.current_user}"
|
||||||
|
|
||||||
cid_take_soup = BeautifulSoup(cid_take_res.content, "html.parser")
|
cid_take_soup = BeautifulSoup(cid_take_res.content, "html.parser")
|
||||||
|
|
||||||
@@ -207,14 +214,39 @@ class ExamTester:
|
|||||||
|
|
||||||
match self.app_name:
|
match self.app_name:
|
||||||
case "physics":
|
case "physics":
|
||||||
assert cid_take_soup.find("label", {"for": "id_a"}).string == question.a
|
assert (
|
||||||
assert cid_take_soup.find("label", {"for": "id_b"}).string == question.b
|
cid_take_soup.find("label", {"for": "id_a"}).string
|
||||||
assert cid_take_soup.find("label", {"for": "id_c"}).string == question.c
|
== question.a
|
||||||
assert cid_take_soup.find("label", {"for": "id_d"}).string == question.d
|
)
|
||||||
assert cid_take_soup.find("label", {"for": "id_e"}).string == question.e
|
assert (
|
||||||
|
cid_take_soup.find("label", {"for": "id_b"}).string
|
||||||
|
== question.b
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
cid_take_soup.find("label", {"for": "id_c"}).string
|
||||||
|
== question.c
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
cid_take_soup.find("label", {"for": "id_d"}).string
|
||||||
|
== question.d
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
cid_take_soup.find("label", {"for": "id_e"}).string
|
||||||
|
== question.e
|
||||||
|
)
|
||||||
case "sbas":
|
case "sbas":
|
||||||
assert cid_take_soup.find("ul", {"class": "sba-answer-list"}) is not None
|
assert (
|
||||||
assert len(cid_take_soup.find("ul", {"class": "sba-answer-list"}).find_all("li")) == 5
|
cid_take_soup.find("ul", {"class": "sba-answer-list"})
|
||||||
|
is not None
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
len(
|
||||||
|
cid_take_soup.find(
|
||||||
|
"ul", {"class": "sba-answer-list"}
|
||||||
|
).find_all("li")
|
||||||
|
)
|
||||||
|
== 5
|
||||||
|
)
|
||||||
|
|
||||||
# As we haven't submitted any answers they should all be blank
|
# As we haven't submitted any answers they should all be blank
|
||||||
assert (
|
assert (
|
||||||
@@ -396,18 +428,21 @@ class ExamTester:
|
|||||||
# Check that we are showing enough questions / answers
|
# Check that we are showing enough questions / answers
|
||||||
match self.app_name:
|
match self.app_name:
|
||||||
case "physics":
|
case "physics":
|
||||||
answer_lis = cid_exam_scores_soup.find_all("li", {"class": "question-part"})
|
answer_lis = cid_exam_scores_soup.find_all(
|
||||||
|
"li", {"class": "question-part"}
|
||||||
|
)
|
||||||
assert len(answer_lis) == 5 * len(self.generated_questions)
|
assert len(answer_lis) == 5 * len(self.generated_questions)
|
||||||
assert len(submitted_answers) == 5 * len(self.generated_questions)
|
assert len(submitted_answers) == 5 * len(self.generated_questions)
|
||||||
for ans in submitted_answers:
|
for ans in submitted_answers:
|
||||||
assert ans.text.strip() == "True"
|
assert ans.text.strip() == "True"
|
||||||
|
|
||||||
case "sbas":
|
case "sbas":
|
||||||
answer_lis = cid_exam_scores_soup.find_all("li", {"class": "user-answer-li"})
|
answer_lis = cid_exam_scores_soup.find_all(
|
||||||
|
"li", {"class": "user-answer-li"}
|
||||||
|
)
|
||||||
assert len(answer_lis) == len(self.generated_questions)
|
assert len(answer_lis) == len(self.generated_questions)
|
||||||
assert len(submitted_answers) == len(self.generated_questions)
|
assert len(submitted_answers) == len(self.generated_questions)
|
||||||
|
|
||||||
|
|
||||||
for ans in submitted_answers:
|
for ans in submitted_answers:
|
||||||
assert ans.text.strip() == "a"
|
assert ans.text.strip() == "a"
|
||||||
|
|
||||||
@@ -426,7 +461,9 @@ class ExamTester:
|
|||||||
)
|
)
|
||||||
match self.app_name:
|
match self.app_name:
|
||||||
case "physics":
|
case "physics":
|
||||||
answer_lis = cid_exam_scores_soup.find_all("li", {"class": "question-part"})
|
answer_lis = cid_exam_scores_soup.find_all(
|
||||||
|
"li", {"class": "question-part"}
|
||||||
|
)
|
||||||
assert len(answer_lis) == 5 * len(self.generated_questions)
|
assert len(answer_lis) == 5 * len(self.generated_questions)
|
||||||
assert len(submitted_answers) == 5 * len(self.generated_questions)
|
assert len(submitted_answers) == 5 * len(self.generated_questions)
|
||||||
|
|
||||||
@@ -435,10 +472,12 @@ class ExamTester:
|
|||||||
|
|
||||||
# The new question will not have any answers
|
# The new question will not have any answers
|
||||||
for ans in submitted_answers[-5:]:
|
for ans in submitted_answers[-5:]:
|
||||||
assert ans.text.strip() == ""
|
assert ans.text.strip() == "Not answered"
|
||||||
|
|
||||||
case "sbas":
|
case "sbas":
|
||||||
answer_lis = cid_exam_scores_soup.find_all("li", {"class": "user-answer-li"})
|
answer_lis = cid_exam_scores_soup.find_all(
|
||||||
|
"li", {"class": "user-answer-li"}
|
||||||
|
)
|
||||||
assert len(answer_lis) == len(self.generated_questions)
|
assert len(answer_lis) == len(self.generated_questions)
|
||||||
assert len(submitted_answers) == len(self.generated_questions)
|
assert len(submitted_answers) == len(self.generated_questions)
|
||||||
|
|
||||||
@@ -451,9 +490,7 @@ class ExamTester:
|
|||||||
# The new question will not have any answers
|
# The new question will not have any answers
|
||||||
for ans in submitted_answers[3:]:
|
for ans in submitted_answers[3:]:
|
||||||
print("test2", ans)
|
print("test2", ans)
|
||||||
assert ans.text.strip() == ""
|
assert ans.text.strip() == "Not answered"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Publish the results!
|
# Publish the results!
|
||||||
self.exam.publish_results = True
|
self.exam.publish_results = True
|
||||||
@@ -471,26 +508,45 @@ class ExamTester:
|
|||||||
correct_answers = cid_exam_scores_soup.find_all(
|
correct_answers = cid_exam_scores_soup.find_all(
|
||||||
"span", {"class": "correct-answer"}
|
"span", {"class": "correct-answer"}
|
||||||
)
|
)
|
||||||
assert len(correct_answers) == 5 * len(self.generated_questions)
|
|
||||||
|
assert len(correct_answers) == self.answers_per_question * len(
|
||||||
|
self.generated_questions
|
||||||
|
)
|
||||||
|
|
||||||
n = 0
|
n = 0
|
||||||
for question in self.generated_questions:
|
for i, question in enumerate(self.generated_questions):
|
||||||
|
match self.app_name:
|
||||||
|
case "physics":
|
||||||
for ans in question.get_answers():
|
for ans in question.get_answers():
|
||||||
assert correct_answers[n].text.strip() == f"Correct answer: {ans}"
|
assert correct_answers[n].text.strip() == f"Correct answer: {ans}"
|
||||||
n = n + 1
|
n = n + 1
|
||||||
|
case "sbas":
|
||||||
|
ans = question.get_correct_answer()
|
||||||
|
assert correct_answers[i].text.strip() == f"Correct answer: {ans}"
|
||||||
|
|
||||||
submitted_answers = cid_exam_scores_soup.find_all(
|
submitted_answers = cid_exam_scores_soup.find_all(
|
||||||
"span", {"class": "submitted-user-answer"}
|
"span", {"class": "submitted-user-answer"}
|
||||||
)
|
)
|
||||||
assert len(submitted_answers) == 5 * len(self.generated_questions)
|
assert len(submitted_answers) == self.answers_per_question * len(
|
||||||
|
self.generated_questions
|
||||||
|
)
|
||||||
|
|
||||||
for ans in submitted_answers[:-5]:
|
print(submitted_answers)
|
||||||
|
|
||||||
|
for ans in submitted_answers[:-self.answers_per_question]:
|
||||||
|
print("------", ans.text.strip())
|
||||||
|
match self.app_name:
|
||||||
|
case "physics":
|
||||||
assert ans.text.strip() == "True"
|
assert ans.text.strip() == "True"
|
||||||
|
case "sbas":
|
||||||
|
assert ans.text.strip() == "a"
|
||||||
|
|
||||||
# The new question will not have any answers
|
# The new question will not have any answers
|
||||||
for ans in submitted_answers[-5:]:
|
for ans in submitted_answers[-self.answers_per_question:]:
|
||||||
assert ans.text.strip() == ""
|
assert ans.text.strip() == "Not answered"
|
||||||
|
|
||||||
|
match self.app_name:
|
||||||
|
case "physics":
|
||||||
assert (
|
assert (
|
||||||
cid_exam_scores_soup.find("div", {"class": "score-overview"})
|
cid_exam_scores_soup.find("div", {"class": "score-overview"})
|
||||||
.find("span", {"id": "total-score"})
|
.find("span", {"id": "total-score"})
|
||||||
@@ -503,24 +559,41 @@ class ExamTester:
|
|||||||
.string
|
.string
|
||||||
== "20"
|
== "20"
|
||||||
)
|
)
|
||||||
|
case "sbas":
|
||||||
|
assert (
|
||||||
|
cid_exam_scores_soup.find("div", {"class": "score-overview"})
|
||||||
|
.find("span", {"id": "total-score"})
|
||||||
|
.string
|
||||||
|
== "3"
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
cid_exam_scores_soup.find("div", {"class": "score-overview"})
|
||||||
|
.find("span", {"id": "max-score"})
|
||||||
|
.string
|
||||||
|
== "4"
|
||||||
|
)
|
||||||
|
|
||||||
def check_cid_scores_user_page2(self):
|
def check_cid_scores_user_page2(self):
|
||||||
|
|
||||||
cid_exam_scores_res = self.client.get(self.exam_scores_url)
|
cid_exam_scores_res = self.client.get(self.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 1 * answer_count
|
||||||
|
match self.app_name:
|
||||||
# As we have answered correctly the new score should be increased by 5
|
case "physics":
|
||||||
assert (
|
assert (
|
||||||
cid_exam_scores_soup.find("div", {"class": "score-overview"})
|
cid_exam_scores_soup.find("div", {"class": "score-overview"})
|
||||||
.find("span", {"id": "total-score"})
|
.find("span", {"id": "total-score"})
|
||||||
.string
|
.string
|
||||||
== "14"
|
== "14"
|
||||||
), f"Current user: {self.current_user}"
|
), f"Invalid answer count, Current user: {self.current_user}"
|
||||||
|
case "sbas":
|
||||||
|
assert (
|
||||||
|
cid_exam_scores_soup.find("div", {"class": "score-overview"})
|
||||||
|
.find("span", {"id": "total-score"})
|
||||||
|
.string
|
||||||
|
== "4"
|
||||||
|
), f"Invalid answer count, Current user: {self.current_user}"
|
||||||
|
|
||||||
# Check what happens when exam is not active
|
# Check what happens when exam is not active
|
||||||
self.exam.active = False
|
self.exam.active = False
|
||||||
@@ -553,6 +626,7 @@ class ExamTester:
|
|||||||
)
|
)
|
||||||
cid_take_res = self.client.get(cid_take_url)
|
cid_take_res = self.client.get(cid_take_url)
|
||||||
cid_take_soup = BeautifulSoup(cid_take_res.content, "html.parser")
|
cid_take_soup = BeautifulSoup(cid_take_res.content, "html.parser")
|
||||||
|
print(cid_take_soup.text)
|
||||||
assert "Exam is currently inactive" in cid_take_soup.text
|
assert "Exam is currently inactive" in cid_take_soup.text
|
||||||
|
|
||||||
# Delete the last question, from both the list
|
# Delete the last question, from both the list
|
||||||
|
|||||||
+11
-7
@@ -470,20 +470,25 @@ class Rapid(QuestionBase):
|
|||||||
return new_answers
|
return new_answers
|
||||||
|
|
||||||
def get_question_json(
|
def get_question_json(
|
||||||
self, based: bool = True, feedback: bool = False, answers: bool = True, history: bool = False, annotations: bool = False
|
self,
|
||||||
|
based: bool = True,
|
||||||
|
feedback: bool = False,
|
||||||
|
answers: bool = True,
|
||||||
|
history: bool = False,
|
||||||
|
annotations: bool = False,
|
||||||
) -> QuestionData:
|
) -> QuestionData:
|
||||||
"""Returns a json representation of the question"""
|
"""Returns a json representation of the question"""
|
||||||
|
|
||||||
# Loop through rapidimage associations
|
# Loop through rapidimage associations
|
||||||
images = []
|
images = []
|
||||||
annotations = []
|
annotations_list = []
|
||||||
feedback_images = []
|
feedback_images = []
|
||||||
for i in self.images.all():
|
for i in self.images.all():
|
||||||
# TODO: add anotations
|
# TODO: add anotations
|
||||||
|
|
||||||
if i.feedback_image == True:
|
if i.feedback_image == True:
|
||||||
if feedback:
|
if feedback:
|
||||||
annotations.append(i.image_annotations)
|
annotations_list.append(i.image_annotations)
|
||||||
if based:
|
if based:
|
||||||
feedback_images.append(image_as_base64(i.image))
|
feedback_images.append(image_as_base64(i.image))
|
||||||
else:
|
else:
|
||||||
@@ -492,7 +497,7 @@ class Rapid(QuestionBase):
|
|||||||
)
|
)
|
||||||
# feedback_images.append("{}/{}".format(settings.REMOTE_URL, i.image.url))
|
# feedback_images.append("{}/{}".format(settings.REMOTE_URL, i.image.url))
|
||||||
else:
|
else:
|
||||||
annotations.append(i.image_annotations)
|
annotations_list.append(i.image_annotations)
|
||||||
if based:
|
if based:
|
||||||
images.append(image_as_base64(i.image))
|
images.append(image_as_base64(i.image))
|
||||||
else:
|
else:
|
||||||
@@ -505,7 +510,7 @@ class Rapid(QuestionBase):
|
|||||||
}
|
}
|
||||||
|
|
||||||
if annotations:
|
if annotations:
|
||||||
json["annotations"] = annotations
|
json["annotations"] = annotations_list
|
||||||
|
|
||||||
if history:
|
if history:
|
||||||
json["history"] = self.history
|
json["history"] = self.history
|
||||||
@@ -736,6 +741,7 @@ class Exam(ExamBase):
|
|||||||
@reversion.register
|
@reversion.register
|
||||||
class UserAnswer(UserAnswerBase):
|
class UserAnswer(UserAnswerBase):
|
||||||
"""User answers by candidate"""
|
"""User answers by candidate"""
|
||||||
|
|
||||||
app_name = "rapids"
|
app_name = "rapids"
|
||||||
|
|
||||||
question = models.ForeignKey(
|
question = models.ForeignKey(
|
||||||
@@ -748,7 +754,6 @@ class UserAnswer(UserAnswerBase):
|
|||||||
|
|
||||||
answer_compare = models.TextField(max_length=500, blank=True)
|
answer_compare = models.TextField(max_length=500, blank=True)
|
||||||
|
|
||||||
|
|
||||||
user = models.ForeignKey(
|
user = models.ForeignKey(
|
||||||
settings.AUTH_USER_MODEL,
|
settings.AUTH_USER_MODEL,
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
@@ -781,7 +786,6 @@ class UserAnswer(UserAnswerBase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
|
||||||
name = self.get_candidate_name()
|
name = self.get_candidate_name()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -57,6 +57,8 @@ def create_rapid_image(question):
|
|||||||
image = tempfile.NamedTemporaryFile(
|
image = tempfile.NamedTemporaryFile(
|
||||||
dir=settings.MEDIA_ROOT, suffix=".jpg", delete=False
|
dir=settings.MEDIA_ROOT, suffix=".jpg", delete=False
|
||||||
)
|
)
|
||||||
|
image.flush()
|
||||||
|
image._committed = True
|
||||||
|
|
||||||
rapid_image = RapidImage(image=image, rapid=question)
|
rapid_image = RapidImage(image=image, rapid=question)
|
||||||
|
|
||||||
@@ -189,6 +191,9 @@ def test_exams(db, client):
|
|||||||
assert res.status_code == 200
|
assert res.status_code == 200
|
||||||
exam_json = json.loads(res.content)
|
exam_json = json.loads(res.content)
|
||||||
print(exam_json)
|
print(exam_json)
|
||||||
|
print(exam_json["questions"].values())
|
||||||
|
print("----")
|
||||||
|
print(question1.get_question_json(answers=False))
|
||||||
|
|
||||||
assert (
|
assert (
|
||||||
question1.get_question_json(answers=False)
|
question1.get_question_json(answers=False)
|
||||||
|
|||||||
+1
-1
@@ -213,7 +213,7 @@ class UserAnswer(UserAnswerBase):
|
|||||||
answer = models.CharField(
|
answer = models.CharField(
|
||||||
max_length=20,
|
max_length=20,
|
||||||
choices=Question.BEST_ANSWER_CHOICES,
|
choices=Question.BEST_ANSWER_CHOICES,
|
||||||
help_text="The correct answer",
|
help_text="The user selected answer",
|
||||||
)
|
)
|
||||||
|
|
||||||
user = models.ForeignKey(
|
user = models.ForeignKey(
|
||||||
|
|||||||
@@ -20,8 +20,8 @@
|
|||||||
<span class="correct-answer">Correct answer: {{correct_answer|safe}}</span> <br />
|
<span class="correct-answer">Correct answer: {{correct_answer|safe}}</span> <br />
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<span class="submitted-user-answer">{{a}}</span>
|
Chosen answer: <span class="submitted-user-answer">{{a}}</span>
|
||||||
Chosen answer: {{chosen_answer|safe}}
|
{{chosen_answer|safe}}
|
||||||
{% if exam.publish_results or view_all_results %}
|
{% if exam.publish_results or view_all_results %}
|
||||||
<span class="answer-{{score}}">(Score: {{score}})</span>
|
<span class="answer-{{score}}">(Score: {{score}})</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -190,22 +190,14 @@ def test_exams(db, client):
|
|||||||
if exam_tester.testing_cid_user:
|
if exam_tester.testing_cid_user:
|
||||||
cid_user_answer = UserAnswer(
|
cid_user_answer = UserAnswer(
|
||||||
question=extra_question,
|
question=extra_question,
|
||||||
a=True,
|
answer = "a",
|
||||||
b=False,
|
|
||||||
c=True,
|
|
||||||
d=False,
|
|
||||||
e=True,
|
|
||||||
cid=cid_user.cid,
|
cid=cid_user.cid,
|
||||||
exam=exam,
|
exam=exam,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
cid_user_answer = UserAnswer(
|
cid_user_answer = UserAnswer(
|
||||||
question=extra_question,
|
question=extra_question,
|
||||||
a=True,
|
answer = "a",
|
||||||
b=False,
|
|
||||||
c=True,
|
|
||||||
d=False,
|
|
||||||
e=True,
|
|
||||||
user=exam_tester.current_user,
|
user=exam_tester.current_user,
|
||||||
exam=exam,
|
exam=exam,
|
||||||
)
|
)
|
||||||
|
|||||||
+10
-10
@@ -72,16 +72,16 @@ urlpatterns = [
|
|||||||
views.UserAnswerDelete.as_view(),
|
views.UserAnswerDelete.as_view(),
|
||||||
name="user_answer_delete",
|
name="user_answer_delete",
|
||||||
),
|
),
|
||||||
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,
|
||||||
name="exam_scores_cid_user",
|
# name="exam_scores_cid_user",
|
||||||
),
|
#),
|
||||||
path(
|
#path(
|
||||||
"exam/<int:pk>/scores/",
|
# "exam/<int:pk>/scores/",
|
||||||
views.exam_scores_cid_user,
|
# views.exam_scores_cid_user,
|
||||||
name="exam_scores_user",
|
# name="exam_scores_user",
|
||||||
),
|
#),
|
||||||
]
|
]
|
||||||
|
|
||||||
urlpatterns.extend(generic_view_urls(views.GenericViews))
|
urlpatterns.extend(generic_view_urls(views.GenericViews))
|
||||||
|
|||||||
+66
-63
@@ -99,69 +99,69 @@ def active_exams(request):
|
|||||||
return render(request, "sbas/available_exam_list.html", {"exams": active_exams})
|
return render(request, "sbas/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)
|
||||||
|
#
|
||||||
exam.check_user_can_review(cid, passcode, request)
|
# exam.check_user_can_review(cid, passcode, request)
|
||||||
|
#
|
||||||
questions = exam.exam_questions.all()
|
# questions = exam.exam_questions.all()
|
||||||
|
#
|
||||||
answers_and_marks = []
|
# answers_and_marks = []
|
||||||
answers_marks = []
|
# answers_marks = []
|
||||||
answers = []
|
# answers = []
|
||||||
|
#
|
||||||
view_all_results = False
|
# view_all_results = False
|
||||||
if request.user.groups.filter(name="view_all_results").exists():
|
# if request.user.groups.filter(name="view_all_results").exists():
|
||||||
view_all_results = True
|
# view_all_results = True
|
||||||
|
#
|
||||||
for q in questions:
|
# for q in questions:
|
||||||
# Get user answer
|
# # Get user answer
|
||||||
if cid is not None:
|
# if cid is not None:
|
||||||
user_answer = q.cid_user_answers.filter(cid=cid, exam__id=pk).first()
|
# user_answer = q.cid_user_answers.filter(cid=cid, exam__id=pk).first()
|
||||||
else:
|
# else:
|
||||||
user_answer = q.cid_user_answers.filter(
|
# user_answer = q.cid_user_answers.filter(
|
||||||
user=request.user, exam__id=pk
|
# user=request.user, exam__id=pk
|
||||||
).first()
|
# ).first()
|
||||||
|
#
|
||||||
if user_answer is None:
|
# if user_answer is None:
|
||||||
answer_score = 0
|
# answer_score = 0
|
||||||
ans = ""
|
# ans = ""
|
||||||
else:
|
# else:
|
||||||
answer_score = user_answer.get_answer_score()
|
# answer_score = user_answer.get_answer_score()
|
||||||
ans = user_answer.get_answer()
|
# ans = user_answer.get_answer()
|
||||||
|
#
|
||||||
correct_answer = q.get_correct_answer()
|
# correct_answer = q.get_correct_answer()
|
||||||
|
#
|
||||||
if not exam.publish_results and not view_all_results:
|
# if not exam.publish_results and not view_all_results:
|
||||||
correct_answer = "*****"
|
# correct_answer = "*****"
|
||||||
answer_score = 0
|
# answer_score = 0
|
||||||
answers.append(ans)
|
# answers.append(ans)
|
||||||
answers_marks.append(answer_score)
|
# answers_marks.append(answer_score)
|
||||||
|
#
|
||||||
merged_ans = (ans, answer_score, correct_answer)
|
# merged_ans = (ans, answer_score, correct_answer)
|
||||||
chosen_answer = q.get_answer_by_choice(ans)
|
# chosen_answer = q.get_answer_by_choice(ans)
|
||||||
answers_and_marks.append((q, *merged_ans, chosen_answer))
|
# answers_and_marks.append((q, *merged_ans, chosen_answer))
|
||||||
|
#
|
||||||
total_score = sum(answers_marks)
|
# total_score = sum(answers_marks)
|
||||||
|
#
|
||||||
max_score = len(questions)
|
# max_score = len(questions)
|
||||||
|
#
|
||||||
return render(
|
# return render(
|
||||||
request,
|
# request,
|
||||||
"sbas/exam_scores_user.html",
|
# "sbas/exam_scores_user.html",
|
||||||
{
|
# {
|
||||||
"exam": exam,
|
# "exam": exam,
|
||||||
"cid": cid,
|
# "cid": cid,
|
||||||
"passcode": passcode,
|
# "passcode": passcode,
|
||||||
"questions": questions,
|
# "questions": questions,
|
||||||
"answers": answers,
|
# "answers": answers,
|
||||||
"answers_marks": answers_marks,
|
# "answers_marks": answers_marks,
|
||||||
"total_score": total_score,
|
# "total_score": total_score,
|
||||||
"max_score": max_score,
|
# "max_score": max_score,
|
||||||
"answers_and_marks": answers_and_marks,
|
# "answers_and_marks": answers_and_marks,
|
||||||
"view_all_results": view_all_results,
|
# "view_all_results": view_all_results,
|
||||||
},
|
# },
|
||||||
)
|
# )
|
||||||
|
|
||||||
|
|
||||||
def exam_start(request, pk):
|
def exam_start(request, pk):
|
||||||
@@ -225,6 +225,9 @@ def exam_take_overview(request, pk, cid=None, passcode=None):
|
|||||||
def exam_take(request, pk: int, sk: int, cid: int = None, passcode: str = None):
|
def exam_take(request, pk: int, sk: int, cid: int = None, passcode: str = None):
|
||||||
exam = get_object_or_404(Exam, pk=pk)
|
exam = get_object_or_404(Exam, pk=pk)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
cid_user_exam = exam.get_or_create_cid_user_exam(cid = cid, user_user=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