import json import pprint from dal import autocomplete from django.shortcuts import render, get_object_or_404, redirect from django import forms from django.utils import timezone # from django.contrib.auth.models import User from django.contrib.auth.decorators import login_required, user_passes_test from django.contrib.auth.models import User from django.core.exceptions import ( FieldError, ObjectDoesNotExist, PermissionDenied, ViewDoesNotExist, ) from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin from django.views.generic import View from django.views.generic.detail import DetailView import pydicom from generic.mixins import SuperuserRequiredMixin from django.views.generic.edit import CreateView, UpdateView, DeleteView, FormView from django.views.generic import ListView from django.views.decorators.csrf import csrf_exempt 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 .forms import ( AddCollectionToCaseForm, CaseCollectionCaseFormSet, CaseCollectionForm, CaseForm, CidReportAnswerForm, CidReportAnswerMarkForm, ConditionAutocompleteForm, ConditionForm, FindingForm, SelfReviewForm, SeriesForm, SeriesImageFormSet, SeriesFormSet, SeriesFindingForm, CaseDifferentialFormSet, StructureForm, UserReportAnswerForm, ) from .models import ( Case, CaseCollection, CaseDetail, CidReportAnswer, Condition, Differential, PathologicalProcess, Presentation, SelfReview, Series, Examination, Finding, Structure, Subspecialty, SeriesFinding, SeriesImage, UncategorisedDicom, UserReportAnswer, ) from .tables import ( CaseCollectionTable, CaseTable, ConditionTable, FindingTable, PathologicalProcessTable, PresentationTable, SeriesTable, StructureTable, SubspecialtyTable, ) from .filters import ( CaseCollectionFilter, CaseFilter, ConditionFilter, FindingFilter, PathologicalProcessFilter, PresentationFilter, SeriesFilter, StructureFilter, SubspecialtyFilter, ) from django_tables2 import SingleTableView, SingleTableMixin from django_filters.views import FilterView from autocomplete import HTMXAutoComplete from .decorators import ( user_is_author_or_atlas_editor, user_has_case_view_access, user_is_author_or_atlas_series_checker_or_atlas_marker, user_is_atlas_editor, user_is_author_or_atlas_series_checker, user_is_atlas_marker, user_is_collection_author_or_atlas_editor, ) from collections import defaultdict import json import statistics import plotly.express as px from django.core.cache import cache from django.conf import settings from helpers.images import compare_dicom_datasets, image_as_base64, print_dicom import logging from copy import deepcopy from django.forms.models import model_to_dict from generic.views import ExamCloneMixin, ExamViews, SeriesImagesZipViewBase from reversion.views import RevisionMixin import reversion from rest_framework import viewsets import os import difflib logger = logging.getLogger(__name__) def get_cases_available_to_user(user): """Returns a queryset of cases available to the user""" return Case.objects.filter(author=user) | Case.objects.filter(open_access=True) if user.is_superuser: return Case.objects.all() else: return Case.objects.filter(author=user) | Case.objects.filter(open_access=True) class AuthorOrCheckerRequiredMixin(object): def get_object(self, *args, **kwargs): obj = super().get_object(*args, **kwargs) if ( self.request.user.groups.filter(name="atlas_editor").exists() or self.request.user.is_superuser ): return obj if self.request.user not in obj.get_author_objects(): raise PermissionDenied() # or Http404 return obj class AtlasEditorRequiredMixin(object): def get_object(self, *args, **kwargs): obj = super().get_object(*args, **kwargs) if ( self.request.user.groups.filter(name="atlas_editor").exists() or self.request.user.is_superuser ): return obj raise PermissionDenied("You must be an atlas editor to do this.") # or Http404 @login_required @user_has_case_view_access def case_detail(request, pk): case = get_object_or_404(Case, pk=pk) # if request.user not in atlas.author.all(): # raise PermissionDenied # logging.debug(atlas.subspecialty.first().name.all()) return render(request, "atlas/case_detail.html", {"case": case}) @login_required @user_is_author_or_atlas_series_checker_or_atlas_marker def series_detail(request, pk, finding_pk=None): series = get_object_or_404(Series, pk=pk) editing_finding = -1 if finding_pk is not None: finding = get_object_or_404(SeriesFinding, pk=finding_pk) series_finding_form = SeriesFindingForm(None, instance=finding) editing_finding = finding.pk else: series_finding_form = SeriesFindingForm(series_id=series.id) # logging.debug(atlas.subspecialty.first().name.all()) return render( request, "atlas/series.html", { "series": series, "series_finding_form": series_finding_form, "editing_finding": editing_finding, }, ) @login_required #@user_is_atlas_editor def condition_detail(request, pk): condition = get_object_or_404(Condition, pk=pk) form = ConditionAutocompleteForm() can_merge = False if request.user.is_superuser or request.user.groups.filter(name="atlas_editor").exists(): can_merge = True # logging.debug(atlas.subspecialty.first().name.all()) return render( request, "atlas/condition_detail.html", {"condition": condition, "form": form, "can_merge": can_merge}, ) # class GetConditionAutocomplete(HTMXAutoComplete): # name = "conditions" # multiselect = False # # class Meta: # model = Condition @login_required @user_is_atlas_editor def condition_merge(request, pk_to_merge): if not request.htmx: raise Http404 condition_to_merge = get_object_or_404(Condition, pk=pk_to_merge) # condition_to_merge_into = get_object_or_404(Condition, pk=pk_to_merge_into) print(condition_to_merge.case_set) if condition_to_merge.rcr_curriculum: return HttpResponse("You cannot merge rcr curriculum items") condition_to_merge_into = request.POST.get("conditions") if not condition_to_merge_into: return HttpResponse("You need to select a condition to merge into.") condition_to_merge_into = get_object_or_404(Condition, pk=condition_to_merge_into) if condition_to_merge == condition_to_merge_into: return HttpResponse("You cannot merge into the same condition.") # move all relevant relationships cases = condition_to_merge.case_set.all().values_list("id", flat=True) condition_to_merge_into.case_set.add(cases) # cases = Case.objects.filter(condition=condition_to_merge) # cases.update(condition=condition_to_merge_into) differentials = Differential.objects.filter(condition=condition_to_merge) differentials.update(condition=condition_to_merge_into) return HttpResponse("Merged") # conditions = Condition.objects.all() # return render(request, "atlas/condition_options.html", {"conditions": conditions}) @login_required #@user_is_atlas_editor def subspecialty_detail(request, pk): subspecialty = get_object_or_404(Subspecialty, pk=pk) # logging.debug(atlas.subspecialty.first().name.all()) return render( request, "atlas/subspecialty_detail.html", { "subspecialty": subspecialty, }, ) @login_required #@user_is_atlas_editor def presentation_detail(request, pk): presentation = get_object_or_404(Presentation, pk=pk) # logging.debug(atlas.subspecialty.first().name.all()) return render( request, "atlas/presentation_detail.html", { "presentation": presentation, }, ) @login_required #@user_is_atlas_editor def pathological_process_detail(request, pk): pathological_process = get_object_or_404(PathologicalProcess, pk=pk) # logging.debug(atlas.subspecialty.first().name.all()) return render( request, "atlas/pathological_process_detail.html", { "pathological_process": pathological_process, }, ) @login_required #@user_is_atlas_editor def structure_detail(request, pk): structure = get_object_or_404(Structure, pk=pk) # logging.debug(atlas.subspecialty.first().name.all()) return render( request, "atlas/structure_detail.html", { "structure": structure, }, ) @login_required #@user_is_atlas_editor def finding_detail(request, pk): finding = get_object_or_404(Finding, pk=pk) # logging.debug(atlas.subspecialty.first().name.all()) return render( request, "atlas/finding_detail.html", { "finding": finding, }, ) @login_required def author_detail(request, pk): # logging.debug(Author.objects.all()) # author = get_object_or_404(Author, pk=pk) author = User.objects.get(pk=pk) if request.user.pk == pk: atlas = Case.objects.filter(author=pk) else: atlas = Case.objects.filter(author=pk, open_access=True) return render( request, "atlas/category_detail.html", {"category": author, "atlas": atlas} ) @user_is_author_or_atlas_editor def author_list(request): authors = User.objects.all() 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 add_case_to_collection(request, collection_id): if not request.htmx: return Http404 collection = get_object_or_404(CaseCollection, pk=collection_id) if request.method == "POST": case = get_object_or_404(Case, pk=request.POST["case"]) print(case) collection.cases.add(case) return HttpResponse("Case added to collection") #if request.method == "POST": cases = get_cases_available_to_user(request.user) return render(request, "atlas/add_case_to_collection.html", {"cases": cases, "collection": collection}) @login_required def user_uploads_series(request, series_instance_uid: str): # We don't restrict to teh uploading user here. Should we? dicoms = UncategorisedDicom.objects.filter(series_instance_uid=series_instance_uid) if not dicoms: raise Http404("Series not found") return render( request, "atlas/user_upload_viewer.html", {"dicoms": dicoms, "series_id": series_instance_uid}, ) @login_required @user_passes_test(lambda u: u.is_superuser) def all_uploads(request, case_id: int | None = None): return user_uploads(request, case_id, user_only=False) @login_required def user_uploads(request, case_id: int | None = None, user_only: bool = True): if user_only: dicoms = UncategorisedDicom.objects.filter(user=request.user) else: dicoms = UncategorisedDicom.objects.filter() data = defaultdict(list) for d in dicoms: tags = d.basic_dicom_tags if tags is None: d.save() tags = d.basic_dicom_tags data[tags["SeriesInstanceUID"]].append((tags, d.created_date)) series_list = [] for series in data: tags, date = data[series][0] series_list.append((series, len(data[series]), tags, date)) print(series_list) series_list.sort(key=lambda l: l[-1]) 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 success_url = reverse_lazy("atlas:case_view") template_name = "confirm_delete.html" class SeriesFindingDelete(RevisionMixin, PermissionRequiredMixin, DeleteView): permission_required = "atlas.delete_seriesfinding" model = SeriesFinding template_name = "confirm_delete.html" # success_url = reverse_lazy("atlas:case_view") def get_success_url(self): pk = self.kwargs["pk"] series_id = get_object_or_404(SeriesFinding, pk=pk).series.id return reverse("atlas:series_detail", kwargs={"pk": series_id}) class SeriesDelete(RevisionMixin, AtlasEditorRequiredMixin, DeleteView): model = Series template_name = "confirm_delete.html" success_url = reverse_lazy("atlas:series_view") class CaseCollectionDelete(RevisionMixin, AtlasEditorRequiredMixin, DeleteView): model = CaseCollection template_name = "confirm_delete.html" success_url = reverse_lazy("atlas:collection_view") class ConditionDelete(RevisionMixin, AtlasEditorRequiredMixin, DeleteView): model = Condition template_name = "confirm_delete.html" success_url = reverse_lazy("atlas:condition_view") class FindingDelete(RevisionMixin, AtlasEditorRequiredMixin, DeleteView): model = Finding template_name = "confirm_delete.html" success_url = reverse_lazy("atlas:finding_view") class StructureDelete(RevisionMixin, AtlasEditorRequiredMixin, DeleteView): model = Structure template_name = "confirm_delete.html" success_url = reverse_lazy("atlas:structure_view") class CaseCollectionCreate(RevisionMixin, LoginRequiredMixin, CreateView): model = CaseCollection form_class = CaseCollectionForm def get_initial(self): # print(self.request) if "pk" in self.kwargs: initial = super(CaseCollectionCreate, self).get_initial() # case = get_object_or_404(Case, pk=self.kwargs["pk"]) # initial["case"] = case.id return initial def get_form_kwargs(self): kwargs = super(CaseCollectionCreate, self).get_form_kwargs() kwargs.update({"user": self.request.user}) return kwargs def get_context_data(self, **kwargs): context = super(CaseCollectionCreate, self).get_context_data(**kwargs) if self.request.POST: context["case_formset"] = CaseCollectionCaseFormSet( self.request.POST, self.request.FILES, form_kwargs={"user": self.request.user}, ) context["case_formset"].full_clean() else: context["case_formset"] = CaseCollectionCaseFormSet( form_kwargs={"user": self.request.user} ) return context def form_valid(self, form): self.object = form.save(commit=False) self.object.save() form.instance.author.add(self.request.user.id) context = self.get_context_data(form=form) case_formset = context["case_formset"] if case_formset.is_valid(): response = super().form_valid(form) case_formset.instance = self.object case_formset.save() return response else: return super().form_invalid(form) class SeriesCreate(RevisionMixin, LoginRequiredMixin, CreateView): model = Series form_class = SeriesForm def get_initial(self): # print(self.request) if "pk" in self.kwargs: initial = super().get_initial() case = get_object_or_404(Case, pk=self.kwargs["pk"]) initial["case"] = case.id return initial def get_form_kwargs(self): kwargs = super(SeriesCreate, self).get_form_kwargs() kwargs.update({"user": self.request.user}) return kwargs def get_context_data(self, **kwargs): context = super(SeriesCreate, self).get_context_data(**kwargs) if self.request.POST: context["image_formset"] = SeriesImageFormSet( self.request.POST, self.request.FILES ) context["image_formset"].full_clean() else: context["image_formset"] = SeriesImageFormSet() return context def form_valid(self, form): self.object = form.save(commit=False) self.object.save() form.instance.author.add(self.request.user.id) context = self.get_context_data(form=form) series_formset = context["image_formset"] if series_formset.is_valid(): response = super().form_valid(form) series_formset.instance = self.object series_formset.save() return response else: return super().form_invalid(form) class CaseCollectionUpdate(RevisionMixin, AuthorOrCheckerRequiredMixin, UpdateView): model = CaseCollection form_class = CaseCollectionForm template_name = "atlas/collection_update_form.html" # fields = '__all__' # #fields = [ 'condition' ] # #initial = {'date_of_death': '05/01/2018'} # exclude = [ 'created_date', 'published_date' ] def get_form_kwargs(self): kwargs = super(CaseCollectionUpdate, self).get_form_kwargs() kwargs.update({"user": self.request.user}) return kwargs def get_context_data(self, **kwargs): context = super(CaseCollectionUpdate, self).get_context_data(**kwargs) if self.request.POST: context["case_formset"] = CaseCollectionCaseFormSet( self.request.POST, self.request.FILES, instance=self.object, form_kwargs={"user": self.request.user}, ) context["case_formset"].full_clean() else: context["case_formset"] = CaseCollectionCaseFormSet( instance=self.object, form_kwargs={"user": self.request.user} ) context["collection"] = context["casecollection"] return context def form_valid(self, form): # save exam orders (there must be a better way to do this) # exam_orders = {} # for exam in self.object.exams.all(): # exam_orders[exam] = list(exam.exam_questions.all()) self.object = form.save(commit=False) self.object.save() form.instance.author.add(self.request.user.id) context = self.get_context_data(form=form) case_formset = context["case_formset"] # logger.debug(formset.is_valid()) if case_formset.is_valid(): response = super().form_valid(form) case_formset.instance = self.object case_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 SeriesUpdate( RevisionMixin, LoginRequiredMixin, AuthorOrCheckerRequiredMixin, UpdateView ): model = Series form_class = SeriesForm # fields = '__all__' # #fields = [ 'condition' ] # #initial = {'date_of_death': '05/01/2018'} # exclude = [ 'created_date', 'published_date' ] def get_context_data(self, **kwargs): context = super(SeriesUpdate, self).get_context_data(**kwargs) if self.request.POST: context["image_formset"] = SeriesImageFormSet( self.request.POST, self.request.FILES, instance=self.object ) context["image_formset"].full_clean() else: context["image_formset"] = SeriesImageFormSet(instance=self.object) return context def get_form_kwargs(self): kwargs = super(SeriesUpdate, self).get_form_kwargs() kwargs.update({"user": self.request.user}) return kwargs def form_valid(self, form): self.object = form.save(commit=False) self.object.save() form.instance.author.add(self.request.user.id) context = self.get_context_data(form=form) image_formset = context["image_formset"] # logger.debug(formset.is_valid()) if image_formset.is_valid(): response = super().form_valid(form) image_formset.instance = self.object image_formset.save() return response else: return super().form_invalid(form) class ConditionUpdate(RevisionMixin, LoginRequiredMixin, UpdateView): model = Condition form_class = ConditionForm class ConditionCreate(RevisionMixin, LoginRequiredMixin, CreateView): model = Condition form_class = ConditionForm class FindingUpdate(RevisionMixin, LoginRequiredMixin, UpdateView): model = Finding form_class = FindingForm class FindingCreate(RevisionMixin, LoginRequiredMixin, CreateView): model = Finding form_class = FindingForm class StructureUpdate(RevisionMixin, LoginRequiredMixin, UpdateView): model = Structure form_class = StructureForm class StructureCreate(RevisionMixin, LoginRequiredMixin, CreateView): model = Structure form_class = StructureForm class AtlasCreateBase(RevisionMixin, LoginRequiredMixin): model = Case form_class = CaseForm def get_form_kwargs(self): kwargs = super(AtlasCreateBase, self).get_form_kwargs() kwargs.update({"user": self.request.user}) return kwargs def get_context_data(self, **kwargs): context = super(AtlasCreateBase, self).get_context_data(**kwargs) if self.request.POST: context["series_formset"] = SeriesFormSet( self.request.POST, self.request.FILES, form_kwargs={"user": self.request.user}, ) context["casedifferential_formset"] = CaseDifferentialFormSet( self.request.POST, self.request.FILES ) context["series_formset"].full_clean() context["casedifferential_formset"].full_clean() else: # context["series_formset"] = SeriesFormSet(instance=Case.objects.filter(author__id=self.request.user.id), queryset=Series.objects.filter(author__id=self.request.user.id)) context["series_formset"] = SeriesFormSet( form_kwargs={"user": self.request.user} ) context["casedifferential_formset"] = CaseDifferentialFormSet() return context def form_valid(self, form): self.object = form.save(commit=False) # Add exam objects self.object.save() form.instance.author.add(self.request.user.id) context = self.get_context_data(form=form) series_formset = context["series_formset"] casedifferential_formset = context["casedifferential_formset"] if series_formset.is_valid() and casedifferential_formset.is_valid(): response = super().form_valid(form) series_formset.instance = self.object series_formset.save() casedifferential_formset.instance = self.object casedifferential_formset.save() # If the normal submit button is pressed we save as normal if "submit" in self.request.POST: return response # else we redirect to the clone url else: return redirect("atlas:case_clone", pk=self.object.pk) else: return super().form_invalid(form) # @login_required class AtlasCreate(AtlasCreateBase, CreateView): def get_initial(self): if "pk" in self.kwargs: initial = super().get_initial() exam = get_object_or_404(CaseCollection, pk=self.kwargs["pk"]) initial["exams"] = [exam.id] return initial ## There has to be a better way... # try: # s = (i.pk for i in self.request.user.atlas_default.site.all()) # self.initial.update({"site": s}) # except AttributeError: # pass # return self.initial class AtlasUpdate( RevisionMixin, LoginRequiredMixin, AuthorOrCheckerRequiredMixin, UpdateView ): model = Case form_class = CaseForm # fields = '__all__' # #fields = [ 'condition' ] # #initial = {'date_of_death': '05/01/2018'} # exclude = [ 'created_date', 'published_date' ] def get_form_kwargs(self): kwargs = super(AtlasUpdate, self).get_form_kwargs() kwargs.update({"user": self.request.user}) return kwargs def get_context_data(self, **kwargs): context = super(AtlasUpdate, self).get_context_data(**kwargs) if self.request.POST: context["series_formset"] = SeriesFormSet( self.request.POST, self.request.FILES, instance=self.object, form_kwargs={"user": self.request.user}, ) context["casedifferential_formset"] = CaseDifferentialFormSet( self.request.POST, self.request.FILES, instance=self.object ) context["series_formset"].full_clean() context["casedifferential_formset"].full_clean() else: context["series_formset"] = SeriesFormSet( instance=self.object, form_kwargs={"user": self.request.user} ) context["casedifferential_formset"] = CaseDifferentialFormSet( instance=self.object ) return context def form_valid(self, form): self.object = form.save(commit=False) self.object.save() form.instance.author.add(self.request.user.id) context = self.get_context_data(form=form) series_formset = context["series_formset"] casedifferential_formset = context["casedifferential_formset"] # logger.debug(formset.is_valid()) if series_formset.is_valid() and casedifferential_formset.is_valid(): response = super().form_valid(form) series_formset.instance = self.object series_formset.save() casedifferential_formset.instance = self.object casedifferential_formset.save() return response else: return super().form_invalid(form) def add_collection_to_case_form(request, case_id): if request.method == "POST": form = AddCollectionToCaseForm(request.POST, user=request.user) if form.is_valid(): case = get_object_or_404(Case, pk=case_id) case.casecollection_set.add(*form.cleaned_data["casecollection"]) # do_something_with_form_data(form.cleaned_data) return HttpResponse("Done") return render(request, "web/htmx_contact_form_confirm.html") else: form = AddCollectionToCaseForm(user=request.user) return render( request, "generic_form.html", { "form": form, "target_url": reverse("atlas:case_collection_form", args=(case_id,)), }, ) # class AddCollectionToCaseView(FormView): # form_class = AddCollectionToCaseForm # template_name = "generic_form.html" # # def form_valid(self, form): # form.save() # return super(AddCollectionToCaseView, self).form_valid(form) # # def get_form_kwargs(self): # kwargs = super(AddCollectionToCaseView, self).get_form_kwargs() # kwargs.update({"user": self.request.user}) # return kwargs # # def get_success_url(self) -> str: # pk = self.kwargs["pk"] # return reverse("atlas:case_detail", kwargs={"pk": pk}) class AtlasClone(AtlasCreateBase, AuthorOrCheckerRequiredMixin, CreateView): """Clones a existing atlas""" # fields = '__all__' # #fields = [ 'condition' ] # #initial = {'date_of_death': '05/01/2018'} # exclude = [ 'created_date', 'published_date' ] def get_initial(self): # print(self.request) old_object = get_object_or_404(Case, pk=self.kwargs["pk"]) if ( self.request.user.groups.filter(name="atlas_editor").exists() or self.request.user.is_superuser ): pass else: if self.request.user not in old_object.get_author_objects(): raise PermissionDenied() # or Http404 else: pass initial_data = model_to_dict(old_object, exclude=["id"]) return initial_data @login_required @user_is_author_or_atlas_editor def atlas_scrap(request, pk): try: atlas = Case.objects.get(pk=pk) except Case.DoesNotExist: raise Http404("Case does not exist") atlas.scrapped = not atlas.scrapped atlas.save() return HttpResponseRedirect(reverse("atlas:case_detail", args=(pk,))) # @login_required # def edit_abnormality_popup(request): # instance = get_object_or_404(Abnormality, pk = pk) # form = AbnormalityForm(request.POST or None) # if form.is_valid(): # instance = form.save() # return HttpResponse('' % (instance.pk, instance)) # return render(request, "atlas/create_simple.html", {'form': form}) @csrf_exempt def get_examination_id(request): if request.accepts("application/json")(): examination_name = request.GET["examination_name"] examination_id = Examination.objects.get(name=examination_name).id data = { "examination_id": examination_id, } return HttpResponse(json.dumps(data), content_type="application/json") return HttpResponse("/") class CaseView(LoginRequiredMixin, SingleTableMixin, FilterView): model = Case table_class = CaseTable template_name = "atlas/view.html" filterset_class = CaseFilter class SeriesView(LoginRequiredMixin, SingleTableMixin, FilterView): model = Series table_class = SeriesTable template_name = "atlas/view.html" filterset_class = SeriesFilter class ConditionView(LoginRequiredMixin, SingleTableMixin, FilterView): model = Condition table_class = ConditionTable template_name = "atlas/view.html" filterset_class = ConditionFilter class SubspecialtyView(LoginRequiredMixin, SingleTableMixin, FilterView): model = Subspecialty table_class = SubspecialtyTable template_name = "atlas/view.html" filterset_class = SubspecialtyFilter class PresentationView(LoginRequiredMixin, SingleTableMixin, FilterView): model = Presentation table_class = PresentationTable template_name = "atlas/view.html" filterset_class = PresentationFilter class PathologicalProcessView(LoginRequiredMixin, SingleTableMixin, FilterView): model = PathologicalProcess table_class = PathologicalProcessTable template_name = "atlas/view.html" filterset_class = PathologicalProcessFilter class StructureView(LoginRequiredMixin, SingleTableMixin, FilterView): model = Structure table_class = StructureTable template_name = "atlas/view.html" filterset_class = StructureFilter class FindingView(LoginRequiredMixin, SingleTableMixin, FilterView): model = Finding table_class = FindingTable template_name = "atlas/view.html" filterset_class = FindingFilter @login_required def case_order_dicom(request, pk): if request.htmx: case = get_object_or_404(Case, pk=pk) fail = [] for series in case.series.all(): try: if request.user in series.get_author_objects(): series.order_by_dicom() else: fail.append(series.pk) except: fail.append(series.pk) if fail: return HttpResponse(f"Unable to order series {','.join([str(i) for i in fail])}") else: return HttpResponse("Series reordered") raise Http404 @login_required @user_is_author_or_atlas_series_checker def series_order_dicom(request, pk): series = get_object_or_404(Series, pk=pk) try: series.order_by_dicom() except: return HttpResponse("

Series does not appear to contain dicoms

") return redirect("atlas:series_detail", pk=pk) @login_required def image_diff(request): if request.htmx: image_ids = request.POST.getlist("image-id") if len(image_ids) == 2: first: SeriesImage = get_object_or_404(SeriesImage, pk=image_ids[0]) second: SeriesImage = get_object_or_404(SeriesImage, pk=image_ids[1]) first_ds = pydicom.read_file(first.image.file) #del first_ds.PixelData #j1 = first_ds.to_json_dict() f = print_dicom(first_ds, join=False) second_ds = pydicom.read_file(second.image.file) #del second_ds.PixelData #j2 = second_ds.to_json_dict() s = print_dicom(second_ds, join=False) diff = difflib.HtmlDiff().make_table(f, s) dict_diff = pprint.pformat(compare_dicom_datasets(first_ds, second_ds)) #return HttpResponse(diff) return HttpResponse(f"
{dict_diff}

{diff}") else: ds_set = [] for pk in image_ids: series_image = get_object_or_404(SeriesImage, pk=pk) ds_set.append(pydicom.read_file(series_image.image.file)) dict_diff = pprint.pformat(compare_dicom_datasets(*ds_set)) return HttpResponse(f"
{dict_diff}
") return HttpResponse("Fail") @login_required def series_image_dicom(request, pk): image = get_object_or_404(SeriesImage, pk=pk) return render(request, "atlas/series_image_dicom.html", {"image": image}) @login_required @user_is_author_or_atlas_series_checker def series_anonymise_dicom(request, pk): series = get_object_or_404(Series, pk=pk) series.anonymise_images() return redirect("atlas:series_detail", pk=pk) @login_required @user_is_author_or_atlas_series_checker def series_order_dicom_instance(request, pk): series = get_object_or_404(Series, pk=pk) try: series.order_by_dicom("InstanceNumber") except: return HttpResponse( "

Series does not appear to contain dicoms (or field InstanceNumber)

" ) return redirect("atlas:series_detail", pk=pk) @login_required @user_is_author_or_atlas_series_checker def series_order_dicom_SeriesInstanceUID(request, pk): series = get_object_or_404(Series, pk=pk) try: series.order_by_dicom("SeriesInstanceUID") except: return HttpResponse( "

Series does not appear to contain dicoms (or field SeriesInstanceUID)

" ) return redirect("atlas:series_detail", pk=pk) @login_required @user_is_author_or_atlas_series_checker def series_order_upload_filename(request, pk): series = get_object_or_404(Series, pk=pk) series.order_by_upload_filename() return redirect("atlas:series_detail", pk=pk) # Vendored in from BaseZipView class SeriesImagesZipView(SeriesImagesZipViewBase, SuperuserRequiredMixin): series_object = Series def create_series_findings(request): # posts = Post.objects.all() response_data = {} if request.POST.get("action") == "post": series_finding_id = int(request.POST.get("series_finding_id")) series_id = request.POST.get("series") findings_ids = json.loads(request.POST.get("findings")) structure_ids = json.loads(request.POST.get("structures")) description = request.POST.get("description") annotation_json = request.POST.get("annotation_json") viewport_json = request.POST.get("viewport_json") series = Series.objects.get(pk=series_id) findings = Finding.objects.filter(pk__in=findings_ids) structures = Structure.objects.filter(pk__in=structure_ids) if series_finding_id > 0: sf = get_object_or_404(SeriesFinding, pk=series_finding_id) sf.series = series sf.description = description sf.annotation_json = annotation_json sf.viewport_json = viewport_json else: sf = SeriesFinding.objects.create( series=series, description=description, annotation_json=annotation_json, viewport_json=viewport_json, ) sf.findings.set(findings) sf.structures.set(structures) sf.save() return JsonResponse({"success": True}) return JsonResponse({"success": False}) return render(request, "create_post.html", {"posts": posts}) class ConditionAutocomplete(autocomplete.Select2QuerySetView): def get_queryset(self): # Don't forget to filter out results depending on the visitor ! if not self.request.user.is_authenticated: return Condition.objects.none() qs = Condition.objects.all() if self.q: # This raises a fielderror which breaks creating a new item if not caught try: qs = qs.filter(name__icontains=self.q) except FieldError: return Condition.objects.none() return qs class ConditionAutocompleteRCRCurriculum(autocomplete.Select2QuerySetView): def get_queryset(self): # Don't forget to filter out results depending on the visitor ! if not self.request.user.is_authenticated: return Condition.objects.none() qs = Condition.objects.filter(rcr_curriculum=True) if self.q: # This raises a fielderror which breaks creating a new item if not caught try: qs = qs.filter(name__icontains=self.q, rcr_curriculum=True) except FieldError: return Condition.objects.none() return qs class PresentationAutocomplete(autocomplete.Select2QuerySetView): def get_queryset(self): # Don't forget to filter out results depending on the visitor ! if not self.request.user.is_authenticated: return Presentation.objects.none() qs = Presentation.objects.all() if self.q: # This raises a fielderror which breaks creating a new item if not caught try: qs = qs.filter(name__icontains=self.q) except FieldError: return Presentation.objects.none() return qs class FindingAutocomplete(autocomplete.Select2QuerySetView): def get_queryset(self): # Don't forget to filter out results depending on the visitor ! if not self.request.user.is_authenticated: return Finding.objects.none() qs = Finding.objects.all() if self.q: # This raises a fielderror which breaks creating a new item if not caught try: qs = qs.filter(name__icontains=self.q) except FieldError: return Finding.objects.none() return qs class StructureAutocomplete(autocomplete.Select2QuerySetView): def get_queryset(self): # Don't forget to filter out results depending on the visitor ! if not self.request.user.is_authenticated: return Structure.objects.none() qs = Structure.objects.all() if self.q: # This raises a fielderror which breaks creating a new item if not caught try: qs = qs.filter(name__icontains=self.q) except FieldError: return Structure.objects.none() return qs @login_required # @user_is_atlas_editor def categories_list(request): # condition = get_object_or_404(Condition, pk=pk) # logging.debug(atlas.subspecialty.first().name.all()) return render( request, "atlas/categories_list.html", # { # "condition": condition, # }, ) # def collection_view(request): # collections = CaseCollection.objects.all() # # return render( # request, "atlas/collection_view.html", {"collections": collections} # ) class CollectionView(LoginRequiredMixin, SingleTableMixin, FilterView): model = CaseCollection table_class = CaseCollectionTable template_name = "atlas/view.html" filterset_class = CaseCollectionFilter @user_is_collection_author_or_atlas_editor def collection_viva(request, pk): collection = get_object_or_404(CaseCollection, pk=pk) cases = collection.cases.all().order_by("casedetail__sort_order") return render( request, "atlas/collection_viva.html", {"collection": collection, "cases": cases, "can_edit": True}, ) @user_is_collection_author_or_atlas_editor def collection_viva_case(request, pk, case_number): collection = get_object_or_404(CaseCollection, pk=pk) cases = collection.cases.all().order_by("casedetail__sort_order") current_case = cases[case_number] return render( request, "atlas/collection_viva_case.html", {"collection": collection, "cases": cases, "current_case": current_case}, ) @user_is_collection_author_or_atlas_editor def collection_viva_series(request, pk, series_id): collection = get_object_or_404(CaseCollection, pk=pk) series = get_object_or_404(Series, pk=series_id) return render( request, "atlas/collection_viva_case.html", {"collection": collection, "current_case": series}, ) @user_is_collection_author_or_atlas_editor def collection_detail(request, pk): collection = get_object_or_404(CaseCollection, pk=pk) cases = collection.cases.all().order_by("casedetail__sort_order") return render( request, "atlas/collection_detail.html", {"collection": collection, "cases": cases, "can_edit": True}, ) def collection_take_start(request, pk): """The starting page for taking / reviewing a case collection Args: request (_type_): _description_ pk (_type_): _description_ Returns: _type_: _description_ """ collection = get_object_or_404(CaseCollection, pk=pk) valid_user = (collection.check_logged_in_user(request),) cid_exam = None if collection.collection_type == "REV" or valid_user: cid_exam = collection.get_cid_user_exams(user_user=request.user).first() template_variables = { "collection": collection, "valid_user": valid_user, "cid_exam": cid_exam, } match collection.collection_type: case "REP": return render( request, "atlas/collection_take_start.html", template_variables ) case "REV": return render( request, "atlas/collection_review_start.html", template_variables ) @user_is_collection_author_or_atlas_editor def collection_mark_overview(request, pk): collection = get_object_or_404(CaseCollection, pk=pk) if collection.collection_type == "REV": raise Http404("Collection in review mode") cases = collection.cases.all().order_by("casedetail__sort_order").prefetch_related() case_details = CaseDetail.objects.filter( case__in=cases, collection=collection ).prefetch_related("case") answers = CidReportAnswer.objects.filter(question__in=case_details) case_map = {} for cd in case_details: case_map[cd] = [] for answer in answers: case_map[answer.question].append(answer) return render( request, "atlas/collection_mark_overview.html", { "collection": collection, "answers": answers, "case_details": case_details, "case_map": case_map, }, ) @user_is_collection_author_or_atlas_editor def collection_mark_question(request, pk, case_number): collection = get_object_or_404(CaseCollection, pk=pk) if collection.collection_type == "REV": raise Http404("Collection in review mode") case, case_count = collection.get_case_by_index(case_number, case_count=True) previous = case_number > 0 next = case_number < (case_count - 1) case_detail = CaseDetail.objects.filter(case=case, collection=collection) answers = CidReportAnswer.objects.filter(question__in=case_detail) answer_forms = [] if request.method == "POST": for answer in answers: if answer: form = CidReportAnswerMarkForm( request.POST, instance=answer, prefix=answer.id ) else: form = CidReportAnswerMarkForm(request.POST, prefix=answer.id) if form.is_valid(): answer = form.save(commit=False) # answer.cid = cid # answer.question = case_detail # answer.published_date = timezone.now() answer.save() kwargs = {"pk": pk} if "save" in request.POST: return redirect( "atlas:collection_mark_question", case_number=case_number, **kwargs ) if "next" in request.POST: return redirect( "atlas:collection_mark_question", case_number=case_number + 1, **kwargs ) elif "previous" in request.POST: return redirect( "atlas:collection_mark_question", case_number=case_number - 1, **kwargs ) elif "goto" in request.POST: return redirect( "atlas:collection_mark_question", case_number=int(request.POST.get("goto")), **kwargs, ) else: for answer in answers: form = CidReportAnswerMarkForm(instance=answer, prefix=answer.pk) answer_forms.append((answer, form)) series_list = case.series.all().prefetch_related("images", "examination", "plane") return render( request, "atlas/collection_mark_question.html", { "collection": collection, "series_list": series_list, "answers": answers, "case_detail": case_detail, "case": case, "case_number": case_number, "answer_forms": answer_forms, "previous": previous, "next": next, }, ) @user_is_collection_author_or_atlas_editor def exam_cids(request, pk): collection = get_object_or_404(CaseCollection, pk=pk) if ( request.user in collection.author.all() or request.user.groups.filter(name="cid_user_manager").exists() ): cid_users = collection.valid_cid_users.all() cid_user_count = cid_users.count() return render( request, "atlas/exam_cids.html", { "collection": collection, "cid_users": cid_users, "cid_user_count": cid_user_count, }, ) raise PermissionDenied def collection_take_overview_user(request, pk): return collection_take_overview(request, pk) def collection_take_overview( request, pk: int, cid: int | None = None, passcode: str | None = None ): """ Summary page that shows which questions a user has attempted (with / without answers) Args: request (_type_): _description_ pk (int): _description_ cid (int | None, optional): _description_. Defaults to None. passcode (str | None, optional): _description_. Defaults to None. Returns: _type_: _description_ """ collection = get_object_or_404(CaseCollection, pk=pk) collection.check_user_can_take(cid, passcode, request) cid_user_exam = collection.get_or_create_cid_user_exam( cid=cid, user_user=request.user ) if request.htmx: if "finish" in request.POST: cid_user_exam.completed = True cid_user_exam.save() return HttpResponse("True") else: raise Http404() cases = collection.cases.all().order_by("casedetail__sort_order").prefetch_related() case_details = CaseDetail.objects.filter(case__in=cases, collection=collection) if cid is not None: answers = CidReportAnswer.objects.filter(cid=cid, question__in=case_details) else: answers = UserReportAnswer.objects.filter( user=request.user, question__in=case_details ) answer_question_map = {} for ans in answers: answer_question_map[ans.question] = ans question_answer_tuples = [] answer_count = 0 for cd in case_details: self_review = cid_user_exam.selfreview_set.filter(case=cd.case) if cd in answer_question_map and answer_question_map[cd].answer: question_answer_tuples.append((cd, answer_question_map[cd], self_review)) answer_count += 1 else: question_answer_tuples.append((cd, None, self_review)) return render( request, "atlas/collection_take_overview.html", { "collection": collection, "cid": cid, "question_answer_tuples": question_answer_tuples, "answer_count": answer_count, "collection_length": len(cases), "passcode": passcode, "cid_user_exam": cid_user_exam, }, ) def collection_case_view_take_user(request, pk: int, case_number: int): return collection_case_view_take(request, pk, case_number) def collection_case_view_take( request, pk: int, case_number: int, cid=None, passcode=None ): """ Displays the page from which the user takes a case in the collection Args: request (_type_): _description_ pk (int): _description_ case_number (int): _description_ cid (_type_, optional): _description_. Defaults to None. passcode (_type_, optional): _description_. Defaults to None. Raises: Http404: If user does not have access Returns: _type_: _description_ """ collection = get_object_or_404(CaseCollection, pk=pk) form = None answer: None | CidReportAnswer | UserReportAnswer = None collection.check_user_can_take(cid, passcode, request) case, case_count = collection.get_case_by_index(case_number, case_count=True) cid_user_exam = collection.get_or_create_cid_user_exam( cid=cid, user_user=request.user ) if cid_user_exam.start_time is None: 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) if cid is not None: answer = case_detail.cidreportanswer_set.filter(cid=cid).first() ReportAnswerForm = CidReportAnswerForm else: answer = case_detail.userreportanswer_set.filter( user=request.user ).first() ReportAnswerForm = UserReportAnswerForm if request.method == "POST": if collection.collection_type == "REP": if not collection.publish_results: if answer: form = ReportAnswerForm(request.POST, instance=answer) else: form = ReportAnswerForm(request.POST) if not cid_user_exam.completed: if form.is_valid(): answer = form.save(commit=False) answer.set_cid_or_user(cid=cid, user=request.user) answer.question = case_detail # answer.published_date = timezone.now() answer.save() cid_user_exam.end_time = timezone.now() cid_user_exam.save() if cid is not None: kwargs = {"pk": pk, "cid": cid, "passcode": passcode} redirect_url = "atlas:collection_case_view_take" overview_url = "atlas:collection_take_overview" else: kwargs = {"pk": pk} redirect_url = "atlas:collection_case_view_take_user" overview_url = "atlas:collection_take_overview_user" if "next" in request.POST: return redirect(redirect_url, case_number=case_number + 1, **kwargs) elif "previous" in request.POST: return redirect(redirect_url, case_number=case_number - 1, **kwargs) elif "finish" in request.POST: return redirect(overview_url, **kwargs) elif "goto" in request.POST: return redirect( redirect_url, case_number=int(request.POST.get("goto")), **kwargs, ) else: if collection.collection_type == "REP": form = ReportAnswerForm(instance=answer) series_list = case.series.all().prefetch_related("images", "examination", "plane") print("case_no") previous = case_number > 0 next = case_number < (case_count - 1) # Set what details a user is / is not able to view when taking the case # this can be configured via the collection edit menu if collection.publish_results or cid_user_exam.completed: completed = True show_title = collection.show_title_post show_history = collection.show_history_post show_description = collection.show_description_post show_discussion = collection.show_discussion_post show_report = collection.show_report_post self_review = cid_user_exam.selfreview_set.filter(case=case) else: completed = False show_title = collection.show_title_pre show_history = collection.show_history_pre show_description = collection.show_description_pre show_discussion = collection.show_discussion_pre show_report = collection.show_report_pre self_review = [] return render( request, "atlas/collection_case_view_take.html", { "form": form, "collection": collection, "case": case, "series_list": series_list, "case_number": case_number, "previous": previous, "next": next, "cid": cid, "passcode": passcode, "answer": answer, "show_title": show_title, "show_history": show_history, "show_description": show_description, "show_discussion": show_discussion, "show_report": show_report, "cid_user_exam": cid_user_exam, "completed": completed, "self_review": self_review, }, ) @user_is_collection_author_or_atlas_editor def collection_case_view(request, pk, case_number): """Edit/management overview of the case (as part of the collection) Args: request (_type_): _description_ pk (_type_): _description_ case_number (_type_): _description_ Raises: Http404: _description_ Returns: _type_: _description_ """ collection = get_object_or_404(CaseCollection, pk=pk) 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") previous = case_number > 0 next = case_number < (case_count - 1) return render( request, "atlas/case_detail.html", { "form": form, "collection": collection, "collection_length": case_count, "case": case, "series_list": series_list, "case_number": case_number, "previous": previous, "next": next, "answer": answer, }, ) def case_dicom_json(request, pk): case = get_object_or_404(Case, pk=pk) return JsonResponse(case.get_case_dicom_json()) def series_dicom_json(request, pk): series = get_object_or_404(Series, pk=pk) return JsonResponse(series.get_ohif_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) cases = collection.cases.all().order_by("casedetail__sort_order").prefetch_related() case_details = CaseDetail.objects.filter( case__in=cases, collection=collection ).prefetch_related("case") cid_user_answers = CidReportAnswer.objects.filter( question__in=case_details, cid=cid ) temp = {} for ans in cid_user_answers: temp[ans.pk] = (ans.question.pk, ans.score, ans.cid, ans.answer) ans.delete() return JsonResponse(temp) @user_is_collection_author_or_atlas_editor def delete_collection_answers(request, exam_id): collection = get_object_or_404(CaseCollection, pk=exam_id) cases = collection.cases.all().order_by("casedetail__sort_order").prefetch_related() case_details = CaseDetail.objects.filter( case__in=cases, collection=collection ).prefetch_related("case") cid_user_answers = CidReportAnswer.objects.filter(question__in=case_details) temp = {} for ans in cid_user_answers: temp[ans.pk] = (ans.question.pk, ans.score, ans.cid, ans.answer) ans.delete() return JsonResponse(temp) # TODO: this needs a major cleanup @user_is_collection_author_or_atlas_editor def collection_scores_cid(request, pk): collection = get_object_or_404(CaseCollection, pk=pk) if not collection.exam_mode: raise Http404("Collection not in exam mode") user_answers_and_marks = defaultdict(list) user_answers_marks = defaultdict(list) user_answers = defaultdict(list) user_names = {} # cid_passcodes = {} by_question = defaultdict(dict) unmarked = set() cases = collection.cases.all().order_by("casedetail__sort_order").prefetch_related() case_details = CaseDetail.objects.filter( case__in=cases, collection=collection ).prefetch_related("case") cid_user_answers = CidReportAnswer.objects.filter(question__in=case_details) # questions = exam.exam_questions.all().prefetch_related("answers") # cid_user_answers = CidReportAnswer.objects.select_related("question").filter( # question__in=questions, exam__id=pk # ) cids = set() case_list = list(cases) # Loop through all candidates for cid_user_answer in cid_user_answers: # Convoluted (probably...) cid = cid_user_answer.cid # cid_passcodes[cid] = cid_user_answer.passcode cids.add(cid) s = cid_user_answer user_names[cid] = cid q = cid_user_answer.question.case # if not s: # # skip if no answer # user_answers_marks[cid].append(0) # user_answers[cid].append("") # by_question[q].append(("", 0)) # continue ans = s.answer answer_score = s.get_answer_score() if answer_score == "unmarked" or answer_score == None: index = case_list.index(q) unmarked.add(index) user_answers[cid].append(ans) user_answers_marks[cid].append(answer_score) user_answers_and_marks[cid].append((ans, answer_score)) by_question[q][cid] = (ans, answer_score, s.feedback) user_scores = {} user_scores_normalised = {} for user in user_answers_marks: user_scores[user] = sum( [i for i in user_answers_marks[user] if (i != "unmarked" and i != None)] ) user_scores_normalised[user] = user_scores[user] # ignore scores of 0 for stats user_scores_list = [i for i in user_scores.values() if i > 0] max_score = len(cases) if len(user_scores_list) < 1: mean = 0 median = 0 mode = 0 fig_html = "" else: mean = statistics.mean(user_scores_list) median = statistics.median(user_scores_list) try: mode = statistics.mode(user_scores_list) except statistics.StatisticsError: mode = "No unique mode" df = user_scores_list fig = px.histogram( df, x=0, title="{}: distribution of scores".format(collection), labels={"0": "Score"}, height=400, width=600, ) fig_html = fig.to_html() collection.stats_mean = mean collection.stats_median = median collection.stats_mode = mode collection.stats_candidates = len(user_scores_list) collection.stats_max_possible = max_score collection.stats_min = min(user_scores_list) collection.stats_max = max(user_scores_list) # collection.stats_graph = fig_html collection.user_scores = user_scores collection.save() return render( request, f"atlas/collection_scores.html", { "cids": sorted(cids), # "cid_passcodes": cid_passcodes, "exam": collection, "collection": collection, "unmarked": unmarked, "questions": cases, "by_question": by_question, "user_answers": dict(user_answers), "user_answers_marks": dict(user_answers_marks), "user_scores": user_scores, "user_scores_normalised": user_scores_normalised, "user_scores_list": user_scores_list, "user_names": user_names, "user_answers_and_marks": user_answers_and_marks, "max_score": max_score, "mean": mean, "median": median, "mode": mode, "plot": fig_html, }, ) 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"]) initial_data = model_to_dict(old_object, exclude=["id"]) # We manually transfer the forign keys / m2m relationships questions = old_object.cases.all().values_list("id", flat=True) authors = old_object.author.all().values_list("id", flat=True) self.cases = list(questions) self.author = list(authors) return initial_data def form_valid(self, form): object = form.save() # Reapply these otherwise they get lost? object.cases.set(self.cases) object.author.set(self.author) 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") def get_initial(self): user_exam_id = self.kwargs["user_exam_id"] case_id = self.kwargs["case_id"] review_id = self.kwargs["pk"] self.user_exam = get_object_or_404(CidUserExam, pk=user_exam_id) self.case = get_object_or_404(Case, pk=case_id) self.review = get_object_or_404(SelfReview, pk=review_id) return {"user_exam": user_exam_id, "case": case_id, "review": review_id} def get_context_data(self, **kwargs): # This can also be accessed via view.**** in the template context = super(SelfReviewDelete, self).get_context_data(**kwargs) context["user_exam"] = self.user_exam context["case"] = self.case context["review"] = self.review return context def get_success_url(self) -> str: return self.user_exam.exam.get_case_take_url(self.case) class SelfReviewUpdate(UpdateView): model = SelfReview form_class = SelfReviewForm def get_initial(self): user_exam_id = self.kwargs["user_exam_id"] case_id = self.kwargs["case_id"] review_id = self.kwargs["pk"] self.user_exam = get_object_or_404(CidUserExam, pk=user_exam_id) self.case = get_object_or_404(Case, pk=case_id) self.review = get_object_or_404(SelfReview, pk=review_id) return {"user_exam": user_exam_id, "case": case_id, "review": review_id} def get_context_data(self, **kwargs): # This can also be accessed via view.**** in the template context = super(SelfReviewUpdate, self).get_context_data(**kwargs) context["user_exam"] = self.user_exam context["case"] = self.case context["review"] = self.review return context def get_success_url(self) -> str: return self.user_exam.exam.get_case_take_url(self.case) # def get_object(self): # return self.review # return # def get_initial(self): # user_exam_id = self.kwargs["user_exam_id"] # case_id = self.kwargs["case_id"] # self.user_exam = get_object_or_404(CidUserExam, pk=user_exam_id) # 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 # fields = ("author", "note_type", "note") def get_form_kwargs(self): 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}) return kwargs def get_initial(self): user_exam_id = self.kwargs["user_exam_id"] case_id = self.kwargs["case_id"] self.user_exam = get_object_or_404(CidUserExam, pk=user_exam_id) self.case = get_object_or_404(Case, pk=case_id) return {"user_exam": user_exam_id, "case": case_id} def get_context_data(self, **kwargs): # This can also be accessed via view.**** in the template context = super(AddSelfReview, self).get_context_data(**kwargs) context["user_exam"] = self.user_exam context["case"] = self.case return context 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 }) # def form_valid(self, form): # model = form.save(commit=False) # # model.save() # # response = super().form_valid(form) # # return response @login_required def uncategorised_dicoms(request): dicoms = UncategorisedDicom.objects.filter(user=request.user) data = [] for d in dicoms: data.append((d.image.name, d.get_basic_dicom_tags())) return JsonResponse(data, safe=False)