diff --git a/anatomy/models.py b/anatomy/models.py
index 3d629556..ce552862 100644
--- a/anatomy/models.py
+++ b/anatomy/models.py
@@ -158,6 +158,16 @@ class AnatomyQuestion(models.Model):
return reverse("anatomy:question_detail", kwargs={"pk": self.pk})
def get_primary_answer(self):
+ ans = self.answers.filter(
+ proposed=False, status=Answer.MarkOptions.CORRECT
+ ).first()
+ # ans = self.answers.first()
+
+ if ans is None:
+ return "None yet..."
+ else:
+ return ans.answer
+
if (
self.answers.filter(
proposed=False, status=Answer.MarkOptions.CORRECT
@@ -434,7 +444,7 @@ class CidUserAnswer(models.Model):
max_length=1,
choices=Answer.MarkOptions.choices,
default=Answer.MarkOptions.UNMARKED,
- blank=True
+ blank=True,
)
def __str__(self):
diff --git a/anatomy/templates/anatomy/exam_overview.html b/anatomy/templates/anatomy/exam_overview.html
index 35c2e5ad..640ad253 100644
--- a/anatomy/templates/anatomy/exam_overview.html
+++ b/anatomy/templates/anatomy/exam_overview.html
@@ -33,7 +33,7 @@
{% endif %}
- {% for question in questions.all %}
+ {% for question in questions %}
-
diff --git a/anatomy/templates/anatomy/exam_scores_new.html b/anatomy/templates/anatomy/exam_scores_new.html
index 2ad3b17a..2f2c2684 100644
--- a/anatomy/templates/anatomy/exam_scores_new.html
+++ b/anatomy/templates/anatomy/exam_scores_new.html
@@ -57,14 +57,14 @@
{% for question in questions %}
| Question {{forloop.counter}}
- {% if question.normal %}
- [N]
- {% else %}
- [A]
- {% endif %}
|
- {% for cid in cids %}
+ {% comment %} {% for cid in cids %}
{{ans_by_question|get_item:question|get_item:cid}} |
+ {% endfor %} {% endcomment %}
+ {% for cid in cids %}
+ {% with by_question|get_item:question|get_item:cid as ans_score %}
+ {{ans_score.0}} |
+ {% endwith %}
{% endfor %}
{% endfor %}
diff --git a/anatomy/views.py b/anatomy/views.py
index 62aef635..61b0e756 100644
--- a/anatomy/views.py
+++ b/anatomy/views.py
@@ -1004,7 +1004,7 @@ def question_save_annotation(request, pk):
GenericExamViews = ExamViews(
- Exam, AnatomyQuestion, CidUserAnswer, "anatomy", "anatomy", loadJsonAnswer
+ Exam, AnatomyQuestion, Answer, CidUserAnswer, "anatomy", "anatomy", loadJsonAnswer
)
diff --git a/generic/views.py b/generic/views.py
index 63cf609d..5379836d 100644
--- a/generic/views.py
+++ b/generic/views.py
@@ -59,6 +59,7 @@ import random
import plotly.express as px
# from rad.views import get_question_and_content_type
+from django.db.models import Prefetch
def normaliseRapidsScore(score):
@@ -240,10 +241,18 @@ def generic_exam_json_edit(request):
class ExamViews(View, LoginRequiredMixin):
def __init__(
- self, exam, question, cid_user_answer, app, question_type, loadJsonAnswer
+ self,
+ exam,
+ question,
+ answer,
+ cid_user_answer,
+ app,
+ question_type,
+ loadJsonAnswer,
):
self.Exam = exam
self.Question = question
+ self.Answer = answer
self.CidUserAnswer = cid_user_answer
self.app_name = app
self.question_type = question_type
@@ -414,7 +423,7 @@ class ExamViews(View, LoginRequiredMixin):
# print(Exam.objects.all())
# exams = Exam.objects.all()
# print("test", Exam.objects.all().get(id=pk))
- exam = get_object_or_404(self.Exam, pk=pk)
+ exam = get_object_or_404(self.Exam.objects.prefetch_related("author"), pk=pk)
if request.user not in exam.author.all():
if not self.check_user_access(request.user, pk):
@@ -428,7 +437,38 @@ class ExamViews(View, LoginRequiredMixin):
if can_edit:
notes = self.exam_notes(request, pk)
- questions = exam.exam_questions.all()
+ if self.app_name == "anatomy":
+ questions = (
+ exam.exam_questions.select_related(
+ "modality",
+ "structure",
+ "body_part",
+ "region",
+ "examination",
+ "question_type",
+ ).all()
+ # .prefetch_related(
+ # Prefetch(
+ # "answers",
+ # queryset=self.Answer.objects.filter(
+ # proposed=False, status=self.Answer.MarkOptions.CORRECT
+ # ),
+ # )
+ # )
+ )
+ elif self.app_name == "rapids":
+ questions = (
+ exam.exam_questions.select_related()
+ .all()
+ .prefetch_related("images", "abnormality", "region", "examination")
+ )
+ elif self.app_name == "physics":
+ questions = (
+ exam.exam_questions.select_related("category").all()
+ # .prefetch_related("images", "abnormality", "region", "examination")
+ )
+ else:
+ questions = exam.exam_questions.select_related().all()
question_number = len(questions)
@@ -868,13 +908,15 @@ class ExamViews(View, LoginRequiredMixin):
user_answers = defaultdict(list)
user_names = {}
- score_by_question = defaultdict(dict)
- ans_by_question = defaultdict(dict)
+ by_question = defaultdict(dict)
unmarked = set()
questions = exam.exam_questions.all()
- cid_user_answers = self.CidUserAnswer.objects.filter(question__in=questions, exam__id=pk)
+ # We could prefect the CidUserAnswers.answers here (if we didn't cache them)
+ cid_user_answers = self.CidUserAnswer.objects.select_related("question").filter(
+ question__in=questions, exam__id=pk
+ )
cids = set()
@@ -905,15 +947,25 @@ class ExamViews(View, LoginRequiredMixin):
user_answers_marks[cid].append(answer_score)
user_answers_and_marks[cid].append((ans, answer_score))
- score_by_question[q][cid] = answer_score
- ans_by_question[q][cid] = ans
+ if self.app_name in ("rapids", "anatomy", "sbas"):
+ by_question[q][cid] = (ans, answer_score)
+ else:
+ zipped_ans_scores = zip(ans, answer_score)
+ by_question[q][cid] = zipped_ans_scores
user_scores = {}
user_scores_normalised = {}
for user in user_answers_marks:
- user_scores[user] = sum(
- [i for i in user_answers_marks[user] if i != "unmarked"]
- )
+
+ if self.app_name in ("rapids", "anatomy", "sbas"):
+ user_scores[user] = sum(
+ [i for i in user_answers_marks[user] if i != "unmarked"]
+ )
+ else:
+ user_scores[user] = sum(
+ [sum(i) for i in user_answers_marks[user] if i != "unmarked"]
+ )
+
if self.app_name == "rapids":
user_scores_normalised[user] = normaliseRapidsScore(
sum([i for i in user_answers_marks[user] if i != "unmarked"])
@@ -923,7 +975,12 @@ class ExamViews(View, LoginRequiredMixin):
user_scores_list = list(user_scores.values())
- max_score = len(questions) * 2
+ if self.app_name in ("rapids", "anatomy"):
+ max_score = len(questions) * 2
+ elif self.app_name == "physics":
+ max_score = len(questions) * 5
+ else:
+ max_score = len(questions)
if len(user_scores_list) < 1:
mean = 0
@@ -962,7 +1019,6 @@ class ExamViews(View, LoginRequiredMixin):
exam.stats_graph = fig_html
exam.save()
-
return render(
request,
f"{self.app_name}/exam_scores_new.html",
@@ -971,8 +1027,7 @@ class ExamViews(View, LoginRequiredMixin):
"exam": exam,
"unmarked": unmarked,
"questions": questions,
- "score_by_question": score_by_question,
- "ans_by_question": ans_by_question,
+ "by_question": by_question,
"user_answers": dict(user_answers),
"user_answers_marks": dict(user_answers_marks),
"user_scores": user_scores,
diff --git a/longs/views.py b/longs/views.py
index 7bcf2b31..22ed6395 100755
--- a/longs/views.py
+++ b/longs/views.py
@@ -1170,7 +1170,7 @@ def long_series_order_upload_filename(request, pk):
return redirect("longs:long_series_detail", pk=pk)
-GenericExamViews = ExamViews(Exam, Long, CidUserAnswer, "longs", "long", loadJsonAnswer)
+GenericExamViews = ExamViews(Exam, Long, None, CidUserAnswer, "longs", "long", loadJsonAnswer)
class ExamCreate(RevisionMixin, LoginRequiredMixin, CreateView):
diff --git a/physics/templates/physics/exam_scores_new.html b/physics/templates/physics/exam_scores_new.html
new file mode 100644
index 00000000..4584043f
--- /dev/null
+++ b/physics/templates/physics/exam_scores_new.html
@@ -0,0 +1,73 @@
+{% extends 'physics/exams.html' %}
+
+{% block content %}
+ {{plot|safe}}
+
+
{{ exam.name }}
+
+
+
+
Stats
+ Candidates: {{cids|length}}
+ Max score: {{max_score}}
+ Mean: {{mean}}, Median {{median}}, Mode {{mode}}
+
+
+
+
+
+ | Candidate ID |
+ Score |
+ {% comment %} Normalised Score | {% endcomment %}
+
+ {% comment %} {% for user, value in user_answers_marks.items %} {% endcomment %}
+ {% for cid in cids %}
+
+ | {{cid}} |
+ {{user_scores|get_item:cid}} |
+ {% comment %} {{user_scores_normalised|get_item:user}} | {% endcomment %}
+
+ {% endfor %}
+
+
+
+
Answers as a table
+
+
+
+ | Candidate |
+ {% for cid in cids %}
+ {% comment %} {{cid}} | {% endcomment %}
+ {{cid}} |
+ {% endfor %}
+
+
+
+ {% for question in questions %}
+
+ | Question {{forloop.counter}}
+ |
+ {% for cid in cids %}
+
+
+ {% with by_question|get_item:question|get_item:cid as ans_score %}
+ {% for ans, score in ans_score %}
+ - {{ans}}
+ {% endfor %}
+ {% endwith %}
+
+ |
+ {% endfor %}
+
+ {% endfor %}
+
+ | Score: |
+ {% for cid in cids %}
+ {{user_scores|get_item:cid}} |
+ {% endfor %}
+
+
+
+
+{% endblock %}
+
diff --git a/physics/urls.py b/physics/urls.py
index 7d905204..71e61d8b 100644
--- a/physics/urls.py
+++ b/physics/urls.py
@@ -26,7 +26,7 @@ urlpatterns = [
name="exam_finish",
),
path("exam//", views.GenericExamViews.exam_overview, name="exam_overview"),
- path("exam//scores", views.exam_scores_cid, name="exam_scores_cid"),
+ path("exam//scores", views.GenericExamViews.exam_scores_cid, name="exam_scores_cid"),
path(
"exam//scores///",
views.exam_scores_cid_user,
diff --git a/physics/views.py b/physics/views.py
index 073f0fe2..1baa5399 100644
--- a/physics/views.py
+++ b/physics/views.py
@@ -464,7 +464,7 @@ def loadJsonAnswer(answer):
GenericExamViews = ExamViews(
- Exam, Question, CidUserAnswer, "physics", "physics", loadJsonAnswer
+ Exam, Question, None, CidUserAnswer, "physics", "physics", loadJsonAnswer
)
GenericViews = GenericViewBase("physics", Question, CidUserAnswer, Exam)
diff --git a/rapids/models.py b/rapids/models.py
index 0933ad00..f6f8b597 100644
--- a/rapids/models.py
+++ b/rapids/models.py
@@ -658,7 +658,7 @@ class CidUserAnswer(models.Model):
max_length=1,
choices=Answer.MarkOptions.choices,
default=Answer.MarkOptions.UNMARKED,
- blank=True
+ blank=True,
)
def __str__(self):
@@ -700,6 +700,7 @@ class CidUserAnswer(models.Model):
def get_answer_score(self, cached=True):
if cached and self.score:
+ print("CACHED")
if self.score == Answer.MarkOptions.CORRECT:
mark = 2
elif self.score == Answer.MarkOptions.HALF_MARK:
@@ -708,6 +709,7 @@ class CidUserAnswer(models.Model):
mark = 0
return mark
+ print("NOT CACHED")
q = self.question
@@ -731,6 +733,9 @@ class CidUserAnswer(models.Model):
mark = "unmarked"
if marked_ans is not None:
+ self.score = marked_ans.status
+ self.save()
+
if marked_ans.status == Answer.MarkOptions.CORRECT:
mark = 2
elif marked_ans.status == Answer.MarkOptions.HALF_MARK:
diff --git a/rapids/templates/rapids/exam_scores_new.html b/rapids/templates/rapids/exam_scores_new.html
index bd9fd93b..4eae3453 100644
--- a/rapids/templates/rapids/exam_scores_new.html
+++ b/rapids/templates/rapids/exam_scores_new.html
@@ -31,11 +31,11 @@
Score |
Normalised Score |
- {% for user, value in user_answers_marks.items %}
+ {% for cid in cids %}
- | {{user}} |
- {{user_scores|get_item:user}} |
- {{user_scores_normalised|get_item:user}} |
+ {{cid}} |
+ {{user_scores|get_item:cid}} |
+ {{user_scores_normalised|get_item:cid}} |
{% endfor %}
@@ -63,7 +63,10 @@
{% endif %}
{% for cid in cids %}
- {{ans_by_question|get_item:question|get_item:cid}} |
+ {% comment %} {{ans_by_question|get_item:question|get_item:cid}} | {% endcomment %}
+ {% with by_question|get_item:question|get_item:cid as ans_score %}
+ {{ans_score.0}} |
+ {% endwith %}
{% endfor %}
{% endfor %}
diff --git a/rapids/views.py b/rapids/views.py
index e03f069c..812f4018 100755
--- a/rapids/views.py
+++ b/rapids/views.py
@@ -1048,7 +1048,7 @@ class QuestionDelete(AuthorOrCheckerRequiredMixin, DeleteView):
GenericExamViews = ExamViews(
- Exam, Rapid, CidUserAnswer, "rapids", "rapid", loadJsonAnswer
+ Exam, Rapid, Answer, CidUserAnswer, "rapids", "rapid", loadJsonAnswer
)
diff --git a/sbas/models.py b/sbas/models.py
index 9f9ef893..e931f853 100644
--- a/sbas/models.py
+++ b/sbas/models.py
@@ -196,13 +196,16 @@ class CidUserAnswer(models.Model):
def __str__(self):
return "{}/{}/{}".format(self.cid, self.exam, self.question.pk)
+ def get_answer_string(self):
+ return self.answer
+
def get_answer(self):
return self.answer
def get_correct(self):
return self.answer == self.question.best_answer
- def get_score(self):
+ def get_answer_score(self):
if self.get_correct():
return 1
return 0
diff --git a/sbas/templates/sbas/exam_scores_new.html b/sbas/templates/sbas/exam_scores_new.html
new file mode 100644
index 00000000..c2116a58
--- /dev/null
+++ b/sbas/templates/sbas/exam_scores_new.html
@@ -0,0 +1,70 @@
+{% extends 'sbas/exams.html' %}
+
+{% block content %}
+ {{plot|safe}}
+
+
{{ exam.name }}
+
+
+
+
Stats
+ Candidates: {{cids|length}}
+ Max score: {{max_score}}
+ Mean: {{mean}}, Median {{median}}, Mode {{mode}}
+
+
+
+
+
+ | Candidate ID |
+ Score |
+ {% comment %} Normalised Score | {% endcomment %}
+
+ {% comment %} {% for user, value in user_answers_marks.items %} {% endcomment %}
+ {% for cid in cids %}
+
+ | {{cid}} |
+ {{user_scores|get_item:cid}} |
+ {% comment %} {{user_scores_normalised|get_item:user}} | {% endcomment %}
+
+ {% endfor %}
+
+
+
+
Answers as a table
+
+
+
+ | Candidate |
+ {% for cid in cids %}
+ {% comment %} {{cid}} | {% endcomment %}
+ {{cid}} |
+ {% endfor %}
+
+
+
+ {% for question in questions %}
+
+ | Question {{forloop.counter}}
+ |
+ {% comment %} {% for cid in cids %}
+ {{ans_by_question|get_item:question|get_item:cid}} |
+ {% endfor %} {% endcomment %}
+ {% for cid in cids %}
+ {% with by_question|get_item:question|get_item:cid as ans_score %}
+ {{ans_score.0}} |
+ {% endwith %}
+ {% endfor %}
+
+ {% endfor %}
+
+ | Score: |
+ {% for cid in cids %}
+ {{user_scores|get_item:cid}} |
+ {% endfor %}
+
+
+
+
+{% endblock %}
+
diff --git a/sbas/urls.py b/sbas/urls.py
index fcad21e6..0e015375 100644
--- a/sbas/urls.py
+++ b/sbas/urls.py
@@ -28,7 +28,7 @@ urlpatterns = [
path("exam//", views.GenericExamViews.exam_overview, name="exam_overview"),
path(
"exam//scores",
- cache_page(60 * 1)(views.exam_scores_cid),
+ views.GenericExamViews.exam_scores_cid,
name="exam_scores_cid",
),
path(
diff --git a/sbas/views.py b/sbas/views.py
index bc15c3e7..1c461eff 100644
--- a/sbas/views.py
+++ b/sbas/views.py
@@ -383,7 +383,7 @@ def loadJsonAnswer(answer):
GenericExamViews = ExamViews(
- Exam, Question, CidUserAnswer, "sbas", "sbas", loadJsonAnswer
+ Exam, Question, None, CidUserAnswer, "sbas", "sbas", loadJsonAnswer
)