more Long improvements
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
# Current target
|
||||
- User management
|
||||
- SQUASH MIGRATIONS
|
||||
|
||||
|
||||
# Ongoing
|
||||
|
||||
@@ -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
@@ -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"
|
||||
|
||||
@@ -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
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user