This commit is contained in:
Ross
2021-11-23 22:38:30 +00:00
parent e3bfb23dc1
commit d53a4b8409
15 changed files with 641 additions and 1 deletions
@@ -0,0 +1 @@
{% include 'confirm_delete.html' %}
+8
View File
@@ -0,0 +1,8 @@
{% extends 'atlas/base.html' %}
{% block content %}
{% for author in authors %}
<p>Author: <a href="{% url 'atlas:author_detail' pk=author.pk %}">{{author.username}}</a>, </p>
{% endfor %}
{% endblock %}
+28
View File
@@ -0,0 +1,28 @@
{% extends 'base.html' %}
{% block title %}
Longs
{% endblock %}
{% block css %}
{% endblock %}
{% block js %}
{% endblock %}
{% block content %}
{% endblock %}
{% block navigation %}
Longs:
{% if request.user.is_authenticated %}
<a href="{% url 'atlas:case_view' %}">Cases</a> /
<a href="{% url 'atlas:series_view' %}">Series</a> /
<a href="{% url 'atlas:create' %}" title="Create a new atlas case">Create Case</a> /
<a href="{% url 'atlas:series_create' %}" title="Create a new image series">Create Series</a>
{% endif %}
{% comment %} </br>
Questions by:
<span id="authors-link"><a href="{% url 'atlas:author_list' %}">author</a></span> {% endcomment %}
{% endblock %}
+20
View File
@@ -0,0 +1,20 @@
{% extends 'atlas/base.html' %}
{% block content %}
<div class="floating-header">
<a href="{% url 'atlas:atlas_update' pk=question.pk %}" title="Edit the Case">Edit</a>
<a href="{% url 'atlas:atlas_clone' pk=question.pk %}" title="Clone the Long (duplicate everything but the images)">Clone</a>
<a href="{% url 'atlas:atlas_delete' pk=question.pk %}" title="Delete the Rapid">Delete</a>
<a href="#" onclick="return window.create_popup_window('{% url 'feedback_create' question_type='atlas' pk=question.pk %}')"> Add Note</a>
{% if request.user.is_superuser %}
<a href="{% url 'admin:atlas_case_change' question.id %}" title="Edit the Long using the admin interface">Admin Edit</a>
{% endif %}
{% include 'atlas/atlas_display_block.html' %}
{% endblock %}
{% block js %}
{% endblock %}
+47
View File
@@ -0,0 +1,47 @@
<div class="atlas {% if question.scrapped %}atlas-scrapped{% endif %}">
<div class="date">
{{ question.created_date|date:"d/m/Y" }}
</div>
<div class="id">
ID: {{ question.id }}
</div>
<p class="pre-whitespace"><b>Description:</b> {{ question.description }}</p>
<p class="pre-whitespace"><b>History:</b> {{ question.history }}</p>
<div class="pre-whitespace multi-image-block"><b>Series:</b>
{% for series in question.series.all %}
<span class="series-block">
<span>
Series {{ forloop.counter }}:
<a href="{% url 'atlas:atlas_series_detail' pk=series.pk %}">
{{series.get_block}}
</a>
<a href="#"
onclick="return window.create_popup_window('/atlas/series/{{series.pk}}', 'Series')">Popup</a>
</span>
</span>
{% endfor %}
<a href="{% url 'atlas:atlas_series_id_create' pk=question.pk %}">Add new series</a><br />
</div>
<p><b>Author(s):</b> {% for author in question.author.all %} <a
href="{% url 'atlas:author_detail' pk=author.pk %}">{{author}}</a>, {% endfor %}</p>
<p><b>Checked by:</b> {% for verified in question.verified.all %} <a
href="{% url 'atlas:verified_detail' pk=verified.pk %}">{{verified}}</a>, {% endfor %}</p>
{% comment %} <p><b>Scrapped:</b> {{ question.scrapped }} <a
href="{% url 'atlas:atlas_scrap' pk=question.pk %}">(toggle)</a> {% endcomment %}
<div class="atlas-answer">
<p class="pre-whitespace">
<details>
<summary><b>Findings</b></summary>
<pre>{{ question.findings | safe}}</pre>
</details>
</p>
</div>
</p>
<div>
{% include 'question_notes.html' %}
</div>
+73
View File
@@ -0,0 +1,73 @@
{% extends "atlas/base.html" %}
<!-- {% load static from static %} -->
{% block css %}
{{form.media}}
{% endblock %}
{% block js %}
<!--<script type="text/javascript" src="/admin/jsi18n/"></script>-->
<script type="text/javascript">
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) {
console.log(id)
$(id + "_to").append('<option value=' + newID + ' title=' + newRepr + ' >' + newRepr + '</option>')
win.close();
}
function add_input_form() {
var form_idx = $('#id_LongSeries_atlas-TOTAL_FORMS').val();
$('#form_set').append($('#empty_form').html().replace(/__prefix__/g, form_idx));
$('#id_LongSeries_atlas-TOTAL_FORMS').val(parseInt(form_idx) + 1);
}
$(document).ready(function () {
$('#add_more').click(() => { add_input_form() });
})
</script>
<!-- {{ form.media }} -->
{% endblock %}
{% block content %}
<h2>Submit Long Case</h2>
Use this form to create a atlas case. Existing associated image sets can be added using this form.
<form action="" method="post" enctype="multipart/form-data" id="atlas-form">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<h3>Series:</h3>
Add image sets here. These can only be added once created (they can also be added to cases on creation).
<input type="button" value="Add More Series" id="add_more">
<div id="form_set">
{% for form in series_formset %}
<ul class="no-error series-formset">
{{form.non_field_errors}}
{{form.errors}}
{{ form.as_ul }}
</ul>
{% endfor %}
</div>
{{ series_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 series-formset'>
{{ series_formset.empty_form.as_ul }}
</ul>
</div>
{% endblock %}
+21
View File
@@ -0,0 +1,21 @@
{% extends 'atlas/base.html' %}
{% load render_table from django_tables2 %}
{% block css %}
{% endblock %}
{% block content %}
<div id="view-filter-options">
<h3>Filter Long </h3>
<form action="" method="get">
{{ filter.form }}
<input class="btn btn-primary btn-sm mt-1 mb-1" type="submit" />
</form>
</div>
{% render_table table %}
{% endblock %}
{% block js %}
{% endblock %}
+13
View File
@@ -0,0 +1,13 @@
{% extends 'atlas/base.html' %}
{% block content %}
{{category}}:
<ul>
{% for atlas in atlas %}
<li><a href="{% url 'atlas:case_detail' pk=atlas.pk %}">{{atlas}}
[{{atlas.get_authors}}]</a></li>
{% endfor %}
</ul>
{% endblock %}
+4
View File
@@ -0,0 +1,4 @@
{% extends 'atlas/base.html' %}
{% block content %}
{% endblock %}
+11
View File
@@ -0,0 +1,11 @@
{% extends "atlas/base.html" %}
{% block content %}
<a href="{% url 'atlas:series_update' pk=series.pk %}" title="Edit the Series">Edit</a>
{% if request.user.is_superuser %}
<a href="{% url 'admin:atlas_series_change' series.id %}" title="Edit the Series using the admin interface">Admin Edit</a>
{% endif %}
{% include 'atlas/series_viewer.html' %}
{% endblock %}
@@ -0,0 +1 @@
{% include 'confirm_delete.html' %}
+357
View File
@@ -0,0 +1,357 @@
{% extends "atlas/base.html" %}
{% load static %}
{% block js %}
<!--<script type="text/javascript" src="/admin/jsi18n/"></script>-->
<script type="text/javascript">
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) {
console.log(id)
$(id + "_to").append('<option value=' + newID + ' title=' + newRepr + ' >' + newRepr + '</option>')
win.close();
}
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);
}
function add_answers_input_form() {
var form_idx = $('#id_answers-TOTAL_FORMS').val();
$('#answer_form_set').append($('#answer_empty_form').html().replace(/__prefix__/g, form_idx));
$('#id_answers-TOTAL_FORMS').val(parseInt(form_idx) + 1);
}
$(document).ready(function () {
$("#add_examination").appendTo($("label[for='id_examination']"));
dropContainer = document.getElementById("drop-container");
dropContainer.ondragover = function (evt) {
evt.preventDefault();
evt.stopPropagation();
};
dropContainer.ondragenter = function (evt) {
console.log("ENTER")
console.log(evt)
$(evt.target).addClass("drop-target-active")
evt.preventDefault();
evt.stopPropagation();
};
dropContainer.ondragleave = function (evt) {
console.log("ENTER")
console.log(evt)
$(evt.target).removeClass("drop-target-active")
evt.preventDefault();
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_answers').click(() => { add_answers_input_form() });
$("input[type=file]").on('change', function () {
$(this).removeClass("image-ident-warning");
$(this).removeClass("image-ident-ok");
console.log("input change1");
console.log("input change", this);
//ocr(this);
//LoadDicomViewer();
});
sortable('.sortable');
sortable('.sortable')[0].addEventListener('sortupdate', function(e) {
/*
This event is triggered when the user stops sorting and the DOM position has not yet changed.
e.detail.item - {HTMLElement} dragged element
Origin Container Data
e.detail.origin.index - {Integer} Index of the element within Sortable Items Only
e.detail.origin.elementIndex - {Integer} Index of the element in all elements in the Sortable Container
e.detail.origin.container - {HTMLElement} Sortable Container that element was moved out of (or copied from)
*/
//updateImagePositions();
$(".temp-thumb").remove()
loadDicomViewer();
});
});
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) {
$("#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);
});
}
const readFile = (file, el) => {
reader(file).then(reader => {
var image = new Image();
image.height = 100;
image.title = file.name;
image.src = reader.result;
image.className = "temp-thumb";
console.log("read" ,el);
$(el).parent().parent().prepend(image);
//images.push(reader.result);
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);
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 img");
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);
}
if (file_element.files.length > 0 || $(ul).find(":contains('Currently:')")) {
n++;
$(ul).find("input[type=number]").val(n);
}
}
}
</script>
{{ form.media }}
{% endblock %}
{% block content %}
<h2>Add Series</h2>
<!-- <a href="{% url 'atlas:atlas_create_defaults' %}">Edit defaults</a> -->
This form will create a image set that can be associated with a atlas question. If the question has already been created it can be linked below.
<form action="" method="post" enctype="multipart/form-data" id="atlas-form">
{% csrf_token %}
<a href="/generic/examination/create" id="add_examination" class="add-popup"
onclick="return showAddPopup(this);"><img src="{% static '/img/icon-addlink.svg' %}"></a>
<table>
{{ form.as_table }}
</table>
<h3>Images:</h3>
<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="single-dicom-viewer" class="dicom-viewer" data-images="" data-annotations=''></div>
<div id="drop-filenames"></div>
</div>
<div id="image_form_set" class="sortable">
{% for form in image_formset %}
<ul class="no-error image-formset">
{{form.non_field_errors}}
{{form.errors}}
{{ 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="images">
</div>
{% endblock %}
+35
View File
@@ -0,0 +1,35 @@
<div>{{ series.modality}}, {{ series.examination }}, {{ series.plane }}, {{ series.contrast }}</div>
{% if series.atlas %}
Associated case:
{% for atlas in series.atlas.all %}
<a href="{% url 'atlas:case_detail' pk=atlas.pk %}">{{atlas}}</a>
{% endfor %}
{% else %}
This series is not associated with any cases.
{% endif %}
<div id="single-dicom-viewer" class="dicom-viewer" data-images="{{ series.get_image_url_array }}" data-annotations=''></div>
<div>Author: {{ series.get_author_display }}</div>
<details>
<summary>Image info</summary>
<div>
<a href="{% url 'atlas:series_order_dicom' pk=series.pk %}" title="orders dicom by slice location">Order dicoms
by slice location</a>
<a href="{% url 'atlas:series_order_dicom_instance' pk=series.pk %}"
title="orders dicom by instance number">Order dicoms by instance number</a>
<a href="{% url 'atlas:series_order_dicom_SeriesInstanceUID' pk=series.pk %}"
title="orders dicom by instance number">Order dicoms by SeriesInstanceUID</a>
<a href="{% url 'atlas:series_order_upload_filename' pk=series.pk %}"
title="orders dicom by uploaded filename">Order by uploaded filename</a>
</div>
{% for image in series.images.all %}
{{image.image.url}}, pos: {{image.position}}, {{image.upload_filename}} [{{image.image.size|filesizeformat}}]<br />
{% comment %} {{image.get_dicom_info|safe}}<br/> {% endcomment %}
{% endfor %}
<p>Total image size: {{series.get_total_image_size|filesizeformat}}</p>
<p><a href="{% url 'atlas:series_download' pk=series.pk %}">Download images</a></p>
</details>
+21
View File
@@ -0,0 +1,21 @@
{% extends 'atlas/base.html' %}
{% load render_table from django_tables2 %}
{% block css %}
{% endblock %}
{% block content %}
<div class="container-fluid">
<div id="view-filter-options">
<h4>Filter</h4>
<form action="" method="get">
{{ filter.form }}
<input class="btn btn-primary btn-sm mt-1 mb-1" type="submit" />
</form>
</div>
{% render_table table %}
</div>
<div id="exam-options"></div>
{% endblock %}
+1 -1
View File
@@ -7,7 +7,7 @@ urlpatterns = [
# path('', views.question_list, name='question_list'),
path("author/<int:pk>/", views.author_detail, name="author_detail"),
path("author/", views.author_list, name="author_list"),
path("cases/", views.CaseView.as_view(), name="atlas_view"),
path("cases/", views.CaseView.as_view(), name="case_view"),
path("series/", views.SeriesView.as_view(), name="series_view"),
path("series/<int:pk>", views.series_detail, name="series_detail"),
path(