.
This commit is contained in:
@@ -580,3 +580,4 @@ function delete_multiple(url, csrf_token) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
window.delete_multiple = delete_multiple
|
window.delete_multiple = delete_multiple
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,373 @@
|
|||||||
|
let active_file_inputs = new Set();
|
||||||
|
|
||||||
|
function add_input_form() {
|
||||||
|
var form_idx = $('#id_images-TOTAL_FORMS').val();
|
||||||
|
$('#image_form_set').append($($('#empty_form').html().replace(/__prefix__/g, form_idx)).on("change",
|
||||||
|
input_change));
|
||||||
|
$('#id_images-TOTAL_FORMS').val(parseInt(form_idx) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function blobToBase64(blob) {
|
||||||
|
return new Promise((resolve, _) => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onloadend = () => resolve(reader.result);
|
||||||
|
reader.readAsDataURL(blob);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function extendInputs(form_id, n) {
|
||||||
|
// Makes sure we have n inputs available
|
||||||
|
// returns available inputs
|
||||||
|
inputs = getUnusedInputs($(`#${form_id} input[type=file][id^=id_images-]`));
|
||||||
|
//fileInput = document.getElementById("id_images-0-image");
|
||||||
|
|
||||||
|
|
||||||
|
// Make sure we have enough input targets
|
||||||
|
input_diff = (n - inputs.length)
|
||||||
|
|
||||||
|
if (input_diff > 0) {
|
||||||
|
for (let j = 0; j < input_diff; j++) {
|
||||||
|
add_input_form()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Need to make sure we include the new ones...
|
||||||
|
inputs = getUnusedInputs($(`#${form_id} input[type=file][id^=id_images-]`));
|
||||||
|
}
|
||||||
|
return inputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCurrentFormFiles(form_id) {
|
||||||
|
inputs = $(`#${form_id} input[type=file]`);
|
||||||
|
|
||||||
|
let files = new Set();
|
||||||
|
for (let j = 0; j < inputs.length; j++) {
|
||||||
|
i = inputs.get(j);
|
||||||
|
if (i.files.length > 0) {
|
||||||
|
files.add(i.files[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUnusedInputs(inputs) {
|
||||||
|
new_inputs = [];
|
||||||
|
for (let i = 0; i < inputs.length; i++) {
|
||||||
|
input = inputs.get(i);
|
||||||
|
|
||||||
|
if (!input.value) {
|
||||||
|
new_inputs.push(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return $(new_inputs);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function addFile(f, feedback) {
|
||||||
|
let dT = new DataTransfer();
|
||||||
|
dT.clearData();
|
||||||
|
dT.items.add(f);
|
||||||
|
for (let i = 0; i < inputs.length; i++) {
|
||||||
|
el = inputs.get(i);
|
||||||
|
|
||||||
|
if (el.files.length == 0) {
|
||||||
|
el.files = dT.files;
|
||||||
|
//ocr(el)
|
||||||
|
|
||||||
|
if (feedback) {
|
||||||
|
arr = el.name.split("-");
|
||||||
|
arr.splice(2, 1, "feedback_image");
|
||||||
|
feedback_el_name = arr.join("-");
|
||||||
|
|
||||||
|
$(`[name=${feedback_el_name}]`).prop("checked", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$(el).change();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function ocr2(url, input) {
|
||||||
|
console.log("OCR", input)
|
||||||
|
Tesseract.recognize(
|
||||||
|
url,
|
||||||
|
'eng',
|
||||||
|
//{ logger: m => console.log(m) }
|
||||||
|
).then(({
|
||||||
|
data: {
|
||||||
|
text
|
||||||
|
}
|
||||||
|
}) => {
|
||||||
|
l = text.toLowerCase();
|
||||||
|
uploading_el = $(`img[data-input-id='${input.id}'], div[data-input-id='${input.id}']`)
|
||||||
|
uploading_el.removeClass("image-ident-loading");
|
||||||
|
console.log("found text", l);
|
||||||
|
if (l.includes("accesion") || l.includes("number") || l.search(
|
||||||
|
/(ref|rk9|rh8|ra9|rbz)\d+/g) > -1) {
|
||||||
|
console.log("Match found ", input);
|
||||||
|
$(input).addClass("image-ident-warning");
|
||||||
|
uploading_el.addClass("image-ident-warning");
|
||||||
|
} else {
|
||||||
|
$(input).addClass("image-ident-ok");
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function input_change(evt) {
|
||||||
|
file = evt.target.files[0];
|
||||||
|
$(evt.target).removeClass("image-ident-warning");
|
||||||
|
$(evt.target).removeClass("image-ident-ok");
|
||||||
|
|
||||||
|
el = evt.target;
|
||||||
|
|
||||||
|
readFileAndProcess2(el);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function showEditPopup(url) {
|
||||||
|
var win = window.open(url, "Edit",
|
||||||
|
'height=500,width=800,resizable=yes,scrollbars=yes');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function showAddPopup(triggeringLink) {
|
||||||
|
var name = triggeringLink.id.replace(/^add_/, '');
|
||||||
|
href = triggeringLink.href;
|
||||||
|
var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
|
||||||
|
win.focus();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function closePopup(win, newID, newRepr, id) {
|
||||||
|
$(id + "_to").append('<option value=' + newID + ' title=' + newRepr + ' >' + newRepr + '</option>')
|
||||||
|
win.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function readFileAndProcess2(el) {
|
||||||
|
|
||||||
|
//file_set = $("#image_form_set input[type=file]");
|
||||||
|
|
||||||
|
//file_set.each(async (n, el) => {
|
||||||
|
|
||||||
|
if (el.files.length > 0) {
|
||||||
|
filename = el.files[0].name;
|
||||||
|
// Probably no need to await here anymore
|
||||||
|
//await readFile(el.files[0], el);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$(el).parent().parent().find(".temp-thumb").remove();
|
||||||
|
|
||||||
|
let url = URL.createObjectURL(el.files[0])
|
||||||
|
|
||||||
|
n = el.id.split("-")[1];
|
||||||
|
$(`#drop-filenames span[data-input-id='${el.id}']`).remove();
|
||||||
|
|
||||||
|
let extra_class = " image-ident-loading";
|
||||||
|
if ($(el).hasClass("image-ident-warning")) {
|
||||||
|
extra_class = " image-ident-warning";
|
||||||
|
} else if ($(el).hasClass("image-ident-ok")) {
|
||||||
|
extra_class = " image-ident-ok";
|
||||||
|
}
|
||||||
|
|
||||||
|
//let extra_class = "";
|
||||||
|
let imageId;
|
||||||
|
if (el.files[0].type.startsWith("image")) {
|
||||||
|
|
||||||
|
let hash = CryptoJS.MD5(el.files[0]).toString();
|
||||||
|
|
||||||
|
base = await blobToBase64(el.files[0]);
|
||||||
|
imageId = "base64://" + base.split(",")[1];
|
||||||
|
imageId2 = base;
|
||||||
|
const element = $(`<img class='temp-thumb' src=${imageId}></div>`).get(0)
|
||||||
|
$(el).parent().parent().prepend(element);
|
||||||
|
|
||||||
|
|
||||||
|
ocr2(url, el);
|
||||||
|
checkHash(hash, el);
|
||||||
|
|
||||||
|
if ($("#drop-filenames").length > 0) {
|
||||||
|
$("#drop-filenames").append(
|
||||||
|
`<span data-input-id='${el.id}'><span>${n}: ${el.files[0].name}</span><img class='uploading${extra_class}' data-input-id='${el.id}' src=${url}></span>`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
imageId = `wadouri:${url}`;
|
||||||
|
imageId2 = imageId
|
||||||
|
|
||||||
|
const element = $(`<div class='temp-thumb' src=${imageId2}></div>`).get(0)
|
||||||
|
$(el).parent().parent().prepend(element);
|
||||||
|
cornerstone.enable(element);
|
||||||
|
cornerstone.loadAndCacheImage(imageId).then(function (image) {
|
||||||
|
cornerstone.displayImage(element, image);
|
||||||
|
cornerstone.resize(element)
|
||||||
|
|
||||||
|
|
||||||
|
if (hash_url) {
|
||||||
|
let pixel_data = image.getPixelData().toString()
|
||||||
|
|
||||||
|
let hash = CryptoJS.MD5(pixel_data).toString();
|
||||||
|
checkHash(hash, el);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
console.log("image", image)
|
||||||
|
|
||||||
|
extractDicomDetails(image.data.byteArray)
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
if ($("#drop-filenames").length > 0) {
|
||||||
|
$("#drop-filenames").append(
|
||||||
|
`<span data-input-id='${el.id}'><span>${n}: ${el.files[0].name}</span><div class='uploading${extra_class}' data-input-id='${el.id}' src=${url}></div></span>`
|
||||||
|
)
|
||||||
|
|
||||||
|
const element2 = $(`#drop-filenames div[data-input-id='${el.id}']`).get(0);
|
||||||
|
cornerstone.enable(element2);
|
||||||
|
cornerstone.loadAndCacheImage(imageId).then(function (image) {
|
||||||
|
cornerstone.displayImage(element2, image);
|
||||||
|
|
||||||
|
$(element2).addClass("temp-thumb-large");
|
||||||
|
cornerstone.resize(element2)
|
||||||
|
setTimeout(function () {
|
||||||
|
ocr2($(element2).find("canvas").get(0).toDataURL(), el);
|
||||||
|
$(element2).removeClass("temp-thumb-large");
|
||||||
|
cornerstone.resize(element2)
|
||||||
|
}, 500);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
active_file_inputs.delete(el)
|
||||||
|
console.log("active", active_file_inputs)
|
||||||
|
// Only load once all queued files have been processed
|
||||||
|
if (active_file_inputs.size < 1) {
|
||||||
|
//load_viewer = true;
|
||||||
|
loadViewer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//})
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkHash(hash, input, url) {
|
||||||
|
$.ajax({
|
||||||
|
url: hash_url,
|
||||||
|
data: {
|
||||||
|
csrfmiddlewaretoken: csrf_token,
|
||||||
|
hash: hash
|
||||||
|
},
|
||||||
|
type: "POST",
|
||||||
|
dataType: "json",
|
||||||
|
})
|
||||||
|
// $.ajax().done(), $.ajax().fail(), $ajax().always() are upto you. Add/change accordingly
|
||||||
|
.done(function (data) {
|
||||||
|
console.log(data);
|
||||||
|
|
||||||
|
if (data.status == "success") {
|
||||||
|
console.log("success");
|
||||||
|
console.log(data.id)
|
||||||
|
if (data.id) {
|
||||||
|
//toastr.info(`id ${data.id}`)
|
||||||
|
uploading_el = $(`img[data-input-id='${input.id}'], div[data-input-id='${input.id}']`)
|
||||||
|
$(input).addClass("image-duplicate");
|
||||||
|
uploading_el.addClass("image-duplicate");
|
||||||
|
|
||||||
|
toastr.warning(`Image (${input.files[0].name}) already exists.<br /><a href='${data.url}'>Click to view</a><br /><button type="button" class="btn clear">Dismiss</button>`, "Duplicate image", {
|
||||||
|
"closeButton": false,
|
||||||
|
"debug": false,
|
||||||
|
"newestOnTop": false,
|
||||||
|
"progressBar": false,
|
||||||
|
"positionClass": "toast-top-full-width",
|
||||||
|
"preventDuplicates": false,
|
||||||
|
"onclick": null,
|
||||||
|
"showDuration": "0",
|
||||||
|
"hideDuration": "0",
|
||||||
|
"timeOut": 0,
|
||||||
|
"extendedTimeOut": 0,
|
||||||
|
"showEasing": "swing",
|
||||||
|
"hideEasing": "linear",
|
||||||
|
"showMethod": "fadeIn",
|
||||||
|
"hideMethod": "fadeOut",
|
||||||
|
"tapToDismiss": false
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.always(function () {
|
||||||
|
console.log('[Done]');
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function updateImagePositions() {
|
||||||
|
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)
|
||||||
|
|
||||||
|
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 (file_element.files.length > 0) {
|
||||||
|
file_name = file_element.files[0].name
|
||||||
|
//$(ul).find("input[type=text]").val(file_name);
|
||||||
|
$(ul).find("input[name*='filename']").val(file_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file_element.files.length > 0 || $(ul).find(":contains('Currently:')")) {
|
||||||
|
n++;
|
||||||
|
$(ul).find("input[type=number]").val(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function loadViewer() {
|
||||||
|
console.log("LOAD VIEWER")
|
||||||
|
// temp fix before starting viewer collapsed
|
||||||
|
$("#single-dicom-viewer").show()
|
||||||
|
//file_set = $("#image_form_set input[type=file]");
|
||||||
|
|
||||||
|
//n = 0;
|
||||||
|
//for (let i = 0; i < file_set.length; i++) {
|
||||||
|
// el = file_set.get(i)
|
||||||
|
// if (el.files.length > 0) {
|
||||||
|
// n++;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
image_set = $("#image_form_set .temp-thumb").get() //.map(function() { return $(this).attr("src")}).get();
|
||||||
|
console.log(image_set);
|
||||||
|
//console.log()
|
||||||
|
//if (n == image_set.length) {
|
||||||
|
const event = new CustomEvent('loadDicomViewer', {
|
||||||
|
"detail": image_set
|
||||||
|
});
|
||||||
|
|
||||||
|
window.dispatchEvent(event);
|
||||||
|
sortable('.sortable');
|
||||||
|
//sortable('.sortable')[0].addEventListener('sortstop', function(e) {
|
||||||
|
updateImagePositions();
|
||||||
|
//}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
from django.test import TestCase
|
|
||||||
|
|
||||||
# Create your tests here.
|
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import json
|
||||||
|
from re import A
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
# from django.contrib.auth.models import User
|
||||||
|
from django.urls import reverse
|
||||||
|
|
||||||
|
from rich.pretty import pprint
|
||||||
|
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
|
from anatomy.views import GenericExamViews as AnatomyExamViews
|
||||||
|
|
||||||
|
from anatomy.models import Exam
|
||||||
|
|
||||||
|
from generic.models import CidUser, CidUserGroup
|
||||||
|
|
||||||
|
APP_NAME = "anatomy"
|
||||||
|
|
||||||
|
def create_cid_user_and_groups(db):
|
||||||
|
group1 = CidUserGroup.objects.create(name="Group1")
|
||||||
|
group2= CidUserGroup.objects.create(name="Group2")
|
||||||
|
|
||||||
|
cid_user_1000 = CidUser.objects.create(cid=1000, passcode="ABCD", group=group1)
|
||||||
|
cid_user_1001 = CidUser.objects.create(cid=1001, passcode="EFGH", group=group1)
|
||||||
|
cid_user_2001 = CidUser.objects.create(cid=2001, passcode="EFGH", group=group2)
|
||||||
|
|
||||||
|
assert cid_user_1000.cid == 1000
|
||||||
|
assert cid_user_1001.cid == 1001
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def create_exam(db):
|
||||||
|
exam: Exam = AnatomyExamViews.Exam.objects.create(name="Cid Exam", exam_mode=True, active=True)
|
||||||
|
|
||||||
|
group1: CidUserGroup = CidUserGroup.objects.get(name="Group1")
|
||||||
|
|
||||||
|
exam.cid_user_groups.add(group1)
|
||||||
|
|
||||||
|
exam.valid_cid_users.add(*group1.ciduser_set.all())
|
||||||
|
|
||||||
|
assert exam in group1.anatomy_cid_user_groups.all()
|
||||||
|
|
||||||
|
assert exam
|
||||||
|
|
||||||
|
def test_exams(db, client):
|
||||||
|
create_cid_user_and_groups(db)
|
||||||
|
create_exam(db)
|
||||||
|
|
||||||
|
active_exams = client.get(reverse(f"{APP_NAME}:active_exams"))
|
||||||
|
assert active_exams.status_code == 200
|
||||||
|
assert len(json.load(active_exams.content)) == 0
|
||||||
|
|
||||||
|
cid_user_1001 = CidUser.objects.get(cid=1001)
|
||||||
|
|
||||||
|
for cid_user in [cid_user_1001]
|
||||||
|
active_exams_cid = client.get(reverse(f"{APP_NAME}:active_exams_cid", ))
|
||||||
|
|
||||||
|
print(active_exams_cid.status_code)
|
||||||
|
print(active_exams_cid.content)
|
||||||
+5
-2
@@ -149,7 +149,7 @@ class SeriesForm(ModelForm):
|
|||||||
"all": ["css/widgets.css"],
|
"all": ["css/widgets.css"],
|
||||||
}
|
}
|
||||||
# Adding this javascript is crucial
|
# Adding this javascript is crucial
|
||||||
js = ["jsi18n.js", "tesseract.min.js"]
|
js = ["jsi18n.js", "tesseract.min.js", "js/upload_form_helpers.js"]
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.user = kwargs.pop(
|
self.user = kwargs.pop(
|
||||||
@@ -239,7 +239,7 @@ class CaseForm(ModelForm):
|
|||||||
"all": ["css/widgets.css"],
|
"all": ["css/widgets.css"],
|
||||||
}
|
}
|
||||||
# Adding this javascript is crucial
|
# Adding this javascript is crucial
|
||||||
js = ["jsi18n.js", "tesseract.min.js"]
|
js = ["jsi18n.js", "tesseract.min.js", "js/upload_form_helpers.js"]
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
logging.info("LOG")
|
logging.info("LOG")
|
||||||
@@ -371,6 +371,7 @@ CaseDifferentialFormSet = inlineformset_factory(
|
|||||||
|
|
||||||
|
|
||||||
class CaseSeriesForm(ModelForm):
|
class CaseSeriesForm(ModelForm):
|
||||||
|
|
||||||
def __init__(self, *args, user, **kwargs):
|
def __init__(self, *args, user, **kwargs):
|
||||||
super(CaseSeriesForm, self).__init__(*args, **kwargs)
|
super(CaseSeriesForm, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
@@ -386,6 +387,8 @@ class CaseSeriesForm(ModelForm):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class CaseCollectionCaseForm(ModelForm):
|
class CaseCollectionCaseForm(ModelForm):
|
||||||
def __init__(self, *args, user, **kwargs):
|
def __init__(self, *args, user, **kwargs):
|
||||||
super(CaseCollectionCaseForm, self).__init__(*args, **kwargs)
|
super(CaseCollectionCaseForm, self).__init__(*args, **kwargs)
|
||||||
|
|||||||
@@ -6,15 +6,10 @@
|
|||||||
<!--<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 crsf_token = "{{csrf_token}}";
|
||||||
|
let hash_url = false;
|
||||||
document.addEventListener('drop', function (e) { e.preventDefault(); }, false);
|
document.addEventListener('drop', function (e) { e.preventDefault(); }, false);
|
||||||
|
|
||||||
function add_input_form() {
|
|
||||||
var form_idx = $('#id_images-TOTAL_FORMS').val();
|
|
||||||
$('#image_form_set').append($('#empty_form').html().replace(/__prefix__/g, form_idx));
|
|
||||||
$('#id_images-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']"));
|
||||||
|
|
||||||
@@ -44,71 +39,93 @@
|
|||||||
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;
|
||||||
|
|
||||||
if (evt.dataTransfer.files.length < 1) {
|
if (file_drop_number < 1) {
|
||||||
toastr.warning(`Drop failed :( try again?`);
|
toastr.warning(`Drop failed (no files), try again.`);
|
||||||
} else {
|
} else {
|
||||||
toastr.info(`Adding ${evt.dataTransfer.files.length} files.`);
|
toastr.info(`Loading ${file_drop_number} dropped image(s).`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get all input elements
|
// Get all input elements
|
||||||
inputs = $("#atlas-form input[type=file][id^=id_images-]");
|
inputs = extendInputs("atlas-form", file_drop_number);
|
||||||
//fileInput = document.getElementById("id_images-0-image");
|
|
||||||
|
|
||||||
// Make sure we have enough input targets
|
console.log("drop inputs", inputs, evt);
|
||||||
input_diff = (evt.dataTransfer.files.length - inputs.length)
|
|
||||||
|
|
||||||
if (input_diff > 0) {
|
|
||||||
for (let j = 0; j < input_diff; j++) {
|
|
||||||
add_input_form()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Need to make sure we include the new ones...
|
|
||||||
inputs = $("#atlas-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();
|
feedback = false;
|
||||||
dT.clearData();
|
if (evt.target.id == "feedback-drop-target") {
|
||||||
dT.items.add(f);
|
feedback = true;
|
||||||
for (let i = 0; i < inputs.length; i++) {
|
|
||||||
el = inputs.get(i)
|
|
||||||
|
|
||||||
if (el.files.length == 0) {
|
|
||||||
el.files = dT.files;
|
|
||||||
//ocr(el)
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
addFile(f, feedback)
|
||||||
})
|
})
|
||||||
|
|
||||||
// // If you want to use some of the dropped files
|
|
||||||
// const dT = new DataTransfer();
|
|
||||||
// dT.items.add(evt.dataTransfer.files[0]);
|
|
||||||
// dT.items.add(evt.dataTransfer.files[3]);
|
|
||||||
// fileInput.files = dT.files;
|
|
||||||
|
|
||||||
loadDicomViewerAndFileImages();
|
|
||||||
|
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
evt.stopPropagation();
|
evt.stopPropagation();
|
||||||
};
|
};
|
||||||
|
//dropContainer.ondrop = function (evt) {
|
||||||
|
// $(evt.target).removeClass("drop-target-active");
|
||||||
|
|
||||||
|
|
||||||
|
// if (evt.dataTransfer.files.length < 1) {
|
||||||
|
// toastr.warning(`Drop failed :( try again?`);
|
||||||
|
// } else {
|
||||||
|
// toastr.info(`Adding ${evt.dataTransfer.files.length} files.`);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Get all input elements
|
||||||
|
// inputs = $("#atlas-form input[type=file][id^=id_images-]");
|
||||||
|
// //fileInput = document.getElementById("id_images-0-image");
|
||||||
|
|
||||||
|
// // Make sure we have enough input targets
|
||||||
|
// input_diff = (evt.dataTransfer.files.length - inputs.length)
|
||||||
|
|
||||||
|
// if (input_diff > 0) {
|
||||||
|
// for (let j = 0; j < input_diff; j++) {
|
||||||
|
// add_input_form()
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Need to make sure we include the new ones...
|
||||||
|
// inputs = $("#atlas-form input[type=file][id^=id_images-]");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Loop through each dropped file and try to assign to an
|
||||||
|
// // input element
|
||||||
|
// [...evt.dataTransfer.files].forEach((f) => {
|
||||||
|
// let dT = new DataTransfer();
|
||||||
|
// dT.clearData();
|
||||||
|
// dT.items.add(f);
|
||||||
|
// for (let i = 0; i < inputs.length; i++) {
|
||||||
|
// el = inputs.get(i)
|
||||||
|
|
||||||
|
// if (el.files.length == 0) {
|
||||||
|
// el.files = dT.files;
|
||||||
|
// //ocr(el)
|
||||||
|
|
||||||
|
// return false
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
|
||||||
|
// // // If you want to use some of the dropped files
|
||||||
|
// // const dT = new DataTransfer();
|
||||||
|
// // dT.items.add(evt.dataTransfer.files[0]);
|
||||||
|
// // dT.items.add(evt.dataTransfer.files[3]);
|
||||||
|
// // fileInput.files = dT.files;
|
||||||
|
|
||||||
|
// loadDicomViewerAndFileImages();
|
||||||
|
|
||||||
|
// evt.preventDefault();
|
||||||
|
// evt.stopPropagation();
|
||||||
|
//};
|
||||||
|
|
||||||
|
|
||||||
$('#add_more_images').click(() => { add_input_form() });
|
$('#add_more_images').click(() => { add_input_form() });
|
||||||
|
|
||||||
$("input[type=file]").on('change', function () {
|
$("input[type=file]").on('change', input_change);
|
||||||
$(this).removeClass("image-ident-warning");
|
|
||||||
$(this).removeClass("image-ident-ok");
|
|
||||||
console.log("input change1");
|
|
||||||
console.log("input change", this);
|
|
||||||
//ocr(this);
|
|
||||||
//LoadDicomViewer();
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -134,63 +151,6 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function ocr(input) {
|
|
||||||
// Tesseract.recognize(f, "eng",{ logger: m => console.log(m) }
|
|
||||||
// ).then(({ data: { text } }) => {
|
|
||||||
// console.log(text);
|
|
||||||
// l = text.toLowerCase();
|
|
||||||
// if (l.includes("accesion") || l.includes("number") || l.search(/(REF|RK9|RH8|RA9)\d+/g)) {
|
|
||||||
// console.log("SHIT", this);
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
|
|
||||||
console.log('{% static "worker.min.js" %}');
|
|
||||||
console.log('{{ STATIC_URL }}/tesseract-core.wasm.js %}');
|
|
||||||
|
|
||||||
const worker = Tesseract.createWorker({
|
|
||||||
workerPath: '{% static "worker.min.js" %}',
|
|
||||||
langPath: '{% static "" %}',
|
|
||||||
//langPath: '{{ STATIC_URL }}',
|
|
||||||
corePath: '{% static "tesseract-core.wasm.js" %}',
|
|
||||||
});
|
|
||||||
|
|
||||||
const url = URL.createObjectURL(input.files[0]);
|
|
||||||
let img = new Image;
|
|
||||||
img.src = url;
|
|
||||||
|
|
||||||
img.onload = function () {
|
|
||||||
|
|
||||||
|
|
||||||
width = img.width;
|
|
||||||
|
|
||||||
// const rectangle = { left: 0, top: 0, width: width, height: 10 }
|
|
||||||
|
|
||||||
|
|
||||||
(async () => {
|
|
||||||
await worker.load();
|
|
||||||
await worker.loadLanguage('eng');
|
|
||||||
await worker.initialize('eng');
|
|
||||||
// const { data: { text } } = await worker.recognize(input.files[0], { rectangle });
|
|
||||||
const { data: { text } } = await worker.recognize(input.files[0]);
|
|
||||||
//console.log(text);
|
|
||||||
l = text.toLowerCase();
|
|
||||||
$(`img[data-input-id='${input.id}'`).removeClass("image-ident-loading");
|
|
||||||
console.log(l);
|
|
||||||
if (l.includes("accesion") || l.includes("number") || l.search(/(ref|rk9|rh8|ra9|rbz)\d+/g) > -1) {
|
|
||||||
$(input).addClass("image-ident-warning");
|
|
||||||
$(`img[data-input-id='${input.id}'`).addClass("image-ident-warning");
|
|
||||||
} else {
|
|
||||||
$(input).addClass("image-ident-ok");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
await worker.terminate();
|
|
||||||
})();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async function loadDicomViewerAndFileImages(callback) {
|
async function loadDicomViewerAndFileImages(callback) {
|
||||||
$("#single-dicom-viewer").empty()
|
$("#single-dicom-viewer").empty()
|
||||||
//images = []
|
//images = []
|
||||||
@@ -263,55 +223,29 @@
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadViewer(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");
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
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 (file_element.files.length > 0) {
|
|
||||||
file_name = file_element.files[0].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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function extractDicomDetails(byteArray) {
|
function extractDicomDetails(byteArray) {
|
||||||
// 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
|
||||||
@@ -486,13 +420,12 @@
|
|||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h3>Images:</h3>
|
<h3>Images:</h3>
|
||||||
<input type="button" value="Add More Images (manually)" id="add_more_images">
|
|
||||||
<div id="drop-container" class="series-drop">Drop images here (or use the buttons below). Dropping images here will overwrite
|
<div id="drop-container" class="series-drop">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
|
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
|
maintained. Note: dicom images are often not exported in order (although their order can be automatically
|
||||||
detected once uploaded)
|
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>
|
{% comment %} <div id="drop-filenames"></div> {% endcomment %}
|
||||||
</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 %}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
DEBUG=1
|
||||||
|
SECRET_KEY=w(s0&(_eb058wvmg@44_repv8)r9@5p8fx*g_@c)1dm&d*ew^u
|
||||||
|
DJANGO_ALLOWED_HOSTS=localhost 127.0.0.1 [::1]
|
||||||
|
|
||||||
|
DB_NAME=rad
|
||||||
|
DB_HOST=db
|
||||||
|
DB_USER=django
|
||||||
|
DB_PASSWORD=postgres
|
||||||
|
DB_PORT=5432
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
build: ./rad
|
||||||
|
command: python manage.py runserver 0.0.0.0:8000
|
||||||
|
volumes:
|
||||||
|
- ./rad/:/usr/src/rad/
|
||||||
|
- ../rts/:/usr/src/rad/rts
|
||||||
|
- ./backups:/usr/src/backups
|
||||||
|
- ./media:/usr/src/media
|
||||||
|
- ./static:/usr/src/static
|
||||||
|
ports:
|
||||||
|
- 8000:8000
|
||||||
|
env_file:
|
||||||
|
- ./.env.dev
|
||||||
|
db:
|
||||||
|
image: postgres:14.2-alpine
|
||||||
|
volumes:
|
||||||
|
- postgres_data:/var/lib/postgresql/data/
|
||||||
|
environment:
|
||||||
|
- POSTGRES_USER=django
|
||||||
|
- POSTGRES_PASSWORD=postgres
|
||||||
|
- POSTGRES_DB=rad
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
postgres_data:
|
||||||
+1
-1
@@ -1587,7 +1587,7 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
answer_score = 0
|
answer_score = 0
|
||||||
ans = "Not answered"
|
ans = "Not answered"
|
||||||
else:
|
else:
|
||||||
if user_answer.normal:
|
if exam.app_name == "rapids" and user_answer.normal:
|
||||||
ans = "Normal"
|
ans = "Normal"
|
||||||
else:
|
else:
|
||||||
ans = user_answer.answer
|
ans = user_answer.answer
|
||||||
|
|||||||
+19
-4
@@ -5,7 +5,10 @@ from . import views
|
|||||||
|
|
||||||
app_name = "physics"
|
app_name = "physics"
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = []
|
||||||
|
|
||||||
|
urlpatterns.extend(
|
||||||
|
[
|
||||||
# path("question/", views.QuestionView.as_view(), name="question_view"),
|
# path("question/", views.QuestionView.as_view(), name="question_view"),
|
||||||
path("question/", views.QuestionView.as_view(), name="question_view"),
|
path("question/", views.QuestionView.as_view(), name="question_view"),
|
||||||
path("question/<int:pk>/", views.question_detail, name="question_detail"),
|
path("question/<int:pk>/", views.question_detail, name="question_detail"),
|
||||||
@@ -25,7 +28,11 @@ urlpatterns = [
|
|||||||
views.exam_take_overview,
|
views.exam_take_overview,
|
||||||
name="exam_take_overview",
|
name="exam_take_overview",
|
||||||
),
|
),
|
||||||
path("exam/<int:pk>/authors", views.ExamAuthorUpdate.as_view(), name="exam_authors"),
|
path(
|
||||||
|
"exam/<int:pk>/authors",
|
||||||
|
views.ExamAuthorUpdate.as_view(),
|
||||||
|
name="exam_authors",
|
||||||
|
),
|
||||||
path("exam/<int:exam_id>/clone", views.ExamClone.as_view(), name="exam_clone"),
|
path("exam/<int:exam_id>/clone", views.ExamClone.as_view(), name="exam_clone"),
|
||||||
path("exam/<int:pk>/update", views.ExamUpdate.as_view(), name="exam_update"),
|
path("exam/<int:pk>/update", views.ExamUpdate.as_view(), name="exam_update"),
|
||||||
path("exam/<int:pk>/delete", views.ExamDelete.as_view(), name="exam_delete"),
|
path("exam/<int:pk>/delete", views.ExamDelete.as_view(), name="exam_delete"),
|
||||||
@@ -38,14 +45,22 @@ urlpatterns = [
|
|||||||
name="user_answer_table_view",
|
name="user_answer_table_view",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"user_answers/<int:pk>", views.UserAnswerView.as_view(), name="user_answer_view"
|
"user_answers/<int:pk>",
|
||||||
|
views.UserAnswerView.as_view(),
|
||||||
|
name="user_answer_view",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"user_answers/<int:pk>/delete",
|
"user_answers/<int:pk>/delete",
|
||||||
views.UserAnswerDelete.as_view(),
|
views.UserAnswerDelete.as_view(),
|
||||||
name="user_answer_delete",
|
name="user_answer_delete",
|
||||||
),
|
),
|
||||||
]
|
path(
|
||||||
|
"exam/<int:pk>/scores/<int:cid>/<str:passcode>/",
|
||||||
|
views.exam_scores_cid_user,
|
||||||
|
name="exam_scores_cid_user",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
urlpatterns.extend(generic_view_urls(views.GenericViews))
|
urlpatterns.extend(generic_view_urls(views.GenericViews))
|
||||||
urlpatterns.extend(generic_exam_urls(views.GenericExamViews))
|
urlpatterns.extend(generic_exam_urls(views.GenericExamViews))
|
||||||
+1
-1
@@ -76,7 +76,7 @@ class RapidForm(ModelForm):
|
|||||||
"all": ["css/widgets.css"],
|
"all": ["css/widgets.css"],
|
||||||
}
|
}
|
||||||
# Adding this javascript is crucial
|
# Adding this javascript is crucial
|
||||||
js = ["jsi18n.js", "tesseract.min.js"]
|
js = ["jsi18n.js", "tesseract.min.js", "js/upload_form_helpers.js"]
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.user = kwargs.pop(
|
self.user = kwargs.pop(
|
||||||
|
|||||||
@@ -6,11 +6,12 @@
|
|||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
let normal = false;
|
let normal = false;
|
||||||
|
let csrf_token = "{{csrf_token}}";
|
||||||
|
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 active_file_inputs = new Set();
|
|
||||||
let load_viewer = false;
|
let load_viewer = false;
|
||||||
|
|
||||||
// async function listAllFilesAndDirs(dirHandle, files_only) {
|
// async function listAllFilesAndDirs(dirHandle, files_only) {
|
||||||
@@ -39,84 +40,13 @@
|
|||||||
// return files;
|
// return files;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
function showEditPopup(url) {
|
|
||||||
var win = window.open(url, "Edit",
|
|
||||||
'height=500,width=800,resizable=yes,scrollbars=yes');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function showAddPopup(triggeringLink) {
|
|
||||||
var name = triggeringLink.id.replace(/^add_/, '');
|
|
||||||
href = triggeringLink.href;
|
|
||||||
var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
|
|
||||||
win.focus();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function closePopup(win, newID, newRepr, id) {
|
|
||||||
$(id + "_to").append('<option value=' + newID + ' title=' + newRepr + ' >' + newRepr + '</option>')
|
|
||||||
win.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener('drop', function (e) {
|
document.addEventListener('drop', function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}, false);
|
}, false);
|
||||||
|
|
||||||
function getUnusedInputs(inputs) {
|
|
||||||
new_inputs = [];
|
|
||||||
for (let i = 0; i < inputs.length; i++) {
|
|
||||||
input = inputs.get(i);
|
|
||||||
|
|
||||||
if (!input.value) {
|
|
||||||
new_inputs.push(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return $(new_inputs);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCurrentFiles() {
|
|
||||||
inputs = $("#rapid-form input[type=file]");
|
|
||||||
|
|
||||||
let files = new Set();
|
|
||||||
for (let j = 0; j < inputs.length; j++) {
|
|
||||||
i = inputs.get(j);
|
|
||||||
if (i.files.length > 0) {
|
|
||||||
files.add(i.files[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return files;
|
|
||||||
}
|
|
||||||
|
|
||||||
function extendInputs(n) {
|
|
||||||
// Makes sure we have n inputs available
|
|
||||||
// returns available inputs
|
|
||||||
inputs = getUnusedInputs($("#rapid-form input[type=file][id^=id_images-]"));
|
|
||||||
//fileInput = document.getElementById("id_images-0-image");
|
|
||||||
|
|
||||||
|
|
||||||
// Make sure we have enough input targets
|
|
||||||
input_diff = (n - inputs.length)
|
|
||||||
|
|
||||||
if (input_diff > 0) {
|
|
||||||
for (let j = 0; j < input_diff; j++) {
|
|
||||||
add_input_form()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Need to make sure we include the new ones...
|
|
||||||
inputs = getUnusedInputs($("#rapid-form input[type=file][id^=id_images-]"));
|
|
||||||
}
|
|
||||||
return inputs;
|
|
||||||
}
|
|
||||||
|
|
||||||
function add_input_form() {
|
|
||||||
var form_idx = $('#id_images-TOTAL_FORMS').val();
|
|
||||||
$('#image_form_set').append($($('#empty_form').html().replace(/__prefix__/g, form_idx)).on("change",
|
|
||||||
input_change));
|
|
||||||
$('#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();
|
||||||
@@ -125,62 +55,6 @@
|
|||||||
$('#id_answers-TOTAL_FORMS').val(parseInt(form_idx) + 1);
|
$('#id_answers-TOTAL_FORMS').val(parseInt(form_idx) + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
function input_change(evt) {
|
|
||||||
file = evt.target.files[0];
|
|
||||||
$(evt.target).removeClass("image-ident-warning");
|
|
||||||
$(evt.target).removeClass("image-ident-ok");
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
el = evt.target;
|
|
||||||
|
|
||||||
|
|
||||||
//readFileAndProcess();
|
|
||||||
readFileAndProcess2(el);
|
|
||||||
|
|
||||||
// Check if we have selected a examination type
|
|
||||||
if (!$("#id_examination_to option").length) {
|
|
||||||
|
|
||||||
// Yes we do read the file twice (here and where loading it in the viewer)
|
|
||||||
var reader = new FileReader();
|
|
||||||
reader.onload = function (file) {
|
|
||||||
var arrayBuffer = reader.result;
|
|
||||||
// Here we have the file data as an ArrayBuffer. dicomParser requires as input a
|
|
||||||
// Uint8Array so we create that here
|
|
||||||
var byteArray = new Uint8Array(arrayBuffer);
|
|
||||||
extractDicomStudyDescription(byteArray)
|
|
||||||
}
|
|
||||||
reader.readAsArrayBuffer(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function addFile(f, feedback) {
|
|
||||||
let dT = new DataTransfer();
|
|
||||||
dT.clearData();
|
|
||||||
dT.items.add(f);
|
|
||||||
for (let i = 0; i < inputs.length; i++) {
|
|
||||||
el = inputs.get(i);
|
|
||||||
|
|
||||||
if (el.files.length == 0) {
|
|
||||||
el.files = dT.files;
|
|
||||||
//ocr(el)
|
|
||||||
|
|
||||||
if (feedback) {
|
|
||||||
arr = el.name.split("-");
|
|
||||||
arr.splice(2, 1, "feedback_image");
|
|
||||||
feedback_el_name = arr.join("-");
|
|
||||||
|
|
||||||
$(`[name=${feedback_el_name}]`).prop("checked", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
$(el).change();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
@@ -223,7 +97,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get all input elements
|
// Get all input elements
|
||||||
inputs = extendInputs(file_drop_number);
|
inputs = extendInputs("rapid-form", file_drop_number);
|
||||||
|
|
||||||
console.log("drop inputs", inputs, evt);
|
console.log("drop inputs", inputs, evt);
|
||||||
|
|
||||||
@@ -250,7 +124,7 @@
|
|||||||
// listAllFilesAndDirs(monitor_directory_handler).then(async function (
|
// listAllFilesAndDirs(monitor_directory_handler).then(async function (
|
||||||
// items) {
|
// items) {
|
||||||
// files_to_add = new Set();
|
// files_to_add = new Set();
|
||||||
// current_files = getCurrentFiles();
|
// current_files = getCurrentFormFiles();
|
||||||
// current_files_tuple = new Set()
|
// current_files_tuple = new Set()
|
||||||
// for (let elem of current_files) {
|
// for (let elem of current_files) {
|
||||||
// current_files_tuple.add(
|
// current_files_tuple.add(
|
||||||
@@ -368,321 +242,83 @@
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function ocr2(url, input) {
|
|
||||||
console.log("OCR", url, input)
|
|
||||||
Tesseract.recognize(
|
|
||||||
url,
|
|
||||||
'eng',
|
|
||||||
{ logger: m => console.log(m) }
|
|
||||||
).then(({ data: { text } }) => {
|
|
||||||
l = text.toLowerCase();
|
|
||||||
uploading_el = $(`img[data-input-id='${input.id}'], div[data-input-id='${input.id}']`)
|
|
||||||
uploading_el.removeClass("image-ident-loading");
|
|
||||||
console.log("found text", l);
|
|
||||||
if (l.includes("accesion") || l.includes("number") || l.search(
|
|
||||||
/(ref|rk9|rh8|ra9|rbz)\d+/g) > -1) {
|
|
||||||
console.log("Match found ", input);
|
|
||||||
$(input).addClass("image-ident-warning");
|
|
||||||
uploading_el.addClass("image-ident-warning");
|
|
||||||
} else {
|
|
||||||
$(input).addClass("image-ident-ok");
|
|
||||||
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
async function blobToBase64(blob) {
|
// async function readFileAndProcess(callback) {
|
||||||
return new Promise((resolve, _) => {
|
// //$("#single-dicom-viewer").empty()
|
||||||
const reader = new FileReader();
|
// //images = []
|
||||||
reader.onloadend = () => resolve(reader.result);
|
// //Function that returns a promise to read the file
|
||||||
reader.readAsDataURL(blob);
|
// const reader = (file) => {
|
||||||
});
|
// return new Promise((resolve, reject) => {
|
||||||
}
|
// const fileReader = new FileReader();
|
||||||
|
// fileReader.onload = () => resolve(fileReader);
|
||||||
|
// fileReader.readAsDataURL(file);
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Add file to the process list
|
||||||
|
// active_file_inputs.add(el)
|
||||||
|
// const readFile = (file, el) => {
|
||||||
|
// reader(file).then(reader => {
|
||||||
|
// console.log("12345", reader)
|
||||||
|
// $(el).parent().parent().find(".temp-thumb").remove();
|
||||||
|
//
|
||||||
|
// if (reader.result.startsWith("data:application/octet-stream;base64")) {
|
||||||
|
// const element = $(`<div class='temp-thumb' src=${reader.result}></div>`).get(0)
|
||||||
|
// $(el).parent().parent().prepend(element);
|
||||||
|
// const imageId = cornerstoneWADOImageLoader.wadouri.fileManager.add(
|
||||||
|
// file
|
||||||
|
// );
|
||||||
|
// cornerstone.enable(element);
|
||||||
|
// cornerstone.loadAndCacheImage(imageId).then(function (image) {
|
||||||
|
// cornerstone.displayImage(element, image);
|
||||||
|
// cornerstone.resize(element)
|
||||||
|
//
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// } else {
|
||||||
|
// var image = new Image();
|
||||||
|
// image.title = file.name;
|
||||||
|
// image.src = reader.result;
|
||||||
|
// image.className = "temp-thumb";
|
||||||
|
// $(el).parent().parent().prepend(image);
|
||||||
|
// //images.push(reader.result);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// active_file_inputs.delete(el)
|
||||||
|
// console.log("active", active_file_inputs)
|
||||||
|
// // Only load once all queued files have been processed
|
||||||
|
// if (active_file_inputs.size < 1) {
|
||||||
|
// loadViewer();
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// file_set = $("#image_form_set input[type=file]");
|
||||||
|
//
|
||||||
|
// file_set.each(async (n, el) => {
|
||||||
|
// if (el.files.length > 0) {
|
||||||
|
// filename = el.files[0].name;
|
||||||
|
// // Probably no need to await here anymore
|
||||||
|
// await readFile(el.files[0], el);
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
async function readFileAndProcess2(el) {
|
// const viewLoadChecker = setInterval(function () {
|
||||||
|
// if (load_viewer) {
|
||||||
//file_set = $("#image_form_set input[type=file]");
|
// load_viewer = false;
|
||||||
|
// loadViewer();
|
||||||
//file_set.each(async (n, el) => {
|
// }
|
||||||
|
// }, 2000);
|
||||||
if (el.files.length > 0) {
|
|
||||||
filename = el.files[0].name;
|
|
||||||
// Probably no need to await here anymore
|
|
||||||
//await readFile(el.files[0], el);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
console.log(el.files[0])
|
function extractDicomDetails(byteArray) {
|
||||||
|
if (!$("#id_examination_to option").length) {
|
||||||
|
|
||||||
$(el).parent().parent().find(".temp-thumb").remove();
|
|
||||||
|
|
||||||
let url = URL.createObjectURL(el.files[0])
|
|
||||||
|
|
||||||
n = el.id.split("-")[1];
|
|
||||||
$(`#drop-filenames span[data-input-id='${el.id}']`).remove();
|
|
||||||
|
|
||||||
let extra_class = " image-ident-loading";
|
|
||||||
if ($(el).hasClass("image-ident-warning")) {
|
|
||||||
extra_class = " image-ident-warning";
|
|
||||||
} else if ($(el).hasClass("image-ident-ok")) {
|
|
||||||
extra_class = " image-ident-ok";
|
|
||||||
}
|
|
||||||
|
|
||||||
//let extra_class = "";
|
|
||||||
let imageId;
|
|
||||||
if (el.files[0].type.startsWith("image")) {
|
|
||||||
|
|
||||||
let hash = CryptoJS.MD5(el.files[0]).toString();
|
|
||||||
console.log("HASH", hash)
|
|
||||||
|
|
||||||
base = await blobToBase64(el.files[0]);
|
|
||||||
imageId = "base64://" + base.split(",")[1];
|
|
||||||
imageId2 = base;
|
|
||||||
const element = $(`<img class='temp-thumb' src=${imageId}></div>`).get(0)
|
|
||||||
$(el).parent().parent().prepend(element);
|
|
||||||
|
|
||||||
|
|
||||||
ocr2(url, el);
|
|
||||||
checkHash(hash, el);
|
|
||||||
|
|
||||||
$("#drop-filenames").append(
|
|
||||||
`<span data-input-id='${el.id}'><span>${n}: ${el.files[0].name}</span><img class='uploading${extra_class}' data-input-id='${el.id}' src=${url}></span>`
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
imageId = `wadouri:${url}`;
|
|
||||||
imageId2 = imageId
|
|
||||||
|
|
||||||
console.log(imageId)
|
|
||||||
const element = $(`<div class='temp-thumb' src=${imageId2}></div>`).get(0)
|
|
||||||
$(el).parent().parent().prepend(element);
|
|
||||||
cornerstone.enable(element);
|
|
||||||
cornerstone.loadAndCacheImage(imageId).then(function (image) {
|
|
||||||
cornerstone.displayImage(element, image);
|
|
||||||
cornerstone.resize(element)
|
|
||||||
|
|
||||||
|
|
||||||
let pixel_data = image.getPixelData().toString()
|
|
||||||
console.log(element, "pixel data", pixel_data)
|
|
||||||
|
|
||||||
let hash = CryptoJS.MD5(pixel_data).toString();
|
|
||||||
console.log(element, "HASH", hash)
|
|
||||||
checkHash(hash, el);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#drop-filenames").append(
|
|
||||||
`<span data-input-id='${el.id}'><span>${n}: ${el.files[0].name}</span><div class='uploading${extra_class}' data-input-id='${el.id}' src=${url}></div></span>`
|
|
||||||
)
|
|
||||||
|
|
||||||
const element2 = $(`#drop-filenames div[data-input-id='${el.id}']`).get(0);
|
|
||||||
cornerstone.enable(element2);
|
|
||||||
cornerstone.loadAndCacheImage(imageId).then(function (image) {
|
|
||||||
cornerstone.displayImage(element2, image);
|
|
||||||
|
|
||||||
$(element2).addClass("temp-thumb-large");
|
|
||||||
cornerstone.resize(element2)
|
|
||||||
setTimeout(function () {
|
|
||||||
ocr2($(element2).find("canvas").get(0).toDataURL(), el);
|
|
||||||
$(element2).removeClass("temp-thumb-large");
|
|
||||||
cornerstone.resize(element2)
|
|
||||||
}, 500);
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
active_file_inputs.delete(el)
|
|
||||||
console.log("active", active_file_inputs)
|
|
||||||
// Only load once all queued files have been processed
|
|
||||||
if (active_file_inputs.size < 1) {
|
|
||||||
load_viewer = true;
|
|
||||||
//loadViewer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//})
|
|
||||||
}
|
|
||||||
|
|
||||||
async function readFileAndProcess(callback) {
|
|
||||||
//$("#single-dicom-viewer").empty()
|
|
||||||
//images = []
|
|
||||||
//Function that returns a promise to read the file
|
|
||||||
const reader = (file) => {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const fileReader = new FileReader();
|
|
||||||
fileReader.onload = () => resolve(fileReader);
|
|
||||||
fileReader.readAsDataURL(file);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add file to the process list
|
|
||||||
active_file_inputs.add(el)
|
|
||||||
const readFile = (file, el) => {
|
|
||||||
reader(file).then(reader => {
|
|
||||||
console.log("12345", reader)
|
|
||||||
$(el).parent().parent().find(".temp-thumb").remove();
|
|
||||||
|
|
||||||
if (reader.result.startsWith("data:application/octet-stream;base64")) {
|
|
||||||
const element = $(`<div class='temp-thumb' src=${reader.result}></div>`).get(0)
|
|
||||||
$(el).parent().parent().prepend(element);
|
|
||||||
const imageId = cornerstoneWADOImageLoader.wadouri.fileManager.add(
|
|
||||||
file
|
|
||||||
);
|
|
||||||
cornerstone.enable(element);
|
|
||||||
cornerstone.loadAndCacheImage(imageId).then(function (image) {
|
|
||||||
cornerstone.displayImage(element, image);
|
|
||||||
cornerstone.resize(element)
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
} else {
|
|
||||||
var image = new Image();
|
|
||||||
image.title = file.name;
|
|
||||||
image.src = reader.result;
|
|
||||||
image.className = "temp-thumb";
|
|
||||||
$(el).parent().parent().prepend(image);
|
|
||||||
//images.push(reader.result);
|
|
||||||
}
|
|
||||||
|
|
||||||
active_file_inputs.delete(el)
|
|
||||||
console.log("active", active_file_inputs)
|
|
||||||
// Only load once all queued files have been processed
|
|
||||||
if (active_file_inputs.size < 1) {
|
|
||||||
loadViewer();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
file_set = $("#image_form_set input[type=file]");
|
|
||||||
|
|
||||||
file_set.each(async (n, el) => {
|
|
||||||
if (el.files.length > 0) {
|
|
||||||
filename = el.files[0].name;
|
|
||||||
// Probably no need to await here anymore
|
|
||||||
await readFile(el.files[0], el);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const viewLoadChecker = setInterval(function () {
|
|
||||||
if (load_viewer) {
|
|
||||||
load_viewer = false;
|
|
||||||
loadViewer();
|
|
||||||
}
|
|
||||||
}, 2000);
|
|
||||||
|
|
||||||
function loadViewer() {
|
|
||||||
console.log("LOAD VIEWER")
|
|
||||||
// temp fix before starting viewer collapsed
|
|
||||||
$("#single-dicom-viewer").show()
|
|
||||||
//file_set = $("#image_form_set input[type=file]");
|
|
||||||
|
|
||||||
//n = 0;
|
|
||||||
//for (let i = 0; i < file_set.length; i++) {
|
|
||||||
// el = file_set.get(i)
|
|
||||||
// if (el.files.length > 0) {
|
|
||||||
// n++;
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
image_set = $("#image_form_set .temp-thumb").get()//.map(function() { return $(this).attr("src")}).get();
|
|
||||||
console.log(image_set);
|
|
||||||
//console.log()
|
|
||||||
//if (n == image_set.length) {
|
|
||||||
const event = new CustomEvent('loadDicomViewer', {
|
|
||||||
"detail": image_set
|
|
||||||
});
|
|
||||||
|
|
||||||
window.dispatchEvent(event);
|
|
||||||
sortable('.sortable');
|
|
||||||
//sortable('.sortable')[0].addEventListener('sortstop', function(e) {
|
|
||||||
updateImagePositions();
|
|
||||||
//}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateImagePositions() {
|
|
||||||
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)
|
|
||||||
|
|
||||||
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 (file_element.files.length > 0) {
|
|
||||||
file_name = file_element.files[0].name
|
|
||||||
//$(ul).find("input[type=text]").val(file_name);
|
|
||||||
$(ul).find("input[name*='filename']").val(file_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (file_element.files.length > 0 || $(ul).find(":contains('Currently:')")) {
|
|
||||||
n++;
|
|
||||||
$(ul).find("input[type=number]").val(n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkHash(hash, input) {
|
|
||||||
$.ajax({
|
|
||||||
url: "{% url 'rapids:rapid_question_by_hash' %}",
|
|
||||||
data: {
|
|
||||||
csrfmiddlewaretoken: '{{ csrf_token }}',
|
|
||||||
hash: hash
|
|
||||||
},
|
|
||||||
type: "POST",
|
|
||||||
dataType: "json",
|
|
||||||
})
|
|
||||||
// $.ajax().done(), $.ajax().fail(), $ajax().always() are upto you. Add/change accordingly
|
|
||||||
.done(function (data) {
|
|
||||||
console.log(data);
|
|
||||||
|
|
||||||
if (data.status == "success") {
|
|
||||||
console.log("success");
|
|
||||||
console.log(data.id)
|
|
||||||
if (data.id) {
|
|
||||||
//toastr.info(`id ${data.id}`)
|
|
||||||
uploading_el = $(`img[data-input-id='${input.id}'], div[data-input-id='${input.id}']`)
|
|
||||||
$(input).addClass("image-duplicate");
|
|
||||||
uploading_el.addClass("image-duplicate");
|
|
||||||
|
|
||||||
toastr.warning(`Image (${input.files[0].name}) already exists.<br /><a href='${data.url}'>Click to view</a><br /><button type="button" class="btn clear">Dismiss</button>`, "Duplicate image", {
|
|
||||||
"closeButton": false,
|
|
||||||
"debug": false,
|
|
||||||
"newestOnTop": false,
|
|
||||||
"progressBar": false,
|
|
||||||
"positionClass": "toast-top-full-width",
|
|
||||||
"preventDuplicates": false,
|
|
||||||
"onclick": null,
|
|
||||||
"showDuration": "0",
|
|
||||||
"hideDuration": "0",
|
|
||||||
"timeOut": 0,
|
|
||||||
"extendedTimeOut": 0,
|
|
||||||
"showEasing": "swing",
|
|
||||||
"hideEasing": "linear",
|
|
||||||
"showMethod": "fadeIn",
|
|
||||||
"hideMethod": "fadeOut",
|
|
||||||
"tapToDismiss": false
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.always(function () {
|
|
||||||
console.log('[Done]');
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function extractDicomStudyDescription(byteArray) {
|
|
||||||
// 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 {
|
||||||
@@ -718,6 +354,7 @@
|
|||||||
console.log(err);
|
console.log(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{{ form.media }}
|
{{ form.media }}
|
||||||
|
|||||||
Reference in New Issue
Block a user