allow viewing of findings in case context
This commit is contained in:
@@ -214,6 +214,11 @@
|
||||
>
|
||||
<span>
|
||||
<a href="{{series.get_absolute_url}}?show_finding={{finding.pk}}"><button class="btn btn-primary btn-sm">View</button></a>
|
||||
<a target="_blank" href="{{series.get_absolute_url}}?show_finding={{finding.pk}}"><button class="btn btn-primary btn-sm">Popup</button></a>
|
||||
|
||||
<button class="btn btn-secondary btn-sm view-finding-modal" data-finding-id="{{ finding.pk }}" data-series-id="{{ series.pk }}">
|
||||
View in Modal
|
||||
</button>
|
||||
</span>
|
||||
{% if finding.findings.all %}
|
||||
<span>
|
||||
@@ -326,8 +331,57 @@
|
||||
|
||||
<p><b>Case size:</b> {{ case.get_total_series_images_size | filesizeformat }}</p>
|
||||
|
||||
<div class="modal fade" id="findingModal" tabindex="-1" aria-labelledby="findingModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="findingModalLabel">Finding Details</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body" id="findingModalBody">
|
||||
<!-- Finding details will be loaded here dynamically -->
|
||||
<p>Loading...</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// From series_viewer.html
|
||||
function loadAnnotationAndViewportOnElement(annotation, viewport, dicom_element, current_image_id_index) {
|
||||
cornerstoneTools.globalImageIdSpecificToolStateManager.clear(dicom_element);
|
||||
cornerstone.getEnabledElement(dicom_element).viewport = viewport
|
||||
cornerstoneTools.globalImageIdSpecificToolStateManager.restoreToolState(
|
||||
annotation);
|
||||
console.log("annotation", annotation)
|
||||
console.log("viewport", viewport)
|
||||
|
||||
//cornerstone.getEnabledElement(dicom_element).toolStateManager.restoreToolState(annotationjson)
|
||||
cornerstone.resize(dicom_element);
|
||||
console.log("current_image_id_index", current_image_id_index)
|
||||
//current_image_id_index = parseInt(current_image_id_index);
|
||||
|
||||
console.log(current_image_id_index, Number.isInteger(current_image_id_index))
|
||||
if (Number.isInteger(current_image_id_index)) {
|
||||
console.log("loading stack index")
|
||||
dicomViewer.loadStackIndex(current_image_id_index, dicom_element);
|
||||
} else {
|
||||
try {
|
||||
console.log("current_image_id_index", current_image_id_index)
|
||||
dicomViewer.loadImageById(current_image_id_index, dicom_element);
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
console.log("loading image by id failed")
|
||||
console.log("loading next annotation image")
|
||||
dicomViewer.getNextAnnotationImage(dicom_element);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const detailsElement = document.getElementById("series-actions");
|
||||
const checkboxes = document.querySelectorAll("input[name='series-ids']");
|
||||
@@ -344,7 +398,59 @@
|
||||
|
||||
// Listen for toggle events on the <details> element
|
||||
detailsElement.addEventListener("toggle", toggleCheckboxes);
|
||||
|
||||
const modal = new bootstrap.Modal(document.getElementById("findingModal"));
|
||||
const modalBody = document.getElementById("findingModalBody");
|
||||
|
||||
document.querySelectorAll(".view-finding-modal").forEach(button => {
|
||||
button.addEventListener("click", function () {
|
||||
const findingId = this.getAttribute("data-finding-id");
|
||||
const seriesId = this.getAttribute("data-series-id");
|
||||
|
||||
// Show loading text
|
||||
modalBody.innerHTML = "<p>Loading...</p>";
|
||||
|
||||
// Fetch the finding details (replace with your actual endpoint)
|
||||
fetch(`/atlas/series_finding/${findingId}/details/`)
|
||||
.then(response => response.text())
|
||||
.then(html => {
|
||||
modalBody.innerHTML = html; // Load the fetched HTML into the modal body
|
||||
const dicomViewer = document.getElementById('single-dicom-viewer');
|
||||
const images = JSON.parse(dicomViewer.getAttribute('data-images'));
|
||||
window.loadDicomViewer(images);
|
||||
annotationjson = JSON.parse(dicomViewer.dataset.annotationjson);
|
||||
viewport = JSON.parse(dicomViewer.dataset.viewportjson);
|
||||
current_image_id_index = dicomViewer.dataset.currentimageid;
|
||||
dicom_element = $(".cornerstone-element").get(0);
|
||||
setTimeout(function () {
|
||||
console.log("loading annotation and viewport")
|
||||
// Load the annotation and viewport on the DICOM element
|
||||
loadAnnotationAndViewportOnElement(annotationjson, viewport, dicom_element, current_image_id_index);
|
||||
}, 200);
|
||||
//loadAnnotationAndViewportOnElement(annotationjson, viewport, dicom_element, current_image_id_index);
|
||||
//const annotations = JSON.parse(dicomViewer.getAttribute('data-annotations'));
|
||||
console.log(images)
|
||||
|
||||
// Initialize the DICOM viewer with the images and annotations
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Error loading finding details:", error);
|
||||
modalBody.innerHTML = "<p>Failed to load finding details.</p>";
|
||||
});
|
||||
|
||||
// Show the modal
|
||||
modal.show();
|
||||
});
|
||||
});
|
||||
|
||||
// Clear the iframe src when the modal is closed
|
||||
document.getElementById("findingModal").addEventListener("hidden.bs.modal", function () {
|
||||
iframe.src = "";
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
|
||||
<div>
|
||||
<h5>Finding: {{ finding.name }}</h5>
|
||||
<p><b>Description:</b> {{ finding.description }}</p>
|
||||
{% if finding.conditions.all %}
|
||||
<p><b>Conditions:</b></p>
|
||||
<ul>
|
||||
{% for condition in finding.conditions.all %}
|
||||
<li>{{ condition.get_link }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% if finding.structures.all %}
|
||||
<p><b>Structures:</b></p>
|
||||
<ul>
|
||||
{% for structure in finding.structures.all %}
|
||||
<li>{{ structure.get_link }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
{% with image_url_array_and_count=finding.series.get_image_url_array_and_count %}
|
||||
<div id="single-dicom-viewer" class="dicom-viewer" data-images='{{ image_url_array_and_count.0 }}' data-annotations='' data-annotationjson={{finding.annotation_json}}
|
||||
data-viewportjson={{finding.viewport_json}} data-findingid={{finding.id}} data-currentimageid={{finding.current_image_id_index}}>
|
||||
</div>
|
||||
{% endwith %}
|
||||
</div>
|
||||
@@ -297,6 +297,7 @@ urlpatterns = [
|
||||
),
|
||||
path("condition/create", views.ConditionCreate.as_view(), name="condition_create"),
|
||||
path("finding/", views.FindingView.as_view(), name="finding_view"),
|
||||
path('series_finding/<int:finding_id>/details/', views.series_finding_details, name='series_finding_details'),
|
||||
path("finding/<int:pk>", views.finding_detail, name="finding_detail"),
|
||||
path(
|
||||
"finding/<int:pk>/delete", views.FindingDelete.as_view(), name="finding_delete"
|
||||
|
||||
@@ -3135,3 +3135,8 @@ def remove_series_from_case(request, series_pk, case_pk):
|
||||
|
||||
return HttpResponse(f"Removed")
|
||||
return HttpResponse("Invalid request.")
|
||||
|
||||
@login_required
|
||||
def series_finding_details(request, finding_id):
|
||||
finding = get_object_or_404(SeriesFinding, pk=finding_id)
|
||||
return render(request, 'atlas/series_finding_details.html', {'finding': finding})
|
||||
Reference in New Issue
Block a user