diff --git a/anatomy/static/js/anatomy.js b/anatomy/static/js/anatomy.js
index 13b414cc..25e7132a 100644
--- a/anatomy/static/js/anatomy.js
+++ b/anatomy/static/js/anatomy.js
@@ -12,6 +12,10 @@ window.control_pressed = false;
$(document).ready(function () {
+ // Select all text input fields and textareas with the clear button
+ clearableTextAreaSetup();
+
+
document.body.addEventListener("htmx:responseError", function(e) {
let el = document.getElementById("htmx-error");
var error;
@@ -193,6 +197,40 @@ window.addEventListener('loadDicomViewerUrls', function (e) {
loadDicomViewer(e.detail.images, e.detail.annotations);
}, false);
+function clearableTextAreaSetup() {
+ const textFields = document.querySelectorAll(".position-relative input[type='text'], .position-relative textarea");
+
+ textFields.forEach((field) => {
+ const wrapper = field.closest(".position-relative");
+ const clearButton = wrapper.querySelector(".clear-input-button");
+
+ // Function to toggle the visibility of the clear button
+ const toggleClearButton = () => {
+ if (field.value.trim() !== "" && wrapper.matches(":hover")) {
+ clearButton.style.display = "inline-block"; // Show the button
+ } else {
+ clearButton.style.display = "none"; // Hide the button
+ }
+ };
+
+ // Initial check
+ toggleClearButton();
+
+ // Add event listeners to monitor changes in the input field or textarea
+ field.addEventListener("input", toggleClearButton);
+
+ // Add hover event listeners to the wrapper
+ wrapper.addEventListener("mouseenter", toggleClearButton);
+ wrapper.addEventListener("mouseleave", toggleClearButton);
+
+ // Add click event to clear the field or textarea
+ clearButton.addEventListener("click", function () {
+ field.value = ""; // Clear the field
+ toggleClearButton();
+ });
+ });
+}
+
function loadDicomViewer(images_to_load, annotations_to_load) {
console.log("loadDicomViewer", images_to_load);
let single_dicom = document.getElementById("single-dicom-viewer");
diff --git a/atlas/forms.py b/atlas/forms.py
index d18d1161..82cda80d 100755
--- a/atlas/forms.py
+++ b/atlas/forms.py
@@ -49,7 +49,7 @@ from generic.models import Examination#, Sign
from django.contrib.admin.widgets import FilteredSelectMultiple
from django.forms.widgets import RadioSelect, TextInput, Textarea, Select
-from crispy_forms.layout import Submit, Layout, Fieldset, Field, Div
+from crispy_forms.layout import Submit, Layout, Fieldset, Field, Div, HTML
from crispy_forms.bootstrap import Accordion, AccordionGroup
from tinymce.widgets import TinyMCE
@@ -71,6 +71,12 @@ from typing import Any
from helpers.cimar import CimarAPI, NotFoundError
from rad.settings import CIMAR_USERNAME, CIMAR_PASSWORD
+class ClearableInput(TextInput):
+ template_name = "crispy_forms/widgets/clearable_input_single_line.html"
+
+class ClearableTextarea(Textarea):
+ template_name = "crispy_forms/widgets/clearable_input.html"
+
class CaseSelect(Select):
template_name = "atlas/case_select_widget.html"
@@ -295,14 +301,16 @@ class SeriesForm(ModelForm):
class CaseForm(ModelForm):
- # exams = ModelMultipleChoiceField(required=False, queryset=Exam.objects.all(),widget=FilteredSelectMultiple(verbose_name="Exams", is_stacked=False))
-
-
class Media:
# Django also includes a few javascript files necessary
# for the operation of this form element. You need to
# include
# in the template.
+ #history = forms.CharField(
+ # widget=ClearableTextarea(attrs={"maxlength": 1000, "rows": 5, "cols": 80}),
+ # required=False,
+ #)
+
css = {
"all": ["css/widgets.css"],
}
@@ -367,7 +375,7 @@ class CaseForm(ModelForm):
"open_access",
"previous_case",
"diagnostic_certainty",
- "cimar_uuid"
+ "cimar_uuid",
),
)
@@ -410,9 +418,11 @@ class CaseForm(ModelForm):
# (False, 'No')])
# "findings": TinyMCE(attrs={"cols": 80, "rows": 20}),
# "mark_scheme": TinyMCE(attrs={"cols": 80, "rows": 30}),
- "description": Textarea(attrs={"cols": 80, "rows": 5}),
- "history": Textarea(attrs={"cols": 80, "rows": 5}),
- "discussion": Textarea(attrs={"cols": 80, "rows": 5}),
+ "title": ClearableInput(attrs={"rows": 1}),
+ "description": ClearableTextarea(attrs={"cols": 80, "rows": 5}),
+ "history": ClearableTextarea(attrs={"maxlength": 2000, "rows": 5}),
+ "discussion": ClearableTextarea(attrs={"maxlength": 2000, "rows": 5}),
+ "report": ClearableTextarea(attrs={"maxlength": 2000, "rows": 5}),
"condition": autocomplete.ModelSelect2Multiple(
url="atlas:condition-autocomplete"
),
diff --git a/atlas/models.py b/atlas/models.py
index d5b354ef..54b7ae52 100644
--- a/atlas/models.py
+++ b/atlas/models.py
@@ -774,6 +774,21 @@ class Series(SeriesBase):
def get_link_headers(self):
return "atlas/series_headers.html"
+ def get_related_findings(self):
+ """Returns the related findings for the series, including findings from other series in the same case."""
+ # Get findings directly related to this series
+ findings = list(self.findings.all())
+
+ # Get findings from other series in the same case
+ if hasattr(self, "case") and self.case.exists():
+ for case in self.case.all():
+ for other_series in case.series.exclude(pk=self.pk):
+ findings.extend(other_series.findings.all())
+
+ # Remove duplicates (by pk)
+ unique_findings = {f.pk: f for f in findings}.values()
+ return list(unique_findings)
+
class CaseCollection(ExamOrCollectionGenericBase):
diff --git a/atlas/templates/atlas/case_display_block.html b/atlas/templates/atlas/case_display_block.html
index 0654b2c2..1f92c72c 100755
--- a/atlas/templates/atlas/case_display_block.html
+++ b/atlas/templates/atlas/case_display_block.html
@@ -214,8 +214,6 @@
>
-
-
diff --git a/atlas/templates/atlas/case_form.html b/atlas/templates/atlas/case_form.html
index 3f947d82..0207334d 100755
--- a/atlas/templates/atlas/case_form.html
+++ b/atlas/templates/atlas/case_form.html
@@ -51,8 +51,70 @@
$('#add_more_resource').click(() => { add_resource_input_form() });
})
-
+{% comment %} document.addEventListener("DOMContentLoaded", function () {
+ // Select all text input fields and textareas
+ const textFields = document.querySelectorAll("input[type='text'], textarea");
+ textFields.forEach((field) => {
+ // Create a clear button for each text field or textarea
+ const clearButton = document.createElement("button");
+ clearButton.type = "button";
+ clearButton.className = "btn btn-danger btn-sm clear-input-button";
+ clearButton.textContent = "×";
+
+ // Wrap the input field or textarea in a container with position-relative
+ const wrapper = document.createElement("div");
+ wrapper.className = "position-relative";
+ field.parentNode.insertBefore(wrapper, field);
+ wrapper.appendChild(field);
+ wrapper.appendChild(clearButton);
+
+ // Function to toggle the visibility of the clear button
+ const toggleClearButton = () => {
+ if (field.value.trim() !== "") {
+ clearButton.classList.add("show"); // Add the "show" class
+ } else {
+ clearButton.classList.remove("show"); // Remove the "show" class
+ }
+ };
+
+ // Initial check
+ toggleClearButton();
+
+ // Add event listeners to monitor changes in the input field or textarea
+ field.addEventListener("input", toggleClearButton);
+
+ // Add click event to clear the field or textarea
+ clearButton.addEventListener("click", function () {
+ field.value = ""; // Clear the field
+ toggleClearButton(); // Update the button visibility
+ });
+ });
+});
+ {% endcomment %}
+
+{% comment %} {% endcomment %}
{% endblock %}
{% block content %}
diff --git a/atlas/templates/atlas/series_finding_related.html b/atlas/templates/atlas/series_finding_related.html
new file mode 100644
index 00000000..081d7b67
--- /dev/null
+++ b/atlas/templates/atlas/series_finding_related.html
@@ -0,0 +1,124 @@
+
+
+
+
Related findings from case
+
+
+
+ Help
+
This form is designed to quickly populate the same finding across different series.
+
Cloning a finding from this form will populate the details with a blank viewport (you will need to add visual annotation and set the display as intended).
+
+
+ {% for finding in findings %}
+
+
{{ finding.title }}
+ {% if finding.description %}
+ Description:
+
{{ finding.description|truncatechars:100 }}
+ {% endif %}
+ {% if finding.findings.all %}
+
+ Findings:
+ {% for subfinding in finding.findings.all %}
+ {{ subfinding.get_link }}{% if not forloop.last %}, {% endif %}
+ {% endfor %}
+
+ {% endif %}
+ {% if finding.structures.all %}
+
+ Structures:
+ {% for structure in finding.structures.all %}
+ {{ structure.get_link }}{% if not forloop.last %}, {% endif %}
+ {% endfor %}
+
+ {% endif %}
+ {% if finding.conditions.all %}
+
+ Conditions:
+ {% for condition in finding.conditions.all %}
+ {{ condition.get_link }}{% if not forloop.last %}, {% endif %}
+ {% endfor %}
+
+ {% endif %}
+
+
+
+
+ {% endfor %}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/atlas/templates/atlas/series_viewer.html b/atlas/templates/atlas/series_viewer.html
index fa404026..799046b3 100755
--- a/atlas/templates/atlas/series_viewer.html
+++ b/atlas/templates/atlas/series_viewer.html
@@ -47,6 +47,13 @@
{% if editing_finding < 1 %}
{% endif %}
+