start integrating cimar

This commit is contained in:
Ross
2025-02-24 11:53:36 +00:00
parent 249bc656c8
commit a0960c9b7c
21 changed files with 826 additions and 8 deletions
+17
View File
@@ -67,6 +67,9 @@ from atlas.helpers import get_cases_available_to_user
from typing import Any
from helpers.cimar import CimarAPI, NotFoundError
from rad.settings import CIMAR_USERNAME, CIMAR_PASSWORD
class CaseSelect(Select):
template_name = "atlas/case_select_widget.html"
@@ -363,6 +366,7 @@ class CaseForm(ModelForm):
"open_access",
"previous_case",
"diagnostic_certainty",
"cimar_uuid"
),
)
@@ -396,6 +400,7 @@ class CaseForm(ModelForm):
"open_access",
"previous_case",
"diagnostic_certainty",
"cimar_uuid"
]
# fields = ['question', 'findings', 'subspecialty', 'references']
widgets = {
@@ -417,6 +422,18 @@ class CaseForm(ModelForm):
"pathological_process": CheckboxSelectMultiple(),
"previous_case": CaseSelect(),
}
def clean_cimar_uuid(self):
cimar_uuid = self.cleaned_data["cimar_uuid"]
if cimar_uuid:
with CimarAPI(username=CIMAR_USERNAME, password=CIMAR_PASSWORD) as api:
try:
study = api.get_study_by_uuid(cimar_uuid)
except NotFoundError:
raise ValidationError("Study not found in CIMAR")
return cimar_uuid
def save(self, commit=True):
# Get the unsaved Case instance
+18
View File
@@ -0,0 +1,18 @@
# Generated by Django 5.1.4 on 2025-02-10 13:07
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('atlas', '0066_case_total_images_series_size'),
]
operations = [
migrations.AddField(
model_name='case',
name='cimar_uuid',
field=models.CharField(blank=True, max_length=255, null=True),
),
]
+14
View File
@@ -46,6 +46,7 @@ from generic.models import (
CidUser,
CidUserExam,
CidUserGroup,
CimarCase,
ExamOrCollectionGenericBase,
ExamUserStatus,
Examination,
@@ -392,6 +393,8 @@ class Case(models.Model, AuthorMixin, QuestionMixin):
total_images_series_size = models.BigIntegerField(null=True, blank=True)
cimar_uuid = models.CharField(max_length=255, null=True, blank=True)
def get_app_name(self):
return "atlas"
@@ -404,6 +407,14 @@ class Case(models.Model, AuthorMixin, QuestionMixin):
# return f"{self.pk}: {self.title}"
return format_html("<a href='{}'>{}</a>", self.get_absolute_url(), str(self))
def get_cimar_case(self):
return get_object_or_404(CimarCase, uuid=self.cimar_uuid)
def get_cimar_case_details(self):
cimar_case = self.get_cimar_case()
return cimar_case.study_details
# def get_base_template(self):
# def get_edit_template_links(self):
@@ -427,6 +438,7 @@ class Case(models.Model, AuthorMixin, QuestionMixin):
def get_case_dicom_json(self, case_title_as_patient_name=True, priors=None):
series_json = []
series: Series
for series in self.series.all():
series_json.append(series.get_series_dicom_json())
@@ -506,6 +518,8 @@ class Case(models.Model, AuthorMixin, QuestionMixin):
return size
+59 -1
View File
@@ -19,7 +19,65 @@
</div>
</div>
{% endif %}
<div><a href="/ohif/viewer/dicomjson?url=https://www.penracourses.org.uk{% url 'atlas:case_dicom_json' case.pk %}">View case in OHIF</a> <a target="_blank" href="/ohif/viewer/dicomjson?url=https://www.penracourses.org.uk{% url 'atlas:case_dicom_json' case.pk %}">(new tab)</a>
<div>
{% if case.cimar_uuid %}
<details><summary>Details</summary>
<br/>
Case uuid: {{case.cimar_uuid}}
<br/>
{{case.get_cimar_case.viewer_link}}?{{cimar_sid}}
<button
hx-get='{% url "cimar_study_viewer_embed" case.cimar_uuid %}'
hx-target="#modals-here"
hx-trigger="click"
data-bs-toggle="modal"
data-bs-target="#modals-here"
class="btn primary">Open Modal</button>
<div id="modals-here"
class="modal modal-blur fade"
style="display: none"
aria-hidden="false"
tabindex="-1">
<div class="modal-dialog modal-lg modal-dialog-centered" role="document">
<div class="modal-content"></div>
</div>
</div>
<a href='{% url "cimar_study_viewer" case.cimar_uuid %}'>Viewer</a>
<details>
<summary>Study details</summary>
{{ case.get_cimar_case.get_pretty_study_details }}
</details>
<details>
<summary>Study schema</summary>
{{ case.get_cimar_case.get_pretty_study_schema }}
</details>
<details>
<summary>Study series</summary>
<button
id='load-series'
hx-get='{% url "cimar_study_series_block" case.cimar_uuid %}'
hx-trigger='load'
hx-swap='outerHTML'>
Load Series
</button>
</details>
<a href='{% url "generic:cimar_case_refresh" case.cimar_uuid %}'>Refresh</a>
</details>
{% else %}
<a href="/ohif/viewer/dicomjson?url=https://www.penracourses.org.uk{% url 'atlas:case_dicom_json' case.pk %}">View case in OHIF</a> <a target="_blank" href="/ohif/viewer/dicomjson?url=https://www.penracourses.org.uk{% url 'atlas:case_dicom_json' case.pk %}">(new tab)</a>
{% endif %}
<div class="date">
{{ case.created_date|date:"d/m/Y" }}
+1
View File
@@ -378,6 +378,7 @@ urlpatterns = [
# path("all_questions/", views.all_questions, name="all_questions"),
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/<int:case_id>/push_to_cimar", views.push_case_to_cimar, name="push_case_to_cimar"),
path("case/create/", views.AtlasCreate.as_view(), name="case_create"),
path(
"case/collection/<int:pk>/create",
+37 -2
View File
@@ -22,6 +22,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMix
from django.views.generic import View
from django.views.generic.detail import DetailView
import pydicom
from pydicom.uid import generate_uid
from generic.mixins import SuperuserRequiredMixin
from django.views.generic.edit import CreateView, UpdateView, DeleteView, FormView
@@ -33,8 +34,9 @@ from django.urls import reverse_lazy, reverse
from django.http import Http404, JsonResponse
from django.http import HttpResponseRedirect, HttpResponse
from generic.models import CidUser, CidUserExam
from generic.models import CidUser, CidUserExam, CimarCase
from atlas.helpers import get_cases_available_to_user
from rad.settings import REMOTE_URL, CIMAR_USERNAME, CIMAR_PASSWORD
from .forms import (
AddCollectionToCaseForm,
@@ -159,6 +161,8 @@ import reversion
import os
import difflib
from helpers.cimar import CimarAPI
logger = logging.getLogger(__name__)
@@ -195,7 +199,7 @@ def case_detail(request, pk):
# raise PermissionDenied
# logging.debug(atlas.subspecialty.first().name.all())
return render(request, "atlas/case_detail.html", {"case": case})
return render(request, "atlas/case_detail.html", {"case": case, "cimar_sid":request.user.userprofile.cimar_sid})
@login_required
@user_is_author_or_atlas_series_checker_or_atlas_marker_or_open_access
@@ -955,6 +959,9 @@ class AtlasCreateBase(RevisionMixin, LoginRequiredMixin):
form.instance.author.add(self.request.user.id)
# Check Cimar Case
#CimarAPI().get_study_by_uuid(self.object.cimar_uuid)
context = self.get_context_data(form=form)
series_formset = context["series_formset"]
casedifferential_formset = context["casedifferential_formset"]
@@ -2390,6 +2397,7 @@ def collection_case_view(request, pk, case_number):
"previous": previous,
"next": next,
"answer": answer,
"cimar_sid":request.user.userprofile.cimar_sid
},
)
@@ -2419,6 +2427,7 @@ def collection_case_dicom_json(request, exam_id, case_id):
return JsonResponse(case_detail.case.get_case_dicom_json(priors=priors))
def case_dicom_json(request, pk):
case = get_object_or_404(Case, pk=pk)
@@ -2971,3 +2980,29 @@ def collection_reset_answers(request, exam_id: int):
else:
raise Http404("Invalid request")
def push_case_to_cimar(request, case_id):
case = get_object_or_404(Case, pk=case_id)
api = CimarAPI()
api.login(username=CIMAR_USERNAME, password=CIMAR_PASSWORD)
# We use the same study_uid for all images in a case
study_uid = generate_uid()
for series in case.get_series():
for image in series.images.all():
data = api.upload_dicom(image.image.path, study_uid=study_uid)
cimar_uuid = api.get_study_by_study_uid(data["study_uid"])["uuid"]
case.cimar_uuid = cimar_uuid
case.save()
cimar_case, created = CimarCase.objects.get_or_create(uuid=cimar_uuid)
cimar_case.refresh_study()
return HttpResponse("Success")