diff --git a/anatomy/models.py b/anatomy/models.py
index ba5539ef..40063fb4 100644
--- a/anatomy/models.py
+++ b/anatomy/models.py
@@ -199,7 +199,15 @@ class AnatomyQuestion(models.Model):
)
)
- def get_unmarked_user_answers(self, exam_pk=None):
+ def get_unmarked_user_answers(self, exam_pk: int|None = None):
+ """_summary_
+
+ Args:
+ exam_pk (_type_, optional): _description_. Defaults to None.
+
+ Returns:
+ set: set of unmarked user strings
+ """
if exam_pk is None:
user_answers = set([i.answer_compare for i in self.cid_user_answers.all()])
else:
diff --git a/anatomy/templates/anatomy/exam_scores_user.html b/anatomy/templates/anatomy/exam_scores_user.html
index 274c85d0..39f80027 100644
--- a/anatomy/templates/anatomy/exam_scores_user.html
+++ b/anatomy/templates/anatomy/exam_scores_user.html
@@ -14,13 +14,16 @@
{% include 'user_score_header.html' %}
-
{% for ans, score, correct_answer in answers_and_marks %}
- - Question {{forloop.counter}} - Correct answer: {{correct_answer }}
-
- {{ans}} ({{score}})
-
- View
- {% endfor %}
+
+ {% for ans, score, correct_answer in answers_and_marks %}
+ - Question {{forloop.counter}} - Correct answer: {{correct_answer}}
+
+
+ {{ans}} ({{score}})
+
+ View
+
+ {% endfor %}
{% include 'user_scores_footer.html' %}
diff --git a/anatomy/tests/test_exams.py b/anatomy/tests/test_exams.py
index 480fbb36..60118b74 100644
--- a/anatomy/tests/test_exams.py
+++ b/anatomy/tests/test_exams.py
@@ -15,7 +15,7 @@ from bs4 import BeautifulSoup
from anatomy.views import GenericExamViews as AnatomyExamViews
-from anatomy.models import Exam, AnatomyQuestion as Question, QuestionType, Structure
+from anatomy.models import Answer, Exam, AnatomyQuestion as Question, QuestionType, Structure
from generic.models import CidUser, CidUserGroup
@@ -66,7 +66,7 @@ def create_question_types(db):
question_type.save()
-def create_question(exam):
+def create_question(exam, answer=None):
# Just assign random stuff
image = tempfile.NamedTemporaryFile(
@@ -79,6 +79,15 @@ def create_question(exam):
image=image.name,
)
question.exams.set([exam])
+
+ # Create an answer for the question
+ if answer is None:
+ answer_text = "testanswer"
+ else:
+ answer_text = answer
+ answer = Answer(question=question, answer=answer_text, answer_compare=answer_text, status=Answer.MarkOptions.CORRECT)
+ answer.save()
+
return question
@@ -219,21 +228,118 @@ def test_exams(db, client):
# Test submission
# give three answers
- print("post_json", post_json)
res = client.post(reverse("global_exam_answers_submit"), data=post_json)
json_res = json.loads(res.content)
- print(json_res)
assert json_res["success"] == True
assert json_res["question_count"] == 3
# We can then check the answers have been submitted (and can be viewed by the user)
+
+ # Get overview scores page
cid_scores_res = client.get(
reverse(f"cid_scores", kwargs={"cid": 1001, "passcode": "EFGH"})
)
cid_scores_soup = BeautifulSoup(cid_scores_res.content, "html.parser")
- assigned_exams = cid_scores_soup.find(id="exam-assigned").find_all("li")
- print(assigned_exams)
+ print(cid_scores_soup.find("div", {"id" : "exam-assigned"}))
+ search_exam = cid_scores_soup.find("div", {"id" : "exam-assigned"}).find("ul", {"class" : exam.app_name}).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
+
+
+ # Load the exam results page
+ cid_exam_scores_res = client.get(
+ reverse(f"{exam.app_name}:exam_scores_cid_user", kwargs={"pk": exam.pk, "cid": 1001, "passcode": "EFGH"})
+ )
+ cid_exam_scores_soup = BeautifulSoup(cid_exam_scores_res.content, "html.parser")
+
+ # Check that we have an alert that exams are not published
+ alert = cid_exam_scores_soup.find("div", {"class": "alert"})
+ assert "Results are not currently published." in str(alert)
+
+
+ answer_lis = cid_exam_scores_soup.find("ul", {"class": "score-answer-list"}).find_all("li")
+
+ assert len(answer_lis) == len(valid_answers)
+
+ for n in range(len(valid_answers)): # Check no answers are visible if results not published
+ answer = valid_answers[n]
+ li = answer_lis[n]
+
+ assert answer["ans"] in str(li) # Check that the saved answer is shown
+
+ assert li.find("span", {"class": "correct-answer"}).string == "*****" # and that the correct answer isn't
+
+ assert cid_exam_scores_soup.find("div", {"class": "score-overview"}) is None # check we hide the score overview if not published
+
+ # Add another question to the exam
+ create_question(exam)
+
+ # Load the exam results page (again)
+ cid_exam_scores_res = client.get(
+ reverse(f"{exam.app_name}:exam_scores_cid_user", kwargs={"pk": exam.pk, "cid": 1001, "passcode": "EFGH"})
+ )
+ cid_exam_scores_soup = BeautifulSoup(cid_exam_scores_res.content, "html.parser")
+
+ answer_lis = cid_exam_scores_soup.find("ul", {"class": "score-answer-list"}).find_all("li")
+ assert len(answer_lis) == len(valid_answers) + 1
+ assert "Not answered" in str(answer_lis[-1])
+
+ # Publish the results!
+ exam.publish_results = True
+ exam.save()
+
+ # Load the exam results page (again)
+ cid_exam_scores_res = client.get(
+ reverse(f"{exam.app_name}:exam_scores_cid_user", kwargs={"pk": exam.pk, "cid": 1001, "passcode": "EFGH"})
+ )
+ cid_exam_scores_soup = BeautifulSoup(cid_exam_scores_res.content, "html.parser")
+
+ assert cid_exam_scores_soup.find("div", {"class": "alert"}) is None # Check our warning banner is gone
+
+ answer_lis = cid_exam_scores_soup.find("ul", {"class": "score-answer-list"}).find_all("li")
+
+ assert len(answer_lis) == len(valid_answers) + 1
+ assert "Not answered" in str(answer_lis[-1])
+
+ # Repetition....
+ for n in range(len(valid_answers)): # Check answers are visible if results published
+ answer = valid_answers[n]
+ li = answer_lis[n]
+
+ assert answer["ans"] in str(li) # Check that the saved answer is shown
+
+ assert li.find("span", {"class": "correct-answer"}).string == "testanswer"
+
+ assert cid_exam_scores_soup.find("div", {"class": "score-overview"}).find("span", {"id": "total-score"}).string == "0 (3 unmarked)"
+
+
+ for question in exam.exam_questions.all():
+ try:
+ new_ans = Answer(question=question, answer=question.get_unmarked_user_answers().pop(), status=Answer.MarkOptions.CORRECT)
+ new_ans.save()
+ except KeyError: # Final question does not have an answer
+ pass
+
+ # Load the exam results page (again)
+ cid_exam_scores_res = client.get(
+ reverse(f"{exam.app_name}:exam_scores_cid_user", kwargs={"pk": exam.pk, "cid": 1001, "passcode": "EFGH"})
+ )
+ cid_exam_scores_soup = BeautifulSoup(cid_exam_scores_res.content, "html.parser")
+
+ assert cid_exam_scores_soup.find("div", {"class": "score-overview"}).find("span", {"id": "total-score"}).string == "6"
\ No newline at end of file
diff --git a/generic/models.py b/generic/models.py
index d120e40b..291f579d 100644
--- a/generic/models.py
+++ b/generic/models.py
@@ -423,12 +423,12 @@ class QuestionNote(models.Model):
return f"{self.note_type} - {self.note}"
EXAM_TYPES = (
- ("Physics", "physics_exams"),
- ("Rapids", "rapid_exams"),
- ("SBAs", "sba_exams"),
- ("Anatomy", "anatomy_exams"),
- ("Longs", "longs_exams"),
- ("CaseCollection", "casecollection_exams"),
+ ("physics", "physics_exams"),
+ ("rapids", "rapid_exams"),
+ ("sbas", "sba_exams"),
+ ("anatomy", "anatomy_exams"),
+ ("longs", "longs_exams"),
+ ("casecollection", "casecollection_exams"),
)
class CidUser(models.Model):
diff --git a/generic/templates/generic/ciduser_form.html b/generic/templates/generic/ciduser_form.html
index 088e5ec8..d20a18f2 100755
--- a/generic/templates/generic/ciduser_form.html
+++ b/generic/templates/generic/ciduser_form.html
@@ -29,7 +29,7 @@
{% with object.get_cid_exams as exam_map %}
- {% for exam_type, exams in exam_map.items %}
+ {% for exam_type, exams in exam_map %}
{{exam_type}}
{% for exam in exams %}
diff --git a/rad/urls.py b/rad/urls.py
index 28cd869b..3699d0b2 100644
--- a/rad/urls.py
+++ b/rad/urls.py
@@ -151,7 +151,7 @@ handler500 = "rad.views.server_error"
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
if settings.DEBUG:
- urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
+ #urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
urlpatterns += static("/rts/", document_root="rts")
import debug_toolbar
diff --git a/templates/cid_scores.html b/templates/cid_scores.html
index 13e15755..23984ba9 100644
--- a/templates/cid_scores.html
+++ b/templates/cid_scores.html
@@ -7,44 +7,44 @@
The following exams will be available to take when active.
{% for exam_type, exams in available_exams %}
-
{{exam_type}}
+
{{exam_type|title}}
{% endfor %}
+
- {% if all_exams %}
-
- Exam results
-
- The following exam results been found. Click to view answers (and scores when the results are published):
- {% for exam_type, exams in all_exams %}
- {% if exams %}
-
{{exam_type|title}}
-
- {% for exam in exams %}
- - {{exam.name}} {% if exam.active %}[Active]{% endif %} {% if exam.publish_results %}[Results Published]{% endif %}
- {% endfor %}
-
- {% endif %}
- {% endfor %}
-
- {% endif %}
+ {% if all_exams %}
+ Exam results
+
+ The following exam results been found. Click to view answers (and scores when the results are published):
+ {% for exam_type, exams in all_exams %}
+ {% if exams %}
+
{{exam_type|title}}
+
+ {% for exam in exams %}
+ - {{exam.name}} {% if exam.active %}[Active]{% endif %} {% if exam.publish_results %}[Results Published]{% endif %}
+ {% endfor %}
+
+ {% endif %}
+ {% endfor %}
+
+ {% endif %}
- {% if case_collections %}
-
-
Case Collection
- The following Case Collections have been found.
-
+
+ {% endif %}
-
+
{% endblock %}
diff --git a/templates/user_scores_footer.html b/templates/user_scores_footer.html
index 2ddfddd4..4605db6c 100644
--- a/templates/user_scores_footer.html
+++ b/templates/user_scores_footer.html
@@ -1,8 +1,10 @@
{% if view_all_results or exam.publish_results %}
-
Total mark: {{ total_score }} / {{max_score}}
+
+
Total marks: {{ total_score }} / {{max_score}}
{% if normalised_score %}
Normalised score: {{ normalised_score }}
{% endif %}
+
{% endif %}
{% if cid %}