import json from dal import autocomplete from django.shortcuts import render, get_object_or_404, redirect from django import forms # 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.detail import DetailView from generic.mixins import SuperuserRequiredMixin from django.views.generic.edit import CreateView, UpdateView, DeleteView 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 .forms import ( CaseForm, SeriesForm, SeriesImageFormSet, SeriesFormSet, CaseDifferentialFormSet, ExaminationForm, SeriesFindingForm, ) from .models import ( Case, Condition, Series, Examination, Finding, Subspecialty, SeriesFinding, SeriesImage, ) from .tables import AtlasTable, SeriesTable from .filters import AtlasFilter, SeriesFilter from django_tables2 import SingleTableView, SingleTableMixin from django_filters.views import FilterView from .decorators import ( user_is_author_or_atlas_checker, user_is_author_or_atlas_checker_or_atlas_marker, user_is_author_or_atlas_series_checker_or_atlas_marker, user_is_atlas_checker, user_is_author_or_atlas_series_checker, user_is_atlas_marker, user_is_author_or_atlas_checker_or_atlas_marker, ) 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 image_as_base64 import logging from copy import deepcopy from django.forms.models import model_to_dict from generic.views import ExamCloneMixin, ExamViews from reversion.views import RevisionMixin import reversion from rest_framework import viewsets from zipview.views import BaseZipView import os logger = logging.getLogger(__name__) class AuthorOrCheckerRequiredMixin(object): def get_object(self, *args, **kwargs): obj = super().get_object(*args, **kwargs) if ( self.request.user.groups.filter(name="atlas_checker").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 @login_required @user_is_author_or_atlas_checker_or_atlas_marker 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): series = get_object_or_404(Series, pk=pk) # if request.user not in atlas.author.all(): # raise PermissionDenied 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}, ) @login_required @user_is_author_or_atlas_checker def author_detail(request, pk): # logging.debug(Author.objects.all()) # author = get_object_or_404(Author, pk=pk) author = User.objects.get(pk=pk) atlas = Case.objects.filter(author=pk) return render( request, "atlas/category_detail.html", {"category": author, "atlas": atlas} ) @user_is_author_or_atlas_checker def author_list(request): authors = User.objects.all() return render(request, "atlas/author_list.html", {"authors": authors}) class AtlasDelete(RevisionMixin, AuthorOrCheckerRequiredMixin, DeleteView): model = Case success_url = reverse_lazy("atlas:case_view") 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, AuthorOrCheckerRequiredMixin, DeleteView): model = Series success_url = reverse_lazy("atlas:series_view") # @login_required # @user_is_author_or_atlas_checker # def case_clone(request, pk): # new_item = get_object_or_404(Case, pk=pk) # new_item.pk = None # autogen a new pk (item_id) # # new_item.name = "Copy of " + new_item.name #need to change uniques # # form = CaseForm(request.POST or None, instance=new_item) # # series_formset = SeriesFormSet() # # if form.is_valid(): # form.instance.author.add(request.user.id) # # # logger.debug(formset.is_valid()) # if series_formset.is_valid(): # response = super().form_valid(form) # series_formset.instance = obj # series_formset.save() # # return response # else: # return super().form_invalid(form) # # context = { # "form": form, # "series_formset": series_formset, # # other context # } # # return render(request, "atlas/case_form.html", context) 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_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 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 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 AtlasCreateBase(RevisionMixin, LoginRequiredMixin, CreateView): 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 ) 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() 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): def get_initial(self): # 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 # ) # context["series_formset"].full_clean() # else: # context["series_formset"] = SeriesFormSet(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"] # # logger.debug(formset.is_valid()) # 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 AtlasClone(AtlasCreateBase): """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"]) initial_data = model_to_dict(old_object, exclude=["id"]) return initial_data @login_required @user_is_author_or_atlas_checker 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}) @login_required def create_examination(request): form = ExaminationForm(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, "name": "Examination"} ) @csrf_exempt def get_examination_id(request): if request.is_ajax(): 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 = AtlasTable template_name = "atlas/view.html" filterset_class = AtlasFilter class SeriesView(LoginRequiredMixin, SingleTableMixin, FilterView): model = Series table_class = SeriesTable template_name = "atlas/view.html" filterset_class = SeriesFilter @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 @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) class SeriesImagesZipView(SuperuserRequiredMixin, BaseZipView): """Download all images from an image series""" def get_files(self): series = Series.objects.get(pk=self.kwargs["pk"]) return [i.image.file for i in series.images.all()] def create_series_findings(request): # posts = Post.objects.all() response_data = {} if request.POST.get("action") == "post": series_id = request.POST.get("series") findings_ids = json.loads(request.POST.get("findings")) 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) sf = SeriesFinding.objects.create( series=series, description=description, annotation_json=annotation_json, viewport_json=viewport_json, ) sf.findings.set(findings) 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: primary = qs.filter(name__icontains=self.q) # synonyms = qs.filter(name__icontains=self.q, primary_name=False) # for s in synonyms: # primary.extend(s.synonym.filter(primary_name=True)) except FieldError: return Condition.objects.none() return primary