Add help tags for enhanced user guidance in templates and improve feedback descriptions in models

This commit is contained in:
Ross
2025-09-08 10:45:33 +01:00
parent 7ff5d1d37e
commit 7cc6b2959a
5 changed files with 347 additions and 180 deletions
@@ -2,6 +2,7 @@
{% load static %}
{% load crispy_forms_tags %}
{% load help_tags %}
{% block title %}
Anatomy
@@ -226,14 +227,13 @@
{% endif %}
<h2>{{object|yesno:"Update,Create"}} Question</h2>
<details class="help-text">
<summary><i class="bi bi-info-circle"></i> Help</summary>
{% help "Help" %}
<p>This form can be used to create an Anatomy question.</p>
<p>Question type can be selected from a predefined list. If you require a more please contact ross.kruger@nhs.net.</p>
<p>Correct and incorrect answers can be defined once the question has been created.</p>
</details>
{% endhelp %}
<form action="" method="post" enctype="multipart/form-data" id="anatomyquestion-form">
{% csrf_token %}
<a href="/anatomy/examination/create" id="add_examination" class="add-popup"
+57
View File
@@ -0,0 +1,57 @@
from django import template
from django.template import Node, TemplateSyntaxError, Variable
register = template.Library()
class HelpNode(Node):
def __init__(self, nodelist, title, icon, extra_class):
self.nodelist = nodelist
self.title = title
self.icon = icon
self.extra_class = extra_class
def resolve(self, val, context):
if isinstance(val, Variable):
try:
return val.resolve(context)
except Exception:
return ''
return val
def render(self, context):
title = self.resolve(self.title, context) or "Help"
icon = self.resolve(self.icon, context) or "bi bi-info-circle"
extra_class = self.resolve(self.extra_class, context) or "help-text"
body = self.nodelist.render(context)
return f'<details class="{extra_class}"><summary><i class="{icon}"></i> {title}</summary>{body}</details>'
@register.tag(name="help")
def do_help(parser, token):
bits = token.split_contents()
title = "Help"
icon = "bi bi-info-circle"
extra_class = "help-text"
for bit in bits[1:]:
if "=" in bit:
key, val = bit.split("=", 1)
if (val.startswith('"') and val.endswith('"')) or (val.startswith("'") and val.endswith("'")):
val = val[1:-1]
else:
val = Variable(val)
if key == "icon":
icon = val
elif key in ("class", "extra_class"):
extra_class = val
else:
raise TemplateSyntaxError(f"Unknown keyword for help tag: {key}")
else:
v = bit
if (v.startswith('"') and v.endswith('"')) or (v.startswith("'") and v.endswith("'")):
title = v[1:-1]
else:
title = Variable(v)
nodelist = parser.parse(("endhelp",))
parser.delete_first_token()
return HelpNode(nodelist, title, icon, extra_class)
+3 -2
View File
@@ -545,11 +545,12 @@ class UserAnswer(UserAnswerBase):
# If score is null then the answer is unmarked
score = models.IntegerField(
default=0, blank=True, validators=[MinValueValidator(0), MaxValueValidator(5)], null=True
default=0, blank=True, validators=[MinValueValidator(0), MaxValueValidator(5)], null=True,
help_text="Score for the answer. If null then the answer is unmarked. This should be number 0-5.",
)
candidate_feedback = models.TextField(
null=True, blank=True, help_text="Feedback for the candidate"
null=True, blank=True, help_text="Feedback for the candidate, this is optional but WILL be shown to the candidate."
)
class CallStateOptions(models.TextChoices):
+108
View File
@@ -1,5 +1,6 @@
{% extends 'shorts/exams.html' %}
{% load crispy_forms_tags %}
{% load help_tags %}
{% block content %}
<a href="{% url 'shorts:exam_question_detail' exam.id question_details.current|add:'-1' %}" title="View the Question">View</a>
@@ -37,6 +38,112 @@
</div>
{% endif %}
{% help "Help" %}
<p>Marking guidance for the 2B exam can be viewed on the RCR site at <a href="https://www.rcr.ac.uk/exams-training/rcr-exams/clinical-radiology-exams/frcr-part-2b-radiology-cr2b/frcr-part-2b-radiology-cr2b-scoring-system/" target="_blank">here</a></p>
<p>For convienience the info is also copied below:</p>
<div class="alert alert-secondary" role="alert">
<h3>FRCR Part 2B Short Cases Marking Guidance</h3>
<p>
Candidates report on <b>25 cases</b> and have the opportunity to attain <b>five marks per case</b>, so a maximum of <b>125 marks</b> across the exam. <b>No half marks</b> will be allocated. Candidate responses are independently double marked and the final score awarded will be the average of the two examiner marks. The marking framework permits a difference of up to one mark between the two examiners. Any score difference of two or above will be flagged for review and discussion.
</p>
<p>
<a href="#">Click here for short case example questions.</a>
</p>
<p>
All questions include question-specific marking guidance to indicate key findings, diagnosis and recommended onward management. Examiners will use the following mark scheme descriptors for examiners alongside the marking guidance to score candidates responses:
</p>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Score</th>
<th>Marking descriptors</th>
</tr>
</thead>
<tbody>
<tr>
<td><b>5</b></td>
<td>
<ul>
<li>Detects all major findings and most minor observations.</li>
<li>Makes correct diagnosis or differential diagnoses using clinical information provided.</li>
<li>Makes safe further management plans with clear recommendations (including MDT or specialist referral if appropriate).</li>
<li>Demonstrates a clear understanding of likely further management required by different MDTs.</li>
<li>Report is clear, logical and concise.</li>
<li>May demonstrate knowledge beyond the core curriculum.</li>
</ul>
</td>
</tr>
<tr>
<td><b>4</b></td>
<td>
<ul>
<li>Detects most major findings (including those marked essential) and most minor observations.</li>
<li>Makes correct diagnosis or differential diagnoses using clinical information provided.</li>
<li>Makes safe further management plans with clear recommendations (including MDT or specialist referral if appropriate).</li>
<li>Report is clear, logical and concise.</li>
</ul>
</td>
</tr>
<tr>
<td><b>3</b></td>
<td>
<ul>
<li>Detects most major findings (including those marked essential) and some minor observations.</li>
<li>May or may not reach the correct diagnosis but demonstrates sensible clinical reasoning.</li>
<li>Safe differential diagnoses offered.</li>
<li>May not know full management plan but demonstrates enough knowledge not to compromise patient safety.</li>
<li>Report is clear although it may not be ideally structured.</li>
</ul>
</td>
</tr>
<tr>
<td><b>2</b></td>
<td>
<ul>
<li>Major/critical observations missed.</li>
<li>Undue significance placed on irrelevant or minor findings.</li>
<li>Limited understanding of pathology.</li>
<li>Fails to make correct diagnosis or provide reasonable differential diagnoses.</li>
<li>Failure to refer to appropriate MDT/clinical team or inappropriate over-investigation e.g. MRI and orthopaedic referral for benign bone lesions.</li>
<li>Unstructured, scattergun report.</li>
</ul>
</td>
</tr>
<tr>
<td><b>1</b></td>
<td>
<ul>
<li>Major/critical observations missed.</li>
<li>Undue significance placed on minor or incidental findings.</li>
<li>Demonstrates no understanding of pathology.</li>
<li>Inappropriate patient management (including over-investigation).</li>
<li>Incoherent, rambling report.</li>
</ul>
</td>
</tr>
<tr>
<td><b>0</b></td>
<td>
<ul>
<li>No answer provided or answer is entirely incorrect or inappropriate/unsafe patient management.</li>
</ul>
</td>
</tr>
</tbody>
</table>
<p>
An Angoff standard setting process is used to set the pass mark for each sitting based on the content used.
</p>
</div>
<p>Ideally each question will have individual marking guidance that will be shown below.</p>
{% endhelp %}
{% if not next_unmarked_id and not unmarked %}
<div class="alert alert-info sticky-alert" role="alert">Success! Marking question complete. <br />
@@ -60,6 +167,7 @@
{% endif %}
<span>Marking Candidate: {{answer.get_candidate_masked}} ({{unmarked|length}} answer(s) left to mark)</span>
<details closed>
<summary title="click to view/hide the question details">Question Details</summary>
<p class="pre-whitespace"><b>History:</b> {{ question.history }}</p>
+1
View File
@@ -3,6 +3,7 @@
{% load django_htmx %}
{% load crispy_forms_tags %}
{% load auth_extras %}
{% load help_tags %}
<html data-bs-theme="dark">
<head>