more Long improvements

This commit is contained in:
Ross
2023-05-22 13:29:07 +01:00
parent 4345f46615
commit ae670c738b
8 changed files with 136 additions and 87 deletions
+1
View File
@@ -1,5 +1,6 @@
# Current target
- User management
- SQUASH MIGRATIONS
# Ongoing
+6
View File
@@ -74,6 +74,12 @@ class QuestionBase(models.Model):
class Meta:
abstract = True
def get_unanswered_mark_and_text(self) -> tuple(int, str):
"""
Override in models if needed
"""
return (0, "Not answered")
class ExamBase(models.Model):
name = models.CharField(max_length=200, help_text="Name of the exam")
+7 -4
View File
@@ -1687,7 +1687,7 @@ class ExamViews(View, LoginRequiredMixin):
else:
cid_user = None
exam.exam_user_status.create(
cid_user=cid, user_user=user, status="downloaded", extra="unbased"
cid_user=cid_user, user_user=user, status="downloaded", extra="unbased"
)
return JsonResponse(exam_json)
@@ -1725,6 +1725,7 @@ class ExamViews(View, LoginRequiredMixin):
question = get_object_or_404(self.Question, pk=sk)
exam = get_object_or_404(self.Exam, pk=pk)
if not exam.active and not self.check_user_access(request.user, pk):
raise Http404("No available exam")
@@ -1733,6 +1734,7 @@ class ExamViews(View, LoginRequiredMixin):
user_id = None
else:
user_id = request.user.pk
if not exam.check_cid_user(cid, passcode, request, user_id):
raise Http404("No available exam")
@@ -1795,10 +1797,11 @@ class ExamViews(View, LoginRequiredMixin):
if not user_answer or user_answer is None:
# skip if no answer
answers_marks.append(0)
score, text = q.get_unanswered_mark_and_text()
answers_marks.append(score)
# answers.append("")
answer_score = 0
ans = "Not answered"
answer_score = score
ans = text
else:
if exam.app_name == "rapids" and user_answer.normal:
ans = "Normal"
-29
View File
@@ -655,35 +655,6 @@ class Exam(ExamBase):
exam_user_status = GenericRelation(ExamUserStatus)
def get_exam_question_json(self, question_id):
q = get_object_or_404(Long, pk=question_id)
# exam_order.append(q.id)
# Loop through longimage associations
images = []
image_titles = []
for series in q.series.all():
# image_array = []
# for i in series.images.all():
# image_array.append(image_as_base64(i.image))
# #image_array.append(i.image.url)
image_array = [image_as_base64(i.image) for i in series.images.all()]
images.append(image_array)
image_titles.append(series.get_examination())
exam_question = {
"title": q.history,
"images": images,
"image_titles": image_titles,
# "feedback_image": [],
# "annotations": [str(q.image_annotations)],
"type": "long",
"images_json": True,
}
return exam_question
def get_exam_json(self, based=True):
questions = self.exam_questions.all()
+58 -16
View File
@@ -4,6 +4,7 @@ from django_tables2.utils import A
from .models import Long, LongSeries, UserAnswer
from django.utils.html import format_html
from django.db.models import Prefetch
from easy_thumbnails.files import get_thumbnailer
@@ -12,10 +13,12 @@ from easy_thumbnails.exceptions import InvalidImageFormatError
class LongImageColumn(tables.Column):
def render(self, value):
blocks =[]
blocks = []
for obj in value.all():
blocks.append(obj.get_block())
return format_html("<span class='multi-image-block'>{}</span>".format("".join(blocks)))
return format_html(
"<span class='multi-image-block'>{}</span>".format("".join(blocks))
)
obj = value.first()
@@ -42,9 +45,17 @@ class LongSeriesImageColumn(tables.Column):
image_object = obj[0].image
try:
thumbnailer = get_thumbnailer(image_object)
return format_html('<img src="/media/{}" /><br/>[{}]', thumbnailer["exam-list"], value.count())
return format_html(
'<img src="/media/{}" /><br/>[{}]',
thumbnailer["exam-list"],
value.count(),
)
except InvalidImageFormatError:
return format_html('<span title="{}">Invalid image url<span><br/>[{}]', image_object, value.count())
return format_html(
'<span title="{}">Invalid image url<span><br/>[{}]',
image_object,
value.count(),
)
class LongTable(tables.Table):
@@ -62,7 +73,7 @@ class LongTable(tables.Table):
)
series = LongImageColumn("Images", orderable=False)
#series = tables.ManyToManyColumn(verbose_name="Exams")
# series = tables.ManyToManyColumn(verbose_name="Exams")
exams = tables.ManyToManyColumn(verbose_name="Exams")
selection = tables.CheckBoxColumn(accessor="pk", orderable=False)
@@ -76,12 +87,19 @@ class LongTable(tables.Table):
def __init__(self, data=None, *args, **kwargs):
super().__init__(
data.prefetch_related(
"series", "author", "exams", "series__images", "series__examination", "series__plane", "series__contrast"
"series",
"author",
"exams",
"series__images",
"series__examination",
"series__plane",
"series__contrast",
),
*args,
**kwargs,
)
class PopupLinkColumn(tables.Column):
def render(self, value):
return format_html("<span>test: {}<span>", value)
@@ -120,7 +138,14 @@ class LongSeriesTable(tables.Table):
def __init__(self, data=None, *args, **kwargs):
super().__init__(
data.prefetch_related(
"long", "long__series", "images", "author", "examination", "plane", "contrast", "modality"
"long",
"long__series",
"images",
"author",
"examination",
"plane",
"contrast",
"modality",
),
*args,
**kwargs,
@@ -128,17 +153,21 @@ class LongSeriesTable(tables.Table):
def render_popup(self, value, record):
print(self)
return format_html("""<a href="#" onclick="return window.create_popup_window('/longs/series/{}', 'Series')" >Popup</a>""", record.pk)
return format_html(
"""<a href="#" onclick="return window.create_popup_window('/longs/series/{}', 'Series')" >Popup</a>""",
record.pk,
)
class UserAnswerTable(tables.Table):
select = tables.CheckBoxColumn(accessor=("pk"))
delete = tables.LinkColumn(
"longs:user_answer_delete", text="Delete", args=[A("pk")], orderable=False
)
view = tables.LinkColumn('longs:user_answer_view',
text='View',
args=[A('pk')],
orderable=False)
view = tables.LinkColumn(
"longs:user_answer_view", text="View", args=[A("pk")], orderable=False
)
class Meta:
model = UserAnswer
template_name = "django_tables2/bootstrap4.html"
@@ -146,11 +175,24 @@ class UserAnswerTable(tables.Table):
"cid",
"question",
"score",
#"normal",
#"answer",
#"answer_compare",
# "normal",
# "answer",
# "answer_compare",
"exam",
"created",
"updated",
)
def __init__(self, data=None, *args, **kwargs):
super().__init__(
data.prefetch_related(
Prefetch(
"question", # "question__exams"#, "author", "exams", "series__images", "series__examination", "series__plane", "series__contrast"
#queryset=Long.objects.select_related().prefetch_related(
# Prefetch("exams")
#),
)
),
*args,
**kwargs,
)
+61 -36
View File
@@ -203,7 +203,21 @@ def test_exams(db, client):
res = client.get(exam_metadata["url"])
assert res.status_code == 200
exam_json = json.loads(res.content)
print(exam_json)
# We can also check the unbased questions
res = client.get(exam_metadata["url"] + "/unbased")
assert res.status_code == 200
exam_json_unbased = json.loads(res.content)
# This show we have different types (int vs string)
assert (
set([str(i) for i in exam_json["exam_order"]])
== set(exam_json["questions"].keys())
== set(exam_json["question_requests"].keys())
== set([str(i) for i in exam_json_unbased["exam_order"]])
== set(exam_json_unbased["questions"].keys())
)
for q in exam_json["question_requests"]:
print("check question:", q)
@@ -212,37 +226,48 @@ def test_exams(db, client):
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
kwargs={
"pk": exam.pk,
"sk": q,
"cid": cid_user.cid,
"passcode": cid_user.passcode,
},
),
follow=True,
)
#assert question_json_res.status_code == 200
print(question_json_res.redirect_chain)
print(question_json_res.content)
# assert question_json_res.status_code == 200
assert len(question_json_res.redirect_chain) == 2
assert question_json_res.redirect_chain[0][1] == 302
assert question_json_res.redirect_chain[1][1] == 302
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
kwargs={
"pk": exam.pk,
"sk": q,
"cid": cid_user.cid,
"passcode": cid_user.passcode,
},
),
follow=True,
)
assert question_json_unbased_res.status_code == 200
question_json = json.loads(question_json_unbased_res.content)
#question_json_res = client.get(question1.get_question_json(answers=False, based=True))
#print(question_json)
# question_json_res = client.get(question1.get_question_json(answers=False, based=True))
assert (
question_json
in exam_json["questions"].values()
# 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
)
assert question_json == q_json
assert len(exam_json["questions"]) == 3
exam.active = False
@@ -303,26 +328,26 @@ def test_exams(db, client):
for questions in exam_json["questions"]:
for qid in questions:
post_data = {
"eid": exam_json["eid"],
"cid": post_cid,
"qid": qid,
"qidn": "1",
"ans": f"Abnormal",
}
post_data2 = {
"eid": exam_json["eid"],
"cid": post_cid,
"qid": qid,
"qidn": "2",
"ans": f"answer {qid}",
}
if testing_cid_user:
post_data["passcode"] = cid_user.passcode
post_data2["passcode"] = cid_user.passcode
# 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"],
"cid": post_cid,
"qid": qid,
"qidn": n,
"ans": f"{name} - {q}",
}
if testing_cid_user:
post_data["passcode"] = cid_user.passcode
valid_answers_extra.append(post_data)
valid_answers.append(post_data2)
valid_answers_extra.append(post_data)
post_json = {
"eid": exam_json["eid"],
@@ -337,8 +362,8 @@ def test_exams(db, client):
json_res = json.loads(res.content)
assert json_res["success"] == True
assert json_res["question_count"] == 6
assert json_res["success"] is True
assert json_res["question_count"] == 5 * 3
# Check the answers now exist in the database
if testing_cid_user:
+1
View File
@@ -1153,6 +1153,7 @@ def question_json_unbased(request, pk):
"""
No (file based) caching is enabled for unbased quesitons
"""
print("UNBASED")
question = get_object_or_404(Long, pk=pk)
question_json = question.get_question_json(based=False)
+2 -2
View File
@@ -290,8 +290,8 @@ def test_exams(db, client):
json_res = json.loads(res.content)
assert json_res["success"] == True
assert json_res["question_count"] == 6
assert json_res["success"] is True
assert json_res["question_count"] == 2 * 3
# Check the answers now exist in the database
if testing_cid_user: