Refactor collection case navigation and question handling

- Updated links in collection_case_priors.html to point to the new collection_case_questions view.
- Modified collection_case_view_take.html to display case history using the new method.
- Changed collection_detail.html to include links to the new collection_case_questions view.
- Added new URL pattern for collection_case_questions in urls.py.
- Refactored collection_case_details view to handle question-related logic and rendering.
- Introduced new CaseQuestionForm for handling question submissions.
- Created new migration to add override_history and redact_history fields to CaseDetail model.
- Added new templates for collection_case_details.html and collection_case_questions.html to manage case questions.
- Enhanced JavaScript functionality for dynamic question management in collection_case_questions.html.
- Updated useranswer model fields in shorts app to improve feedback and scoring.
This commit is contained in:
Ross
2025-09-15 11:17:57 +01:00
parent 779559997e
commit b9f6b7c837
11 changed files with 231 additions and 23 deletions
+76 -7
View File
@@ -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"""
<details class="mb-3">
<summary class="h6" style="cursor:pointer;"><i class="bi bi-info-circle"></i> Show original case history</summary>
<div class="mt-2 p-2 bg-dark text-light border rounded">
<label class="form-label fw-bold">Original history (read-only)</label>
<div id="original-history" style="white-space: pre-wrap;">{case_history_html}</div>
<div class="form-text text-secondary">Use the buttons below to copy this into the override field or clear the override.</div>
<div class="mt-2">
<button type="button" class="btn btn-sm btn-outline-light" id="use-original-history">Copy original into override</button>
<button type="button" class="btn btn-sm btn-outline-danger ms-2" id="clear-override-history">Clear override</button>
</div>
</div>
</details>
<script>
document.addEventListener('DOMContentLoaded', function() {{
var copyBtn = document.getElementById('use-original-history');
var clearBtn = document.getElementById('clear-override-history');
var original = '{case_history_html.replace("'", "\\'").replace("\\n", "\\\\n")}';
var overrideField = document.getElementById('{override_id}');
function setOverride(val) {{
if(!overrideField) return; overrideField.value = val; overrideField.dispatchEvent(new Event('input',{{bubbles:true}})); }}
if(copyBtn) copyBtn.addEventListener('click', function(e){{ setOverride(original); }});
if(clearBtn) clearBtn.addEventListener('click', function(e){{ setOverride(''); }});
}});
</script>
"""
# 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: