diff --git a/atlas/api.py b/atlas/api.py index 7733bfc4..0c617671 100644 --- a/atlas/api.py +++ b/atlas/api.py @@ -32,6 +32,11 @@ class SeriesSchema(ModelSchema): def resolve_case_id(obj): 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) def upload_dicom(request, files: List[UploadedFile] = File(...)): @@ -73,9 +78,7 @@ def uncategorised_dicoms(request): return data - -@router.get("/import_dicoms", auth=django_auth, response=List[SeriesSchema]) -def import_dicoms(request): +def import_dicoms_helper(request, case_id: int | None=None): dicoms = UncategorisedDicom.objects.filter(user=request.user) data = defaultdict(list) @@ -104,16 +107,30 @@ def import_dicoms(request): # We might only want to add the author during creation.... series.author.add(request.user) + if case_id is not None: + series.case.add(case_id) + for dicom, dicom_tags in data[series_uid]: series_image = SeriesImage(image=dicom.image, series=series) series_image.save() - dicom.image.delete() + #dicom.image.delete() dicom.delete() series_list.append(series) 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]) def orphan_series(request): #print(request.user.series.filter(case=None)) @@ -137,4 +154,6 @@ def series_remove_duplicate_images(request, series_id: int): 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) \ No newline at end of file diff --git a/atlas/migrations/0015_alter_seriesimage_series.py b/atlas/migrations/0015_alter_seriesimage_series.py new file mode 100644 index 00000000..c2e611cf --- /dev/null +++ b/atlas/migrations/0015_alter_seriesimage_series.py @@ -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'), + ), + ] diff --git a/atlas/models.py b/atlas/models.py index b513ae05..3b4f3457 100644 --- a/atlas/models.py +++ b/atlas/models.py @@ -392,6 +392,7 @@ def extract_image_dicom_json_from_ds(ds, url, image_index): else: d[key] = val + # Is it worth trying on fake dicom tags?..... if d == {}: d["SOPInstanceUID"] = f"1.2.840.1111.{image_index}" 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): image = models.FileField(upload_to=image_directory_path) 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): diff --git a/atlas/templates/atlas/user_uploads.html b/atlas/templates/atlas/user_uploads.html index ee30fdd3..c9678685 100644 --- a/atlas/templates/atlas/user_uploads.html +++ b/atlas/templates/atlas/user_uploads.html @@ -1,36 +1,47 @@ {% extends 'atlas/base.html' %} {% block content %} -

Uploaded dicoms

+

Uploaded dicoms

- -{% if dicoms %} - - - - + {% if case %} +

Importing into case {{case}}

+ {% endif %} -
+ + {% if case %} + -
+ {% else %} + + {% endif %} + +
+ +
{% else %} - No uploads awaiting importing have been found. -{% endif %} + No uploads awaiting importing have been found. + {% endif %} -

View orphan series

+

View orphan series

{% endblock %} diff --git a/atlas/urls.py b/atlas/urls.py index 90af8aaa..a71da05e 100755 --- a/atlas/urls.py +++ b/atlas/urls.py @@ -35,6 +35,7 @@ urlpatterns = [ path("collection/", views.CollectionView.as_view(), name="collection_view"), path("collection/user", views.user_collections, name="user_collections"), path("uploads", views.user_uploads, name="user_uploads"), + path("uploads/case/", views.user_uploads, name="user_uploads"), path( "collection/create", views.CaseCollectionCreate.as_view(), diff --git a/atlas/views.py b/atlas/views.py index b9c8a66e..c11b04c6 100755 --- a/atlas/views.py +++ b/atlas/views.py @@ -291,18 +291,39 @@ def author_list(request): return render(request, "atlas/author_list.html", {"authors": authors}) + def index(request): return render(request, "atlas/index.html", {}) + def user_collections(request): collections = request.user.user_casecollection_exams.all() 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) - 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): model = Case @@ -1319,7 +1340,6 @@ def collection_take_overview( return HttpResponse("True") else: raise Http404() - 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): 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.save() - if not collection.review_only(): - if collection.collection_type == "REP": case_detail = CaseDetail.objects.get(case=case, collection=collection) @@ -1419,7 +1436,6 @@ def collection_case_view_take( if request.method == "POST": if collection.collection_type == "REP": - if not collection.publish_results: if answer: form = ReportAnswerForm(request.POST, instance=answer) @@ -1534,7 +1550,6 @@ def collection_case_view(request, pk, case_number): form = None answer = None - case, case_count = collection.get_case_by_index(case_number, case_count=True) 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): case = get_object_or_404(Case, pk=pk) return JsonResponse(case.get_case_dicom_json()) + @user_is_collection_author_or_atlas_editor def delete_collection_cid_answers(request, exam_id, cid): collection = get_object_or_404(CaseCollection, pk=exam_id) @@ -1755,6 +1772,7 @@ GenericExamViews = ExamViews( CaseCollection, Case, None, CidReportAnswer, "atlas", "casecollection" ) + class CollectionCloneMixin: def get_initial(self): old_object = get_object_or_404(self.model, pk=self.kwargs["exam_id"]) @@ -1777,13 +1795,15 @@ class CollectionCloneMixin: object.save() return HttpResponseRedirect(object.get_absolute_url()) + class CollectionClone(CollectionCloneMixin, CaseCollectionCreate): """Clone exam view""" + class SelfReviewDelete(DeleteView): model = SelfReview template_name = "confirm_delete.html" - #success_url = reverse_lazy("atlas:finding_view") + # success_url = reverse_lazy("atlas:finding_view") def get_initial(self): user_exam_id = self.kwargs["user_exam_id"] @@ -1806,9 +1826,8 @@ class SelfReviewDelete(DeleteView): def get_success_url(self) -> str: return self.user_exam.exam.get_case_take_url(self.case) -class SelfReviewUpdate( - UpdateView -): + +class SelfReviewUpdate(UpdateView): model = SelfReview form_class = SelfReviewForm @@ -1833,10 +1852,10 @@ class SelfReviewUpdate( def get_success_url(self) -> str: return self.user_exam.exam.get_case_take_url(self.case) - #def get_object(self): + # def get_object(self): # return self.review # return - #def get_initial(self): + # def get_initial(self): # user_exam_id = self.kwargs["user_exam_id"] # case_id = self.kwargs["case_id"] @@ -1844,6 +1863,7 @@ class SelfReviewUpdate( # self.case = get_object_or_404(Case, pk=case_id) # return {"user_exam": user_exam_id, "case": case_id} + class AddSelfReview(CreateView): model = SelfReview form_class = SelfReviewForm @@ -1854,7 +1874,7 @@ class AddSelfReview(CreateView): kwargs = super(AddSelfReview, self).get_form_kwargs() # update the kwargs for the form init method with yours 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 def get_initial(self): @@ -1874,7 +1894,8 @@ class AddSelfReview(CreateView): def get_success_url(self) -> str: 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): # model = form.save(commit=False) @@ -1892,7 +1913,6 @@ def uncategorised_dicoms(request): data = [] for d in dicoms: - data.append((d.image.name, d.get_basic_dicom_tags())) - return JsonResponse(data, safe=False) \ No newline at end of file + return JsonResponse(data, safe=False) diff --git a/longs/migrations/0008_alter_longseriesimage_series.py b/longs/migrations/0008_alter_longseriesimage_series.py new file mode 100644 index 00000000..0877167b --- /dev/null +++ b/longs/migrations/0008_alter_longseriesimage_series.py @@ -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'), + ), + ] diff --git a/longs/models.py b/longs/models.py index 2d063d86..5df6dc5b 100644 --- a/longs/models.py +++ b/longs/models.py @@ -354,7 +354,7 @@ def test_image_validator(file): class LongSeriesImage(SeriesImageBase): image = models.FileField(upload_to=image_directory_path) series = models.ForeignKey( - "LongSeries", related_name="images", on_delete=models.SET_NULL, null=True + "LongSeries", related_name="images", on_delete=models.CASCADE, null=True )