This commit is contained in:
Ross
2025-11-11 11:12:36 +00:00
parent 9ebcd24938
commit 9a9ce11ccb
4 changed files with 620 additions and 622 deletions
@@ -238,8 +238,8 @@
{% csrf_token %} {% csrf_token %}
<a href="/anatomy/examination/create" id="add_examination" class="add-popup" <a href="/anatomy/examination/create" id="add_examination" class="add-popup"
onclick="return showAddPopup(this);"><img src="{% static 'img/icon-addlink.svg' %}"></a> onclick="return showAddPopup(this);"><img src="{% static 'img/icon-addlink.svg' %}"></a>
<a href="/anatomy/body_part/create" id="add_body_part" class="add-popup" <a href="/anatomy/body_part/create" id="add_body_part" class="add-popup"
onclick="return showAddPopup(this);"><img src="{% static 'img/icon-addlink.svg' %}"></a> onclick="return showAddPopup(this);"><img src="{% static 'img/icon-addlink.svg' %}"></a>
{{ form|crispy }} {{ form|crispy }}
<div id="drop-container" class="drop-target">Drop image here <div id="drop-container" class="drop-target">Drop image here
+230 -230
View File
@@ -5,109 +5,109 @@
{% block js %} {% block js %}
<!--<script type="text/javascript" src="/admin/jsi18n/"></script>--> <!--<script type="text/javascript" src="/admin/jsi18n/"></script>-->
<script type="text/javascript"> <script type="text/javascript">
function showEditPopup(url) { function showEditPopup(url) {
var win = window.open(url, "Edit", var win = window.open(url, "Edit",
'height=500,width=800,resizable=yes,scrollbars=yes'); 'height=500,width=800,resizable=yes,scrollbars=yes');
return false; return false;
} }
function showAddPopup(triggeringLink) { function showAddPopup(triggeringLink) {
var name = triggeringLink.id.replace(/^add_/, ''); var name = triggeringLink.id.replace(/^add_/, '');
href = triggeringLink.href; href = triggeringLink.href;
var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes'); var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
win.focus(); win.focus();
return false; return false;
} }
function closePopup(win, newID, newRepr, id) { function closePopup(win, newID, newRepr, id) {
console.log(id) console.log(id)
$(id + "_to").append('<option value=' + newID + ' title=' + newRepr + ' >' + newRepr + '</option>') $(id + "_to").append('<option value=' + newID + ' title=' + newRepr + ' >' + newRepr + '</option>')
win.close(); win.close();
} }
document.addEventListener('drop', function (e) { e.preventDefault(); }, false); document.addEventListener('drop', function (e) { e.preventDefault(); }, false);
function add_input_form() { function add_input_form() {
var form_idx = $('#id_images-TOTAL_FORMS').val(); var form_idx = $('#id_images-TOTAL_FORMS').val();
$('#image_form_set').append($('#empty_form').html().replace(/__prefix__/g, form_idx)); $('#image_form_set').append($('#empty_form').html().replace(/__prefix__/g, form_idx));
$('#id_images-TOTAL_FORMS').val(parseInt(form_idx) + 1); $('#id_images-TOTAL_FORMS').val(parseInt(form_idx) + 1);
} }
function add_answers_input_form() { function add_answers_input_form() {
var form_idx = $('#id_answers-TOTAL_FORMS').val(); var form_idx = $('#id_answers-TOTAL_FORMS').val();
$('#answer_form_set').append($('#answer_empty_form').html().replace(/__prefix__/g, form_idx)); $('#answer_form_set').append($('#answer_empty_form').html().replace(/__prefix__/g, form_idx));
$('#id_answers-TOTAL_FORMS').val(parseInt(form_idx) + 1); $('#id_answers-TOTAL_FORMS').val(parseInt(form_idx) + 1);
} }
$(document).ready(function () { $(document).ready(function () {
$("#add_examination").appendTo($("label[for='id_examination']")); $("#add_examination").appendTo($("label[for='id_examination']"));
dropContainer = document.getElementById("drop-container"); dropContainer = document.getElementById("drop-container");
dropContainer.ondragover = function (evt) { dropContainer.ondragover = function (evt) {
evt.preventDefault(); evt.preventDefault();
evt.stopPropagation(); evt.stopPropagation();
}; };
dropContainer.ondragenter = function (evt) { dropContainer.ondragenter = function (evt) {
console.log("ENTER") console.log("ENTER")
console.log(evt) console.log(evt)
$(evt.target).addClass("drop-target-active") $(evt.target).addClass("drop-target-active")
evt.preventDefault(); evt.preventDefault();
evt.stopPropagation(); evt.stopPropagation();
}; };
dropContainer.ondragleave = function (evt) { dropContainer.ondragleave = function (evt) {
console.log("ENTER") console.log("ENTER")
console.log(evt) console.log(evt)
$(evt.target).removeClass("drop-target-active") $(evt.target).removeClass("drop-target-active")
evt.preventDefault(); evt.preventDefault();
evt.stopPropagation(); evt.stopPropagation();
}; };
dropContainer.ondrop = function (evt) { dropContainer.ondrop = function (evt) {
$(evt.target).removeClass("drop-target-active"); $(evt.target).removeClass("drop-target-active");
if (evt.dataTransfer.files.length < 1) { if (evt.dataTransfer.files.length < 1) {
toastr.warning(`Drop failed :( try again?`); toastr.warning(`Drop failed :( try again?`);
} else { } else {
toastr.info(`Adding ${evt.dataTransfer.files.length} files.`); toastr.info(`Adding ${evt.dataTransfer.files.length} files.`);
} }
// Get all input elements // Get all input elements
inputs = $("#long-form input[type=file][id^=id_images-]"); inputs = $("#long-form input[type=file][id^=id_images-]");
//fileInput = document.getElementById("id_images-0-image"); //fileInput = document.getElementById("id_images-0-image");
// Make sure we have enough input targets // Make sure we have enough input targets
input_diff = (evt.dataTransfer.files.length - inputs.length) input_diff = (evt.dataTransfer.files.length - inputs.length)
if (input_diff > 0) { if (input_diff > 0) {
for (let j = 0; j < input_diff; j++) { for (let j = 0; j < input_diff; j++) {
add_input_form() add_input_form()
} }
// Need to make sure we include the new ones... // Need to make sure we include the new ones...
inputs = $("#long-form input[type=file][id^=id_images-]"); inputs = $("#long-form input[type=file][id^=id_images-]");
} }
// Loop through each dropped file and try to assign to an // Loop through each dropped file and try to assign to an
// input element // input element
[...evt.dataTransfer.files].forEach((f) => { [...evt.dataTransfer.files].forEach((f) => {
let dT = new DataTransfer(); let dT = new DataTransfer();
dT.clearData(); dT.clearData();
dT.items.add(f); dT.items.add(f);
for (let i = 0; i < inputs.length; i++) { for (let i = 0; i < inputs.length; i++) {
el = inputs.get(i) el = inputs.get(i)
if (el.files.length == 0) { if (el.files.length == 0) {
el.files = dT.files; el.files = dT.files;
//ocr(el) //ocr(el)
return false return false
}
} }
} })
})
// // If you want to use some of the dropped files // // If you want to use some of the dropped files
// const dT = new DataTransfer(); // const dT = new DataTransfer();
@@ -115,31 +115,31 @@
// dT.items.add(evt.dataTransfer.files[3]); // dT.items.add(evt.dataTransfer.files[3]);
// fileInput.files = dT.files; // fileInput.files = dT.files;
loadDicomViewerAndFileImages(); loadDicomViewerAndFileImages();
evt.preventDefault(); evt.preventDefault();
evt.stopPropagation(); evt.stopPropagation();
}; };
$('#add_more_images').click(() => { add_input_form() }); $('#add_more_images').click(() => { add_input_form() });
$('#add_more_answers').click(() => { add_answers_input_form() }); $('#add_more_answers').click(() => { add_answers_input_form() });
$("input[type=file]").on('change', function () { $("input[type=file]").on('change', function () {
$(this).removeClass("image-ident-warning"); $(this).removeClass("image-ident-warning");
$(this).removeClass("image-ident-ok"); $(this).removeClass("image-ident-ok");
console.log("input change1"); console.log("input change1");
console.log("input change", this); console.log("input change", this);
//ocr(this); //ocr(this);
//LoadDicomViewer(); //LoadDicomViewer();
}); });
sortable('.sortable'); sortable('.sortable');
sortable('.sortable')[0].addEventListener('sortupdate', function(e) { sortable('.sortable')[0].addEventListener('sortupdate', function(e) {
/* /*
This event is triggered when the user stops sorting and the DOM position has not yet changed. This event is triggered when the user stops sorting and the DOM position has not yet changed.
@@ -153,15 +153,15 @@
*/ */
//updateImagePositions(); //updateImagePositions();
$(".temp-thumb").remove() $(".temp-thumb").remove()
loadDicomViewer(); loadDicomViewer();
});
}); });
});
function ocr(input) { function ocr(input) {
// Tesseract.recognize(f, "eng",{ logger: m => console.log(m) } // Tesseract.recognize(f, "eng",{ logger: m => console.log(m) }
// ).then(({ data: { text } }) => { // ).then(({ data: { text } }) => {
// console.log(text); // console.log(text);
@@ -171,186 +171,186 @@
// } // }
// }) // })
console.log('{% static "worker.min.js" %}'); console.log('{% static "worker.min.js" %}');
console.log('{{ STATIC_URL }}/tesseract-core.wasm.js %}'); console.log('{{ STATIC_URL }}/tesseract-core.wasm.js %}');
const worker = Tesseract.createWorker({ const worker = Tesseract.createWorker({
workerPath: '{% static "worker.min.js" %}', workerPath: '{% static "worker.min.js" %}',
langPath: '{% static "" %}', langPath: '{% static "" %}',
//langPath: '{{ STATIC_URL }}', //langPath: '{{ STATIC_URL }}',
corePath: '{% static "tesseract-core.wasm.js" %}', corePath: '{% static "tesseract-core.wasm.js" %}',
}); });
const url = URL.createObjectURL(input.files[0]); const url = URL.createObjectURL(input.files[0]);
let img = new Image; let img = new Image;
img.src = url; img.src = url;
img.onload = function () { img.onload = function () {
width = img.width; width = img.width;
// const rectangle = { left: 0, top: 0, width: width, height: 10 } // const rectangle = { left: 0, top: 0, width: width, height: 10 }
(async () => { (async () => {
await worker.load(); await worker.load();
await worker.loadLanguage('eng'); await worker.loadLanguage('eng');
await worker.initialize('eng'); await worker.initialize('eng');
// const { data: { text } } = await worker.recognize(input.files[0], { rectangle }); // const { data: { text } } = await worker.recognize(input.files[0], { rectangle });
const { data: { text } } = await worker.recognize(input.files[0]); const { data: { text } } = await worker.recognize(input.files[0]);
//console.log(text); //console.log(text);
l = text.toLowerCase(); l = text.toLowerCase();
$(`img[data-input-id='${input.id}'`).removeClass("image-ident-loading"); $(`img[data-input-id='${input.id}'`).removeClass("image-ident-loading");
console.log(l); console.log(l);
if (l.includes("accesion") || l.includes("number") || l.search(/(ref|rk9|rh8|ra9|rbz)\d+/g) > -1) { if (l.includes("accesion") || l.includes("number") || l.search(/(ref|rk9|rh8|ra9|rbz)\d+/g) > -1) {
$(input).addClass("image-ident-warning"); $(input).addClass("image-ident-warning");
$(`img[data-input-id='${input.id}'`).addClass("image-ident-warning"); $(`img[data-input-id='${input.id}'`).addClass("image-ident-warning");
} else { } else {
$(input).addClass("image-ident-ok"); $(input).addClass("image-ident-ok");
} }
await worker.terminate(); await worker.terminate();
})(); })();
}
} }
}
async function loadDicomViewerAndFileImages(callback) { async function loadDicomViewerAndFileImages(callback) {
$("#single-dicom-viewer").empty() $("#single-dicom-viewer").empty()
//images = [] //images = []
//Function that returns a promise to read the file //Function that returns a promise to read the file
const reader = (file) => { const reader = (file) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const fileReader = new FileReader(); const fileReader = new FileReader();
fileReader.onload = () => resolve(fileReader); fileReader.onload = () => resolve(fileReader);
fileReader.readAsDataURL(file); fileReader.readAsDataURL(file);
}); });
} }
const readFile = (file, el) => { const readFile = (file, el) => {
reader(file).then(reader => { reader(file).then(reader => {
var image = new Image(); var image = new Image();
image.height = 100; image.height = 100;
image.title = file.name; image.title = file.name;
image.src = reader.result; image.src = reader.result;
image.className = "temp-thumb"; image.className = "temp-thumb";
console.log("read" ,el); console.log("read" ,el);
$(el).parent().parent().prepend(image); $(el).parent().parent().prepend(image);
//images.push(reader.result); //images.push(reader.result);
loadViewer(images); loadViewer(images);
}); });
}
file_set = $("#image_form_set input[type=file]");
file_set.each(async (n, el) => {
console.log(el);
if (el.files.length > 0) {
filename = el.files[0].name;
await readFile(el.files[0], el);
}
})
console.log("images from files", images);
} }
function loadViewer(images) {
file_set = $("#image_form_set input[type=file]");
file_set.each(async (n, el) => {
console.log(el);
if (el.files.length > 0) {
filename = el.files[0].name;
await readFile(el.files[0], el);
}
})
console.log("images from files", images);
}
function loadViewer(images) {
//dicomViewer.loadCornerstone($("#single-dicom-viewer"), null, images, annotations); //dicomViewer.loadCornerstone($("#single-dicom-viewer"), null, images, annotations);
file_set = $("#image_form_set input[type=file]"); file_set = $("#image_form_set input[type=file]");
n = 0; n = 0;
for (let i = 0; i < file_set.length; i++) { for (let i = 0; i < file_set.length; i++) {
el = file_set.get(i) el = file_set.get(i)
if (el.files.length > 0) { if (el.files.length > 0) {
n++; n++;
}
} }
}
image_set = $("#image_form_set img"); image_set = $("#image_form_set img");
if (n == image_set.length) { if (n == image_set.length) {
const event = new CustomEvent('loadDicomViewer', { "detail": image_set }); const event = new CustomEvent('loadDicomViewer', { "detail": image_set });
window.dispatchEvent(event); window.dispatchEvent(event);
sortable('.sortable'); sortable('.sortable');
//sortable('.sortable')[0].addEventListener('sortstop', function(e) { //sortable('.sortable')[0].addEventListener('sortstop', function(e) {
updateImagePositions(); updateImagePositions();
}
} }
} function updateImagePositions() {
console.log("UP pos");
function updateImagePositions() { file_set = $("#image_form_set ul");
console.log("UP pos");
file_set = $("#image_form_set ul"); n = 0;
for (let i = 0; i < file_set.length; i++) {
ul = file_set.get(i);
console.log(ul)
n = 0; file_element = $(ul).find("input[type=file]").get(0);
for (let i = 0; i < file_set.length; i++) {
ul = file_set.get(i);
console.log(ul)
file_element = $(ul).find("input[type=file]").get(0);
// If a (new) file is being uploaded we save it's name prior to it being hashed // If a (new) file is being uploaded we save it's name prior to it being hashed
if (file_element.files.length > 0) { if (file_element.files.length > 0) {
file_name = file_element.files[0].name file_name = file_element.files[0].name
$(ul).find("input[type=text]").val(file_name); $(ul).find("input[type=text]").val(file_name);
}
if (file_element.files.length > 0 || $(ul).find(":contains('Currently:')")) {
n++;
$(ul).find("input[type=number]").val(n);
}
} }
if (file_element.files.length > 0 || $(ul).find(":contains('Currently:')")) {
n++;
$(ul).find("input[type=number]").val(n);
}
} }
</script>
} {{ form.media }}
</script>
{{ form.media }}
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<h2>Series Form</h2> <h2>Series Form</h2>
This form will create a image set that can be associated with a long question. If the question has already been created it can be linked below. This form will create a image set that can be associated with a long question. If the question has already been created it can be linked below.
<form action="" method="post" enctype="multipart/form-data" id="long-form"> <form action="" method="post" enctype="multipart/form-data" id="long-form">
{% csrf_token %} {% csrf_token %}
<a href="/generic/examination/create" id="add_examination" class="add-popup" <a href="/generic/examination/create" id="add_examination" class="add-popup"
onclick="return showAddPopup(this);"><img src="{% static 'img/icon-addlink.svg' %}"></a> onclick="return showAddPopup(this);"><img src="{% static 'img/icon-addlink.svg' %}"></a>
<table> <table>
{{ form.as_table }} {{ form.as_table }}
</table> </table>
<h3>Images:</h3> <h3>Images:</h3>
<input type="button" value="Add More Images" id="add_more_images"> <input type="button" value="Add More Images" id="add_more_images">
<div id="drop-container" class="">Drop images here (or use the buttons below). Dropping images here will overwrite existing images in the series. When drag and dropping make sure to drag the first image or the order will not be maintained. Note: dicom images are often not exported in order (although their order can be automatically detected once uploaded) <div id="drop-container" class="">Drop images here (or use the buttons below). Dropping images here will overwrite existing images in the series. When drag and dropping make sure to drag the first image or the order will not be maintained. Note: dicom images are often not exported in order (although their order can be automatically detected once uploaded)
<div id="single-dicom-viewer" class="dicom-viewer" data-images="" data-annotations=''></div> <div id="single-dicom-viewer" class="dicom-viewer" data-images="" data-annotations=''></div>
<div id="drop-filenames"></div> <div id="drop-filenames"></div>
</div> </div>
<div id="image_form_set" class="sortable"> <div id="image_form_set" class="sortable">
{% for form in image_formset %} {% for form in image_formset %}
<ul class="no-error image-formset"> <ul class="no-error image-formset">
{{form.non_field_errors}} {{form.non_field_errors}}
{{form.errors}} {{form.errors}}
{{ form.as_ul }} {{ form.as_ul }}
</ul>
{% endfor %}
</div>
{{ image_formset.management_form }}
<input type="submit" class="submit-button" value="Submit" name="submit">
</form>
<div id="empty_form" style="display:none">
<ul class='no_error image-formset'>
{{ image_formset.empty_form.as_ul }}
</ul> </ul>
{% endfor %}
</div> </div>
{{ image_formset.management_form }} <div id="images">
<input type="submit" class="submit-button" value="Submit" name="submit">
</form>
<div id="empty_form" style="display:none">
<ul class='no_error image-formset'>
{{ image_formset.empty_form.as_ul }}
</ul>
</div>
<div id="images">
</div> </div>
{% endblock %} {% endblock %}
+218 -219
View File
@@ -5,14 +5,14 @@
{% block js %} {% block js %}
<!--<script type="text/javascript" src="/admin/jsi18n/"></script>--> <!--<script type="text/javascript" src="/admin/jsi18n/"></script>-->
<script type="text/javascript"> <script type="text/javascript">
let csrf_token = "{{csrf_token}}"; let csrf_token = "{{csrf_token}}";
let hash_url = "{% url 'rapids:rapid_question_by_hash' %}"; let hash_url = "{% url 'rapids:rapid_question_by_hash' %}";
//let monitor_directory_handler = false; //let monitor_directory_handler = false;
//let monitor_interval_id = false; //let monitor_interval_id = false;
// set to hold the files that are still being processed // set to hold the files that are still being processed
let load_viewer = false; let load_viewer = false;
// async function listAllFilesAndDirs(dirHandle, files_only) { // async function listAllFilesAndDirs(dirHandle, files_only) {
// const files = []; // const files = [];
@@ -41,80 +41,80 @@
// } // }
document.addEventListener('drop', function (e) { document.addEventListener('drop', function (e) {
e.preventDefault(); e.preventDefault();
}, false); }, false);
function add_answers_input_form() { function add_answers_input_form() {
var form_idx = $('#id_answers-TOTAL_FORMS').val(); var form_idx = $('#id_answers-TOTAL_FORMS').val();
$('#answer_form_set').append($('#answer_empty_form').html().replace(/__prefix__/g, form_idx)); $('#answer_form_set').append($('#answer_empty_form').html().replace(/__prefix__/g, form_idx));
$(`#id_answers-${form_idx}-status`).val(2); $(`#id_answers-${form_idx}-status`).val(2);
$('#id_answers-TOTAL_FORMS').val(parseInt(form_idx) + 1); $('#id_answers-TOTAL_FORMS').val(parseInt(form_idx) + 1);
} }
$(document).ready(function () { $(document).ready(function () {
$("#add_abnormality").appendTo($("#abnormality-label")); $("#add_abnormality").appendTo($("#abnormality-label"));
$("#add_examination").appendTo($("#examination-label")); $("#add_examination").appendTo($("#examination-label"));
$("#add_region").appendTo($("#region-label")); $("#add_region").appendTo($("#region-label"));
dropContainer = document.getElementById("drop-container"); dropContainer = document.getElementById("drop-container");
dropContainer.ondragover = function (evt) { dropContainer.ondragover = function (evt) {
evt.preventDefault(); evt.preventDefault();
evt.stopPropagation(); evt.stopPropagation();
}; };
dropContainer.ondragenter = function (evt) { dropContainer.ondragenter = function (evt) {
console.log("ENTER") console.log("ENTER")
console.log(evt) console.log(evt)
$(evt.target).addClass("drop-target-active") $(evt.target).addClass("drop-target-active")
evt.preventDefault(); evt.preventDefault();
evt.stopPropagation(); evt.stopPropagation();
}; };
dropContainer.ondragleave = function (evt) { dropContainer.ondragleave = function (evt) {
console.log("ENTER") console.log("ENTER")
console.log(evt) console.log(evt)
$(evt.target).removeClass("drop-target-active") $(evt.target).removeClass("drop-target-active")
evt.preventDefault(); evt.preventDefault();
evt.stopPropagation(); evt.stopPropagation();
}; };
dropContainer.ondrop = function (evt) { dropContainer.ondrop = function (evt) {
$(evt.target).removeClass("drop-target-active"); $(evt.target).removeClass("drop-target-active");
file_drop_number = evt.dataTransfer.files.length; file_drop_number = evt.dataTransfer.files.length;
if (file_drop_number < 1) { if (file_drop_number < 1) {
toastr.warning(`Drop failed (no files), try again.`); toastr.warning(`Drop failed (no files), try again.`);
} else { } else {
toastr.info(`Loading ${file_drop_number} dropped image(s).`); toastr.info(`Loading ${file_drop_number} dropped image(s).`);
} }
// Get all input elements // Get all input elements
inputs = extendInputs("rapid-form", file_drop_number); inputs = extendInputs("rapid-form", file_drop_number);
console.log("drop inputs", inputs, evt); console.log("drop inputs", inputs, evt);
// Loop through each dropped file and try to assign to an // Loop through each dropped file and try to assign to an
// input element // input element
[...evt.dataTransfer.files].forEach((f) => { [...evt.dataTransfer.files].forEach((f) => {
feedback = false; feedback = false;
if (evt.target.id == "feedback-drop-target") { if (evt.target.id == "feedback-drop-target") {
feedback = true; feedback = true;
} }
addFile(f, feedback) addFile(f, feedback)
}) })
evt.preventDefault(); evt.preventDefault();
evt.stopPropagation(); evt.stopPropagation();
}; };
// $('#directory-picker-button').click(() => { // $('#directory-picker-button').click(() => {
@@ -170,71 +170,71 @@
$('#add_more_images').click(() => { $('#add_more_images').click(() => {
add_input_form() add_input_form()
}); });
$('#add_more_answers').click(() => { $('#add_more_answers').click(() => {
add_answers_input_form() add_answers_input_form()
}); });
$("input[type=file]").on('change', input_change); $("input[type=file]").on('change', input_change);
$("#rapid-form").on("submit", (evt) => { $("#rapid-form").on("submit", (evt) => {
submit = true; submit = true;
if (!$normal) { if (!$normal) {
if ($('#id_answers-TOTAL_FORMS').val() == "0") { if ($('#id_answers-TOTAL_FORMS').val() == "0") {
submit = false;
add_answers_input_form();
toastr.error(`Question that are not normal require an answer`);
evt.preventDefault();
}
}
if (!$("#id_examination_to option").length) {
submit = false; submit = false;
add_answers_input_form(); toastr.error("Please add examination type")
toastr.error(`Question that are not normal require an answer`);
evt.preventDefault(); evt.preventDefault();
} }
} if (submit) {
toastr.info(`Submiting question... please wait`, '', {
"closeButton": false,
"debug": false,
"newestOnTop": false,
"progressBar": true,
"positionClass": "toast-top-full-width",
"preventDuplicates": false,
"onclick": null,
"showDuration": "300",
"hideDuration": "1000",
"timeOut": "5000",
"extendedTimeOut": "1000",
"showEasing": "swing",
"hideEasing": "linear",
"showMethod": "fadeIn",
"hideMethod": "fadeOut"
});
}
})
});
function extractDicomDetails(byteArray) {
if (!$("#id_examination_to option").length) { if (!$("#id_examination_to option").length) {
submit = false;
toastr.error("Please add examination type")
evt.preventDefault();
}
if (submit) {
toastr.info(`Submiting question... please wait`, '', {
"closeButton": false,
"debug": false,
"newestOnTop": false,
"progressBar": true,
"positionClass": "toast-top-full-width",
"preventDuplicates": false,
"onclick": null,
"showDuration": "300",
"hideDuration": "1000",
"timeOut": "5000",
"extendedTimeOut": "1000",
"showEasing": "swing",
"hideEasing": "linear",
"showMethod": "fadeIn",
"hideMethod": "fadeOut"
});
}
})
});
function extractDicomDetails(byteArray) {
if (!$("#id_examination_to option").length) {
// We need to setup a try/catch block because parseDicom will throw an exception // We need to setup a try/catch block because parseDicom will throw an exception
// if you attempt to parse a non dicom part 10 file (or one that is corrupted) // if you attempt to parse a non dicom part 10 file (or one that is corrupted)
try { try {
// parse byteArray into a DataSet object using the parseDicom library // parse byteArray into a DataSet object using the parseDicom library
var dataSet = dicomParser.parseDicom(byteArray); var dataSet = dicomParser.parseDicom(byteArray);
// dataSet contains the parsed elements. Each element is available via a property // dataSet contains the parsed elements. Each element is available via a property
// in the dataSet.elements object. The property name is based on the elements group // in the dataSet.elements object. The property name is based on the elements group
@@ -244,157 +244,156 @@
// To access the data for an element, we need to know its type and its tag. // To access the data for an element, we need to know its type and its tag.
// We will get the sopInstanceUid from the file which is a string and // We will get the sopInstanceUid from the file which is a string and
// has the tag (0020,000D) // has the tag (0020,000D)
var study_description = dataSet.string('x00081030').toLowerCase(); var study_description = dataSet.string('x00081030').toLowerCase();
console.log("STUDY", study_description); console.log("STUDY", study_description);
// try to match with a study description (if not already selected) // try to match with a study description (if not already selected)
if ($(`#id_examination_to option`).length < 1) { if ($(`#id_examination_to option`).length < 1) {
option = $(`#id_examination_from option[title*='${study_description}' i]`); option = $(`#id_examination_from option[title*='${study_description}' i]`);
if (option.length) { if (option.length) {
option.appendTo($("#id_examination_to")); option.appendTo($("#id_examination_to"));
toastr.success(`Examination set to ${option.attr('title')} from dicom data`); toastr.success(`Examination set to ${option.attr('title')} from dicom data`);
} else { } else {
toastr.warning( toastr.warning(
`Unable to set examination ${study_description} from dicom data (it needs creating)`); `Unable to set examination ${study_description} from dicom data (it needs creating)`);
}
}
} catch (err) {
console.log(err);
} }
} }
} catch (err) {
console.log(err);
} }
} </script>
} <script type="text/hyperscript">
</script> init
<script type="text/hyperscript"> set global $normal to false
init if (#id_normal_0.checked )
set global $normal to false Fade(.abnormal-fields) then
if (#id_normal_0.checked ) set global $normal to true then
Fade(.abnormal-fields) then
set global $normal to true then
remove @required from <#answer_form_set textarea/> then remove @required from <#answer_form_set textarea/> then
remove @required from <#answer_empty_form textarea.required/> remove @required from <#answer_empty_form textarea.required/>
end end
end end
def Fade(elt) def Fade(elt)
if elt's style's opacity != "0" if elt's style's opacity != "0"
transition element elt opacity to 0 over 350ms transition element elt opacity to 0 over 350ms
transition element elt transform to "translateY(0.25rem)" over 350ms transition element elt transform to "translateY(0.25rem)" over 350ms
hide elt hide elt
else else
show elt show elt
transition element elt opacity to 1 over 350ms transition element elt opacity to 1 over 350ms
transition element elt transform to "translateY(0rem)" over 350ms transition element elt transform to "translateY(0rem)" over 350ms
end end
end end
on change from #id_normal_0 on change from #id_normal_0
if (#id_normal_0.checked ) if (#id_normal_0.checked )
Fade(.abnormal-fields) then Fade(.abnormal-fields) then
set global $normal to true then set global $normal to true then
remove @required from <#answer_form_set textarea/> then remove @required from <#answer_form_set textarea/> then
remove @required from <#answer_empty_form textarea.required/> remove @required from <#answer_empty_form textarea.required/>
end end
on change from #id_normal_1 on change from #id_normal_1
if (#id_normal_1.checked ) if (#id_normal_1.checked )
Fade(.abnormal-fields) then Fade(.abnormal-fields) then
set global $normal to false then set global $normal to false then
add @required to <#answer_form_set textarea/> then add @required to <#answer_form_set textarea/> then
add @required to <#answer_empty_form textarea.required/> add @required to <#answer_empty_form textarea.required/>
end end
</script> </script>
{{ form.media }} {{ form.media }}
{% endblock %} {% endblock %}
{% block content %} {% block content %}
{% if object %} {% if object %}
{% include "rapids/question_link_header.html" %} {% include "rapids/question_link_header.html" %}
{% endif %} {% endif %}
<h2>Submit Rapid</h2> <h2>Submit Rapid</h2>
<p> <p>
<h3>Instructions</h3> <h3>Instructions</h3>
Abnormality, Region and Laterality are used to categorise within the system (they are not used for marking). String Abnormality, Region and Laterality are used to categorise within the system (they are not used for marking). String
answers that match those added below will be used for automatic marking. Multiple images can be added to a question. answers that match those added below will be used for automatic marking. Multiple images can be added to a question.
If If
the feedback image box is checked they will not be displayed when taking the question. the feedback image box is checked they will not be displayed when taking the question.
</p> </p>
<!-- <div> <!-- <div>
To monitor a directory select it here.<br /> To monitor a directory select it here.<br />
<button id="directory-picker-button">Pick a directory</button> <button id="directory-picker-button">Pick a directory</button>
<span id="directory-picker-display">No directory monitored</span> <span id="directory-picker-display">No directory monitored</span>
</div> --> </div> -->
<form action="" method="post" enctype="multipart/form-data" id="rapid-form"> <form action="" method="post" enctype="multipart/form-data" id="rapid-form">
{% comment %} {% csrf_token %} {% endcomment %} {% comment %} {% csrf_token %} {% endcomment %}
<a href="/rapids/abnormality/create" id="add_abnormality" class="add-popup" <a href="/rapids/abnormality/create" id="add_abnormality" class="add-popup"
onclick="return showAddPopup(this);"><img src="{% static 'img/icon-addlink.svg' %}"></a> onclick="return showAddPopup(this);"><img src="{% static 'img/icon-addlink.svg' %}"></a>
<a href="/rapids/examination/create" id="add_examination" class="add-popup" <a href="/rapids/examination/create" id="add_examination" class="add-popup"
onclick="return showAddPopup(this);"><img src="{% static 'img/icon-addlink.svg' %}"></a> onclick="return showAddPopup(this);"><img src="{% static 'img/icon-addlink.svg' %}"></a>
<a href="/rapids/region/create" id="add_region" class="add-popup" onclick="return showAddPopup(this);"><img <a href="/rapids/region/create" id="add_region" class="add-popup" onclick="return showAddPopup(this);"><img
src="{% static 'img/icon-addlink.svg' %}"></a> src="{% static 'img/icon-addlink.svg' %}"></a>
{% crispy form form.helper %} {% crispy form form.helper %}
<div class="abnormal-fields"> <div class="abnormal-fields">
<h3>Answers:</h3> <h3>Answers:</h3>
<input type="button" value="Add More Answers" id="add_more_answers"> <input type="button" value="Add More Answers" id="add_more_answers">
<div id="answer_form_set"> <div id="answer_form_set">
{% for form in answer_formset %} {% for form in answer_formset %}
<ul class="no-error answer-formset"> <ul class="no-error answer-formset">
{{form.non_field_errors}} {{form.non_field_errors}}
{{form.errors}} {{form.errors}}
{{ form.as_ul }} {{ form.as_ul }}
</ul> </ul>
{% endfor %} {% endfor %}
</div> </div>
{{ answer_formset.management_form }} {{ answer_formset.management_form }}
</div> </div>
<h3>Images:</h3> <h3>Images:</h3>
<div id="drop-container" class="drop-target">Drop images here (or use the buttons below)<div <div id="drop-container" class="drop-target">Drop images here (or use the buttons below)<div
id="feedback-drop-target">Feedback image?<br />drop those here</div> id="feedback-drop-target">Feedback image?<br />drop those here</div>
<div id="drop-filenames"></div> <div id="drop-filenames"></div>
</div> </div>
<input type="button" value="Add More Images" id="add_more_images"> <input type="button" value="Add More Images" id="add_more_images">
<div id="image_form_set"> <div id="image_form_set">
{% for form in image_formset %} {% for form in image_formset %}
<ul class="no-error image-formset"> <ul class="no-error image-formset">
{{form.non_field_errors}} {{form.non_field_errors}}
{{form.errors}} {{form.errors}}
{{ form.as_ul }} {{ form.as_ul }}
</ul>
{% endfor %}
</div>
{{ image_formset.management_form }}
<input type="submit" class="submit-button" value="Submit" name="submit">
</form>
<div id="empty_form" style="display:none">
<ul class='no_error image-formset'>
{{ image_formset.empty_form.as_ul }}
</ul>
</div>
<div id="answer_empty_form" style="display:none">
<ul class='no_error answer-formset'>
{{ answer_formset.empty_form.as_ul }}
</ul> </ul>
{% endfor %}
</div> </div>
{{ image_formset.management_form }}
<input type="submit" class="submit-button" value="Submit" name="submit">
</form>
<div id="empty_form" style="display:none">
<ul class='no_error image-formset'>
{{ image_formset.empty_form.as_ul }}
</ul>
</div>
<div id="answer_empty_form" style="display:none">
<ul class='no_error answer-formset'>
{{ answer_formset.empty_form.as_ul }}
</ul>
</div>
<div id="single-dicom-viewer" class="rapid-dicom-viewer" data-images="" data-annotations='' style="display:none"></div> <div id="single-dicom-viewer" class="rapid-dicom-viewer" data-images="" data-annotations='' style="display:none"></div>
{% endblock %} {% endblock %}
{% block css %} {% block css %}
<style> <style>
.add-popup { .add-popup {
float: left; float: left;
} }
</style> </style>
{% endblock css %} {% endblock css %}
+170 -171
View File
@@ -5,14 +5,14 @@
{% block js %} {% block js %}
<!--<script type="text/javascript" src="/admin/jsi18n/"></script>--> <!--<script type="text/javascript" src="/admin/jsi18n/"></script>-->
<script type="text/javascript"> <script type="text/javascript">
let csrf_token = "{{csrf_token}}"; let csrf_token = "{{csrf_token}}";
let hash_url = "{% url 'shorts:question_by_hash' %}"; let hash_url = "{% url 'shorts:question_by_hash' %}";
//let monitor_directory_handler = false; //let monitor_directory_handler = false;
//let monitor_interval_id = false; //let monitor_interval_id = false;
// set to hold the files that are still being processed // set to hold the files that are still being processed
let load_viewer = false; let load_viewer = false;
// async function listAllFilesAndDirs(dirHandle, files_only) { // async function listAllFilesAndDirs(dirHandle, files_only) {
// const files = []; // const files = [];
@@ -41,78 +41,78 @@
// } // }
document.addEventListener('drop', function (e) { document.addEventListener('drop', function (e) {
e.preventDefault(); e.preventDefault();
}, false); }, false);
function add_answers_input_form() { function add_answers_input_form() {
var form_idx = $('#id_answers-TOTAL_FORMS').val(); var form_idx = $('#id_answers-TOTAL_FORMS').val();
$('#answer_form_set').append($('#answer_empty_form').html().replace(/__prefix__/g, form_idx)); $('#answer_form_set').append($('#answer_empty_form').html().replace(/__prefix__/g, form_idx));
$(`#id_answers-${form_idx}-status`).val(2); $(`#id_answers-${form_idx}-status`).val(2);
$('#id_answers-TOTAL_FORMS').val(parseInt(form_idx) + 1); $('#id_answers-TOTAL_FORMS').val(parseInt(form_idx) + 1);
} }
$(document).ready(function () { $(document).ready(function () {
$("#add_examination").appendTo($("#examination-label")); $("#add_examination").appendTo($("#examination-label"));
dropContainer = document.getElementById("drop-container"); dropContainer = document.getElementById("drop-container");
dropContainer.ondragover = function (evt) { dropContainer.ondragover = function (evt) {
evt.preventDefault(); evt.preventDefault();
evt.stopPropagation(); evt.stopPropagation();
}; };
dropContainer.ondragenter = function (evt) { dropContainer.ondragenter = function (evt) {
console.log("ENTER") console.log("ENTER")
console.log(evt) console.log(evt)
$(evt.target).addClass("drop-target-active") $(evt.target).addClass("drop-target-active")
evt.preventDefault(); evt.preventDefault();
evt.stopPropagation(); evt.stopPropagation();
}; };
dropContainer.ondragleave = function (evt) { dropContainer.ondragleave = function (evt) {
console.log("ENTER") console.log("ENTER")
console.log(evt) console.log(evt)
$(evt.target).removeClass("drop-target-active") $(evt.target).removeClass("drop-target-active")
evt.preventDefault(); evt.preventDefault();
evt.stopPropagation(); evt.stopPropagation();
}; };
dropContainer.ondrop = function (evt) { dropContainer.ondrop = function (evt) {
$(evt.target).removeClass("drop-target-active"); $(evt.target).removeClass("drop-target-active");
file_drop_number = evt.dataTransfer.files.length; file_drop_number = evt.dataTransfer.files.length;
if (file_drop_number < 1) { if (file_drop_number < 1) {
toastr.warning(`Drop failed (no files), try again.`); toastr.warning(`Drop failed (no files), try again.`);
} else { } else {
toastr.info(`Loading ${file_drop_number} dropped image(s).`); toastr.info(`Loading ${file_drop_number} dropped image(s).`);
} }
// Get all input elements // Get all input elements
inputs = extendInputs("rapid-form", file_drop_number); inputs = extendInputs("rapid-form", file_drop_number);
console.log("drop inputs", inputs, evt); console.log("drop inputs", inputs, evt);
// Loop through each dropped file and try to assign to an // Loop through each dropped file and try to assign to an
// input element // input element
[...evt.dataTransfer.files].forEach((f) => { [...evt.dataTransfer.files].forEach((f) => {
feedback = false; feedback = false;
if (evt.target.id == "feedback-drop-target") { if (evt.target.id == "feedback-drop-target") {
feedback = true; feedback = true;
} }
addFile(f, feedback) addFile(f, feedback)
}) })
evt.preventDefault(); evt.preventDefault();
evt.stopPropagation(); evt.stopPropagation();
}; };
// $('#directory-picker-button').click(() => { // $('#directory-picker-button').click(() => {
@@ -168,71 +168,71 @@
$('#add_more_images').click(() => { $('#add_more_images').click(() => {
add_input_form() add_input_form()
}); });
$('#add_more_answers').click(() => { $('#add_more_answers').click(() => {
add_answers_input_form() add_answers_input_form()
}); });
$("input[type=file]").on('change', input_change); $("input[type=file]").on('change', input_change);
$("#rapid-form").on("submit", (evt) => { $("#rapid-form").on("submit", (evt) => {
submit = true; submit = true;
if (!$normal) { if (!$normal) {
if ($('#id_answers-TOTAL_FORMS').val() == "0") { if ($('#id_answers-TOTAL_FORMS').val() == "0") {
submit = false;
add_answers_input_form();
toastr.error(`Question that are not normal require an answer`);
evt.preventDefault();
}
}
if (!$("#id_examination_to option").length) {
submit = false; submit = false;
add_answers_input_form(); toastr.error("Please add examination type")
toastr.error(`Question that are not normal require an answer`);
evt.preventDefault(); evt.preventDefault();
} }
} if (submit) {
toastr.info(`Submiting question... please wait`, '', {
"closeButton": false,
"debug": false,
"newestOnTop": false,
"progressBar": true,
"positionClass": "toast-top-full-width",
"preventDuplicates": false,
"onclick": null,
"showDuration": "300",
"hideDuration": "1000",
"timeOut": "5000",
"extendedTimeOut": "1000",
"showEasing": "swing",
"hideEasing": "linear",
"showMethod": "fadeIn",
"hideMethod": "fadeOut"
});
}
})
});
function extractDicomDetails(byteArray) {
if (!$("#id_examination_to option").length) { if (!$("#id_examination_to option").length) {
submit = false;
toastr.error("Please add examination type")
evt.preventDefault();
}
if (submit) {
toastr.info(`Submiting question... please wait`, '', {
"closeButton": false,
"debug": false,
"newestOnTop": false,
"progressBar": true,
"positionClass": "toast-top-full-width",
"preventDuplicates": false,
"onclick": null,
"showDuration": "300",
"hideDuration": "1000",
"timeOut": "5000",
"extendedTimeOut": "1000",
"showEasing": "swing",
"hideEasing": "linear",
"showMethod": "fadeIn",
"hideMethod": "fadeOut"
});
}
})
});
function extractDicomDetails(byteArray) {
if (!$("#id_examination_to option").length) {
// We need to setup a try/catch block because parseDicom will throw an exception // We need to setup a try/catch block because parseDicom will throw an exception
// if you attempt to parse a non dicom part 10 file (or one that is corrupted) // if you attempt to parse a non dicom part 10 file (or one that is corrupted)
try { try {
// parse byteArray into a DataSet object using the parseDicom library // parse byteArray into a DataSet object using the parseDicom library
var dataSet = dicomParser.parseDicom(byteArray); var dataSet = dicomParser.parseDicom(byteArray);
// dataSet contains the parsed elements. Each element is available via a property // dataSet contains the parsed elements. Each element is available via a property
// in the dataSet.elements object. The property name is based on the elements group // in the dataSet.elements object. The property name is based on the elements group
@@ -242,98 +242,97 @@
// To access the data for an element, we need to know its type and its tag. // To access the data for an element, we need to know its type and its tag.
// We will get the sopInstanceUid from the file which is a string and // We will get the sopInstanceUid from the file which is a string and
// has the tag (0020,000D) // has the tag (0020,000D)
var study_description = dataSet.string('x00081030').toLowerCase(); var study_description = dataSet.string('x00081030').toLowerCase();
console.log("STUDY", study_description); console.log("STUDY", study_description);
// try to match with a study description (if not already selected) // try to match with a study description (if not already selected)
if ($(`#id_examination_to option`).length < 1) { if ($(`#id_examination_to option`).length < 1) {
option = $(`#id_examination_from option[title*='${study_description}' i]`); option = $(`#id_examination_from option[title*='${study_description}' i]`);
if (option.length) { if (option.length) {
option.appendTo($("#id_examination_to")); option.appendTo($("#id_examination_to"));
toastr.success(`Examination set to ${option.attr('title')} from dicom data`); toastr.success(`Examination set to ${option.attr('title')} from dicom data`);
} else { } else {
toastr.warning( toastr.warning(
`Unable to set examination ${study_description} from dicom data (it needs creating)`); `Unable to set examination ${study_description} from dicom data (it needs creating)`);
}
}
} catch (err) {
console.log(err);
} }
} }
} catch (err) {
console.log(err);
} }
} </script>
}
</script>
{{ form.media }} {{ form.media }}
{% endblock %} {% endblock %}
{% block content %} {% block content %}
{% if object %} {% if object %}
{% include "shorts/question_link_header.html" %} {% include "shorts/question_link_header.html" %}
{% endif %} {% endif %}
<h2>Submit Shorts Question</h2> <h2>Submit Shorts Question</h2>
<h3>Instructions</h3> <h3>Instructions</h3>
<p> <p>
Use this form to create a new short style question/case. Multiple images can be added to a question. Use this form to create a new short style question/case. Multiple images can be added to a question.
If the feedback image box is checked they will not be displayed when taking the question. If the feedback image box is checked they will not be displayed when taking the question.
</p> </p>
<p>Once you have created the question with this form you can add relevant findings. <p>Once you have created the question with this form you can add relevant findings.
</p> </p>
<!-- <div> <!-- <div>
To monitor a directory select it here.<br /> To monitor a directory select it here.<br />
<button id="directory-picker-button">Pick a directory</button> <button id="directory-picker-button">Pick a directory</button>
<span id="directory-picker-display">No directory monitored</span> <span id="directory-picker-display">No directory monitored</span>
</div> --> </div> -->
<form action="" method="post" enctype="multipart/form-data" id="rapid-form"> <form action="" method="post" enctype="multipart/form-data" id="rapid-form">
{% comment %} {% csrf_token %} {% endcomment %} {% comment %} {% csrf_token %} {% endcomment %}
<a href="/rapids/examination/create" id="add_examination" class="add-popup" <a href="/rapids/examination/create" id="add_examination" class="add-popup"
onclick="return showAddPopup(this);"><img src="{% static 'img/icon-addlink.svg' %}"></a> onclick="return showAddPopup(this);"><img src="{% static 'img/icon-addlink.svg' %}"></a>
{% crispy form form.helper %} {% crispy form form.helper %}
<h3>Images:</h3> <h3>Images:</h3>
<div id="drop-container" class="drop-target">Drop images here (or use the buttons below)<div <div id="drop-container" class="drop-target">Drop images here (or use the buttons below)<div
id="feedback-drop-target">Feedback image?<br />drop those here</div> id="feedback-drop-target">Feedback image?<br />drop those here</div>
<div id="drop-filenames"></div> <div id="drop-filenames"></div>
</div> </div>
<input type="button" value="Add More Images" id="add_more_images"> <input type="button" value="Add More Images" id="add_more_images">
<div id="image_form_set"> <div id="image_form_set">
{% for form in image_formset %} {% for form in image_formset %}
<ul class="no-error image-formset"> <ul class="no-error image-formset">
{{form.non_field_errors}} {{form.non_field_errors}}
{{form.errors}} {{form.errors}}
{{ form.as_ul }} {{ form.as_ul }}
</ul>
{% endfor %}
</div>
{{ image_formset.management_form }}
<input type="submit" class="submit-button" value="Submit" name="submit">
</form>
<div id="empty_form" style="display:none">
<ul class='no_error image-formset'>
{{ image_formset.empty_form.as_ul }}
</ul>
</div>
<div id="answer_empty_form" style="display:none">
<ul class='no_error answer-formset'>
{{ answer_formset.empty_form.as_ul }}
</ul> </ul>
{% endfor %}
</div> </div>
{{ image_formset.management_form }}
<input type="submit" class="submit-button" value="Submit" name="submit">
</form>
<div id="empty_form" style="display:none">
<ul class='no_error image-formset'>
{{ image_formset.empty_form.as_ul }}
</ul>
</div>
<div id="answer_empty_form" style="display:none">
<ul class='no_error answer-formset'>
{{ answer_formset.empty_form.as_ul }}
</ul>
</div>
<div id="single-dicom-viewer" class="rapid-dicom-viewer" data-images="" data-annotations='' style="display:none"></div> <div id="single-dicom-viewer" class="rapid-dicom-viewer" data-images="" data-annotations='' style="display:none"></div>
{% endblock %} {% endblock %}
{% block css %} {% block css %}
<style> <style>
.add-popup { .add-popup {
float: left; float: left;
} }
</style> </style>
{% endblock css %} {% endblock css %}