This commit is contained in:
Ross
2021-08-20 10:26:58 +01:00
parent ad3094851d
commit 01c98cd2e1
12 changed files with 176 additions and 97 deletions
+1 -1
View File
@@ -178,7 +178,7 @@ function loadDicomViewer(images_to_load, annotations_to_load) {
console.log(images); console.log(images);
let annotations = single_dicom.dataset.annotations; let annotations = single_dicom.dataset.annotations;
if (annotations != undefined) { if (annotations_to_load != undefined) {
annotations = annotations_to_load; annotations = annotations_to_load;
} else if (annotations && annotations.indexOf(",") > 0) { } else if (annotations && annotations.indexOf(",") > 0) {
annotations = JSON.parse(annotations); annotations = JSON.parse(annotations);
@@ -0,0 +1,18 @@
# Generated by Django 3.2.6 on 2021-08-20 09:09
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('rapids', '0029_rapidimage_image_annotations'),
]
operations = [
migrations.AlterField(
model_name='rapidimage',
name='image_annotations',
field=models.TextField(blank=True, help_text='Stores a JSON representation of annotations to be applied by cornerstonetools', null=True),
),
]
+1 -1
View File
@@ -388,7 +388,7 @@ class RapidImage(models.Model):
image = models.FileField(upload_to=image_directory_path) image = models.FileField(upload_to=image_directory_path)
image_annotations = models.TextField( image_annotations = models.TextField(
blank=True, blank=True, null=True,
help_text="Stores a JSON representation of annotations to be applied by cornerstonetools", help_text="Stores a JSON representation of annotations to be applied by cornerstonetools",
) )
@@ -89,15 +89,16 @@
<summary> <summary>
Image viewer Image viewer
</summary> </summary>
<div id="single-dicom-viewer" class="dicom-viewer" data-images="{{ question.get_image_url_array }}" data-annotations='{{question.get_image_annotations}}'> <div id="single-dicom-viewer" class="dicom-viewer" data-images="{{ question.get_image_url_array }}"
data-annotations='{{question.get_image_annotations}}'>
</div> </div>
<button id="save-annotations">Save Annotations</button> <button id="save-annotations">Save Annotations</button>
</details> </details>
<div class="pre-whitespace multi-image-block"><b>Image annotations:</b> <div class=""><b>Image annotations:</b>
{% for image in question.images.all %} {% for image in question.images.all %}
<span class="image-block"> <span class="image-block">
Image {{ forloop.counter }}: {{image.image_annotations}} Image {{ forloop.counter }}: {{image.image_annotations}}<br />
</span> </span>
{% endfor %} {% endfor %}
</div> </div>
+6 -3
View File
@@ -1160,10 +1160,13 @@ def question_save_annotation(request, pk):
question_images = question.images.all() question_images = question.images.all()
for n, annotation in enumerate(image_annotations): for n, image in enumerate(question_images):
annotation = image_annotations[n]
if annotation != "undefined": if annotation != "undefined":
question_images[n].image_annotations = annotation image.image_annotations = annotation
question_images[n].save() else:
image.image_annotations = ""
image.save()
data = {"status": "success"} data = {"status": "success"}
return JsonResponse(data, status=200) return JsonResponse(data, status=200)
+1 -1
View File
@@ -198,7 +198,7 @@
#djDebug #djDebugToolbarHandle { #djDebug #djDebugToolbarHandle {
position: fixed; position: fixed;
transform: rotate(-90deg); transform: translateY(-100%) rotate(-90deg);
transform-origin: right bottom; transform-origin: right bottom;
background-color: #fff; background-color: #fff;
border: 1px solid #111; border: 1px solid #111;
+27 -16
View File
@@ -7,21 +7,30 @@ $$.on(djDebug, "click", ".switchHistory", function (event) {
const newStoreId = this.dataset.storeId; const newStoreId = this.dataset.storeId;
const tbody = this.closest("tbody"); const tbody = this.closest("tbody");
tbody const highlighted = tbody.querySelector(".djdt-highlighted");
.querySelector(".djdt-highlighted") if (highlighted) {
.classList.remove("djdt-highlighted"); highlighted.classList.remove("djdt-highlighted");
}
this.closest("tr").classList.add("djdt-highlighted"); this.closest("tr").classList.add("djdt-highlighted");
ajaxForm(this).then(function (data) { ajaxForm(this).then(function (data) {
djDebug.setAttribute("data-store-id", newStoreId); djDebug.setAttribute("data-store-id", newStoreId);
Object.keys(data).forEach(function (panelId) { // Check if response is empty, it could be due to an expired store_id.
const panel = document.getElementById(panelId); if (Object.keys(data).length === 0) {
if (panel) { const container = document.getElementById("djdtHistoryRequests");
panel.outerHTML = data[panelId].content; container.querySelector(
document.getElementById("djdt-" + panelId).outerHTML = 'button[data-store-id="' + newStoreId + '"]'
data[panelId].button; ).innerHTML = "Switch [EXPIRED]";
} } else {
}); Object.keys(data).forEach(function (panelId) {
const panel = document.getElementById(panelId);
if (panel) {
panel.outerHTML = data[panelId].content;
document.getElementById("djdt-" + panelId).outerHTML =
data[panelId].button;
}
});
}
}); });
}); });
@@ -29,12 +38,14 @@ $$.on(djDebug, "click", ".refreshHistory", function (event) {
event.preventDefault(); event.preventDefault();
const container = document.getElementById("djdtHistoryRequests"); const container = document.getElementById("djdtHistoryRequests");
ajaxForm(this).then(function (data) { ajaxForm(this).then(function (data) {
// Remove existing rows first then re-populate with new data
container
.querySelectorAll("tr[data-store-id]")
.forEach(function (node) {
node.remove();
});
data.requests.forEach(function (request) { data.requests.forEach(function (request) {
if ( container.innerHTML = request.content + container.innerHTML;
!container.querySelector('[data-store-id="' + request.id + '"]')
) {
container.innerHTML = request.content + container.innerHTML;
}
}); });
}); });
}); });
+72 -56
View File
@@ -1,59 +1,75 @@
const timingOffset = performance.timing.navigationStart, import { $$ } from "./utils.js";
timingEnd = performance.timing.loadEventEnd,
totalTime = timingEnd - timingOffset; function insertBrowserTiming() {
function getLeft(stat) { console.log(["inserted"]);
return ((performance.timing[stat] - timingOffset) / totalTime) * 100.0; const timingOffset = performance.timing.navigationStart,
} timingEnd = performance.timing.loadEventEnd,
function getCSSWidth(stat, endStat) { totalTime = timingEnd - timingOffset;
let width = function getLeft(stat) {
((performance.timing[endStat] - performance.timing[stat]) / totalTime) * return ((performance.timing[stat] - timingOffset) / totalTime) * 100.0;
100.0; }
// Calculate relative percent (same as sql panel logic) function getCSSWidth(stat, endStat) {
width = (100.0 * width) / (100.0 - getLeft(stat)); let width =
return width < 1 ? "2px" : width + "%"; ((performance.timing[endStat] - performance.timing[stat]) /
} totalTime) *
function addRow(tbody, stat, endStat) { 100.0;
const row = document.createElement("tr"); // Calculate relative percent (same as sql panel logic)
if (endStat) { width = (100.0 * width) / (100.0 - getLeft(stat));
// Render a start through end bar return width < 1 ? "2px" : width + "%";
row.innerHTML = }
"<td>" + function addRow(tbody, stat, endStat) {
stat.replace("Start", "") + const row = document.createElement("tr");
"</td>" + if (endStat) {
'<td><svg class="djDebugLineChart" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 100 5" preserveAspectRatio="none"><rect y="0" height="5" fill="#ccc" /></svg></td>' + // Render a start through end bar
"<td>" + row.innerHTML =
(performance.timing[stat] - timingOffset) + "<td>" +
" (+" + stat.replace("Start", "") +
(performance.timing[endStat] - performance.timing[stat]) + "</td>" +
")</td>"; '<td><svg class="djDebugLineChart" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 100 5" preserveAspectRatio="none"><rect y="0" height="5" fill="#ccc" /></svg></td>' +
row.querySelector("rect").setAttribute( "<td>" +
"width", (performance.timing[stat] - timingOffset) +
getCSSWidth(stat, endStat) " (+" +
); (performance.timing[endStat] - performance.timing[stat]) +
} else { ")</td>";
// Render a point in time row.querySelector("rect").setAttribute(
row.innerHTML = "width",
"<td>" + getCSSWidth(stat, endStat)
stat + );
"</td>" + } else {
'<td><svg class="djDebugLineChart" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 100 5" preserveAspectRatio="none"><rect y="0" height="5" fill="#ccc" /></svg></td>' + // Render a point in time
"<td>" + row.innerHTML =
(performance.timing[stat] - timingOffset) + "<td>" +
"</td>"; stat +
row.querySelector("rect").setAttribute("width", 2); "</td>" +
'<td><svg class="djDebugLineChart" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 100 5" preserveAspectRatio="none"><rect y="0" height="5" fill="#ccc" /></svg></td>' +
"<td>" +
(performance.timing[stat] - timingOffset) +
"</td>";
row.querySelector("rect").setAttribute("width", 2);
}
row.querySelector("rect").setAttribute("x", getLeft(stat));
tbody.appendChild(row);
}
const browserTiming = document.getElementById("djDebugBrowserTiming");
// Determine if the browser timing section has already been rendered.
if (browserTiming.classList.contains("djdt-hidden")) {
const tbody = document.getElementById("djDebugBrowserTimingTableBody");
// This is a reasonably complete and ordered set of timing periods (2 params) and events (1 param)
addRow(tbody, "domainLookupStart", "domainLookupEnd");
addRow(tbody, "connectStart", "connectEnd");
addRow(tbody, "requestStart", "responseEnd"); // There is no requestEnd
addRow(tbody, "responseStart", "responseEnd");
addRow(tbody, "domLoading", "domComplete"); // Spans the events below
addRow(tbody, "domInteractive");
addRow(tbody, "domContentLoadedEventStart", "domContentLoadedEventEnd");
addRow(tbody, "loadEventStart", "loadEventEnd");
browserTiming.classList.remove("djdt-hidden");
} }
row.querySelector("rect").setAttribute("x", getLeft(stat));
tbody.appendChild(row);
} }
const tbody = document.getElementById("djDebugBrowserTimingTableBody"); const djDebug = document.getElementById("djDebug");
// This is a reasonably complete and ordered set of timing periods (2 params) and events (1 param) // Insert the browser timing now since it's possible for this
addRow(tbody, "domainLookupStart", "domainLookupEnd"); // script to miss the initial panel load event.
addRow(tbody, "connectStart", "connectEnd"); insertBrowserTiming();
addRow(tbody, "requestStart", "responseEnd"); // There is no requestEnd $$.onPanelRender(djDebug, "TimerPanel", insertBrowserTiming);
addRow(tbody, "responseStart", "responseEnd");
addRow(tbody, "domLoading", "domComplete"); // Spans the events below
addRow(tbody, "domInteractive");
addRow(tbody, "domContentLoadedEventStart", "domContentLoadedEventEnd");
addRow(tbody, "loadEventStart", "loadEventEnd");
document.getElementById("djDebugBrowserTiming").classList.remove("djdt-hidden");
+27 -12
View File
@@ -20,7 +20,8 @@ const djdt = {
if (!this.className) { if (!this.className) {
return; return;
} }
const current = document.getElementById(this.className); const panelId = this.className;
const current = document.getElementById(panelId);
if ($$.visible(current)) { if ($$.visible(current)) {
djdt.hide_panels(); djdt.hide_panels();
} else { } else {
@@ -39,13 +40,24 @@ const djdt = {
window.location window.location
); );
url.searchParams.append("store_id", store_id); url.searchParams.append("store_id", store_id);
url.searchParams.append("panel_id", this.className); url.searchParams.append("panel_id", panelId);
ajax(url).then(function (data) { ajax(url).then(function (data) {
inner.previousElementSibling.remove(); // Remove AJAX loader inner.previousElementSibling.remove(); // Remove AJAX loader
inner.innerHTML = data.content; inner.innerHTML = data.content;
$$.executeScripts(data.scripts); $$.executeScripts(data.scripts);
$$.applyStyles(inner); $$.applyStyles(inner);
djDebug.dispatchEvent(
new CustomEvent("djdt.panel.render", {
detail: { panelId: panelId },
})
);
}); });
} else {
djDebug.dispatchEvent(
new CustomEvent("djdt.panel.render", {
detail: { panelId: panelId },
})
);
} }
} }
} }
@@ -178,6 +190,7 @@ const djdt = {
requestAnimationFrame(function () { requestAnimationFrame(function () {
djdt.handleDragged = false; djdt.handleDragged = false;
}); });
djdt.ensure_handle_visibility();
} }
}); });
const show = const show =
@@ -198,6 +211,15 @@ const djdt = {
e.classList.remove("djdt-active"); e.classList.remove("djdt-active");
}); });
}, },
ensure_handle_visibility() {
const handle = document.getElementById("djDebugToolbarHandle");
// set handle position
const handleTop = Math.min(
localStorage.getItem("djdt.top") || 0,
window.innerHeight - handle.offsetWidth
);
handle.style.top = handleTop + "px";
},
hide_toolbar() { hide_toolbar() {
djdt.hide_panels(); djdt.hide_panels();
@@ -205,16 +227,8 @@ const djdt = {
const handle = document.getElementById("djDebugToolbarHandle"); const handle = document.getElementById("djDebugToolbarHandle");
$$.show(handle); $$.show(handle);
// set handle position djdt.ensure_handle_visibility();
let handleTop = localStorage.getItem("djdt.top"); window.addEventListener("resize", djdt.ensure_handle_visibility);
if (handleTop) {
handleTop = Math.min(
handleTop,
window.innerHeight - handle.offsetHeight
);
handle.style.top = handleTop + "px";
}
document.removeEventListener("keydown", onKeyDown); document.removeEventListener("keydown", onKeyDown);
localStorage.setItem("djdt.show", "false"); localStorage.setItem("djdt.show", "false");
@@ -237,6 +251,7 @@ const djdt = {
$$.hide(document.getElementById("djDebugToolbarHandle")); $$.hide(document.getElementById("djDebugToolbarHandle"));
$$.show(document.getElementById("djDebugToolbar")); $$.show(document.getElementById("djDebugToolbar"));
localStorage.setItem("djdt.show", "true"); localStorage.setItem("djdt.show", "true");
window.removeEventListener("resize", djdt.ensure_handle_visibility);
}, },
cookie: { cookie: {
get(key) { get(key) {
+15
View File
@@ -7,6 +7,21 @@ const $$ = {
} }
}); });
}, },
onPanelRender(root, panelId, fn) {
/*
This is a helper function to attach a handler for a `djdt.panel.render`
event of a specific panel.
root: The container element that the listener should be attached to.
panelId: The Id of the panel.
fn: A function to execute when the event is triggered.
*/
root.addEventListener("djdt.panel.render", function (event) {
if (event.detail.panelId === panelId) {
fn.call(event);
}
});
},
show(element) { show(element) {
element.classList.remove("djdt-hidden"); element.classList.remove("djdt-hidden");
}, },
+1 -1
View File
@@ -178,7 +178,7 @@ function loadDicomViewer(images_to_load, annotations_to_load) {
console.log(images); console.log(images);
let annotations = single_dicom.dataset.annotations; let annotations = single_dicom.dataset.annotations;
if (annotations != undefined) { if (annotations_to_load != undefined) {
annotations = annotations_to_load; annotations = annotations_to_load;
} else if (annotations && annotations.indexOf(",") > 0) { } else if (annotations && annotations.indexOf(",") > 0) {
annotations = JSON.parse(annotations); annotations = JSON.parse(annotations);
+3 -3
View File
@@ -11,6 +11,7 @@ cornerstoneWADOImageLoader.external.cornerstone = cornerstone;
cornerstoneTools.init(); cornerstoneTools.init();
let tool_state = {};
export function loadCornerstone(main_element, db, images, annotations_to_load, load_as_stack = false) { export function loadCornerstone(main_element, db, images, annotations_to_load, load_as_stack = false) {
@@ -180,15 +181,14 @@ export function loadCornerstone(main_element, db, images, annotations_to_load, l
function loadAnnotation(imageId, annotation) { function loadAnnotation(imageId, annotation) {
console.log("loadAnnotations", imageId, annotations); console.log("loadAnnotations", imageId, annotation);
const toolStateManager = cornerstoneTools.globalImageIdSpecificToolStateManager; const toolStateManager = cornerstoneTools.globalImageIdSpecificToolStateManager;
if (annotation == undefined || annotation.length < 1 || annotation == [undefined]) { return } if (annotation == undefined || annotation.length < 1 || annotation == [undefined]) { return }
console.log(annotation); console.log("1234", annotation);
let tool_state_no_id = JSON.parse(annotation); let tool_state_no_id = JSON.parse(annotation);
let tool_state = {};
tool_state[imageId] = tool_state_no_id; tool_state[imageId] = tool_state_no_id;
toolStateManager.restoreToolState(tool_state); toolStateManager.restoreToolState(tool_state);