more cid user anatomy testing
This commit is contained in:
+9
-1
@@ -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:
|
if exam_pk is None:
|
||||||
user_answers = set([i.answer_compare for i in self.cid_user_answers.all()])
|
user_answers = set([i.answer_compare for i in self.cid_user_answers.all()])
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -14,12 +14,15 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="anatomy">
|
<div class="anatomy">
|
||||||
{% include 'user_score_header.html' %}
|
{% include 'user_score_header.html' %}
|
||||||
<ul class="score-answer-list">{% for ans, score, correct_answer in answers_and_marks %}
|
<ul class="score-answer-list">
|
||||||
<li class="user-answer-li">Question {{forloop.counter}} - Correct answer: <span class="correct-answer">{{correct_answer }}</span></li>
|
{% for ans, score, correct_answer in answers_and_marks %}
|
||||||
|
<li class="user-answer-li" data-question-number="{{forloop.counter}}">Question {{forloop.counter}} - Correct answer: <span class="correct-answer">{{correct_answer}}</span>
|
||||||
|
<br/>
|
||||||
<span class="user-answer-score user-answer-score-{{score}} physics-ans">
|
<span class="user-answer-score user-answer-score-{{score}} physics-ans">
|
||||||
<pre>{{ans}}</pre> ({{score}})
|
<pre>{{ans}}</pre> ({{score}})
|
||||||
</span>
|
</span>
|
||||||
<span class="view-question-link" data-qn={{forloop.counter0}}>View</span>
|
<span class="view-question-link" data-qn={{forloop.counter0}}>View</span>
|
||||||
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
{% include 'user_scores_footer.html' %}
|
{% include 'user_scores_footer.html' %}
|
||||||
|
|||||||
+112
-6
@@ -15,7 +15,7 @@ from bs4 import BeautifulSoup
|
|||||||
|
|
||||||
from anatomy.views import GenericExamViews as AnatomyExamViews
|
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
|
from generic.models import CidUser, CidUserGroup
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@ def create_question_types(db):
|
|||||||
question_type.save()
|
question_type.save()
|
||||||
|
|
||||||
|
|
||||||
def create_question(exam):
|
def create_question(exam, answer=None):
|
||||||
# Just assign random stuff
|
# Just assign random stuff
|
||||||
|
|
||||||
image = tempfile.NamedTemporaryFile(
|
image = tempfile.NamedTemporaryFile(
|
||||||
@@ -79,6 +79,15 @@ def create_question(exam):
|
|||||||
image=image.name,
|
image=image.name,
|
||||||
)
|
)
|
||||||
question.exams.set([exam])
|
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
|
return question
|
||||||
|
|
||||||
|
|
||||||
@@ -219,21 +228,118 @@ def test_exams(db, client):
|
|||||||
|
|
||||||
# Test submission
|
# Test submission
|
||||||
# give three answers
|
# give three answers
|
||||||
print("post_json", post_json)
|
|
||||||
res = client.post(reverse("global_exam_answers_submit"), data=post_json)
|
res = client.post(reverse("global_exam_answers_submit"), data=post_json)
|
||||||
|
|
||||||
json_res = json.loads(res.content)
|
json_res = json.loads(res.content)
|
||||||
print(json_res)
|
|
||||||
|
|
||||||
assert json_res["success"] == True
|
assert json_res["success"] == True
|
||||||
assert json_res["question_count"] == 3
|
assert json_res["question_count"] == 3
|
||||||
|
|
||||||
# We can then check the answers have been submitted (and can be viewed by the user)
|
# 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(
|
cid_scores_res = client.get(
|
||||||
reverse(f"cid_scores", kwargs={"cid": 1001, "passcode": "EFGH"})
|
reverse(f"cid_scores", kwargs={"cid": 1001, "passcode": "EFGH"})
|
||||||
)
|
)
|
||||||
|
|
||||||
cid_scores_soup = BeautifulSoup(cid_scores_res.content, "html.parser")
|
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"
|
||||||
+6
-6
@@ -423,12 +423,12 @@ class QuestionNote(models.Model):
|
|||||||
return f"{self.note_type} - {self.note}"
|
return f"{self.note_type} - {self.note}"
|
||||||
|
|
||||||
EXAM_TYPES = (
|
EXAM_TYPES = (
|
||||||
("Physics", "physics_exams"),
|
("physics", "physics_exams"),
|
||||||
("Rapids", "rapid_exams"),
|
("rapids", "rapid_exams"),
|
||||||
("SBAs", "sba_exams"),
|
("sbas", "sba_exams"),
|
||||||
("Anatomy", "anatomy_exams"),
|
("anatomy", "anatomy_exams"),
|
||||||
("Longs", "longs_exams"),
|
("longs", "longs_exams"),
|
||||||
("CaseCollection", "casecollection_exams"),
|
("casecollection", "casecollection_exams"),
|
||||||
)
|
)
|
||||||
|
|
||||||
class CidUser(models.Model):
|
class CidUser(models.Model):
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
|
|
||||||
<p>
|
<p>
|
||||||
{% with object.get_cid_exams as exam_map %}
|
{% with object.get_cid_exams as exam_map %}
|
||||||
{% for exam_type, exams in exam_map.items %}
|
{% for exam_type, exams in exam_map %}
|
||||||
<h4>{{exam_type}}</h4>
|
<h4>{{exam_type}}</h4>
|
||||||
<ul>
|
<ul>
|
||||||
{% for exam in exams %}
|
{% for exam in exams %}
|
||||||
|
|||||||
+1
-1
@@ -151,7 +151,7 @@ handler500 = "rad.views.server_error"
|
|||||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||||
|
|
||||||
if settings.DEBUG:
|
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(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
||||||
urlpatterns += static("/rts/", document_root="rts")
|
urlpatterns += static("/rts/", document_root="rts")
|
||||||
import debug_toolbar
|
import debug_toolbar
|
||||||
|
|||||||
@@ -7,16 +7,16 @@
|
|||||||
<div id="exam-assigned">
|
<div id="exam-assigned">
|
||||||
The following exams will be available to take when active.
|
The following exams will be available to take when active.
|
||||||
{% for exam_type, exams in available_exams %}
|
{% for exam_type, exams in available_exams %}
|
||||||
<h4>{{exam_type}}</h4>
|
<h4>{{exam_type|title}}</h4>
|
||||||
<ul class='{{exam_type}}'>
|
<ul class='{{exam_type}}'>
|
||||||
{% for exam in exams %}
|
{% for exam in exams %}
|
||||||
<li class="exam"><a href="{{exam.get_take_url}}?cid={{cid}}&passcode={{passcode}}" target="_blank">{{exam.name}} {% if exam.active %}[Active]{% endif %}</a></li>
|
<li class="exam" data-exam-id="{{exam.pk}}"><a href="{{exam.get_take_url}}?cid={{cid}}&passcode={{passcode}}" target="_blank">{{exam.name}} {% if exam.active %}[Active]{% endif %}</a></li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
{% if all_exams %}
|
{% if all_exams %}
|
||||||
</div>
|
|
||||||
<h3>Exam results</h3>
|
<h3>Exam results</h3>
|
||||||
<div id="exam-results">
|
<div id="exam-results">
|
||||||
The following exam results been found. Click to view answers (and scores when the results are published):
|
The following exam results been found. Click to view answers (and scores when the results are published):
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
<h4>{{exam_type|title}}</h4>
|
<h4>{{exam_type|title}}</h4>
|
||||||
<ul class='{{exam_type}}'>
|
<ul class='{{exam_type}}'>
|
||||||
{% for exam in exams %}
|
{% for exam in exams %}
|
||||||
<li class="exam"><a href="{% url exam_type|add:':exam_scores_cid_user' pk=exam.pk cid=cid passcode=passcode %}">{{exam.name}}</a> {% if exam.active %}[Active]{% endif %} {% if exam.publish_results %}[Results Published]{% endif %}</li>
|
<li class="exam" data-exam-id="{{exam.pk}}"><a href="{% url exam_type|add:':exam_scores_cid_user' pk=exam.pk cid=cid passcode=passcode %}">{{exam.name}}</a> {% if exam.active %}[Active]{% endif %} {% if exam.publish_results %}[Results Published]{% endif %}</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
{% if view_all_results or exam.publish_results %}
|
{% if view_all_results or exam.publish_results %}
|
||||||
<br /> Total mark: {{ total_score }} / {{max_score}}
|
<div class="score-overview">
|
||||||
|
<br /> Total marks: <span id="total-score">{{ total_score }}</span> / <span id="max-score">{{max_score}}</span>
|
||||||
{% if normalised_score %}
|
{% if normalised_score %}
|
||||||
<br /> Normalised score: {{ normalised_score }}
|
<br /> Normalised score: {{ normalised_score }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div>
|
<div>
|
||||||
{% if cid %}
|
{% if cid %}
|
||||||
|
|||||||
Reference in New Issue
Block a user