start migration to dv3d
This commit is contained in:
+1
-1
@@ -175,7 +175,7 @@ class StructureForm(ModelForm):
|
||||
class SeriesFindingForm(ModelForm):
|
||||
class Meta:
|
||||
model = SeriesFinding
|
||||
exclude = ["series", "annotation_json", "viewport_json", "current_image_id_index"]
|
||||
exclude = ["series", "annotation_json", "viewport_json", "current_image_id_index", "annotation_json_3d", "viewer_state_3d"]
|
||||
|
||||
widgets = {
|
||||
"findings": autocomplete.ModelSelect2Multiple(
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
# Generated by Django 5.1.4 on 2025-06-09 10:05
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('atlas', '0069_caseprior_prior_visibility'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='seriesfinding',
|
||||
name='annotation_json_3d',
|
||||
field=models.JSONField(default=dict),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='seriesfinding',
|
||||
name='viewer_state_3d',
|
||||
field=models.JSONField(default=dict),
|
||||
),
|
||||
]
|
||||
@@ -628,6 +628,8 @@ class SeriesFinding(FindingBase):
|
||||
|
||||
def __str__(self) -> str:
|
||||
findings = self.findings.all().values_list("name")
|
||||
if self.series is None:
|
||||
return f"SeriesFinding: {findings}/{self.description} (no series)"
|
||||
return f"SeriesFinding: {self.series.id}/{findings}/{self.description}"
|
||||
|
||||
@reversion.register
|
||||
|
||||
@@ -3,28 +3,28 @@
|
||||
<div>{{ series.modality}}, {{ series.examination }}, {{ series.plane }}, {{ series.contrast }}</div>
|
||||
<div>Description: {{series.description}}</div>
|
||||
|
||||
Associated case:
|
||||
{% for case in series.case.all %}
|
||||
<span id="case-link-{{ case.pk }}" class="case-item">
|
||||
Associated case:
|
||||
{% for case in series.case.all %}
|
||||
<span id="case-link-{{ case.pk }}" class="case-item">
|
||||
<a href="{% url 'atlas:case_detail' pk=case.pk %}">{{case}}</a>
|
||||
{% if can_edit %}
|
||||
<button hx-post="{% url 'atlas:remove_series_from_case' series.pk case.pk %}"
|
||||
hx-target="#case-link-{{ case.pk }}"
|
||||
hx-swap="outerHTML"
|
||||
hx-confirm="Are you sure you want to remove this series from the case?"
|
||||
class="btn btn-link btn-sm button-unset remove-button">
|
||||
Remove
|
||||
</button>
|
||||
{% endif %}
|
||||
</span>
|
||||
{% empty %}
|
||||
This series is not associated with any cases.
|
||||
{% if can_edit %}
|
||||
<button hx-post="{% url 'atlas:remove_series_from_case' series.pk case.pk %}"
|
||||
hx-target="#case-link-{{ case.pk }}"
|
||||
hx-swap="outerHTML"
|
||||
hx-confirm="Are you sure you want to remove this series from the case?"
|
||||
class="btn btn-link btn-sm button-unset remove-button">
|
||||
Remove
|
||||
</button>
|
||||
{% endif %}
|
||||
</span>
|
||||
{% empty %}
|
||||
This series is not associated with any cases.
|
||||
{% if can_edit %}
|
||||
<details>
|
||||
<summary>Add Series to Case</summary>
|
||||
<form hx-post="{% url 'atlas:add_series_to_case' series.pk %}"
|
||||
hx-target="#add-series-to-case-result"
|
||||
hx-swap="innerHTML">
|
||||
hx-target="#add-series-to-case-result"
|
||||
hx-swap="innerHTML">
|
||||
{% csrf_token %}
|
||||
<label for="case-select">Select Case:</label>
|
||||
<select name="case_id" id="case-select" required>
|
||||
@@ -36,23 +36,29 @@
|
||||
</form>
|
||||
<div id="add-series-to-case-result"></div>
|
||||
</details>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% with image_url_array_and_count=series.get_image_url_array_and_count %}
|
||||
<div id="single-dicom-viewer" class="dicom-viewer" data-images="{{ image_url_array_and_count.0 }}" data-annotations=''>
|
||||
</div>
|
||||
|
||||
<div id="root" class="dicom-viewer-root" data-images="{{ image_url_array_and_count.0 }}"
|
||||
style="width: 1000px; height: 600px; box-sizing: border-box; background: #222;"
|
||||
data-auto-cache-stack=true
|
||||
></div>
|
||||
|
||||
|
||||
{% if can_edit %}
|
||||
{% if editing_finding < 1 %}
|
||||
<button id="add-finding-button">Add finding</button>
|
||||
{% endif %}
|
||||
<button id="clone-finding-button" title="Click to copy the details of a finding that is already associated with the series/case"
|
||||
hx-get="{% url 'atlas:series_finding_related' series.pk %}"
|
||||
hx-target="#clone-findings-modal"
|
||||
hx-trigger="click"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#clone-findings-modal"
|
||||
hx-get="{% url 'atlas:series_finding_related' series.pk %}"
|
||||
hx-target="#clone-findings-modal"
|
||||
hx-trigger="click"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#clone-findings-modal"
|
||||
>Clone existing finding</button>
|
||||
<button id="reset-viewport-button">Reset viewport</button>
|
||||
<div id="finding-form">
|
||||
@@ -80,6 +86,11 @@
|
||||
<div class="finding-box">
|
||||
<button id="finding-{{finding.pk}}" class="view-finding-button" data-annotationjson={{finding.annotation_json}}
|
||||
data-viewportjson={{finding.viewport_json}} data-findingid={{finding.id}} data-currentimageid={{finding.current_image_id_index}}>Click to view</button>
|
||||
|
||||
{% if finding.viewer_state_3d %}
|
||||
<button id="finding-{{finding.pk}}-3d" class="view-finding-button-3d" data-annotationjson3d='{{finding.annotation_json_3d}}' data-viewerstatejson='{{finding.viewer_state_3d}}'>Click to view 3D</button>
|
||||
{% endif %}
|
||||
|
||||
<span class="view-finding-details">
|
||||
Finding(s): {% for f in finding.findings.all %}{{f.get_link}}{% endfor %}<br />
|
||||
Structure(s): {% for s in finding.structures.all %}{{s.get_link}}{% endfor %}<br />
|
||||
@@ -95,6 +106,11 @@
|
||||
<pre>{{finding.viewport_json}}</pre>
|
||||
<h4>Image ID</h4>
|
||||
<pre>{{finding.current_image_id_index}}</pre>
|
||||
|
||||
<h4>3D Annotation JSON</h4>
|
||||
<pre>{{finding.annotation_json_3d}}</pre>
|
||||
<h4>Viewer State</h4>
|
||||
<pre>{{finding.viewer_state_3d}}</pre>
|
||||
</div>
|
||||
{% endif %}
|
||||
</span>
|
||||
@@ -124,76 +140,76 @@
|
||||
|
||||
|
||||
<div>Author: {{ series.get_author_display }}</div>
|
||||
{% if can_edit %}
|
||||
<details>
|
||||
<summary>Series info</summary>
|
||||
<div>
|
||||
<a href="{% url 'atlas:series_anonymise_dicom' pk=series.pk %}"
|
||||
title="Anonymise dicom images">Anonymise dicoms</a><br />
|
||||
<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><br/>
|
||||
<a href="{% url 'api-1:series_split_by_tag' series.pk 'ImageType' %}"
|
||||
title="split series by dicom tag ImageType">Split the series by the ImageType dicom tag</a>
|
||||
</div>
|
||||
{% if can_edit %}
|
||||
<details>
|
||||
<summary>
|
||||
Image details
|
||||
</summary>
|
||||
<form hx-post="{% url 'atlas:image_diff' %}"
|
||||
hx-target="#temp"
|
||||
>
|
||||
{% for image in series.get_images %}
|
||||
[{{ image.id }}] <a href='{% url "atlas:series_image_dicom" image.pk %}'>{{image.image.url}}</a>, pos: {{image.position}}, {{image.upload_filename}}
|
||||
<summary>Series info</summary>
|
||||
<div>
|
||||
<a href="{% url 'atlas:series_anonymise_dicom' pk=series.pk %}"
|
||||
title="Anonymise dicom images">Anonymise dicoms</a><br />
|
||||
<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><br/>
|
||||
<a href="{% url 'api-1:series_split_by_tag' series.pk 'ImageType' %}"
|
||||
title="split series by dicom tag ImageType">Split the series by the ImageType dicom tag</a>
|
||||
</div>
|
||||
<details>
|
||||
<summary>
|
||||
Image details
|
||||
</summary>
|
||||
<form hx-post="{% url 'atlas:image_diff' %}"
|
||||
hx-target="#temp"
|
||||
>
|
||||
{% for image in series.get_images %}
|
||||
[{{ image.id }}] <a href='{% url "atlas:series_image_dicom" image.pk %}'>{{image.image.url}}</a>, pos: {{image.position}}, {{image.upload_filename}}
|
||||
|
||||
|
||||
{% if image.image %}
|
||||
[{{image.get_file_size|filesizeformat}}]
|
||||
{% endif %}
|
||||
{% if image.image %}
|
||||
[{{image.get_file_size|filesizeformat}}]
|
||||
{% endif %}
|
||||
|
||||
{{image.image_md5_hash}} ({{image.is_dicom}}) {{image.image_blake3_hash}}
|
||||
{{image.image_md5_hash}} ({{image.is_dicom}}) {{image.image_blake3_hash}}
|
||||
|
||||
<input type="checkbox" name="image-id" value="{{image.id}}">
|
||||
<input type="checkbox" name="image-id" value="{{image.id}}">
|
||||
|
||||
<br />
|
||||
<br />
|
||||
{% comment %} {{image.get_dicom_info|safe}}<br /> {% endcomment %}
|
||||
|
||||
{% endfor %}
|
||||
<button>Diff files</button>
|
||||
</form>
|
||||
<span id="temp"></span>
|
||||
</details>
|
||||
{% endfor %}
|
||||
<button>Diff files</button>
|
||||
</form>
|
||||
<span id="temp"></span>
|
||||
</details>
|
||||
|
||||
<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>
|
||||
<span id="diff-controls">show <span id="diff-show-all" _="on click
|
||||
log '1'
|
||||
for row in .diff.rows
|
||||
show row
|
||||
end">all</span> <span id="diff-show-diff" _="
|
||||
on click
|
||||
log 'go'
|
||||
for row in <.diff tr/>
|
||||
log row.children[0].innerHTML
|
||||
if row.children[0].innerHTML is empty
|
||||
hide row
|
||||
end
|
||||
end
|
||||
">diff</span></span>
|
||||
<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>
|
||||
<span id="diff-controls">show <span id="diff-show-all" _="on click
|
||||
log '1'
|
||||
for row in .diff.rows
|
||||
show row
|
||||
end">all</span> <span id="diff-show-diff" _="
|
||||
on click
|
||||
log 'go'
|
||||
for row in <.diff tr/>
|
||||
log row.children[0].innerHTML
|
||||
if row.children[0].innerHTML is empty
|
||||
hide row
|
||||
end
|
||||
end
|
||||
">diff</span></span>
|
||||
|
||||
{% endif %}
|
||||
|
||||
<div id="clone-findings-modal"
|
||||
class="modal modal-blur fade"
|
||||
style="display: none"
|
||||
aria-hidden="false"
|
||||
tabindex="-1">
|
||||
class="modal modal-blur fade"
|
||||
style="display: none"
|
||||
aria-hidden="false"
|
||||
tabindex="-1">
|
||||
<div class="modal-dialog modal-dialog-centered related-finding-modal" style="max-width: 700px;">
|
||||
<div class="modal-content"></div>
|
||||
</div>
|
||||
@@ -284,6 +300,17 @@
|
||||
cornerstone.resize(dicom_element, true);
|
||||
|
||||
});
|
||||
$(".view-finding-button-3d").each((n, el) => {
|
||||
$(el).click((e) => {
|
||||
dicom_element = $(".cornerstone-element").get(0);
|
||||
annotationjson3d = JSON.parse(e.currentTarget.dataset.annotationjson3d);
|
||||
viewer_state_json = JSON.parse(e.currentTarget.dataset.viewerstatejson);
|
||||
console.log("Loading 3D annotation and viewer state", annotationjson3d, viewer_state_json);
|
||||
importAnnotations_root(annotationjson3d);
|
||||
importViewerState_root(viewer_state_json);
|
||||
$(el).addClass("active");
|
||||
});
|
||||
});
|
||||
|
||||
$(".view-finding-button").each((n, el) => {
|
||||
$(el).click((e) => {
|
||||
@@ -293,6 +320,11 @@
|
||||
current_image_id_index = e.currentTarget.dataset.currentimageid;
|
||||
console.log(e.currentTarget, current_image_id_index)
|
||||
loadAnnotationAndViewportOnElement(annotationjson, viewport, dicom_element, current_image_id_index);
|
||||
|
||||
importLegacyViewport_root(viewport)
|
||||
importLegacyAnnotations_root(annotationjson);
|
||||
jumpToSliceByImageId_root(0, current_image_id_index);
|
||||
console.log("Loaded annotation and viewport on element", dicom_element, annotationjson, viewport, current_image_id_index);
|
||||
$(el).addClass("active");
|
||||
|
||||
|
||||
@@ -368,6 +400,8 @@
|
||||
annotation_json: JSON.stringify(cornerstoneTools.globalImageIdSpecificToolStateManager
|
||||
.saveToolState()),
|
||||
viewport_json: JSON.stringify(c.viewport),
|
||||
annotation_json_3d: window.exportAnnotations_root(),
|
||||
viewer_state_3d: window.exportViewerState_root(),
|
||||
findings: JSON.stringify($('#finding-form select[name="findings"]').find(":selected")
|
||||
.map((i, el) => {
|
||||
return $(el).val()
|
||||
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
{% load static %}
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite + React + TS</title>
|
||||
</head>
|
||||
<body>
|
||||
{% with image_url_array_and_count=series.get_image_url_array_and_count %}
|
||||
{% comment %} <div id="single-dicom-viewer" class="dicom-viewer" data-images="{{ image_url_array_and_count.0 }}" data-annotations=''>
|
||||
</div> {% endcomment %}
|
||||
|
||||
<div id="root" class="dicom-viewer-root" data-images="{{ image_url_array_and_count.0 }}"
|
||||
style="width: 1000px; height: 600px; box-sizing: border-box; background: #222;"
|
||||
></div>
|
||||
|
||||
{% endwith %}
|
||||
<script type="module" src="{% static 'dv3d/index-DCNn6AQH.js' %}" defer="defer" type="module"></script>
|
||||
|
||||
<input type="file" id="dicomInput" multiple />
|
||||
<button onclick="sendToViewer()">Send to Viewer</button>
|
||||
<button onclick="testLoadWadouriStack()">Test Load wadouri Stack</button>
|
||||
<script>
|
||||
function sendToViewer() {
|
||||
const files = document.getElementById('dicomInput').files;
|
||||
if (window.loadDicomStackFromFiles) {
|
||||
window.loadDicomStackFromFiles(files);
|
||||
} else {
|
||||
alert("Viewer API not ready");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function testLoadWadouriStack() {
|
||||
const urls = [
|
||||
"https://www.penracourses.org.uk/media/atlas/dicom/IMG_757_XQQyovo.dcm",
|
||||
"https://www.penracourses.org.uk/media/atlas/dicom/IMG_766_8fWI0Qc.dcm",
|
||||
"https://www.penracourses.org.uk/media/atlas/dicom/IMG_774_c8IpxUu.dcm",
|
||||
"https://www.penracourses.org.uk/media/atlas/dicom/IMG_758_6WKI7ws.dcm",
|
||||
"https://www.penracourses.org.uk/media/atlas/dicom/IMG_767_TduVOb8.dcm",
|
||||
"https://www.penracourses.org.uk/media/atlas/dicom/IMG_775_7zWneQ0.dcm",
|
||||
"https://www.penracourses.org.uk/media/atlas/dicom/IMG_759_BtKzgS5.dcm",
|
||||
"https://www.penracourses.org.uk/media/atlas/dicom/IMG_768_RdvtM8K.dcm",
|
||||
"https://www.penracourses.org.uk/media/atlas/dicom/IMG_776_LhVY3Ty.dcm",
|
||||
"https://www.penracourses.org.uk/media/atlas/dicom/IMG_760_a8CiR4J.dcm",
|
||||
"https://www.penracourses.org.uk/media/atlas/dicom/IMG_769_YHyJ1l6.dcm",
|
||||
"https://www.penracourses.org.uk/media/atlas/dicom/IMG_777_8IFYJmL.dcm",
|
||||
"https://www.penracourses.org.uk/media/atlas/dicom/IMG_761_vyvO1EN.dcm",
|
||||
"https://www.penracourses.org.uk/media/atlas/dicom/IMG_770_vQwi4LW.dcm",
|
||||
"https://www.penracourses.org.uk/media/atlas/dicom/IMG_778_BwGAMOR.dcm",
|
||||
"https://www.penracourses.org.uk/media/atlas/dicom/IMG_762_DhSgQsm.dcm",
|
||||
"https://www.penracourses.org.uk/media/atlas/dicom/IMG_771_xifitCl.dcm",
|
||||
"https://www.penracourses.org.uk/media/atlas/dicom/IMG_779_LyA1zYn.dcm",
|
||||
"https://www.penracourses.org.uk/media/atlas/dicom/IMG_763_aVjff3H.dcm",
|
||||
"https://www.penracourses.org.uk/media/atlas/dicom/IMG_772_cDQZtxQ.dcm",
|
||||
"https://www.penracourses.org.uk/media/atlas/dicom/IMG_780_qaZqsqg.dcm",
|
||||
"https://www.penracourses.org.uk/media/atlas/dicom/IMG_764_dDoeldL.dcm",
|
||||
"https://www.penracourses.org.uk/media/atlas/dicom/IMG_773_93Hvedg.dcm",
|
||||
"https://www.penracourses.org.uk/media/atlas/dicom/IMG_781_bEx9Owg.dcm",
|
||||
"https://www.penracourses.org.uk/media/atlas/dicom/IMG_765_qmd5mYJ.dcm"
|
||||
];
|
||||
const wadouriList = urls.map(url => "wadouri:" + url);
|
||||
if (window.loadDicomStackFromWadouriList) {
|
||||
window.loadDicomStackFromWadouriList(wadouriList);
|
||||
} else {
|
||||
alert("Viewer API not ready");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,47 @@
|
||||
{% extends 'atlas/base.html' %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h2>SeriesFinding Migration Status</h2>
|
||||
<table class="table table-bordered table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Description</th>
|
||||
<th>Series</th>
|
||||
<th>annotation_json_3d</th>
|
||||
<th>viewer_state_3d</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for f in findings_status %}
|
||||
<tr>
|
||||
<td>{{ f.id }}</td>
|
||||
<td>{{ f.str }}</td>
|
||||
<td>
|
||||
{% if f.series %}
|
||||
<a href="{% url 'atlas:series_detail' f.series.pk %}">{{ f.series }}</a>
|
||||
{% else %}
|
||||
<span class="text-muted">None</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if f.annotation_json_3d %}
|
||||
<span class="badge bg-success">Populated</span>
|
||||
{% else %}
|
||||
<span class="badge bg-danger">Empty</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if f.viewer_state_3d %}
|
||||
<span class="badge bg-success">Populated</span>
|
||||
{% else %}
|
||||
<span class="badge bg-danger">Empty</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{% endblock content %}
|
||||
+3
-1
@@ -266,6 +266,8 @@ urlpatterns = [
|
||||
),
|
||||
path('series/<int:series_pk>/add-to-case/', views.add_series_to_case, name='add_series_to_case'),
|
||||
path('series/<int:series_pk>/<int:case_pk>/remove-from-case/', views.remove_series_from_case, name='remove_series_from_case'),
|
||||
path('series_finding/<int:finding_id>/details/', views.series_finding_details, name='series_finding_details'),
|
||||
path('series_finding/migration-status/', views.seriesfinding_migration_status, name='seriesfinding_migration_status'),
|
||||
path("uncategorised_dicoms/", views.uncategorised_dicoms, name="uncategorised_dicoms_view"),
|
||||
path("question_schema/", views.QuestionSchemaView.as_view(), name="question_schema_overview"),
|
||||
path("question_schema/<int:pk>", views.question_schema_detail, name="question_schema_detail"),
|
||||
@@ -297,7 +299,6 @@ urlpatterns = [
|
||||
),
|
||||
path("condition/create", views.ConditionCreate.as_view(), name="condition_create"),
|
||||
path("finding/", views.FindingView.as_view(), name="finding_view"),
|
||||
path('series_finding/<int:finding_id>/details/', views.series_finding_details, name='series_finding_details'),
|
||||
path("finding/<int:pk>", views.finding_detail, name="finding_detail"),
|
||||
path(
|
||||
"finding/<int:pk>/delete", views.FindingDelete.as_view(), name="finding_delete"
|
||||
@@ -323,6 +324,7 @@ urlpatterns = [
|
||||
path("series/<int:pk>/thumbnail", views.series_thumbnail, name="series_thumbnail"),
|
||||
# TODO: case context series viewing (so that we can view series in the context of a case)
|
||||
path("series/<int:pk>", views.series_detail, name="series_detail"),
|
||||
path("series/<int:pk>/viewer", views.series_viewer, name="series_viewer"),
|
||||
path("series/<int:pk>/authors", views.SeriesAuthorUpdate.as_view(), name="series_authors"),
|
||||
path("series/<int:series_id>/finding/related", views.series_finding_related, name="series_finding_related"),
|
||||
path("series/", views.SeriesView.as_view(), name="series_view"),
|
||||
|
||||
+37
-1
@@ -231,6 +231,19 @@ def series_thumbnail(request, pk, fail_loudly=False):
|
||||
|
||||
return HttpResponse(thumbnail)
|
||||
|
||||
@login_required
|
||||
@user_is_author_or_atlas_series_checker_or_atlas_marker_or_open_access
|
||||
def series_viewer(request, pk, finding_pk=None):
|
||||
series = get_object_or_404(Series, pk=pk)
|
||||
context = {
|
||||
"series": series,
|
||||
}
|
||||
|
||||
return render(
|
||||
request,
|
||||
"atlas/series_viewer_test.html",
|
||||
context,
|
||||
)
|
||||
|
||||
@login_required
|
||||
@user_is_author_or_atlas_series_checker_or_atlas_marker_or_open_access
|
||||
@@ -1440,6 +1453,8 @@ def create_series_findings(request):
|
||||
annotation_json = request.POST.get("annotation_json")
|
||||
viewport_json = request.POST.get("viewport_json")
|
||||
current_image_id_index = request.POST.get("current_image_id_index")
|
||||
annotation_json_3d = request.POST.get("annotation_json_3d")
|
||||
viewer_state_3d = request.POST.get("viewer_state_3d")
|
||||
|
||||
series = Series.objects.get(pk=series_id)
|
||||
findings = Finding.objects.filter(pk__in=findings_ids)
|
||||
@@ -1464,6 +1479,8 @@ def create_series_findings(request):
|
||||
sf.structures.set(structures)
|
||||
sf.conditions.set(conditions)
|
||||
sf.current_image_id_index = current_image_id_index
|
||||
sf.annotation_json_3d = annotation_json_3d
|
||||
sf.viewer_state_3d = viewer_state_3d
|
||||
sf.save()
|
||||
return JsonResponse({"success": True})
|
||||
|
||||
@@ -3145,4 +3162,23 @@ def series_finding_details(request, finding_id):
|
||||
def series_finding_related(request, series_id):
|
||||
series = get_object_or_404(Series, pk=series_id)
|
||||
findings = series.get_related_findings()
|
||||
return render(request, 'atlas/series_finding_related.html', {'series': series, 'findings': findings})
|
||||
return render(request, 'atlas/series_finding_related.html', {'series': series, 'findings': findings})
|
||||
|
||||
def seriesfinding_migration_status(request):
|
||||
"""
|
||||
View to list all SeriesFinding objects and their migration status.
|
||||
Shows whether annotation_json_3d and viewer_state_3d are populated.
|
||||
"""
|
||||
findings = SeriesFinding.objects.all().select_related()
|
||||
findings_status = []
|
||||
for finding in findings:
|
||||
findings_status.append({
|
||||
"id": finding.id,
|
||||
"str": str(finding),
|
||||
"annotation_json_3d": bool(finding.annotation_json_3d),
|
||||
"viewer_state_3d": bool(finding.viewer_state_3d),
|
||||
"series": finding.series
|
||||
})
|
||||
return render(request, "atlas/seriesfinding_migration_status.html", {
|
||||
"findings_status": findings_status
|
||||
})
|
||||
Reference in New Issue
Block a user