.
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 3.2.13 on 2022-06-20 21:39
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('rapids', '0058_auto_20220521_1020'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='ciduseranswer',
|
||||
name='callstate',
|
||||
field=models.CharField(blank=True, choices=[('C', 'Correct Call'), ('O', 'Overcall'), ('U', 'Undercall'), ('W', 'Incorrect Call'), ('P', 'Partial Call')], max_length=1, null=True),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 3.2.13 on 2022-06-26 15:12
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('rapids', '0059_ciduseranswer_callstate'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='exam',
|
||||
name='exam_results_emailed',
|
||||
field=models.DateTimeField(default=None, null=True),
|
||||
),
|
||||
]
|
||||
+28
-1
@@ -775,6 +775,17 @@ class CidUserAnswer(models.Model):
|
||||
blank=True,
|
||||
)
|
||||
|
||||
class CallStateOptions(models.TextChoices):
|
||||
CORRECT = "C", _("Correct Call")
|
||||
OVERCALL = "O", _("Overcall")
|
||||
UNDERCALL = "U", _("Undercall")
|
||||
INCORRECTCALL = "W", _("Incorrect Call")
|
||||
PARTIALCALL = "P", _("Partial Call")
|
||||
|
||||
callstate = models.CharField(
|
||||
max_length=1, choices=CallStateOptions.choices, blank=True, null=True
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
|
||||
name = self.cid
|
||||
@@ -823,6 +834,9 @@ class CidUserAnswer(models.Model):
|
||||
def get_absolute_url(self):
|
||||
return reverse("rapids:user_answer_view", kwargs={"pk": self.pk})
|
||||
|
||||
def get_answer_callstate(self):
|
||||
return self.callstate.label
|
||||
|
||||
def get_answer_score(self, cached=True):
|
||||
if cached and self.score:
|
||||
print("CACHED")
|
||||
@@ -840,10 +854,20 @@ class CidUserAnswer(models.Model):
|
||||
|
||||
# First step we check that the normal/abnormal states match
|
||||
if q.normal != self.normal:
|
||||
if q.normal:
|
||||
self.callstate = CidUserAnswer.CallStateOptions.OVERCALL
|
||||
else:
|
||||
self.callstate = CidUserAnswer.CallStateOptions.UNDERCALL
|
||||
|
||||
self.score = Answer.MarkOptions.INCORRECT
|
||||
self.save()
|
||||
# If they don't match the answer is wrong (score is 0)
|
||||
return 0
|
||||
# If both are normal full marks
|
||||
elif q.normal and self.normal:
|
||||
self.callstate = CidUserAnswer.CallStateOptions.CORRECT
|
||||
self.score = Answer.MarkOptions.CORRECT
|
||||
self.save()
|
||||
return 2
|
||||
|
||||
# Then compare answer strings (as per anatomy questions)
|
||||
@@ -864,13 +888,16 @@ 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:
|
||||
self.callstate = CidUserAnswer.CallStateOptions.CORRECT
|
||||
mark = 2
|
||||
elif marked_ans.status == Answer.MarkOptions.HALF_MARK:
|
||||
self.callstate = CidUserAnswer.CallStateOptions.PARTIALCALL
|
||||
mark = 1
|
||||
elif marked_ans.status == Answer.MarkOptions.INCORRECT:
|
||||
self.callstate = CidUserAnswer.CallStateOptions.INCORRECTCALL
|
||||
mark = 0
|
||||
self.save()
|
||||
|
||||
return mark
|
||||
|
||||
@@ -34,9 +34,9 @@
|
||||
{% for cid in cids %}
|
||||
<tr>
|
||||
{% if cid|slice:":1" == "u" %}
|
||||
<td>{{cid}}</td>
|
||||
<td>{{cid}}</td>
|
||||
{% else %}
|
||||
<td><a href="{% url 'cid_scores_admin' cid %}">{{cid}}</a></td>
|
||||
<td><a href="{% url 'cid_scores_admin' cid %}">{{cid}}</a></td>
|
||||
{% endif %}
|
||||
<td>{{user_scores|get_item:cid}}</td>
|
||||
<td>{{user_scores_normalised|get_item:cid}}</td>
|
||||
@@ -68,7 +68,7 @@
|
||||
</td>
|
||||
{% for cid in cids %}
|
||||
{% with by_question|get_item:question|get_item:cid as ans_score %}
|
||||
<td class="user-answer-score-{{ans_score.1}}" title="answer score: {{ans_score.1}}">{{ans_score.0}}</td>
|
||||
<td class="user-answer-score-{{ans_score.1}}" title="answer score: {{ans_score.1}} [{{ans_score.2}}]">{{ans_score.0}}</td>
|
||||
{% endwith %}
|
||||
{% endfor %}
|
||||
</tr>
|
||||
@@ -81,6 +81,41 @@
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
Call breakdown
|
||||
<ul>
|
||||
{% for user, breakdown in user_answers_callstates.items %}
|
||||
<li>
|
||||
{{user}} - {{breakdown}}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
<details>
|
||||
<summary>Email results</summary>
|
||||
User results emailed: {{exam.exam_results_emailed|default:"Never"}}<br/>
|
||||
<button id="email-results-button" title="Email results to users and their supervisors">Email user results</button>
|
||||
<button id="email-results-check-button" title="Check the status of emailed results">Check email status</button>
|
||||
</details>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
$(document).ready(() => {
|
||||
$("#email-results-button").click((evt) => {
|
||||
if (confirm("This will email results, please make sure scores have been refreshed before continuing")) {
|
||||
window.location = "{% url 'rapids:exam_report_email' exam_id=exam.pk %}";
|
||||
|
||||
}
|
||||
evt.preventDefault();
|
||||
|
||||
})
|
||||
$("#email-results-check-button").click((evt) => {
|
||||
window.location = "{% url 'rapids:exam_report_email_status' exam_id=exam.pk %}";
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
@@ -36,11 +36,11 @@ def create_superuser(db, django_user_model):
|
||||
@pytest.fixture(params=EXAM_VIEWS)
|
||||
def set_up_exams(request, django_user_model, create_superuser):
|
||||
basic_user = django_user_model.objects.create_user(
|
||||
"user1", "user@user.net", "pass2"
|
||||
"basicuser", "user@user.net", "pass2"
|
||||
)
|
||||
|
||||
e1 = request.param.Exam.objects.create(name="test exam", exam_mode=True)
|
||||
e = request.param.Exam.objects.create(name="test exam 2", exam_mode=True)
|
||||
e = request.param.Exam.objects.create(name="test exam basic user", exam_mode=True)
|
||||
e.author.add(basic_user)
|
||||
return request.param
|
||||
|
||||
@@ -64,12 +64,31 @@ def test_index2(client, set_up_exams):
|
||||
== "2 exams found."
|
||||
)
|
||||
|
||||
def test_overview(client, set_up_exams):
|
||||
client.login(username="admin", password="adminpassword")
|
||||
|
||||
e1 = set_up_exams.Exam.objects.get(name="test exam").pk
|
||||
e2 = set_up_exams.Exam.objects.get(name="test exam basic user").pk
|
||||
|
||||
response = client.get(reverse(f"{set_up_exams.app_name}:exam_overview", args=[e1]))
|
||||
assert response.status_code == 200
|
||||
|
||||
response = client.get(reverse(f"{set_up_exams.app_name}:exam_overview", args=[e2]))
|
||||
assert response.status_code == 200
|
||||
|
||||
client.login(username="basicuser", password="pass2")
|
||||
response = client.get(reverse(f"{set_up_exams.app_name}:exam_overview", args=[e1]))
|
||||
assert response.status_code == 403
|
||||
|
||||
response = client.get(reverse(f"{set_up_exams.app_name}:exam_overview", args=[e2]))
|
||||
assert response.status_code == 200
|
||||
|
||||
|
||||
@pytest.mark.parametrize("exam_views", EXAM_VIEWS)
|
||||
def test_index(client, create_superuser, exam_views, django_user_model):
|
||||
|
||||
basic_user = django_user_model.objects.create_user(
|
||||
"user1", "user@user.net", "pass2"
|
||||
"basicuser", "user@user.net", "pass2"
|
||||
)
|
||||
|
||||
e1 = exam_views.Exam.objects.create(name="test exam", exam_mode=True)
|
||||
|
||||
@@ -72,6 +72,10 @@ urlpatterns = [
|
||||
name="exam_question_detail",
|
||||
),
|
||||
path("exam/<int:pk>/", views.GenericExamViews.exam_overview, name="exam_overview"),
|
||||
path("exam/<int:exam_id>/report/email", views.GenericExamViews.exam_report_email, name="exam_report_email"),
|
||||
path("exam/<int:exam_id>/report/email/status", views.GenericExamViews.exam_report_email_status, name="exam_report_email_status"),
|
||||
path("exam/<int:exam_id>/report/<int:user_id>", views.GenericExamViews.exam_user_report, name="exam_user_report"),
|
||||
path("exam/<int:exam_id>/report/<int:user_id>/email", views.GenericExamViews.exam_user_report_email, name="exam_user_report_email"),
|
||||
path("exam/<int:pk>/authors", views.ExamAuthorUpdate.as_view(), name="exam_authors"),
|
||||
path(
|
||||
"exam/<int:pk>/json_edit",
|
||||
|
||||
Reference in New Issue
Block a user