140 lines
5.4 KiB
Python
140 lines
5.4 KiB
Python
from django.test import TestCase, Client
|
|
from django.urls import reverse
|
|
from django.contrib.auth.models import User
|
|
from generic.models import CidUserExam, CidUser
|
|
from physics.models import Exam, Question, UserAnswer
|
|
from atlas.models import Case
|
|
from atlas.forms import CaseForm
|
|
|
|
class ScoresStatsTests(TestCase):
|
|
def setUp(self):
|
|
self.admin = User.objects.create_superuser(
|
|
username="admin",
|
|
email="admin@example.com",
|
|
password="adminpassword"
|
|
)
|
|
self.client = Client()
|
|
self.client.force_login(self.admin)
|
|
|
|
def test_physics_stats_with_pass_mark_and_exclusion(self):
|
|
# Create Exam
|
|
exam = Exam.objects.create(name="Physics Test Exam", exam_mode=True)
|
|
exam.author.add(self.admin)
|
|
|
|
# Create Questions (max score will be 10 points)
|
|
q1 = Question.objects.create(
|
|
stem="stem1",
|
|
a="a", a_answer=True,
|
|
b="b", b_answer=False,
|
|
c="c", c_answer=True,
|
|
d="d", d_answer=False,
|
|
e="e", e_answer=True,
|
|
)
|
|
q1.exams.add(exam)
|
|
q2 = Question.objects.create(
|
|
stem="stem2",
|
|
a="a", a_answer=True,
|
|
b="b", b_answer=False,
|
|
c="c", c_answer=True,
|
|
d="d", d_answer=False,
|
|
e="e", e_answer=True,
|
|
)
|
|
q2.exams.add(exam)
|
|
|
|
# Create two candidates: cid=1 (score=5/10), cid=2 (score=3/10)
|
|
UserAnswer.objects.create(
|
|
question=q1, exam=exam, cid=1,
|
|
a=True, b=False, c=True, d=False, e=True
|
|
)
|
|
UserAnswer.objects.create(
|
|
question=q2, exam=exam, cid=1,
|
|
a=False, b=True, c=False, d=True, e=False
|
|
)
|
|
|
|
UserAnswer.objects.create(
|
|
question=q1, exam=exam, cid=2,
|
|
a=True, b=True, c=True, d=True, e=False
|
|
)
|
|
|
|
# 1. Test stats calculations without custom pass mark (defaults to 50% = 5)
|
|
url = reverse("physics:exam_scores_all", kwargs={"pk": exam.pk})
|
|
response = self.client.get(f"{url}?stats_only=true")
|
|
self.assertEqual(response.status_code, 200)
|
|
context = response.context
|
|
self.assertEqual(context["pass_mark_threshold"], 5.0)
|
|
self.assertEqual(context["passed_candidates_count"], 1)
|
|
self.assertEqual(context["pass_rate"], 50.0)
|
|
|
|
# 2. Update pass mark to 3.0 via HTMX update_pass_mark url
|
|
update_url = reverse("physics:update_pass_mark", kwargs={"pk": exam.pk})
|
|
update_response = self.client.post(update_url, {"pass_mark": "3.0"}, HTTP_HX_REQUEST="true")
|
|
self.assertEqual(update_response.status_code, 302)
|
|
exam.refresh_from_db()
|
|
self.assertEqual(exam.pass_mark, 3.0)
|
|
|
|
# Fetch stats again and verify updated pass mark threshold and pass rate
|
|
response = self.client.get(f"{url}?stats_only=true")
|
|
context = response.context
|
|
self.assertEqual(context["pass_mark_threshold"], 3.0)
|
|
self.assertEqual(context["passed_candidates_count"], 2)
|
|
self.assertEqual(context["pass_rate"], 100.0)
|
|
|
|
# 3. Exclude cid=2 from stats
|
|
exclude_url = reverse("physics:toggle_candidate_stats_exclusion", kwargs={"pk": exam.pk})
|
|
exclude_response = self.client.post(f"{exclude_url}?candidate=c/2")
|
|
self.assertEqual(exclude_response.status_code, 200)
|
|
|
|
# Check database status
|
|
cid_user = CidUser.objects.get(cid=2)
|
|
cux = CidUserExam.objects.get(object_id=exam.pk, cid_user=cid_user)
|
|
self.assertTrue(cux.exclude_from_stats)
|
|
|
|
# Fetch stats again and verify cid=2 is excluded
|
|
response = self.client.get(f"{url}?stats_only=true")
|
|
context = response.context
|
|
self.assertEqual(len(context["cids"]), 1)
|
|
self.assertIn("c/1", context["cids"])
|
|
self.assertNotIn("c/2", context["cids"])
|
|
self.assertEqual(context["mean"], 5.0)
|
|
self.assertEqual(context["passed_candidates_count"], 1)
|
|
self.assertEqual(context["pass_rate"], 100.0)
|
|
|
|
def test_case_form_save_commit_false_does_not_persist(self):
|
|
form_data = {
|
|
"title": "Unsaved Case Form Test",
|
|
"description": "Unsaved description",
|
|
"diagnostic_certainty": 3,
|
|
"open_access": True,
|
|
}
|
|
form = CaseForm(data=form_data, user=self.admin)
|
|
self.assertTrue(form.is_valid())
|
|
case_inst = form.save(commit=False)
|
|
self.assertIsNone(case_inst.pk)
|
|
self.assertEqual(Case.objects.filter(title="Unsaved Case Form Test").count(), 0)
|
|
|
|
def test_case_linked_series_info(self):
|
|
# Create a chain of 3 cases: c1 -> c2 -> c3
|
|
c1 = Case.objects.create(title="Case 1")
|
|
c2 = Case.objects.create(title="Case 2", previous_case=c1)
|
|
c3 = Case.objects.create(title="Case 3", previous_case=c2)
|
|
|
|
# Verify positions and totals
|
|
info1 = c1.get_series_info()
|
|
self.assertTrue(info1["in_series"])
|
|
self.assertEqual(info1["position"], 1)
|
|
self.assertEqual(info1["total"], 3)
|
|
|
|
info2 = c2.get_series_info()
|
|
self.assertTrue(info2["in_series"])
|
|
self.assertEqual(info2["position"], 2)
|
|
self.assertEqual(info2["total"], 3)
|
|
|
|
info3 = c3.get_series_info()
|
|
self.assertTrue(info3["in_series"])
|
|
self.assertEqual(info3["position"], 3)
|
|
self.assertEqual(info3["total"], 3)
|
|
|
|
c4 = Case.objects.create(title="Case 4")
|
|
info4 = c4.get_series_info()
|
|
self.assertFalse(info4["in_series"])
|