pre series form update

This commit is contained in:
Ross
2022-07-24 11:11:58 +01:00
parent fde658c71c
commit 8f5c878bab
11 changed files with 286 additions and 91 deletions
+1 -1
View File
@@ -78,7 +78,7 @@ class AnatomyQuestion(models.Model):
image = models.ImageField(
upload_to=image_directory_path,
help_text="The image to use for the question. Ideally use use unmarked images and annotate (arrow) them on the test system. If you wish to reuse an image that is already uploaded 'clone' the question that contains it.As a preference",
help_text="The image to use for the question. Ideally use use unmarked images and annotate (arrow) them on the test system. If you wish to reuse an image that is already uploaded 'clone' the question that contains it.",
)
image_annotations = models.TextField(
+6
View File
@@ -280,6 +280,12 @@ button a {
border: 1px dashed white;
}
.series-drop {
position: sticky;
height: 600px;
bottom: -500px;
}
.submit-button {
position: sticky;
bottom: 0;
-1
View File
@@ -1 +0,0 @@
/home/ross/dv/dicomViewer.css
+148
View File
@@ -0,0 +1,148 @@
.canvas-panel {
height: 100%;
position: relative;
color: white;
}
#dicom-overlay {
position: absolute;
bottom: 0;
}
#image-thumbs {
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
overflow-y: auto;
max-height: 100%;
max-width: 30%;
user-select: none;
}
.thumbnail {
pointer-events: none;
display: block;
margin: auto;
}
.thumb {
position: relative;
border: 1px dotted blue;
touch-action: none;
user-select: none;
}
.thumb-active {
border: 5px dotted blue;
}
.thumb span {
position: absolute;
right: 0;
color: lightblue;
}
.cornerstone-element {
position: relative;
height: 100%;
}
.dicom-button {
font-size: 40px;
z-index: 99;
opacity: 20%;
color: gray;
top: 0;
}
.dicom-button:hover {
opacity: 100%;
}
.dicom-button-highlight {
opacity: 50%;
color: lightblue;
}
#dicom-toggle-mode-button {
position: absolute;
left: 0;
}
#dicom-settings-button {
position: absolute;
right: 0;
}
#dicom-fullscreen-button {
position: absolute;
transform: translate(50%, -20%);
right: 50%;
}
#dicom-collapse-button {
position: absolute;
transform: translate(0, -50%);
top: 50%;
right: 0;
}
#dicom-info-button {
position: absolute;
transform: translate(0, -75%);
right: 0;
top: 100%;
}
#dicom-settings-panel {
display: none;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
padding-left: 30px;
padding-right: 30px;
padding-bottom: 20px;
background-color: rgba(0, 0, 0, 0.8); /* Black w/ opacity */
max-width: 100%;
max-height: 100%;
z-index: 999;
}
#dicom-log-panel {
display: none;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
padding-left: 30px;
padding-right: 30px;
padding-bottom: 20px;
background-color: rgba(0, 0, 0, 0.8); /* Black w/ opacity */
width: 90%;
height: 90%;
z-index: 999;
}
#dicom-window-panel {
position: absolute;
top: 100%;
transform: translate(0, -100%);
opacity: 0.0;
}
#dicom-window-panel:hover {
opacity: 1;
}
#dicom-summary-panel {
position: relative;
z-index: 999;
max-width: 100%;
height: 100%;
color: red;
top: -100%;
overflow: scroll;
display: none;
}
+29
View File
@@ -16,6 +16,7 @@ from django.shortcuts import get_object_or_404
from atlas.models import (
Case,
CaseCollection,
CaseDetail,
CidReportAnswer,
Differential,
Finding,
@@ -39,6 +40,7 @@ from tinymce.widgets import TinyMCE
from dal import autocomplete
import logging
class ConditionForm(ModelForm):
class Meta:
@@ -240,9 +242,20 @@ class CaseForm(ModelForm):
js = ["jsi18n.js", "tesseract.min.js"]
def __init__(self, *args, **kwargs):
logging.info("LOG")
logging.debug(kwargs)
self.user = kwargs.pop(
"user"
) # To get request.user. Do not use kwargs.pop('user', None) due to potential security hole
# This is populated if we create the case from an exam/collection
self.collection = None
if "exams" in kwargs["initial"]:
collections = kwargs["initial"].pop("exams")
logging.debug(collections)
self.collection = get_object_or_404(CaseCollection, pk=collections[0])
if kwargs.get("instance"):
# We get the 'initial' keyword argument or initialize it
# as a dict if it didn't exist.
@@ -251,6 +264,7 @@ class CaseForm(ModelForm):
# a list of primary key for the selected data.
# initial["exams"] = [t.pk for t in kwargs["instance"].exams.all()]
ModelForm.__init__(self, *args, **kwargs)
super(CaseForm, self).__init__(*args, **kwargs)
@@ -307,6 +321,9 @@ class CaseForm(ModelForm):
# Get the unsaved Case instance
instance = ModelForm.save(self, False)
# Prepare a 'save_m2m' method for the form,
old_save_m2m = self.save_m2m
@@ -324,6 +341,18 @@ class CaseForm(ModelForm):
instance.save()
self.save_m2m()
logging.debug(f"{self.collection=}")
if self.collection is not None:
logging.debug(f"{self.collection=}")
case_no = self.collection.cases.count() + 1
logging.debug(f"{case_no=}")
logging.debug(f"{instance=}")
# Create through model
#cd = CaseDetail(case=instance, collection=exam, sort_order=case_no)
self.collection.cases.add(instance, through_defaults={"sort_order": case_no})
return instance
@@ -117,8 +117,8 @@
Add collection</button>
<div id="collection-form"></div>
<ul>
{% for p in case.casecollection_set.all %}
<li>{{p.name}}</li>
{% for collection in case.casecollection_set.all %}
<li><a href="{% url 'atlas:collection_detail' pk=collection.pk %}">{{collection.name}}</a></li>
{% endfor %}
</ul>
</p>
+4
View File
@@ -51,6 +51,10 @@
{% block content %}
<h2>Submit Case</h2>
Use this form to create a atlas case. Existing associated image sets can be added using this form.
{% if form.collection %}
<div class="alert alert-info" role="alert">Creating a case in the collection: <a href="{% url 'atlas:collection_detail' pk=form.collection.pk %}">{{form.collection.name}}</a></div>
{% endif %}
<form action="" method="post" enctype="multipart/form-data" id="atlas-form">
{% csrf_token %}
@@ -3,7 +3,8 @@
<br/>Collection: {{collection.name}}-> <a href="{% url 'atlas:collection_detail' pk=collection.pk %}">Overview</a> /
<a href="{% url 'atlas:collection_mark_overview' collection.pk %}">Mark</a> /
<a href="{% url 'atlas:collection_scores_cid' collection.pk %}">Scores</a> /
<a href="{% url 'atlas:exam_cids' collection.pk %}">Candidates</a>
<a href="{% url 'atlas:exam_cids' collection.pk %}">Candidates</a> /
<a href="{% url 'atlas:atlas_create_exam' pk=collection.pk %}">Add New Case</a>
<div class="floating-header">
<a href="{% url 'atlas:collection_update' collection.id %}" title="Edit the Collection">Edit</a>
\ <a href="{% url 'atlas:collection_delete' collection.id %}" title="Delete the Collection">Delete</a>
+2 -2
View File
@@ -486,8 +486,8 @@
</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
<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
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)
+1
View File
@@ -127,6 +127,7 @@ urlpatterns = [
path("case/<int:pk>/scrap", views.atlas_scrap, name="case_scrap"),
path("case/<int:pk>/delete", views.CaseDelete.as_view(), name="case_delete"),
path("case/create/", views.AtlasCreate.as_view(), name="case_create"),
path("case/collection/<int:pk>/create", views.AtlasCreate.as_view(), name="atlas_create_exam"),
path("series/create", views.SeriesCreate.as_view(), name="series_create"),
path(
"series/<int:pk>/create",
+14 -7
View File
@@ -645,13 +645,20 @@ class AtlasCreateBase(RevisionMixin, LoginRequiredMixin):
# @login_required
class AtlasCreate(AtlasCreateBase, CreateView):
def get_initial(self):
# There has to be a better way...
try:
s = (i.pk for i in self.request.user.atlas_default.site.all())
self.initial.update({"site": s})
except AttributeError:
pass
return self.initial
if "pk" in self.kwargs:
initial = super().get_initial()
exam = get_object_or_404(CaseCollection, pk=self.kwargs["pk"])
initial["exams"] = [exam.id]
return initial
## There has to be a better way...
#try:
# s = (i.pk for i in self.request.user.atlas_default.site.all())
# self.initial.update({"site": s})
#except AttributeError:
# pass
#return self.initial
class AtlasUpdate(
+77 -77
View File
@@ -6,38 +6,38 @@
<script type="text/javascript">
let normal = false;
let monitor_directory_handler = false;
let monitor_interval_id = false;
//let monitor_directory_handler = false;
//let monitor_interval_id = false;
// set to hold the files that are still being processed
let active_file_inputs = new Set();
let load_viewer = false;
async function listAllFilesAndDirs(dirHandle, files_only) {
const files = [];
for await (let [name, handle] of dirHandle) {
const {
kind
} = handle;
if (handle.kind === 'directory') {
if (files_only != true) {
files.push({
name,
handle,
kind
});
}
files.push(...await listAllFilesAndDirs(handle));
} else {
files.push({
name,
handle,
kind
});
}
}
return files;
}
// async function listAllFilesAndDirs(dirHandle, files_only) {
// const files = [];
// for await (let [name, handle] of dirHandle) {
// const {
// kind
// } = handle;
// if (handle.kind === 'directory') {
// if (files_only != true) {
// files.push({
// name,
// handle,
// kind
// });
// }
// files.push(...await listAllFilesAndDirs(handle));
// } else {
// files.push({
// name,
// handle,
// kind
// });
// }
// }
// return files;
// }
function showEditPopup(url) {
var win = window.open(url, "Edit",
@@ -243,56 +243,56 @@
};
$('#directory-picker-button').click(() => {
window.showDirectoryPicker().then((handler) => {
monitor_directory_handler = handler;
monitor_interval_id = setInterval(function () {
listAllFilesAndDirs(monitor_directory_handler).then(async function (
items) {
files_to_add = new Set();
current_files = getCurrentFiles();
current_files_tuple = new Set()
for (let elem of current_files) {
current_files_tuple.add(
`${elem.size}, ${elem.name}, ${elem.lastModified}`
);
}
console.log(current_files_tuple)
for (var i = 0; i < items.length; i++) {
f = await items[i].handle.getFile()
console.log("f", f)
if (!current_files_tuple.has(
`${f.size}, ${f.name}, ${f.lastModified}`
)) {
files_to_add.add(f);
}
}
// file objects differ enough that the following does not work
//let distinct_set = new Set([...files_to_add].filter(
// x => !current_files.has(x)));
extendInputs(files_to_add.size);
files_to_add.forEach((file) => {
addFile(file, false);
});
});
}, 5000);
$("#directory-picker-display").text(monitor_directory_handler.name)
.after(
$(
" <span>[clear]</span>").click((evt) => {
$(evt.target).remove();
$("#directory-picker-display").text("");
monitor_directory_handler = false;
clearInterval(monitor_interval_id);
//const files = await listAllFilesAndDirs(directoryHandle);
}));
});
});
// $('#directory-picker-button').click(() => {
// window.showDirectoryPicker().then((handler) => {
// monitor_directory_handler = handler;
// monitor_interval_id = setInterval(function () {
// listAllFilesAndDirs(monitor_directory_handler).then(async function (
// items) {
// files_to_add = new Set();
// current_files = getCurrentFiles();
// current_files_tuple = new Set()
// for (let elem of current_files) {
// current_files_tuple.add(
// `${elem.size}, ${elem.name}, ${elem.lastModified}`
// );
// }
//
// console.log(current_files_tuple)
// for (var i = 0; i < items.length; i++) {
//
// f = await items[i].handle.getFile()
// console.log("f", f)
// if (!current_files_tuple.has(
// `${f.size}, ${f.name}, ${f.lastModified}`
// )) {
// files_to_add.add(f);
// }
// }
//
// // file objects differ enough that the following does not work
// //let distinct_set = new Set([...files_to_add].filter(
// // x => !current_files.has(x)));
//
//
// extendInputs(files_to_add.size);
// files_to_add.forEach((file) => {
// addFile(file, false);
// });
// });
// }, 5000);
// $("#directory-picker-display").text(monitor_directory_handler.name)
// .after(
// $(
// " <span>[clear]</span>").click((evt) => {
// $(evt.target).remove();
// $("#directory-picker-display").text("");
// monitor_directory_handler = false;
// clearInterval(monitor_interval_id);
// //const files = await listAllFilesAndDirs(directoryHandle);
// }));
// });
// });