diff --git a/atlas/forms.py b/atlas/forms.py
index e9eb1af2..5f23e2e0 100755
--- a/atlas/forms.py
+++ b/atlas/forms.py
@@ -15,6 +15,7 @@ from django.forms import (
CheckboxSelectMultiple,
SplitDateTimeWidget,
)
+from django.utils.html import escape
from django.forms import inlineformset_factory
from django.shortcuts import get_object_or_404
from django_jsonforms.forms import JSONSchemaField
@@ -67,7 +68,8 @@ from autocomplete import (
register as autocomplete_register,
)
-import logging
+from loguru import logger
+
from generic.forms import (
ExamAuthorFormMixin,
@@ -471,7 +473,7 @@ class CaseForm(ModelForm):
self.collection = None
if kwargs["initial"] is not None and "exams" in kwargs["initial"]:
collections = kwargs["initial"].pop("exams")
- logging.debug(collections)
+ logger.debug(collections)
self.collection = get_object_or_404(CaseCollection, pk=collections[0])
@@ -606,13 +608,13 @@ class CaseForm(ModelForm):
instance.save()
self.save_m2m()
- logging.debug(f"{self.collection=}")
+ logger.debug(f"{self.collection=}")
if self.collection is not None:
- logging.debug(f"{self.collection=}")
+ logger.debug(f"{self.collection=}")
case_no = self.collection.cases.count() + 1
- logging.debug(f"{case_no=}")
- logging.debug(f"{instance=}")
+ logger.debug(f"{case_no=}")
+ logger.debug(f"{instance=}")
# Create through model
# cd = CaseDetail(case=instance, collection=exam, sort_order=case_no)
@@ -1010,7 +1012,7 @@ class SvelteJSONEditorWidgetOverride(SvelteJSONEditorWidget):
template_name = "atlas/svelte_jsoneditor_widget_override.html"
-class CaseDetailForm(ModelForm):
+class CaseQuestionForm(ModelForm):
class Meta:
model = CaseDetail
fields = ["question_schema", "question_answers"] # , "user"]
@@ -1020,6 +1022,73 @@ class CaseDetailForm(ModelForm):
"question_answers": SvelteJSONEditorWidgetOverride(),
}
+class CaseDetailForm(ModelForm):
+ class Meta:
+ model = CaseDetail
+ fields = ["redact_history", "override_history"]
+
+ def __init__(self, *args, case_history: str = None, **kwargs):
+ """
+ case_history: optional explicit history text to show (falls back to instance.case.history)
+ """
+ super().__init__(*args, **kwargs)
+
+ # Determine original history text (safe-escaped for HTML)
+ if case_history is None:
+ try:
+ case_history = self.instance.case.history if self.instance and getattr(self.instance, "case", None) else ""
+ except Exception:
+ case_history = ""
+ case_history_html = escape(case_history or "No history available.")
+
+ # Ensure override_history has a stable id we can reference from the inline script
+ override_id = self.fields["override_history"].widget.attrs.get("id", "id_override_history")
+ self.fields["override_history"].widget.attrs["id"] = override_id
+ # Optionally make it a textarea style appearance if not already
+ self.fields["override_history"].widget.attrs.setdefault("rows", 6)
+ self.fields["override_history"].widget.attrs.setdefault("class", "form-control")
+
+ # Build crispy helper/layout embedding the original history and buttons tied to override_history
+ self.helper = FormHelper()
+ self.helper.form_tag = False
+
+ # Inline HTML block with buttons and a script that copies/clears the override field.
+ # The script references the explicit override_id above.
+ history_block = f"""
+
+ Show original case history
+
+
+
{case_history_html}
+
Use the buttons below to copy this into the override field or clear the override.
+
+
+
+
+
+
+
+ """
+
+ # Compose layout: history block then the override field and redact checkbox
+ self.helper.layout = Layout(
+ HTML(history_block),
+ Field("override_history"),
+ Field("redact_history")
+ )
+
+
class QuestionSchemaForm(ModelForm):
class Meta:
diff --git a/atlas/migrations/0075_casedetail_override_history_and_more.py b/atlas/migrations/0075_casedetail_override_history_and_more.py
new file mode 100644
index 00000000..b65908cc
--- /dev/null
+++ b/atlas/migrations/0075_casedetail_override_history_and_more.py
@@ -0,0 +1,23 @@
+# Generated by Django 5.1.4 on 2025-09-15 09:31
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('atlas', '0074_alter_seriesimage_image_md5_hash'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='casedetail',
+ name='override_history',
+ field=models.TextField(blank=True, help_text='This will override the case history for the purpose of the exam/collection.', null=True),
+ ),
+ migrations.AddField(
+ model_name='casedetail',
+ name='redact_history',
+ field=models.BooleanField(default=False, help_text='Set to true if the history should be redacted whilst taking the case.'),
+ ),
+ ]
diff --git a/atlas/models.py b/atlas/models.py
index 534bb7d1..dbd92e38 100644
--- a/atlas/models.py
+++ b/atlas/models.py
@@ -1190,6 +1190,16 @@ class CaseDetail(models.Model):
sort_order = models.IntegerField(default=1000)
+ redact_history = models.BooleanField(
+ default=False,
+ help_text="Set to true if the history should be redacted whilst taking the case."
+ )
+
+ override_history = models.TextField(
+ null=True, blank=True,
+ help_text="This will override the case history for the purpose of the exam/collection."
+ )
+
class Meta:
ordering = ("sort_order",)
@@ -1213,6 +1223,15 @@ class CaseDetail(models.Model):
def default_viewerstate_string(self):
return json.dumps(self.default_viewerstate) if self.default_viewerstate else "{}"
+ def get_history_pre(self):
+ if self.collection.show_history_pre:
+ if self.redact_history:
+ return "[Redacted]"
+ if self.override_history and self.override_history != "":
+ return self.override_history
+ return self.case.history
+ return ""
+
class CasePrior(models.Model):
case_detail = models.ForeignKey(CaseDetail, on_delete=models.CASCADE)
prior_case = models.ForeignKey(Case, on_delete=models.CASCADE, related_name="prior_case")
diff --git a/atlas/templates/atlas/collection_case_details.html b/atlas/templates/atlas/collection_case_details.html
new file mode 100644
index 00000000..cdbb0bef
--- /dev/null
+++ b/atlas/templates/atlas/collection_case_details.html
@@ -0,0 +1,34 @@
+{% extends 'atlas/exams.html' %}
+{% load crispy_forms_tags %}
+
+{% block content %}
+
+
+ {% if previous %}
+ Previous question
+ {% endif %}
+ Viewing question as part of collection: {{collection.name}} [{{case_number|add:1}}/{{collection_length}}]
+ {% if next %}
+ Next question
+ {% endif %}
+