further improvements
This commit is contained in:
+24
-5
@@ -32,6 +32,11 @@ class SeriesSchema(ModelSchema):
|
|||||||
def resolve_case_id(obj):
|
def resolve_case_id(obj):
|
||||||
return [i.id for i in obj.case.all()]
|
return [i.id for i in obj.case.all()]
|
||||||
|
|
||||||
|
class CaseSchema(ModelSchema):
|
||||||
|
class Config:
|
||||||
|
model = Case
|
||||||
|
model_fields = ["id", "title"]
|
||||||
|
|
||||||
|
|
||||||
@router.post("/upload_dicom", auth=django_auth)
|
@router.post("/upload_dicom", auth=django_auth)
|
||||||
def upload_dicom(request, files: List[UploadedFile] = File(...)):
|
def upload_dicom(request, files: List[UploadedFile] = File(...)):
|
||||||
@@ -73,9 +78,7 @@ def uncategorised_dicoms(request):
|
|||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
def import_dicoms_helper(request, case_id: int | None=None):
|
||||||
@router.get("/import_dicoms", auth=django_auth, response=List[SeriesSchema])
|
|
||||||
def import_dicoms(request):
|
|
||||||
dicoms = UncategorisedDicom.objects.filter(user=request.user)
|
dicoms = UncategorisedDicom.objects.filter(user=request.user)
|
||||||
|
|
||||||
data = defaultdict(list)
|
data = defaultdict(list)
|
||||||
@@ -104,16 +107,30 @@ def import_dicoms(request):
|
|||||||
# We might only want to add the author during creation....
|
# We might only want to add the author during creation....
|
||||||
series.author.add(request.user)
|
series.author.add(request.user)
|
||||||
|
|
||||||
|
if case_id is not None:
|
||||||
|
series.case.add(case_id)
|
||||||
|
|
||||||
for dicom, dicom_tags in data[series_uid]:
|
for dicom, dicom_tags in data[series_uid]:
|
||||||
series_image = SeriesImage(image=dicom.image, series=series)
|
series_image = SeriesImage(image=dicom.image, series=series)
|
||||||
series_image.save()
|
series_image.save()
|
||||||
dicom.image.delete()
|
#dicom.image.delete()
|
||||||
|
|
||||||
dicom.delete()
|
dicom.delete()
|
||||||
series_list.append(series)
|
series_list.append(series)
|
||||||
|
|
||||||
return series_list
|
return series_list
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/import_dicoms", auth=django_auth, response=List[SeriesSchema])
|
||||||
|
def import_dicoms(request):
|
||||||
|
|
||||||
|
return import_dicoms_helper(request)
|
||||||
|
|
||||||
|
@router.get("/import_dicoms/{case_id}", auth=django_auth, response=List[SeriesSchema])
|
||||||
|
def import_dicoms_case(request, case_id: int):
|
||||||
|
|
||||||
|
return import_dicoms_helper(request, case_id=case_id)
|
||||||
|
|
||||||
@router.get("/orphan_series", auth=django_auth, response=List[SeriesSchema])
|
@router.get("/orphan_series", auth=django_auth, response=List[SeriesSchema])
|
||||||
def orphan_series(request):
|
def orphan_series(request):
|
||||||
#print(request.user.series.filter(case=None))
|
#print(request.user.series.filter(case=None))
|
||||||
@@ -137,4 +154,6 @@ def series_remove_duplicate_images(request, series_id: int):
|
|||||||
|
|
||||||
return len(dupes)
|
return len(dupes)
|
||||||
|
|
||||||
|
@router.get("/get_cases_user", auth=django_auth, response=List[CaseSchema])
|
||||||
|
def get_cases_user(request):
|
||||||
|
return Case.objects.filter(author=request.user)
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# Generated by Django 4.1.4 on 2023-08-14 12:31
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('atlas', '0014_series_series_instance_uid_alter_series_examination_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='seriesimage',
|
||||||
|
name='series',
|
||||||
|
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='images', to='atlas.series'),
|
||||||
|
),
|
||||||
|
]
|
||||||
+2
-1
@@ -392,6 +392,7 @@ def extract_image_dicom_json_from_ds(ds, url, image_index):
|
|||||||
else:
|
else:
|
||||||
d[key] = val
|
d[key] = val
|
||||||
|
|
||||||
|
# Is it worth trying on fake dicom tags?.....
|
||||||
if d == {}:
|
if d == {}:
|
||||||
d["SOPInstanceUID"] = f"1.2.840.1111.{image_index}"
|
d["SOPInstanceUID"] = f"1.2.840.1111.{image_index}"
|
||||||
d["SeriesInstanceUID"] = f"1.2.840.1112.1"
|
d["SeriesInstanceUID"] = f"1.2.840.1112.1"
|
||||||
@@ -402,7 +403,7 @@ def extract_image_dicom_json_from_ds(ds, url, image_index):
|
|||||||
class SeriesImage(SeriesImageBase):
|
class SeriesImage(SeriesImageBase):
|
||||||
image = models.FileField(upload_to=image_directory_path)
|
image = models.FileField(upload_to=image_directory_path)
|
||||||
series = models.ForeignKey(
|
series = models.ForeignKey(
|
||||||
"Series", related_name="images", on_delete=models.SET_NULL, null=True
|
"Series", related_name="images", on_delete=models.CASCADE, null=True
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_dicom_data(self):
|
def get_dicom_data(self):
|
||||||
|
|||||||
@@ -1,36 +1,47 @@
|
|||||||
{% extends 'atlas/base.html' %}
|
{% extends 'atlas/base.html' %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h2>Uploaded dicoms</h2>
|
<h2>Uploaded dicoms</h2>
|
||||||
|
|
||||||
|
|
||||||
{% if dicoms %}
|
|
||||||
<ul>
|
|
||||||
{% for dicom in dicoms %}
|
|
||||||
<li>
|
|
||||||
{{dicom.image}} {{dicom.get_basic_dicom_tags}}
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
|
{% if series_list %}
|
||||||
<button
|
<ul>
|
||||||
hx-get="{% url 'api-1:clear_dicoms' %}"
|
{% for series, n, tags in series_list %}
|
||||||
hx-confirm="This will clear all uploads that have not been imported into series, Continue?"
|
<li>
|
||||||
>Delete Uploads</button>
|
{{series}} [{{n}} images] <br/>
|
||||||
<button
|
{{tags}}
|
||||||
hx-get="{% url 'api-1:import_dicoms' %}"
|
</li>
|
||||||
hx-target=".imported"
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
>Import</button>
|
{% if case %}
|
||||||
|
<p>Importing into case {{case}}</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<div class="imported">
|
<button
|
||||||
|
hx-get="{% url 'api-1:clear_dicoms' %}"
|
||||||
|
hx-confirm="This will clear all uploads that have not been imported into series, Continue?"
|
||||||
|
>Delete Uploads</button>
|
||||||
|
{% if case %}
|
||||||
|
<button
|
||||||
|
hx-get="{% url 'api-1:import_dicoms_case' case.id %}"
|
||||||
|
hx-target=".imported"
|
||||||
|
>Import</button>
|
||||||
|
|
||||||
</div>
|
{% else %}
|
||||||
|
<button
|
||||||
|
hx-get="{% url 'api-1:import_dicoms' %}"
|
||||||
|
hx-target=".imported"
|
||||||
|
>Import</button>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="imported">
|
||||||
|
|
||||||
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
No uploads awaiting importing have been found.
|
No uploads awaiting importing have been found.
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<p><a href="{% url 'atlas:series_view' %}?case=null">View orphan series</a></p>
|
<p><a href="{% url 'atlas:series_view' %}?case=null">View orphan series</a></p>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ urlpatterns = [
|
|||||||
path("collection/", views.CollectionView.as_view(), name="collection_view"),
|
path("collection/", views.CollectionView.as_view(), name="collection_view"),
|
||||||
path("collection/user", views.user_collections, name="user_collections"),
|
path("collection/user", views.user_collections, name="user_collections"),
|
||||||
path("uploads", views.user_uploads, name="user_uploads"),
|
path("uploads", views.user_uploads, name="user_uploads"),
|
||||||
|
path("uploads/case/<int:case_id>", views.user_uploads, name="user_uploads"),
|
||||||
path(
|
path(
|
||||||
"collection/create",
|
"collection/create",
|
||||||
views.CaseCollectionCreate.as_view(),
|
views.CaseCollectionCreate.as_view(),
|
||||||
|
|||||||
+38
-18
@@ -291,18 +291,39 @@ def author_list(request):
|
|||||||
|
|
||||||
return render(request, "atlas/author_list.html", {"authors": authors})
|
return render(request, "atlas/author_list.html", {"authors": authors})
|
||||||
|
|
||||||
|
|
||||||
def index(request):
|
def index(request):
|
||||||
return render(request, "atlas/index.html", {})
|
return render(request, "atlas/index.html", {})
|
||||||
|
|
||||||
|
|
||||||
def user_collections(request):
|
def user_collections(request):
|
||||||
collections = request.user.user_casecollection_exams.all()
|
collections = request.user.user_casecollection_exams.all()
|
||||||
|
|
||||||
return render(request, "atlas/user_collections.html", {"collections": collections})
|
return render(request, "atlas/user_collections.html", {"collections": collections})
|
||||||
|
|
||||||
def user_uploads(request):
|
|
||||||
|
def user_uploads(request, case_id: int | None = None):
|
||||||
dicoms = UncategorisedDicom.objects.filter(user=request.user)
|
dicoms = UncategorisedDicom.objects.filter(user=request.user)
|
||||||
|
|
||||||
return render(request, "atlas/user_uploads.html", {"dicoms": dicoms})
|
data = defaultdict(list)
|
||||||
|
for d in dicoms:
|
||||||
|
tags = d.get_basic_dicom_tags()
|
||||||
|
|
||||||
|
data[tags["SeriesInstanceUID"]].append(tags)
|
||||||
|
|
||||||
|
series_list = []
|
||||||
|
for series in data:
|
||||||
|
series_list.append((series, len(data[series]), data[series][0]))
|
||||||
|
print(series_list)
|
||||||
|
|
||||||
|
case = False
|
||||||
|
if case_id is not None:
|
||||||
|
case = get_object_or_404(Case, pk=case_id)
|
||||||
|
|
||||||
|
return render(
|
||||||
|
request, "atlas/user_uploads.html", {"series_list": series_list, "case": case}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class CaseDelete(RevisionMixin, AuthorOrCheckerRequiredMixin, DeleteView):
|
class CaseDelete(RevisionMixin, AuthorOrCheckerRequiredMixin, DeleteView):
|
||||||
model = Case
|
model = Case
|
||||||
@@ -1319,7 +1340,6 @@ def collection_take_overview(
|
|||||||
return HttpResponse("True")
|
return HttpResponse("True")
|
||||||
else:
|
else:
|
||||||
raise Http404()
|
raise Http404()
|
||||||
|
|
||||||
|
|
||||||
cases = collection.cases.all().order_by("casedetail__sort_order").prefetch_related()
|
cases = collection.cases.all().order_by("casedetail__sort_order").prefetch_related()
|
||||||
|
|
||||||
@@ -1361,7 +1381,6 @@ def collection_take_overview(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def collection_case_view_take_user(request, pk: int, case_number: int):
|
def collection_case_view_take_user(request, pk: int, case_number: int):
|
||||||
return collection_case_view_take(request, pk, case_number)
|
return collection_case_view_take(request, pk, case_number)
|
||||||
|
|
||||||
@@ -1402,9 +1421,7 @@ def collection_case_view_take(
|
|||||||
cid_user_exam.start_time = timezone.now()
|
cid_user_exam.start_time = timezone.now()
|
||||||
cid_user_exam.save()
|
cid_user_exam.save()
|
||||||
|
|
||||||
|
|
||||||
if not collection.review_only():
|
if not collection.review_only():
|
||||||
|
|
||||||
if collection.collection_type == "REP":
|
if collection.collection_type == "REP":
|
||||||
case_detail = CaseDetail.objects.get(case=case, collection=collection)
|
case_detail = CaseDetail.objects.get(case=case, collection=collection)
|
||||||
|
|
||||||
@@ -1419,7 +1436,6 @@ def collection_case_view_take(
|
|||||||
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
if collection.collection_type == "REP":
|
if collection.collection_type == "REP":
|
||||||
|
|
||||||
if not collection.publish_results:
|
if not collection.publish_results:
|
||||||
if answer:
|
if answer:
|
||||||
form = ReportAnswerForm(request.POST, instance=answer)
|
form = ReportAnswerForm(request.POST, instance=answer)
|
||||||
@@ -1534,7 +1550,6 @@ def collection_case_view(request, pk, case_number):
|
|||||||
form = None
|
form = None
|
||||||
answer = None
|
answer = None
|
||||||
|
|
||||||
|
|
||||||
case, case_count = collection.get_case_by_index(case_number, case_count=True)
|
case, case_count = collection.get_case_by_index(case_number, case_count=True)
|
||||||
|
|
||||||
series_list = case.series.all().prefetch_related("images", "examination", "plane")
|
series_list = case.series.all().prefetch_related("images", "examination", "plane")
|
||||||
@@ -1558,11 +1573,13 @@ def collection_case_view(request, pk, case_number):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def case_dicom_json(request, pk):
|
def case_dicom_json(request, pk):
|
||||||
case = get_object_or_404(Case, pk=pk)
|
case = get_object_or_404(Case, pk=pk)
|
||||||
|
|
||||||
return JsonResponse(case.get_case_dicom_json())
|
return JsonResponse(case.get_case_dicom_json())
|
||||||
|
|
||||||
|
|
||||||
@user_is_collection_author_or_atlas_editor
|
@user_is_collection_author_or_atlas_editor
|
||||||
def delete_collection_cid_answers(request, exam_id, cid):
|
def delete_collection_cid_answers(request, exam_id, cid):
|
||||||
collection = get_object_or_404(CaseCollection, pk=exam_id)
|
collection = get_object_or_404(CaseCollection, pk=exam_id)
|
||||||
@@ -1755,6 +1772,7 @@ GenericExamViews = ExamViews(
|
|||||||
CaseCollection, Case, None, CidReportAnswer, "atlas", "casecollection"
|
CaseCollection, Case, None, CidReportAnswer, "atlas", "casecollection"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class CollectionCloneMixin:
|
class CollectionCloneMixin:
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
old_object = get_object_or_404(self.model, pk=self.kwargs["exam_id"])
|
old_object = get_object_or_404(self.model, pk=self.kwargs["exam_id"])
|
||||||
@@ -1777,13 +1795,15 @@ class CollectionCloneMixin:
|
|||||||
object.save()
|
object.save()
|
||||||
return HttpResponseRedirect(object.get_absolute_url())
|
return HttpResponseRedirect(object.get_absolute_url())
|
||||||
|
|
||||||
|
|
||||||
class CollectionClone(CollectionCloneMixin, CaseCollectionCreate):
|
class CollectionClone(CollectionCloneMixin, CaseCollectionCreate):
|
||||||
"""Clone exam view"""
|
"""Clone exam view"""
|
||||||
|
|
||||||
|
|
||||||
class SelfReviewDelete(DeleteView):
|
class SelfReviewDelete(DeleteView):
|
||||||
model = SelfReview
|
model = SelfReview
|
||||||
template_name = "confirm_delete.html"
|
template_name = "confirm_delete.html"
|
||||||
#success_url = reverse_lazy("atlas:finding_view")
|
# success_url = reverse_lazy("atlas:finding_view")
|
||||||
|
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
user_exam_id = self.kwargs["user_exam_id"]
|
user_exam_id = self.kwargs["user_exam_id"]
|
||||||
@@ -1806,9 +1826,8 @@ class SelfReviewDelete(DeleteView):
|
|||||||
def get_success_url(self) -> str:
|
def get_success_url(self) -> str:
|
||||||
return self.user_exam.exam.get_case_take_url(self.case)
|
return self.user_exam.exam.get_case_take_url(self.case)
|
||||||
|
|
||||||
class SelfReviewUpdate(
|
|
||||||
UpdateView
|
class SelfReviewUpdate(UpdateView):
|
||||||
):
|
|
||||||
model = SelfReview
|
model = SelfReview
|
||||||
form_class = SelfReviewForm
|
form_class = SelfReviewForm
|
||||||
|
|
||||||
@@ -1833,10 +1852,10 @@ class SelfReviewUpdate(
|
|||||||
def get_success_url(self) -> str:
|
def get_success_url(self) -> str:
|
||||||
return self.user_exam.exam.get_case_take_url(self.case)
|
return self.user_exam.exam.get_case_take_url(self.case)
|
||||||
|
|
||||||
#def get_object(self):
|
# def get_object(self):
|
||||||
# return self.review
|
# return self.review
|
||||||
# return
|
# return
|
||||||
#def get_initial(self):
|
# def get_initial(self):
|
||||||
# user_exam_id = self.kwargs["user_exam_id"]
|
# user_exam_id = self.kwargs["user_exam_id"]
|
||||||
# case_id = self.kwargs["case_id"]
|
# case_id = self.kwargs["case_id"]
|
||||||
|
|
||||||
@@ -1844,6 +1863,7 @@ class SelfReviewUpdate(
|
|||||||
# self.case = get_object_or_404(Case, pk=case_id)
|
# self.case = get_object_or_404(Case, pk=case_id)
|
||||||
# return {"user_exam": user_exam_id, "case": case_id}
|
# return {"user_exam": user_exam_id, "case": case_id}
|
||||||
|
|
||||||
|
|
||||||
class AddSelfReview(CreateView):
|
class AddSelfReview(CreateView):
|
||||||
model = SelfReview
|
model = SelfReview
|
||||||
form_class = SelfReviewForm
|
form_class = SelfReviewForm
|
||||||
@@ -1854,7 +1874,7 @@ class AddSelfReview(CreateView):
|
|||||||
kwargs = super(AddSelfReview, self).get_form_kwargs()
|
kwargs = super(AddSelfReview, self).get_form_kwargs()
|
||||||
# update the kwargs for the form init method with yours
|
# update the kwargs for the form init method with yours
|
||||||
kwargs.update(self.kwargs) # self.kwargs contains all url conf params
|
kwargs.update(self.kwargs) # self.kwargs contains all url conf params
|
||||||
#kwargs.update({"user": self.request.user})
|
# kwargs.update({"user": self.request.user})
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
@@ -1874,7 +1894,8 @@ class AddSelfReview(CreateView):
|
|||||||
|
|
||||||
def get_success_url(self) -> str:
|
def get_success_url(self) -> str:
|
||||||
return self.user_exam.exam.get_case_take_url(self.case)
|
return self.user_exam.exam.get_case_take_url(self.case)
|
||||||
#return reverse("atlas:collection_take_overview_user", kwargs={"pk": self.user_exam.collection.id })
|
# return reverse("atlas:collection_take_overview_user", kwargs={"pk": self.user_exam.collection.id })
|
||||||
|
|
||||||
|
|
||||||
# def form_valid(self, form):
|
# def form_valid(self, form):
|
||||||
# model = form.save(commit=False)
|
# model = form.save(commit=False)
|
||||||
@@ -1892,7 +1913,6 @@ def uncategorised_dicoms(request):
|
|||||||
|
|
||||||
data = []
|
data = []
|
||||||
for d in dicoms:
|
for d in dicoms:
|
||||||
|
|
||||||
data.append((d.image.name, d.get_basic_dicom_tags()))
|
data.append((d.image.name, d.get_basic_dicom_tags()))
|
||||||
|
|
||||||
return JsonResponse(data, safe=False)
|
return JsonResponse(data, safe=False)
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# Generated by Django 4.1.4 on 2023-08-14 12:31
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('longs', '0007_longseriesimage_image_md5_hash_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='longseriesimage',
|
||||||
|
name='series',
|
||||||
|
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='images', to='longs.longseries'),
|
||||||
|
),
|
||||||
|
]
|
||||||
+1
-1
@@ -354,7 +354,7 @@ def test_image_validator(file):
|
|||||||
class LongSeriesImage(SeriesImageBase):
|
class LongSeriesImage(SeriesImageBase):
|
||||||
image = models.FileField(upload_to=image_directory_path)
|
image = models.FileField(upload_to=image_directory_path)
|
||||||
series = models.ForeignKey(
|
series = models.ForeignKey(
|
||||||
"LongSeries", related_name="images", on_delete=models.SET_NULL, null=True
|
"LongSeries", related_name="images", on_delete=models.CASCADE, null=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user