tidy up some longs related stuff

This commit is contained in:
Ross
2024-07-08 13:14:11 +01:00
parent bcfcd8da82
commit 19a9a27747
11 changed files with 140 additions and 44 deletions
+5 -2
View File
@@ -51,13 +51,16 @@ class LongAnswerForm(ModelForm):
class MarkLongQuestionSingleForm(ModelForm):
class Meta:
model = UserAnswer
fields = ["score"]
fields = ["score", "candidate_feedback"]
widgets = {
"candidate_feedback": Textarea(attrs={"rows": 3}),
}
class MarkLongQuestionDoubleForm(ModelForm):
class Meta:
model = AnswerMarks
fields = ["score", "mark_reason", "candidate_feedback"]
fields = ["score", "mark_reason"]
class ExaminationForm(ModelForm):
@@ -0,0 +1,22 @@
# Generated by Django 5.0.2 on 2024-07-08 11:49
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('longs', '0024_alter_exam_end_date_alter_exam_exam_results_emailed_and_more'),
]
operations = [
migrations.RemoveField(
model_name='answermarks',
name='candidate_feedback',
),
migrations.AddField(
model_name='useranswer',
name='candidate_feedback',
field=models.TextField(blank=True, help_text='Feedback for the candidate', null=True),
),
]
+9 -7
View File
@@ -328,12 +328,13 @@ class Long(QuestionBase):
return url
def get_unanswered_mark_and_text(self) -> tuple[int, tuple[str, str, str, str, str]]:
def get_unanswered_mark_and_text(self) -> tuple[int, str]:
"""
Long cases are receive a mark between 4 and 8
Therefore unmarked = 4
This has changed to a 3 if unanswered
Therefore unmarked = 3
"""
return (4, ("Not answered",) * 5)
return (3, ("Not answered",) * 5)
# def GetNonFeedbackQuestionImages(self):
# return self.get_images()
@@ -612,6 +613,7 @@ class UserAnswer(UserAnswerBase):
class ScoreOptions(models.TextChoices):
UNMARKED = "", _("Unmarked")
#UNANSWERED = "3", _("Unanswered")
FOUR = "4", _("4")
FOUR_HALF = "4.5", _("4.5")
FIVE = "5", _("5")
@@ -630,6 +632,10 @@ class UserAnswer(UserAnswerBase):
help_text="The final score for the candidate",
)
candidate_feedback = models.TextField(
null=True, blank=True, help_text="Feedback for the candidate"
)
# mark = models.ManyToManyField(
# "AnswerMarks", related_name="user_answer", blank=True
# )
@@ -752,10 +758,6 @@ class AnswerMarks(models.Model):
help_text="Reason for the given mark - not visible to candidates",
)
candidate_feedback = models.TextField(
null=True, blank=True, help_text="Feedback for the candidate"
)
marker = models.ForeignKey(
settings.AUTH_USER_MODEL, related_name="marker", on_delete=models.CASCADE
)
+2
View File
@@ -2,6 +2,8 @@
{% extends 'generic/exam_scores_base.html' %}
{% block table_answers %}
aoeu
eu
{% for question in questions %}
<tr>
<td><a href="{% url 'anatomy:mark' exam_pk=exam.pk sk=forloop.counter0 %}">Question {{forloop.counter}}</a>
+22 -5
View File
@@ -16,10 +16,14 @@
<div>
{% comment %} <h4>Answers</h4> {% endcomment %}
<ul class="score-answer-list">
{% for ans, score, correct_answer in answers_and_marks %}
<li class="user-answer-li" data-question-number="{{forloop.counter}}"><b>Question {{forloop.counter}}</b> <span class="view-question-link-longs"
{% for ans, score, correct_answer, feedback in answers_and_marks %}
<li class="user-answer-li
{% if ans.0 == 'Not answered' %}
not-answered
{% endif %}
" data-question-number="{{forloop.counter}}"><b>Question {{forloop.counter}}</b> <span class="view-question-link-longs"
data-qn={{forloop.counter0}}>View</span>
<ul>
<ul class="">
<li class="Observations" data-qidn="1">Observations</br>
<pre>{{ans.0}}</pre>
</li>
@@ -35,8 +39,14 @@
<li class="Management" data-qidn="5">Management</br>
<pre>{{ans.4}}</pre>
</li>
{% if exam.publish_results %}
<span class="answer-score">{{score}}</span>
{% if view_all_results or exam.publish_results %}
Question score: <span class="answer-score">{{score}}</span>
{% if feedback %}
<p>Question feedback: {{feedback}}</p>
{% endif %}
{% endif %}
</ul>
</li>
@@ -47,6 +57,13 @@
{% include 'user_scores_footer.html' %}
</div>
{% endblock %}
{% block css %}
<style>
.not-answered {
opacity: 0.5;
}
</style>
{% endblock css %}
{% block js %}
<script>
$(document).ready(function () {
+2 -1
View File
@@ -1,4 +1,5 @@
{% extends 'longs/exams.html' %}
{% load crispy_forms_tags %}
{% block content %}
<a href="{% url 'longs:exam_question_detail' exam.id question_details.current %}" title="View the Question">View</a>
@@ -111,7 +112,7 @@
<div class="marking">
<form method="POST" class="post-form">{% csrf_token %}
<input type="hidden" name="form_id" value="mark_form">
{{ form.as_p }}
{{ form|crispy }}
<button type="submit" name="save" class="save btn btn-default" title="Save answer">Save</button>
{% if next_unmarked_id %}
<button type="submit" name="next" class="save btn btn-default"
+2 -2
View File
@@ -601,7 +601,7 @@ def test_exams(db, client):
cid_exam_scores_soup.find("div", {"class": "score-overview"})
.find("span", {"id": "total-score"})
.string
== "4 (3 unmarked)"
== "3 (3 unmarked)"
)
# Mark the questions
@@ -636,7 +636,7 @@ def test_exams(db, client):
cid_exam_scores_soup.find("div", {"class": "score-overview"})
.find("span", {"id": "total-score"})
.string
== "28.0"
== "27.0"
)
generated_questions.pop(-1).delete()
+45 -16
View File
@@ -75,7 +75,15 @@ from django.forms.models import model_to_dict
from longs.forms import LongCreationDefaultForm
from longs.models import LongCreationDefault
from generic.views import AuthorRequiredMixin, ExamCloneMixin, ExamCreateBase, ExamDeleteBase, ExamUpdateBase, ExamViews, SeriesImagesZipViewBase
from generic.views import (
AuthorRequiredMixin,
ExamCloneMixin,
ExamCreateBase,
ExamDeleteBase,
ExamUpdateBase,
ExamViews,
SeriesImagesZipViewBase,
)
from reversion.views import RevisionMixin
import reversion
@@ -271,7 +279,10 @@ def long_clone(request, pk):
new_item.pk = None # autogen a new pk (item_id)
# new_item.name = "Copy of " + new_item.name #need to change uniques
if request.user not in new_item.get_author_objects() and not request.user.is_superuser:
if (
request.user not in new_item.get_author_objects()
and not request.user.is_superuser
):
raise PermissionDenied() # or Http404
form = LongForm(request.POST or None, instance=new_item)
@@ -413,10 +424,14 @@ class LongCreateBase(RevisionMixin, LoginRequiredMixin, CreateView):
def get_context_data(self, **kwargs):
context = super(LongCreateBase, self).get_context_data(**kwargs)
queryset = LongSeries.objects.select_related("modality", "examination", "plane").prefetch_related("long").filter(pk=62)#.values()
queryset = (
LongSeries.objects.select_related("modality", "examination", "plane")
.prefetch_related("long")
.filter(pk=62)
) # .values()
print("TEST")
print(queryset)
#queryset = LongSeries.objects.all()
# queryset = LongSeries.objects.all()
if self.request.POST:
context["series_formset"] = SeriesFormSet(
self.request.POST, self.request.FILES, queryset=queryset
@@ -624,7 +639,6 @@ class LongSeriesView(LoginRequiredMixin, SingleTableMixin, FilterView):
filterset_class = LongSeriesFilter
@user_is_long_marker
@reversion.create_revision()
def mark_answer_override(request, exam_id, question_number, cid):
@@ -704,6 +718,7 @@ def mark_answer(request, exam_id, question_number, answer_id, override=False):
# Extract score from form and save it to the object
answer.score = form.cleaned_data["score"]
answer.candidate_feedback = form.cleaned_data["candidate_feedback"]
answer.save()
if "next" in request.POST:
@@ -724,7 +739,12 @@ def mark_answer(request, exam_id, question_number, answer_id, override=False):
# return redirect("longs:mark", pk=exam_id, sk=n - 1)
else:
form = MarkLongQuestionSingleForm(initial={"score": answer.score})
form = MarkLongQuestionSingleForm(
initial={
"score": answer.score,
"candidate_feedback": answer.candidate_feedback,
}
)
else:
if request.method == "POST":
@@ -794,7 +814,10 @@ def mark_answer(request, exam_id, question_number, answer_id, override=False):
marks = set(answer.mark.values_list("score", flat=True))
if len(marks) > 1 or override:
discrepancy_form = MarkLongQuestionSingleForm(
initial={"score": answer.score}
initial={
"score": answer.score,
"candidate_feedback": answer.candidate_feedback,
}
)
return render(
@@ -838,9 +861,7 @@ def mark(request, exam_id, sk):
user_answers = question.cid_user_answers.filter(exam__id=exam_id)
unmarked_count = user_answers.filter(
score=UserAnswer.ScoreOptions.UNMARKED
).count()
unmarked_count = user_answers.filter(score=UserAnswer.ScoreOptions.UNMARKED).count()
if exam.double_mark:
marker_unmarked_count = question.get_unmarked_user_answer_count(
@@ -872,9 +893,9 @@ def mark(request, exam_id, sk):
)
#@login_required
#@user_is_long_marker
#def exam_scores_all(request, pk):
# @login_required
# @user_is_long_marker
# def exam_scores_all(request, pk):
# exam = get_object_or_404(Exam, pk=pk)
#
# questions = exam.exam_questions.all()
@@ -981,7 +1002,7 @@ def mark(request, exam_id, sk):
# )
#def exam_scores_cid_user(request, pk, cid, passcode):
# def exam_scores_cid_user(request, pk, cid, passcode):
# exam = get_object_or_404(Exam, pk=pk)
#
# # TODO:Need some kind of test for cid
@@ -1125,6 +1146,7 @@ def long_series_order_upload_filename(request, pk):
GenericExamViews = ExamViews(Exam, Long, None, UserAnswer, "longs", "long")
class ExamCreate(ExamCreateBase):
model = Exam
form_class = ExamForm
@@ -1138,17 +1160,24 @@ class ExamUpdate(ExamUpdateBase, AuthorOrCheckerRequiredMixin):
class ExamDelete(AuthorOrCheckerRequiredMixin, ExamDeleteBase):
model = Exam
class ExamClone(ExamCloneMixin, ExamCreate):
"""Clone exam view"""
class ExamAuthorUpdate(RevisionMixin, CheckCanEditMixin, LoginRequiredMixin, AuthorRequiredMixin, UpdateView):
class ExamAuthorUpdate(
RevisionMixin,
CheckCanEditMixin,
LoginRequiredMixin,
AuthorRequiredMixin,
UpdateView,
):
model = Exam
form_class = ExamAuthorForm
template_name = "author_form.html"
#class ExamViewSet(RevisionMixin, viewsets.ModelViewSet):
# class ExamViewSet(RevisionMixin, viewsets.ModelViewSet):
# queryset = Exam.objects.all().order_by("name")
# serializer_class = ExamSerializer