fix longs tests
This commit is contained in:
+19
-6
@@ -345,9 +345,10 @@ class Long(QuestionBase):
|
||||
|
||||
return url
|
||||
|
||||
def get_unanswered_mark_and_text(self) -> tuple[int, str]:
|
||||
def get_unanswered_mark_and_text(self) -> tuple[int, tuple[str, str, str, str, str]]:
|
||||
"""
|
||||
Override in models if needed
|
||||
Long cases are receive a mark between 4 and 8
|
||||
Therefore unmarked = 4
|
||||
"""
|
||||
return (4, ("Not answered",) * 5)
|
||||
|
||||
@@ -862,7 +863,9 @@ class UserAnswer(UserAnswerBase):
|
||||
return "hidden"
|
||||
return "/".join(f"{str(i.score)}" for i in mark_objects)
|
||||
|
||||
def get_answer_score(self):
|
||||
def get_answer_score(self) -> float | str:
|
||||
"""Returns the answers score.
|
||||
If the answer is unmarked the string "unmarked" is returned"""
|
||||
if self.score == "":
|
||||
return "unmarked"
|
||||
return float(self.score)
|
||||
@@ -873,8 +876,19 @@ class UserAnswer(UserAnswerBase):
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_answer(self):
|
||||
(
|
||||
def get_answer(
|
||||
self,
|
||||
) -> tuple[
|
||||
models.TextField,
|
||||
models.TextField,
|
||||
models.TextField,
|
||||
models.TextField,
|
||||
models.TextField,
|
||||
]:
|
||||
"""Returns a tuple containing the users answers (model fields)
|
||||
|
||||
"""
|
||||
return (
|
||||
self.answer_observations,
|
||||
self.answer_interpretation,
|
||||
self.answer_principle_diagnosis,
|
||||
@@ -901,7 +915,6 @@ Management:
|
||||
"""
|
||||
|
||||
|
||||
|
||||
@reversion.register
|
||||
class AnswerMarks(models.Model):
|
||||
score = models.CharField(max_length=3, choices=UserAnswer.ScoreOptions.choices)
|
||||
|
||||
@@ -13,38 +13,34 @@
|
||||
</div>
|
||||
<div class="longs">
|
||||
{% include 'user_score_header.html' %}
|
||||
<ul>
|
||||
{% for score in answers_marks %}
|
||||
<li class="user-answer-li">Question {{forloop.counter}}</li>
|
||||
<span class="user-answer-score user-answer-score-{{score}}">
|
||||
<pre>{{ans}}</pre> ({{score}})
|
||||
</span>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<div>
|
||||
<h4>Answers</h4>
|
||||
<ul class="long-answer">{% for a,b,c,d,e in answer_text %}
|
||||
<li class="user-answer-li"><b>Question {{forloop.counter}}</b> <span class="view-question-link-longs"
|
||||
data-qn={{forloop.counter0}}>View</span></li>
|
||||
|
||||
<ul>
|
||||
<li class="user-answer-li">Observation</br>
|
||||
<pre>{{a}}</pre>
|
||||
{% comment %} <h4>Answers</h4> {% endcomment %}
|
||||
<ul class="score-answer-list">
|
||||
{% for ans, score, correct_answer in answers_and_marks %}
|
||||
<li class="user-answer-li" data-question-number="{{forloop.counter}}"><b>Question {{forloop.counter}}</b> <span class="view-question-link-longs"
|
||||
data-qn={{forloop.counter0}}>View</span>
|
||||
<ul>
|
||||
<li class="Observations" data-qidn="1">Observations</br>
|
||||
<pre>{{ans.0}}</pre>
|
||||
</li>
|
||||
<li class="Interpretation" data-qidn="2">Interpretation</br>
|
||||
<pre>{{ans.1}}</pre>
|
||||
</li>
|
||||
<li class="Principle Diagnosis" data-qidn="3">Principle Diagnosis</br>
|
||||
<pre>{{ans.2}}</pre>
|
||||
</li>
|
||||
<li class="Differential Diagnosis" data-qidn="4">Differential Diagnosis</br>
|
||||
<pre>{{ans.3}}</pre>
|
||||
</li>
|
||||
<li class="Management" data-qidn="5">Management</br>
|
||||
<pre>{{ans.4}}</pre>
|
||||
</li>
|
||||
{% if exam.publish_results %}
|
||||
<span class="answer-score">{{score}}</span>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</li>
|
||||
<li class="user-answer-li">Interpretation</br>
|
||||
<pre>{{b}}</pre>
|
||||
</li>
|
||||
<li class="user-answer-li">Principle Diagnosis</br>
|
||||
<pre>{{c}}</pre>
|
||||
</li>
|
||||
<li class="user-answer-li">Differential Diagnosis</br>
|
||||
<pre>{{d}}</pre>
|
||||
</li>
|
||||
<li class="user-answer-li">Management</br>
|
||||
<pre>{{e}}</pre>
|
||||
</li>
|
||||
</ul>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
@@ -73,42 +69,42 @@
|
||||
})
|
||||
// $.ajax().done(), $.ajax().fail(), $ajax().always() are upto you. Add/change accordingly
|
||||
.done(function (data) {
|
||||
console.log(data);
|
||||
console.log(data);
|
||||
|
||||
let event = new CustomEvent('loadDicomViewerUrls', {
|
||||
"detail": {
|
||||
"images": data.images[0],
|
||||
"annotations": data.annotations
|
||||
}
|
||||
});
|
||||
let event = new CustomEvent('loadDicomViewerUrls', {
|
||||
"detail": {
|
||||
"images": data.images[0],
|
||||
"annotations": data.annotations
|
||||
}
|
||||
});
|
||||
|
||||
window.dispatchEvent(event);
|
||||
n = parseInt(question_number) + 1
|
||||
$(".question-display-block .question-number").empty().append(n);
|
||||
$("#history").empty().append(data.title);
|
||||
$("#series").empty();
|
||||
data.image_titles.forEach((element, index) => {
|
||||
$("#series").append($(
|
||||
`<span class="image-link" title="click to view">${index + 1}: ${element}</span> `
|
||||
).click(() => {
|
||||
$("#single-dicom-viewer").empty();
|
||||
let event = new CustomEvent('loadDicomViewerUrls', {
|
||||
"detail": {
|
||||
"images": data.images[index],
|
||||
"annotations": data.annotations
|
||||
}
|
||||
});
|
||||
window.dispatchEvent(event);
|
||||
n = parseInt(question_number) + 1
|
||||
$(".question-display-block .question-number").empty().append(n);
|
||||
$("#history").empty().append(data.title);
|
||||
$("#series").empty();
|
||||
data.image_titles.forEach((element, index) => {
|
||||
$("#series").append($(
|
||||
`<span class="image-link" title="click to view">${index + 1}: ${element}</span> `
|
||||
).click(() => {
|
||||
$("#single-dicom-viewer").empty();
|
||||
let event = new CustomEvent('loadDicomViewerUrls', {
|
||||
"detail": {
|
||||
"images": data.images[index],
|
||||
"annotations": data.annotations
|
||||
}
|
||||
});
|
||||
|
||||
window.dispatchEvent(event);
|
||||
window.dispatchEvent(event);
|
||||
|
||||
}));
|
||||
}));
|
||||
|
||||
})
|
||||
$(".inner-display-block").show();
|
||||
})
|
||||
$(".inner-display-block").show();
|
||||
})
|
||||
.always(function () {
|
||||
console.log('[Done]');
|
||||
})
|
||||
console.log('[Done]');
|
||||
})
|
||||
})
|
||||
|
||||
$(".view-question-link").first().click();
|
||||
|
||||
+146
-80
@@ -1,3 +1,4 @@
|
||||
from collections import defaultdict
|
||||
import json
|
||||
import os
|
||||
from re import A
|
||||
@@ -23,6 +24,7 @@ from longs.models import (
|
||||
Long as Question,
|
||||
LongSeries,
|
||||
LongSeriesImage,
|
||||
|
||||
)
|
||||
|
||||
from generic.models import CidUser, CidUserGroup, UserUserGroup
|
||||
@@ -31,6 +33,8 @@ from django.contrib.auth.models import User
|
||||
|
||||
from rad.test_helpers import AssertNotFound, create_cid_user_and_groups
|
||||
|
||||
|
||||
|
||||
APP_NAME = "longs"
|
||||
|
||||
|
||||
@@ -223,18 +227,30 @@ def test_exams(db, client):
|
||||
print("check question:", q)
|
||||
# check redirects to the json file work
|
||||
# TODO: sort out testing of the saved JSON files
|
||||
question_json_res = client.get(
|
||||
reverse(
|
||||
f"longs:exam_question_json_cid",
|
||||
kwargs={
|
||||
"pk": exam.pk,
|
||||
"sk": q,
|
||||
"cid": cid_user.cid,
|
||||
"passcode": cid_user.passcode,
|
||||
},
|
||||
),
|
||||
follow=True,
|
||||
)
|
||||
try:
|
||||
question_json_res = client.get(
|
||||
reverse(
|
||||
f"longs:exam_question_json_cid",
|
||||
kwargs={
|
||||
"pk": exam.pk,
|
||||
"sk": q,
|
||||
"cid": cid_user.cid,
|
||||
"passcode": cid_user.passcode,
|
||||
},
|
||||
),
|
||||
follow=True,
|
||||
)
|
||||
except AttributeError: # UserUser has no cid attribute
|
||||
question_json_res = client.get(
|
||||
reverse(
|
||||
f"longs:exam_question_json",
|
||||
kwargs={
|
||||
"pk": exam.pk,
|
||||
"sk": q,
|
||||
},
|
||||
),
|
||||
follow=True,
|
||||
)
|
||||
# assert question_json_res.status_code == 200
|
||||
assert len(question_json_res.redirect_chain) == 2
|
||||
assert question_json_res.redirect_chain[0][1] == 302
|
||||
@@ -242,18 +258,42 @@ def test_exams(db, client):
|
||||
|
||||
assert question_json_res.redirect_chain[1][0].endswith(f"{q}.json")
|
||||
|
||||
question_json_unbased_res = client.get(
|
||||
reverse(
|
||||
f"longs:exam_question_json_unbased_cid",
|
||||
kwargs={
|
||||
"pk": exam.pk,
|
||||
"sk": q,
|
||||
"cid": cid_user.cid,
|
||||
"passcode": cid_user.passcode,
|
||||
},
|
||||
),
|
||||
follow=True,
|
||||
)
|
||||
print(cid_user, testing_cid_user)
|
||||
|
||||
if testing_cid_user:
|
||||
question_json_unbased_res = client.get(
|
||||
reverse(
|
||||
f"longs:exam_question_json_unbased_cid",
|
||||
kwargs={
|
||||
"pk": exam.pk,
|
||||
"sk": q,
|
||||
"cid": cid_user.cid,
|
||||
"passcode": cid_user.passcode,
|
||||
},
|
||||
),
|
||||
follow=True,
|
||||
)
|
||||
else:
|
||||
test = reverse(
|
||||
f"longs:exam_question_json_unbased",
|
||||
kwargs={
|
||||
"pk": exam.pk,
|
||||
"sk": q,
|
||||
},
|
||||
)
|
||||
print(test)
|
||||
question_json_unbased_res = client.get(
|
||||
|
||||
reverse(
|
||||
f"longs:exam_question_json_unbased",
|
||||
kwargs={
|
||||
"pk": exam.pk,
|
||||
"sk": q,
|
||||
},
|
||||
),
|
||||
follow=True,
|
||||
)
|
||||
|
||||
assert question_json_unbased_res.status_code == 200
|
||||
question_json = json.loads(question_json_unbased_res.content)
|
||||
|
||||
@@ -262,9 +302,7 @@ def test_exams(db, client):
|
||||
# As we store references to the questions we have to check individually
|
||||
assert q in exam_json["questions"].keys()
|
||||
|
||||
q_json = Question.objects.get(pk=q).get_question_json(
|
||||
based=False
|
||||
)
|
||||
q_json = Question.objects.get(pk=q).get_question_json(based=False)
|
||||
|
||||
assert question_json == q_json
|
||||
|
||||
@@ -325,17 +363,20 @@ def test_exams(db, client):
|
||||
# Long answers currently send a seperate object for each component
|
||||
valid_answers = []
|
||||
valid_answers_extra = []
|
||||
valid_answers_by_question = defaultdict(dict)
|
||||
|
||||
sections = (
|
||||
("1", "answer observations"),
|
||||
("2", "answer interpretation"),
|
||||
("3", "answer principle diagnosis"),
|
||||
("4", "answer differential diagnosis"),
|
||||
("5", "answer management"),
|
||||
)
|
||||
|
||||
i = 0
|
||||
for questions in exam_json["questions"]:
|
||||
for qid in questions:
|
||||
# Long answers submit 5 sections
|
||||
sections = (
|
||||
("1", "answer observations"),
|
||||
("2", "answer interpretation"),
|
||||
("3", "answer principle diagnosis"),
|
||||
("4", "answer differential diagnosis"),
|
||||
("5", "answer management"),
|
||||
)
|
||||
for n, name in sections:
|
||||
post_data = {
|
||||
"eid": exam_json["eid"],
|
||||
@@ -348,6 +389,8 @@ def test_exams(db, client):
|
||||
post_data["passcode"] = cid_user.passcode
|
||||
|
||||
valid_answers_extra.append(post_data)
|
||||
valid_answers_by_question[i][n] = post_data
|
||||
i = i + 1
|
||||
|
||||
post_json = {
|
||||
"eid": exam_json["eid"],
|
||||
@@ -469,21 +512,32 @@ def test_exams(db, client):
|
||||
|
||||
answer_lis = cid_exam_scores_soup.find(
|
||||
"ul", {"class": "score-answer-list"}
|
||||
).find_all("li")
|
||||
).find_all("li", {"class": "user-answer-li"})
|
||||
|
||||
assert len(answer_lis) == len(valid_answers)
|
||||
assert len(answer_lis) == len(valid_answers_by_question)
|
||||
|
||||
pprint(valid_answers_by_question)
|
||||
print(cid_user)
|
||||
|
||||
for n in range(
|
||||
len(valid_answers)
|
||||
len(valid_answers_by_question)
|
||||
): # Check no answers are visible if results not published
|
||||
answer = valid_answers[n]
|
||||
li = answer_lis[n]
|
||||
lis = answer_lis[n]
|
||||
#n = str(n + 1) # questions are not zero indexed (lists are)
|
||||
|
||||
assert answer["ans"] in str(li) # Check that the saved answer is shown
|
||||
for qidn, section_title in sections:
|
||||
print(n, qidn)
|
||||
answer = valid_answers_by_question[n][qidn]
|
||||
|
||||
assert not li.find(
|
||||
"span", {"class": "correct-answer"}
|
||||
) # and that the correct answer isn't
|
||||
li = lis.find("li", attrs={"data-qidn": qidn})
|
||||
|
||||
assert li["class"][0].lower() in section_title
|
||||
|
||||
assert answer["ans"] in str(li) # Check that the saved answer is shown
|
||||
|
||||
# assert not li.find(
|
||||
# "span", {"class": "correct-answer"}
|
||||
# ) # and that the correct answer isn't
|
||||
|
||||
assert (
|
||||
cid_exam_scores_soup.find("div", {"class": "score-overview"}) is None
|
||||
@@ -498,9 +552,10 @@ def test_exams(db, client):
|
||||
|
||||
answer_lis = cid_exam_scores_soup.find(
|
||||
"ul", {"class": "score-answer-list"}
|
||||
).find_all("li")
|
||||
assert len(answer_lis) == len(valid_answers) + 1
|
||||
).find_all("li", {"class": "user-answer-li"})
|
||||
assert len(answer_lis) == len(valid_answers_by_question) + 1
|
||||
assert "Not answered" in str(answer_lis[-1])
|
||||
assert str(answer_lis[-1]).count("Not answered") == 5
|
||||
|
||||
# Publish the results!
|
||||
exam.publish_results = True
|
||||
@@ -516,51 +571,62 @@ def test_exams(db, client):
|
||||
|
||||
answer_lis = cid_exam_scores_soup.find(
|
||||
"ul", {"class": "score-answer-list"}
|
||||
).find_all("li")
|
||||
).find_all("li", {"class": "user-answer-li"})
|
||||
|
||||
assert len(answer_lis) == len(valid_answers) + 1
|
||||
assert len(answer_lis) == len(valid_answers_by_question) + 1
|
||||
assert "Not answered" in str(answer_lis[-1])
|
||||
assert str(answer_lis[-1]).count("Not answered") == 5
|
||||
|
||||
# Repetition....
|
||||
for n in range(
|
||||
len(valid_answers)
|
||||
): # Check answers are visible if results published
|
||||
answer = valid_answers[n]
|
||||
li = answer_lis[n]
|
||||
len(valid_answers_by_question)
|
||||
): # Check no answers are visible if results not published
|
||||
lis = answer_lis[n]
|
||||
#n = str(n + 1) # questions are not zero indexed (lists are)
|
||||
|
||||
assert answer["ans"] in str(li) # Check that the saved answer is shown
|
||||
for qidn, section_title in sections:
|
||||
answer = valid_answers_by_question[n][qidn]
|
||||
|
||||
assert li.find("span", {"class": "correct-answer"}).string == "testanswer"
|
||||
li = lis.find("li", attrs={"data-qidn": qidn})
|
||||
|
||||
if users_tested == 1:
|
||||
# First user the answers will not have been saved
|
||||
assert (
|
||||
cid_exam_scores_soup.find("div", {"class": "score-overview"})
|
||||
.find("span", {"id": "total-score"})
|
||||
.string
|
||||
== "0 (3 unmarked)"
|
||||
)
|
||||
else:
|
||||
# Subsequent users should already have answers marked (apart from the last question which we have deleted)
|
||||
assert (
|
||||
cid_exam_scores_soup.find("div", {"class": "score-overview"})
|
||||
.find("span", {"id": "total-score"})
|
||||
.string
|
||||
== "4 (1 unmarked)"
|
||||
)
|
||||
assert li["class"][0].lower() in section_title
|
||||
|
||||
assert answer["ans"] in str(li) # Check that the saved answer is shown
|
||||
|
||||
print(cid_exam_scores_soup.find("div", {"class": "score-overview"}))
|
||||
|
||||
# For long cases an unmarked answers is given a score of 4
|
||||
# Each user needs marking individually
|
||||
assert (
|
||||
cid_exam_scores_soup.find("div", {"class": "score-overview"})
|
||||
.find("span", {"id": "total-score"})
|
||||
.string
|
||||
== "4 (3 unmarked)"
|
||||
)
|
||||
|
||||
# Mark the questions
|
||||
# For longs this needs to be done for each user
|
||||
question: Question
|
||||
for question in exam.exam_questions.all():
|
||||
print(question)
|
||||
print(question.get_compare_answers())
|
||||
try:
|
||||
new_ans = Answer(
|
||||
question=question,
|
||||
answer=question.get_unmarked_user_answers().pop(),
|
||||
status=Answer.MarkOptions.CORRECT,
|
||||
)
|
||||
new_ans.save()
|
||||
except IndexError: # Final question does not have an answer
|
||||
pass
|
||||
|
||||
answers = question.get_unmarked_user_answers()
|
||||
print(answers)
|
||||
for answer in answers:
|
||||
answer.score = UserAnswer.ScoreOptions.EIGHT
|
||||
answer.save()
|
||||
|
||||
#try:
|
||||
# new_ans = Answer(
|
||||
# question=question,
|
||||
# answer=question.get_unmarked_user_answers().pop(),
|
||||
# status=Answer.MarkOptions.CORRECT,
|
||||
# )
|
||||
# new_ans.save()
|
||||
#except IndexError: # Final question does not have an answer
|
||||
# pass
|
||||
|
||||
# TODO: test double marking
|
||||
|
||||
# Load the exam results page (again)
|
||||
cid_exam_scores_res = client.get(exam_scores_url)
|
||||
@@ -570,7 +636,7 @@ def test_exams(db, client):
|
||||
cid_exam_scores_soup.find("div", {"class": "score-overview"})
|
||||
.find("span", {"id": "total-score"})
|
||||
.string
|
||||
== "6"
|
||||
== "28.0"
|
||||
)
|
||||
|
||||
generated_questions.pop(-1).delete()
|
||||
|
||||
Reference in New Issue
Block a user