diff --git a/atlas/forms.py b/atlas/forms.py
index 0898438c..f3b567d2 100755
--- a/atlas/forms.py
+++ b/atlas/forms.py
@@ -1062,6 +1062,14 @@ class AddCollectionToCaseForm(Form):
class SelfReviewForm(ModelForm):
+ SCORE_CHOICES = [
+ (1, "1 - Poor"),
+ (2, "2 - Limited"),
+ (3, "3 - Adequate"),
+ (4, "4 - Good"),
+ (5, "5 - Excellent"),
+ ]
+
class Meta:
model = SelfReview
fields = ["user_exam", "case", "comments", "findings", "interpretation"]
@@ -1076,6 +1084,44 @@ class SelfReviewForm(ModelForm):
ModelForm.__init__(self, *args, **kwargs)
super(SelfReviewForm, self).__init__(*args, **kwargs)
+ # Render scoring fields as explicit 1-5 selector choices for cleaner UX.
+ self.fields["findings"] = forms.TypedChoiceField(
+ choices=self.SCORE_CHOICES,
+ coerce=int,
+ empty_value=None,
+ required=False,
+ widget=RadioSelect,
+ label="Findings",
+ help_text="How well did you identify key findings?",
+ )
+ self.fields["interpretation"] = forms.TypedChoiceField(
+ choices=self.SCORE_CHOICES,
+ coerce=int,
+ empty_value=None,
+ required=False,
+ widget=RadioSelect,
+ label="Interpretation",
+ help_text="How strong was your overall interpretation?",
+ )
+
+ self.fields["comments"].widget.attrs.update(
+ {
+ "rows": 5,
+ "placeholder": "What went well? What would you do differently next time?",
+ }
+ )
+
+ self.helper = FormHelper()
+ self.helper.form_method = "post"
+ self.helper.form_tag = False
+ self.helper.layout = Layout(
+ Field("user_exam"),
+ Field("case"),
+ Field("comments"),
+ Field("findings"),
+ Field("interpretation"),
+ )
+
class UncategorisedDicomForm(ModelForm):
class Meta:
diff --git a/atlas/templates/atlas/collection_case_view_take.html b/atlas/templates/atlas/collection_case_view_take.html
index c30e7e0d..b21dcdcc 100644
--- a/atlas/templates/atlas/collection_case_view_take.html
+++ b/atlas/templates/atlas/collection_case_view_take.html
@@ -214,9 +214,23 @@
{% if collection.self_review %}
-
- Add self review
-
+
{% if self_review %}
Self Feedback
@@ -263,9 +277,23 @@
{% if question_completed %}
{% if collection.self_review %}
-
- Add self review
-
+
{% if self_review %}
Self Feedback
@@ -306,6 +334,17 @@
goto
+
+
+
{% endblock %}
@@ -590,6 +667,89 @@
}
});
+ window.openSelfReviewPopup = function (url) {
+ var features = 'width=980,height=840,resizable=yes,scrollbars=yes';
+ window.open(url, 'selfReviewPopup', features);
+ };
+
+ window.selfReviewCurrentEmbedUrl = null;
+
+ window.bindSelfReviewEmbedForm = function () {
+ var root = document.getElementById('selfReviewSidebarBody');
+ if (!root) return;
+
+ var form = root.querySelector('form.self-review-embed-form');
+ if (!form) return;
+
+ if (form.dataset.bound === '1') return;
+ form.dataset.bound = '1';
+
+ form.addEventListener('submit', async function (event) {
+ event.preventDefault();
+
+ var formData = new FormData(form);
+ var csrftokenInput = form.querySelector('input[name="csrfmiddlewaretoken"]');
+ var csrftoken = csrftokenInput ? csrftokenInput.value : '';
+
+ try {
+ var resp = await fetch(window.selfReviewCurrentEmbedUrl || window.location.href, {
+ method: 'POST',
+ body: formData,
+ headers: {
+ 'X-Requested-With': 'XMLHttpRequest',
+ 'X-CSRFToken': csrftoken,
+ },
+ credentials: 'same-origin',
+ });
+
+ if (resp.redirected) {
+ root.innerHTML = '
Self review saved. You can continue reviewing this case.
';
+ setTimeout(function () { window.closeSelfReviewSidebar(); }, 700);
+ return;
+ }
+
+ var html = await resp.text();
+ root.innerHTML = html;
+ window.bindSelfReviewEmbedForm();
+ } catch (e) {
+ var msg = root.querySelector('.self-review-embed-message');
+ if (msg) {
+ msg.innerHTML = '
Unable to save self review. Please try again.
';
+ }
+ }
+ });
+ };
+
+ window.closeSelfReviewSidebar = function () {
+ var sidebar = document.getElementById('selfReviewSidebar');
+ if (sidebar) {
+ sidebar.classList.remove('open');
+ }
+ };
+
+ window.openSelfReviewSidebar = async function (url) {
+ var sidebar = document.getElementById('selfReviewSidebar');
+ var body = document.getElementById('selfReviewSidebarBody');
+ if (!sidebar || !body) {
+ window.location.href = url;
+ return;
+ }
+
+ var embedUrl = url + (url.indexOf('?') === -1 ? '?embed=1' : '&embed=1');
+ window.selfReviewCurrentEmbedUrl = embedUrl;
+ body.innerHTML = '
Loading self-review form...
';
+ sidebar.classList.add('open');
+
+ try {
+ var resp = await fetch(embedUrl, { credentials: 'same-origin' });
+ var html = await resp.text();
+ body.innerHTML = html;
+ window.bindSelfReviewEmbedForm();
+ } catch (e) {
+ body.innerHTML = '
Could not load self-review sidebar content. Use Popup as fallback.
';
+ }
+ };
+
{% endblock js %}
diff --git a/atlas/templates/atlas/partials/_selfreview_form_embed.html b/atlas/templates/atlas/partials/_selfreview_form_embed.html
new file mode 100644
index 00000000..e52d76a1
--- /dev/null
+++ b/atlas/templates/atlas/partials/_selfreview_form_embed.html
@@ -0,0 +1,83 @@
+{% load crispy_forms_tags %}
+
+
+
+
+
+
+ Record a quick reflection while reviewing this case.
+
+
+ Case: {{ case }}
+
+
+
+
+
+
+
diff --git a/atlas/templates/atlas/selfreview_form.html b/atlas/templates/atlas/selfreview_form.html
index 5d1f48c9..ef1a1ba9 100644
--- a/atlas/templates/atlas/selfreview_form.html
+++ b/atlas/templates/atlas/selfreview_form.html
@@ -1,38 +1,88 @@
{% extends "base.html" %}
+{% load crispy_forms_tags %}
{% block content %}
-
Add Self Review
-
- Adding self feedback for {{user_exam.exam}}/{{case}}
-
-
- This form allows you to record your own feedback for the question / case. This can take the form of a note or a ranking (1-5) of your findings and interpretation.
-