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, ConditionForm, FindingForm, SeriesForm, SeriesImageFormSet, SeriesFormSet, SeriesFindingForm, CaseDifferentialFormSet, StructureForm, ) from .models import ( Case, Condition, PathologicalProcess, Presentation, Series, Examination, Finding, Structure, Subspecialty, SeriesFinding, SeriesImage, ) from .tables import ( CaseTable, ConditionTable, FindingTable, PathologicalProcessTable, PresentationTable, SeriesTable, StructureTable, SubspecialtyTable, ) from .filters import ( CaseFilter, ConditionFilter, FindingFilter, PathologicalProcessFilter, PresentationFilter, SeriesFilter, StructureFilter, SubspecialtyFilter, ) 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, 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_checker def condition_detail(request, pk): condition = get_object_or_404(Condition, pk=pk) # logging.debug(atlas.subspecialty.first().name.all()) return render( request, "atlas/condition_detail.html", { "condition": condition, }, ) @login_required @user_is_atlas_checker 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_checker 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_checker 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_checker 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_checker 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 @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 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, AuthorOrCheckerRequiredMixin, DeleteView): model = Series template_name = "confirm_delete.html" success_url = reverse_lazy("atlas:series_view") class ConditionDelete(RevisionMixin, AuthorOrCheckerRequiredMixin, DeleteView): model = Condition template_name = "confirm_delete.html" success_url = reverse_lazy("atlas:condition_view") class FindingDelete(RevisionMixin, AuthorOrCheckerRequiredMixin, DeleteView): model = Finding template_name = "confirm_delete.html" success_url = reverse_lazy("atlas:finding_view") class StructureDelete(RevisionMixin, AuthorOrCheckerRequiredMixin, DeleteView): model = Structure template_name = "confirm_delete.html" success_url = reverse_lazy("atlas:structure_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 ConditionUpdate( RevisionMixin, LoginRequiredMixin, AuthorOrCheckerRequiredMixin, UpdateView ): model = Condition form_class = ConditionForm class ConditionCreate(RevisionMixin, LoginRequiredMixin, CreateView): model = Condition form_class = ConditionForm class FindingUpdate( RevisionMixin, LoginRequiredMixin, AuthorOrCheckerRequiredMixin, UpdateView ): model = Finding form_class = FindingForm class FindingCreate(RevisionMixin, LoginRequiredMixin, CreateView): model = Finding form_class = FindingForm class StructureUpdate( RevisionMixin, LoginRequiredMixin, AuthorOrCheckerRequiredMixin, 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 ) 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, CreateView): 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["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) 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) class AtlasClone(AtlasCreateBase, 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"]) 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}) @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 = 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 @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("