start removing lots of print
This commit is contained in:
@@ -113,8 +113,6 @@ def get_exam_user_status_cid_user(request, exam_id: int, cid: int):
|
|||||||
if not exam.check_user_can_edit(request.user):
|
if not exam.check_user_can_edit(request.user):
|
||||||
raise HttpError(HTTPStatus.UNAUTHORIZED,"Exam author required")
|
raise HttpError(HTTPStatus.UNAUTHORIZED,"Exam author required")
|
||||||
|
|
||||||
print(exam_id, cid)
|
|
||||||
|
|
||||||
return exam.exam_user_status.filter(cid_user_exam__cid_user__cid=cid)
|
return exam.exam_user_status.filter(cid_user_exam__cid_user__cid=cid)
|
||||||
|
|
||||||
@router.get('/exam/{exam_id}/user_status/{user_id}/user', response=List[ExamStatusSchema])
|
@router.get('/exam/{exam_id}/user_status/{user_id}/user', response=List[ExamStatusSchema])
|
||||||
|
|||||||
@@ -492,7 +492,6 @@ class Exam(ExamBase):
|
|||||||
return answer
|
return answer
|
||||||
|
|
||||||
def get_question_user_user_answer(self, question_index, user):
|
def get_question_user_user_answer(self, question_index, user):
|
||||||
print(user)
|
|
||||||
question = self.get_question_by_index(question_index)
|
question = self.get_question_by_index(question_index)
|
||||||
answer = UserAnswer.objects.filter(question=question, user=user.pk, exam=self)
|
answer = UserAnswer.objects.filter(question=question, user=user.pk, exam=self)
|
||||||
|
|
||||||
|
|||||||
@@ -194,8 +194,6 @@ def test_exams(db, client):
|
|||||||
exam.save()
|
exam.save()
|
||||||
|
|
||||||
res = client.get(exam_metadata["url"])
|
res = client.get(exam_metadata["url"])
|
||||||
print("PRE ERRER", exam_metadata["url"])
|
|
||||||
print(res.content)
|
|
||||||
assert res.status_code == 404
|
assert res.status_code == 404
|
||||||
|
|
||||||
exam.active = True
|
exam.active = True
|
||||||
|
|||||||
@@ -646,22 +646,6 @@ class AnatomyQuestionUpdate(UpdateQuestionMixin):
|
|||||||
context = self.get_context_data(form=form)
|
context = self.get_context_data(form=form)
|
||||||
#formset = context["answer_formset"]
|
#formset = context["answer_formset"]
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
## logger.debug(formset.is_valid())
|
|
||||||
#if formset.is_valid():
|
|
||||||
# response = super().form_valid(form)
|
|
||||||
# formset.instance = self.object
|
|
||||||
# formset.save()
|
|
||||||
|
|
||||||
# # restore exam orders
|
|
||||||
# #for exam in self.object.exams.all():
|
|
||||||
# # if exam in exam_orders and self.object in exam_orders[exam]:
|
|
||||||
# # print(exam_orders[exam])
|
|
||||||
# # exam.exam_questions.set(exam_orders[exam])
|
|
||||||
# # exam.save()
|
|
||||||
|
|
||||||
# return response
|
|
||||||
#else:
|
|
||||||
# return super().form_invalid(form)
|
|
||||||
|
|
||||||
|
|
||||||
class QuestionClone(AnatomyQuestionCreateBase):
|
class QuestionClone(AnatomyQuestionCreateBase):
|
||||||
@@ -797,7 +781,6 @@ def question_save_annotation(request, pk):
|
|||||||
question = get_object_or_404(AnatomyQuestion, pk=pk)
|
question = get_object_or_404(AnatomyQuestion, pk=pk)
|
||||||
|
|
||||||
question.image_annotations = request.POST.get("annotation")
|
question.image_annotations = request.POST.get("annotation")
|
||||||
print(question.image_annotations)
|
|
||||||
|
|
||||||
question.save()
|
question.save()
|
||||||
data = {"status": "success"}
|
data = {"status": "success"}
|
||||||
|
|||||||
+54
-68
@@ -128,15 +128,10 @@ def uncategorised_dicoms(request):
|
|||||||
|
|
||||||
|
|
||||||
def import_dicoms_helper(request, case_id: int | None = None):
|
def import_dicoms_helper(request, case_id: int | None = None):
|
||||||
# TODO: move this out of the api (would be better with HTMX)
|
|
||||||
|
|
||||||
|
|
||||||
# dicoms = UncategorisedDicom.objects.filter(user=request.user)
|
|
||||||
if "selection" in request.POST:
|
if "selection" in request.POST:
|
||||||
dicoms = UncategorisedDicom.objects.filter(
|
dicoms = UncategorisedDicom.objects.filter(
|
||||||
series_instance_uid__in=request.POST.getlist("selection")
|
series_instance_uid__in=request.POST.getlist("selection")
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
dicoms = UncategorisedDicom.objects.filter(user=request.user)
|
dicoms = UncategorisedDicom.objects.filter(user=request.user)
|
||||||
|
|
||||||
@@ -145,73 +140,66 @@ def import_dicoms_helper(request, case_id: int | None = None):
|
|||||||
else:
|
else:
|
||||||
order = None
|
order = None
|
||||||
|
|
||||||
data = defaultdict(list)
|
# Group dicoms by SeriesInstanceUID, but don't keep all tags in memory
|
||||||
for d in dicoms:
|
series_map = {}
|
||||||
|
for d in dicoms.iterator(): # Use iterator() to avoid loading all at once
|
||||||
tags = d.basic_dicom_tags
|
tags = d.basic_dicom_tags
|
||||||
|
series_uid = tags["SeriesInstanceUID"]
|
||||||
|
|
||||||
data[tags["SeriesInstanceUID"]].append((d, tags))
|
if series_uid not in series_map:
|
||||||
|
# Get or create Series and related objects as needed
|
||||||
series_list = []
|
|
||||||
for series_uid in data:
|
|
||||||
# Hmm probably not a good idea to blinding create a modality...
|
|
||||||
try:
|
|
||||||
modality = Modality.objects.get(short_code=tags["Modality"])
|
|
||||||
except Modality.DoesNotExist:
|
|
||||||
modality = Modality.objects.create(short_code=tags["Modality"], modality=tags["Modality"])
|
|
||||||
|
|
||||||
|
|
||||||
tags = data[series_uid][0][1]
|
|
||||||
|
|
||||||
try:
|
|
||||||
with transaction.atomic():
|
|
||||||
series, created = Series.objects.get_or_create(
|
|
||||||
series_instance_uid=tags["SeriesInstanceUID"],
|
|
||||||
defaults={
|
|
||||||
"modality": modality,
|
|
||||||
"description": tags["SeriesDescription"],
|
|
||||||
}
|
|
||||||
)
|
|
||||||
if created and tags["StudyDescription"]:
|
|
||||||
examination, created_exam = Examination.objects.get_or_create(
|
|
||||||
examination=tags["StudyDescription"]
|
|
||||||
)
|
|
||||||
if created_exam:
|
|
||||||
examination.modality = modality
|
|
||||||
examination.save()
|
|
||||||
series.examination = examination
|
|
||||||
series.save()
|
|
||||||
except IntegrityError:
|
|
||||||
# If a race condition still occurs, fetch the existing series
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
if 'series' not in locals():
|
|
||||||
series = Series.objects.get(series_instance_uid=tags["SeriesInstanceUID"])
|
|
||||||
|
|
||||||
|
|
||||||
# We might only want to add the author during creation....
|
|
||||||
series.author.add(request.user)
|
|
||||||
|
|
||||||
if case_id is not None:
|
|
||||||
case_object = get_object_or_404(Case, pk=case_id)
|
|
||||||
try:
|
try:
|
||||||
series.case.add(case_object)
|
modality = Modality.objects.get(short_code=tags["Modality"])
|
||||||
|
except Modality.DoesNotExist:
|
||||||
|
modality = Modality.objects.create(short_code=tags["Modality"], modality=tags["Modality"])
|
||||||
|
|
||||||
|
series_tags = tags # Save the first tag for this series
|
||||||
|
try:
|
||||||
|
with transaction.atomic():
|
||||||
|
series, created = Series.objects.get_or_create(
|
||||||
|
series_instance_uid=series_uid,
|
||||||
|
defaults={
|
||||||
|
"modality": modality,
|
||||||
|
"description": tags.get("SeriesDescription", ""),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if created and tags.get("StudyDescription"):
|
||||||
|
examination, created_exam = Examination.objects.get_or_create(
|
||||||
|
examination=tags["StudyDescription"]
|
||||||
|
)
|
||||||
|
if created_exam:
|
||||||
|
examination.modality = modality
|
||||||
|
examination.save()
|
||||||
|
series.examination = examination
|
||||||
|
series.save()
|
||||||
except IntegrityError:
|
except IntegrityError:
|
||||||
# The relation already exists, safe to ignore
|
series = Series.objects.get(series_instance_uid=series_uid)
|
||||||
pass
|
|
||||||
|
|
||||||
for dicom, dicom_tags in data[series_uid]:
|
# Add author and case if needed
|
||||||
series_image = SeriesImage(
|
series.author.add(request.user)
|
||||||
image=dicom.image,
|
if case_id is not None:
|
||||||
series=series,
|
case_object = get_object_or_404(Case, pk=case_id)
|
||||||
is_dicom=True,
|
try:
|
||||||
image_blake3_hash=dicom.image_blake3_hash,
|
series.case.add(case_object)
|
||||||
)
|
except IntegrityError:
|
||||||
series_image.save()
|
pass
|
||||||
# dicom.image.delete()
|
|
||||||
|
|
||||||
dicom.delete()
|
series_map[series_uid] = series
|
||||||
|
else:
|
||||||
|
series = series_map[series_uid]
|
||||||
|
|
||||||
|
# Create and save SeriesImage immediately
|
||||||
|
series_image = SeriesImage(
|
||||||
|
image=d.image,
|
||||||
|
series=series,
|
||||||
|
is_dicom=True,
|
||||||
|
image_blake3_hash=d.image_blake3_hash,
|
||||||
|
)
|
||||||
|
series_image.save()
|
||||||
|
d.delete() # Remove UncategorisedDicom immediately
|
||||||
|
|
||||||
|
# Optionally, re-order series if needed
|
||||||
|
for series in series_map.values():
|
||||||
match order:
|
match order:
|
||||||
case "order-series-instance-number":
|
case "order-series-instance-number":
|
||||||
series.order_by_dicom("InstanceNumber")
|
series.order_by_dicom("InstanceNumber")
|
||||||
@@ -220,9 +208,7 @@ def import_dicoms_helper(request, case_id: int | None = None):
|
|||||||
case _:
|
case _:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
series_list.append((series, series.get_link()))
|
return [(series, series.get_link()) for series in series_map.values()]
|
||||||
|
|
||||||
return series_list
|
|
||||||
|
|
||||||
|
|
||||||
@router.post(
|
@router.post(
|
||||||
|
|||||||
@@ -84,9 +84,6 @@ class CaseSelect(Select):
|
|||||||
def create_option(self, *args, **kwargs):
|
def create_option(self, *args, **kwargs):
|
||||||
option = super().create_option(*args, **kwargs)
|
option = super().create_option(*args, **kwargs)
|
||||||
|
|
||||||
print(option)
|
|
||||||
|
|
||||||
|
|
||||||
return option
|
return option
|
||||||
|
|
||||||
def get_context(self, name: str, value, attrs) -> dict[str, Any]:
|
def get_context(self, name: str, value, attrs) -> dict[str, Any]:
|
||||||
@@ -694,7 +691,6 @@ class CidReportAnswerMarkForm(ModelForm):
|
|||||||
#
|
#
|
||||||
# def __init__(self, *args, user, **kwargs):
|
# def __init__(self, *args, user, **kwargs):
|
||||||
# super(AddCollectionToCaseForm, self).__init__(*args, **kwargs)
|
# super(AddCollectionToCaseForm, self).__init__(*args, **kwargs)
|
||||||
# print("INIT", args, kwargs)
|
|
||||||
#
|
#
|
||||||
# if not user.groups.filter(name="atlas_editor").exists():
|
# if not user.groups.filter(name="atlas_editor").exists():
|
||||||
# queryset = CaseCollection.objects.filter(author__id=user.id)
|
# queryset = CaseCollection.objects.filter(author__id=user.id)
|
||||||
@@ -736,7 +732,6 @@ class AddCollectionToCaseForm(Form):
|
|||||||
|
|
||||||
def __init__(self, *args, user, **kwargs):
|
def __init__(self, *args, user, **kwargs):
|
||||||
super(AddCollectionToCaseForm, self).__init__(*args, **kwargs)
|
super(AddCollectionToCaseForm, self).__init__(*args, **kwargs)
|
||||||
print("INIT", args, kwargs)
|
|
||||||
|
|
||||||
if not user.groups.filter(name="atlas_editor").exists():
|
if not user.groups.filter(name="atlas_editor").exists():
|
||||||
queryset = CaseCollection.objects.filter(author__id=user.id)
|
queryset = CaseCollection.objects.filter(author__id=user.id)
|
||||||
@@ -752,21 +747,16 @@ class AddCollectionToCaseForm(Form):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
print("SAVE", args, kwargs)
|
|
||||||
instance = super(AddCollectionToCaseForm, self).save(*args, **kwargs)
|
instance = super(AddCollectionToCaseForm, self).save(*args, **kwargs)
|
||||||
|
|
||||||
# Here we save the modified project selection back into the database
|
# Here we save the modified project selection back into the database
|
||||||
instance.casecollection_set.set(self.cleaned_data['casecollection'])
|
instance.casecollection_set.set(self.cleaned_data['casecollection'])
|
||||||
|
|
||||||
print(f"{instance=}")
|
|
||||||
|
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
def clean_casecollection(self):
|
def clean_casecollection(self):
|
||||||
collection_set = self.cleaned_data["casecollection"]
|
collection_set = self.cleaned_data["casecollection"]
|
||||||
|
|
||||||
print(f"{collection_set=}")
|
|
||||||
|
|
||||||
for col in collection_set:
|
for col in collection_set:
|
||||||
if col not in self.valid_collections:
|
if col not in self.valid_collections:
|
||||||
raise ValidationError("Invalid collection")
|
raise ValidationError("Invalid collection")
|
||||||
|
|||||||
@@ -1215,10 +1215,7 @@ class BaseReportAnswer(models.Model):
|
|||||||
if self.question.question_schema is None or not "properties" in self.question.question_schema:
|
if self.question.question_schema is None or not "properties" in self.question.question_schema:
|
||||||
return []
|
return []
|
||||||
answers = []
|
answers = []
|
||||||
print("1", self.json_answer)
|
|
||||||
print("2", self.question.question_answers)
|
|
||||||
for name, value in self.question.question_schema["properties"].items():
|
for name, value in self.question.question_schema["properties"].items():
|
||||||
print(name, value)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
user_answer = self.json_answer[name]
|
user_answer = self.json_answer[name]
|
||||||
@@ -1229,11 +1226,8 @@ class BaseReportAnswer(models.Model):
|
|||||||
except KeyError: # If the schema has changed?
|
except KeyError: # If the schema has changed?
|
||||||
correct_answer = ""
|
correct_answer = ""
|
||||||
|
|
||||||
print(correct_answer)
|
|
||||||
|
|
||||||
match value:
|
match value:
|
||||||
case {"type": "string", "enum": _}:
|
case {"type": "string", "enum": _}:
|
||||||
print("YES", value)
|
|
||||||
automark = True
|
automark = True
|
||||||
answer_is_correct = user_answer == correct_answer
|
answer_is_correct = user_answer == correct_answer
|
||||||
case {"type": "string"}:
|
case {"type": "string"}:
|
||||||
@@ -1366,8 +1360,6 @@ class UncategorisedDicom(models.Model):
|
|||||||
# duplicate = obj
|
# duplicate = obj
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
print(image_hash)
|
|
||||||
|
|
||||||
if obj := SeriesImage.objects.filter(image_blake3_hash=image_hash).first():
|
if obj := SeriesImage.objects.filter(image_blake3_hash=image_hash).first():
|
||||||
duplicate = obj
|
duplicate = obj
|
||||||
|
|
||||||
@@ -1409,8 +1401,6 @@ class UncategorisedDicom(models.Model):
|
|||||||
ds = pydicom.dcmread(self.image)
|
ds = pydicom.dcmread(self.image)
|
||||||
return ds
|
return ds
|
||||||
except pydicom.errors.InvalidDicomError:
|
except pydicom.errors.InvalidDicomError:
|
||||||
print("Error getting dicom dataset")
|
|
||||||
print(self.image)
|
|
||||||
return {"SeriesInstanceUID": "1234"}
|
return {"SeriesInstanceUID": "1234"}
|
||||||
|
|
||||||
def get_basic_dicom_tags(self, dataset=None):
|
def get_basic_dicom_tags(self, dataset=None):
|
||||||
|
|||||||
@@ -196,11 +196,6 @@ class PopupLinkColumn(tables.Column):
|
|||||||
return obj.get_thumbnail()[0]
|
return obj.get_thumbnail()[0]
|
||||||
|
|
||||||
|
|
||||||
def create_link(test):
|
|
||||||
print(test)
|
|
||||||
return "Hello"
|
|
||||||
|
|
||||||
|
|
||||||
class SeriesTable(tables.Table):
|
class SeriesTable(tables.Table):
|
||||||
edit = tables.LinkColumn(
|
edit = tables.LinkColumn(
|
||||||
"atlas:series_update", text="Edit", args=[A("pk")], orderable=False
|
"atlas:series_update", text="Edit", args=[A("pk")], orderable=False
|
||||||
|
|||||||
@@ -39,8 +39,8 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
<details><summary>Upload settings</summary>
|
<details><summary>Upload settings</summary>
|
||||||
<form>
|
<form>
|
||||||
<div>
|
<div>
|
||||||
<fieldset><legend>Select how series should be ordered when uploaded</legend>
|
<fieldset><legend>Select how series should be ordered when uploaded</legend>
|
||||||
<div class="helptext">
|
<div class="helptext">
|
||||||
This will affect how series are displayed when viewing with the built in site viewer. By default when exporting from Insight the order is not maintained so you should choose to order either by slice location or instance number.<br/>
|
This will affect how series are displayed when viewing with the built in site viewer. By default when exporting from Insight the order is not maintained so you should choose to order either by slice location or instance number.<br/>
|
||||||
If you are not sure, leave this as the default.
|
If you are not sure, leave this as the default.
|
||||||
@@ -57,8 +57,8 @@
|
|||||||
<input type="radio" id="order-series-none" value="order-series-none" name="order-series" >
|
<input type="radio" id="order-series-none" value="order-series-none" name="order-series" >
|
||||||
<label for="order-series-none">None</label><br/>
|
<label for="order-series-none">None</label><br/>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
@@ -69,15 +69,15 @@
|
|||||||
title="Deleted selected uploads"
|
title="Deleted selected uploads"
|
||||||
>Delete Uploads</button>
|
>Delete Uploads</button>
|
||||||
|
|
||||||
<button id="import-dicoms-sequential-button"
|
<button id="import-dicoms-sequential-button"
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-primary"
|
class="btn btn-primary"
|
||||||
>Import {% if case %}into case{% endif %} (one at a time)</button>
|
>Import {% if case %}into case{% endif %} (one at a time)</button>
|
||||||
|
|
||||||
<div id="import-status"></div>
|
<div id="import-status"></div>
|
||||||
|
|
||||||
<div class="indicator"><div class="loader"></div>Loading...</div>
|
<div class="indicator"><div class="loader"></div>Loading...</div>
|
||||||
<div id="import-status"></div>
|
<div id="import-status"></div>
|
||||||
|
|
||||||
<div class="imported">
|
<div class="imported">
|
||||||
|
|
||||||
@@ -89,136 +89,138 @@
|
|||||||
|
|
||||||
<p><a href="{% url 'atlas:series_view' %}?case=null&sort=-modified_date">View orphan series</a></p>
|
<p><a href="{% url 'atlas:series_view' %}?case=null&sort=-modified_date">View orphan series</a></p>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.getElementById('import-dicoms-sequential-button').addEventListener('click', async function() {
|
const importButton = document.getElementById('import-dicoms-sequential-button');
|
||||||
const checkboxes = Array.from(document.querySelectorAll('input[name="selection"]:checked'));
|
if (importButton) {
|
||||||
const allCheckboxes = Array.from(document.querySelectorAll('input[name="selection"]'));
|
document.getElementById('import-dicoms-sequential-button').addEventListener('click', async function() {
|
||||||
|
const checkboxes = Array.from(document.querySelectorAll('input[name="selection"]:checked'));
|
||||||
|
const allCheckboxes = Array.from(document.querySelectorAll('input[name="selection"]'));
|
||||||
// Only include checkboxes that are not disabled (i.e., not already imported)
|
// Only include checkboxes that are not disabled (i.e., not already imported)
|
||||||
const selectableCheckboxes = allCheckboxes.filter(cb => !cb.disabled);
|
const selectableCheckboxes = allCheckboxes.filter(cb => !cb.disabled);
|
||||||
const toImport = checkboxes.length
|
const toImport = checkboxes.length
|
||||||
? checkboxes.filter(cb => !cb.disabled)
|
? checkboxes.filter(cb => !cb.disabled)
|
||||||
: selectableCheckboxes;
|
: selectableCheckboxes;
|
||||||
if (toImport.length === 0) {
|
if (toImport.length === 0) {
|
||||||
alert("Please select at least one series to import (not already imported).");
|
alert("Please select at least one series to import (not already imported).");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const statusDiv = document.getElementById('import-status');
|
const statusDiv = document.getElementById('import-status');
|
||||||
statusDiv.innerHTML = '';
|
statusDiv.innerHTML = '';
|
||||||
const orderSeries = document.querySelector('input[name="order-series"]:checked').value;
|
const orderSeries = document.querySelector('input[name="order-series"]:checked').value;
|
||||||
const csrfToken = '{{ csrf_token }}';
|
const csrfToken = '{{ csrf_token }}';
|
||||||
{% if case %}
|
{% if case %}
|
||||||
const importUrl = "{% url 'api-1:import_dicoms_case' case.id %}";
|
const importUrl = "{% url 'api-1:import_dicoms_case' case.id %}";
|
||||||
{% else %}
|
{% else %}
|
||||||
const importUrl = "{% url 'api-1:import_dicoms' %}";
|
const importUrl = "{% url 'api-1:import_dicoms' %}";
|
||||||
{% endif %}
|
{% endif %}
|
||||||
for (let i = 0; i < toImport.length; i++) {
|
for (let i = 0; i < toImport.length; i++) {
|
||||||
const seriesId = toImport[i].value;
|
const seriesId = toImport[i].value;
|
||||||
statusDiv.innerHTML += `<div id="import-status-${seriesId}">Importing series ${seriesId}...</div>`;
|
statusDiv.innerHTML += `<div id="import-status-${seriesId}">Importing series ${seriesId}...</div>`;
|
||||||
try {
|
try {
|
||||||
const response = await fetch(importUrl, {
|
const response = await fetch(importUrl, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/x-www-form-urlencoded",
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
"X-CSRFToken": csrfToken,
|
"X-CSRFToken": csrfToken,
|
||||||
},
|
},
|
||||||
body: `selection=${encodeURIComponent(seriesId)}&order-series=${encodeURIComponent(orderSeries)}`
|
body: `selection=${encodeURIComponent(seriesId)}&order-series=${encodeURIComponent(orderSeries)}`
|
||||||
});
|
});
|
||||||
const text = await response.text();
|
const text = await response.text();
|
||||||
document.getElementById(`import-status-${seriesId}`).innerHTML = `Series ${seriesId}: ${text}`;
|
document.getElementById(`import-status-${seriesId}`).innerHTML = `Series ${seriesId}: ${text}`;
|
||||||
// Mark as imported in the UI and make unselectable
|
// Mark as imported in the UI and make unselectable
|
||||||
const li = document.querySelector(`#series-list input[value="${seriesId}"]`)?.closest('li');
|
const li = document.querySelector(`#series-list input[value="${seriesId}"]`)?.closest('li');
|
||||||
if (li && !li.classList.contains('imported')) {
|
if (li && !li.classList.contains('imported')) {
|
||||||
li.classList.add('imported');
|
li.classList.add('imported');
|
||||||
// Add badge if not already present
|
// Add badge if not already present
|
||||||
if (!li.querySelector('.badge.bg-success')) {
|
if (!li.querySelector('.badge.bg-success')) {
|
||||||
const badge = document.createElement('span');
|
const badge = document.createElement('span');
|
||||||
badge.className = 'badge bg-success ms-2';
|
badge.className = 'badge bg-success ms-2';
|
||||||
badge.textContent = 'Imported';
|
badge.textContent = 'Imported';
|
||||||
li.appendChild(badge);
|
li.appendChild(badge);
|
||||||
}
|
}
|
||||||
// Disable checkbox and prevent further selection
|
// Disable checkbox and prevent further selection
|
||||||
const checkbox = li.querySelector('input[name="selection"]');
|
const checkbox = li.querySelector('input[name="selection"]');
|
||||||
if (checkbox) {
|
if (checkbox) {
|
||||||
checkbox.disabled = true;
|
checkbox.disabled = true;
|
||||||
checkbox.checked = false;
|
checkbox.checked = false;
|
||||||
}
|
}
|
||||||
// Remove click handler for selection
|
// Remove click handler for selection
|
||||||
li.querySelector('span').onclick = null;
|
li.querySelector('span').onclick = null;
|
||||||
li.style.pointerEvents = "none";
|
li.style.pointerEvents = "none";
|
||||||
li.style.opacity = "0.7";
|
li.style.opacity = "0.7";
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
document.getElementById(`import-status-${seriesId}`).innerHTML = `Series ${seriesId}: <span style="color:red;">Failed</span>`;
|
document.getElementById(`import-status-${seriesId}`).innerHTML = `Series ${seriesId}: <span style="color:red;">Failed</span>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
statusDiv.innerHTML += "<div>All done.</div>";
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
</script>
|
||||||
statusDiv.innerHTML += "<div>All done.</div>";
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
{% block css %}
|
{% block css %}
|
||||||
<style>
|
<style>
|
||||||
.indicator {
|
.indicator {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.indicator.htmx-request {
|
.indicator.htmx-request {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.loader {
|
.loader {
|
||||||
width: 50px;
|
width: 50px;
|
||||||
aspect-ratio: 1;
|
aspect-ratio: 1;
|
||||||
display: grid;
|
display: grid;
|
||||||
}
|
}
|
||||||
.loader::before,
|
.loader::before,
|
||||||
.loader::after {
|
.loader::after {
|
||||||
content:"";
|
content:"";
|
||||||
grid-area: 1/1;
|
grid-area: 1/1;
|
||||||
--c:no-repeat radial-gradient(farthest-side,#25b09b 92%,#0000);
|
--c:no-repeat radial-gradient(farthest-side,#25b09b 92%,#0000);
|
||||||
background:
|
background:
|
||||||
var(--c) 50% 0,
|
var(--c) 50% 0,
|
||||||
var(--c) 50% 100%,
|
var(--c) 50% 100%,
|
||||||
var(--c) 100% 50%,
|
var(--c) 100% 50%,
|
||||||
var(--c) 0 50%;
|
var(--c) 0 50%;
|
||||||
background-size: 12px 12px;
|
background-size: 12px 12px;
|
||||||
animation: l12 1s infinite;
|
animation: l12 1s infinite;
|
||||||
}
|
}
|
||||||
.loader::before {
|
.loader::before {
|
||||||
margin: 4px;
|
margin: 4px;
|
||||||
filter: hue-rotate(45deg);
|
filter: hue-rotate(45deg);
|
||||||
background-size: 8px 8px;
|
background-size: 8px 8px;
|
||||||
animation-timing-function: linear
|
animation-timing-function: linear
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes l12 {
|
@keyframes l12 {
|
||||||
100%{transform: rotate(.5turn)}
|
100%{transform: rotate(.5turn)}
|
||||||
}
|
}
|
||||||
|
|
||||||
#series-list {
|
#series-list {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
#series-list li {
|
#series-list li {
|
||||||
}
|
}
|
||||||
#series-list .selected {
|
#series-list .selected {
|
||||||
color: white;
|
color: white;
|
||||||
border: 1px solid #52057b;
|
border: 1px solid #52057b;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
margin: -1px;
|
margin: -1px;
|
||||||
|
|
||||||
}
|
}
|
||||||
.series-block-popup-link {
|
.series-block-popup-link {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
#series-list li.imported {
|
#series-list li.imported {
|
||||||
border-color: #e6ffe6;
|
border-color: #e6ffe6;
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
{% endblock css %}
|
{% endblock css %}
|
||||||
|
|
||||||
@@ -37,7 +37,6 @@ def test_condition_tree_structure(db):
|
|||||||
assert len(child2.get_parents()) == 1
|
assert len(child2.get_parents()) == 1
|
||||||
assert parent in child2.get_parents()
|
assert parent in child2.get_parents()
|
||||||
|
|
||||||
print(parent.get_descendents())
|
|
||||||
|
|
||||||
#@pytest.fixture
|
#@pytest.fixture
|
||||||
#def create_basic_user(db, django_user_model):
|
#def create_basic_user(db, django_user_model):
|
||||||
|
|||||||
@@ -568,7 +568,6 @@ class SeriesBase(models.Model):
|
|||||||
n = n + 1
|
n = n + 1
|
||||||
|
|
||||||
def order_by_dicom(self, field="SliceLocation"):
|
def order_by_dicom(self, field="SliceLocation"):
|
||||||
print(f"Ordering by dicom field {field}")
|
|
||||||
images = self.images.filter(removed=False)
|
images = self.images.filter(removed=False)
|
||||||
|
|
||||||
|
|
||||||
@@ -577,9 +576,6 @@ class SeriesBase(models.Model):
|
|||||||
for i in images:
|
for i in images:
|
||||||
files.append((i, pydicom.dcmread(i.image.path)))
|
files.append((i, pydicom.dcmread(i.image.path)))
|
||||||
|
|
||||||
print("images to order")
|
|
||||||
print(files)
|
|
||||||
|
|
||||||
# print("file count: {}".format(len(files)))
|
# print("file count: {}".format(len(files)))
|
||||||
|
|
||||||
# skip files with no SliceLocation (eg scout views)
|
# skip files with no SliceLocation (eg scout views)
|
||||||
@@ -594,8 +590,6 @@ class SeriesBase(models.Model):
|
|||||||
else:
|
else:
|
||||||
skipcount = skipcount + 1
|
skipcount = skipcount + 1
|
||||||
|
|
||||||
print("skipped, no {}: {}".format(field, skipcount))
|
|
||||||
|
|
||||||
if not slices:
|
if not slices:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -607,7 +601,6 @@ class SeriesBase(models.Model):
|
|||||||
for f in slices:
|
for f in slices:
|
||||||
i = map[f[field].value]
|
i = map[f[field].value]
|
||||||
i.position = n
|
i.position = n
|
||||||
print(f"Setting position {n} for {i}")
|
|
||||||
i.save()
|
i.save()
|
||||||
n = n + 1
|
n = n + 1
|
||||||
|
|
||||||
|
|||||||
+1
-11
@@ -93,38 +93,29 @@ def get_dicom_order(path):
|
|||||||
files = []
|
files = []
|
||||||
|
|
||||||
for fname in glob.glob(path, recursive=False):
|
for fname in glob.glob(path, recursive=False):
|
||||||
print("loading: {}".format(fname))
|
|
||||||
files.append((fname, pydicom.dcmread(fname)))
|
files.append((fname, pydicom.dcmread(fname)))
|
||||||
|
|
||||||
print("file count: {}".format(len(files)))
|
|
||||||
|
|
||||||
# skip files with no SliceLocation (eg scout views)
|
# skip files with no SliceLocation (eg scout views)
|
||||||
slices = []
|
slices = []
|
||||||
map = {}
|
map = {}
|
||||||
skipcount = 0
|
skipcount = 0
|
||||||
for fname, f in files:
|
for fname, f in files:
|
||||||
print(dir(f))
|
|
||||||
print(fname)
|
|
||||||
if hasattr(f, 'SliceLocation'):
|
if hasattr(f, 'SliceLocation'):
|
||||||
slices.append(f)
|
slices.append(f)
|
||||||
map[f.SliceLocation] = fname
|
map[f.SliceLocation] = fname
|
||||||
else:
|
else:
|
||||||
skipcount = skipcount + 1
|
skipcount = skipcount + 1
|
||||||
|
|
||||||
print("skipped, no SliceLocation: {}".format(skipcount))
|
|
||||||
|
|
||||||
# ensure they are in the correct order
|
# ensure they are in the correct order
|
||||||
slices = sorted(slices, key=lambda s: s.SliceLocation)
|
slices = sorted(slices, key=lambda s: s.SliceLocation)
|
||||||
|
|
||||||
#print(slices)
|
return slices
|
||||||
for i in slices:
|
|
||||||
print(map[i.SliceLocation])
|
|
||||||
|
|
||||||
#get_dicom_order("/home/ross/Downloads/DICOM HEAD/STD4/SER1/*.dcm")
|
#get_dicom_order("/home/ross/Downloads/DICOM HEAD/STD4/SER1/*.dcm")
|
||||||
|
|
||||||
def pretty_print_dicom(dataset, indent=0):
|
def pretty_print_dicom(dataset, indent=0):
|
||||||
l = print_dicom(dataset, indent)
|
l = print_dicom(dataset, indent)
|
||||||
print(l)
|
|
||||||
return "".join(l)
|
return "".join(l)
|
||||||
|
|
||||||
|
|
||||||
@@ -183,7 +174,6 @@ def get_image_dicom_hash(img, dataset=None, hash_type="md5", direct_pixel_data=T
|
|||||||
hasher.update(dataset.PixelData)
|
hasher.update(dataset.PixelData)
|
||||||
except AttributeError as e:
|
except AttributeError as e:
|
||||||
print("Error getting pixel data hash from dataset")
|
print("Error getting pixel data hash from dataset")
|
||||||
print(dataset)
|
|
||||||
raise InvalidDicomError("Error getting pixel data hash from dataset")
|
raise InvalidDicomError("Error getting pixel data hash from dataset")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ if __name__ == "__main__":
|
|||||||
if os.environ.get('RUN_MAIN'):
|
if os.environ.get('RUN_MAIN'):
|
||||||
if settings.DEBUG_CONTAINER:
|
if settings.DEBUG_CONTAINER:
|
||||||
import debugpy
|
import debugpy
|
||||||
print("DEBUG LISTEN")
|
|
||||||
debugpy.listen(("0.0.0.0", 3459))
|
debugpy.listen(("0.0.0.0", 3459))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
+146
-146
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user