start atlas app
This commit is contained in:
Executable
+547
@@ -0,0 +1,547 @@
|
||||
import json
|
||||
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 (
|
||||
ObjectDoesNotExist,
|
||||
PermissionDenied,
|
||||
ViewDoesNotExist,
|
||||
)
|
||||
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
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 (
|
||||
AtlasForm,
|
||||
SeriesForm,
|
||||
SeriesImageFormSet,
|
||||
MarkAtlasQuestionDoubleForm,
|
||||
MarkAtlasQuestionSingleForm,
|
||||
SeriesFormSet,
|
||||
ExaminationForm,
|
||||
ExamForm,
|
||||
)
|
||||
from .models import (
|
||||
Case,
|
||||
Series,
|
||||
Examination,
|
||||
)
|
||||
from .tables import AtlasTable, SeriesTable, UserAnswerTable
|
||||
from .filters import AtlasFilter, SeriesFilter, UserAnswerFilter
|
||||
|
||||
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 atlas.forms import AtlasCreationDefaultForm
|
||||
from atlas.models import AtlasCreationDefault
|
||||
|
||||
from generic.views import ExamCloneMixin, ExamViews
|
||||
from reversion.views import RevisionMixin
|
||||
import reversion
|
||||
|
||||
|
||||
from rest_framework import viewsets
|
||||
|
||||
from .serializers import ExamSerializer
|
||||
|
||||
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):
|
||||
atlas = 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", {"question": atlas})
|
||||
|
||||
|
||||
@login_required
|
||||
@user_is_author_or_atlas_series_checker_or_atlas_marker
|
||||
def atlas_series_detail(request, pk):
|
||||
series = get_object_or_404(Series, pk=pk)
|
||||
|
||||
# if request.user not in atlas.author.all():
|
||||
# raise PermissionDenied
|
||||
|
||||
# logging.debug(atlas.subspecialty.first().name.all())
|
||||
return render(request, "atlas/atlas_series.html", {"series": series})
|
||||
|
||||
|
||||
@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:atlas_view")
|
||||
|
||||
|
||||
class SeriesDelete(RevisionMixin, AuthorOrCheckerRequiredMixin, DeleteView):
|
||||
model = Series
|
||||
success_url = reverse_lazy("atlas: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 = AtlasForm(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/atlas_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()
|
||||
|
||||
# Do this in the form.save()
|
||||
# new_exams = list(form.cleaned_data['exams'].values_list("pk", flat=True))
|
||||
# self.object.exams.clear()
|
||||
# self.object.exams.add(*new_exams)
|
||||
|
||||
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 = AtlasForm
|
||||
|
||||
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["series_formset"].full_clean()
|
||||
else:
|
||||
context["series_formset"] = SeriesFormSet()
|
||||
return context
|
||||
|
||||
def form_valid(self, form):
|
||||
|
||||
self.object = form.save(commit=False)
|
||||
|
||||
# Add exam objects
|
||||
|
||||
self.object.save()
|
||||
|
||||
# Do this in the form.save()
|
||||
# new_exams = list(form.cleaned_data['exams'].values_list("pk", flat=True))
|
||||
# self.object.exams.clear()
|
||||
# self.object.exams.add(*new_exams)
|
||||
|
||||
form.instance.author.add(self.request.user.id)
|
||||
|
||||
context = self.get_context_data(form=form)
|
||||
series_formset = context["series_formset"]
|
||||
if series_formset.is_valid():
|
||||
response = super().form_valid(form)
|
||||
series_formset.instance = self.object
|
||||
series_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 = AtlasForm
|
||||
|
||||
# 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):
|
||||
# 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()
|
||||
|
||||
# Do this in the form.save()
|
||||
# new_exams = list(form.cleaned_data['exams'].values_list("pk", flat=True))
|
||||
# self.object.exams.clear()
|
||||
# self.object.exams.add(*new_exams)
|
||||
|
||||
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()
|
||||
|
||||
# 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 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('<script>opener.closePopup(window, "%s", "%s", "#id_abnormality");</script>' % (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(
|
||||
'<script>opener.closePopup(window, "%s", "%s", "#id_examination");</script>'
|
||||
% (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 AtlasView(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 atlas_series_order_dicom(request, pk):
|
||||
series = get_object_or_404(Series, pk=pk)
|
||||
try:
|
||||
series.order_by_dicom()
|
||||
except:
|
||||
return HttpResponse("<h1>Series does not appear to contain dicoms</h1>")
|
||||
|
||||
return redirect("atlas:atlas_series_detail", pk=pk)
|
||||
|
||||
|
||||
@login_required
|
||||
@user_is_author_or_atlas_series_checker
|
||||
def atlas_series_order_dicom_instance(request, pk):
|
||||
series = get_object_or_404(Series, pk=pk)
|
||||
try:
|
||||
series.order_by_dicom("InstanceNumber")
|
||||
except:
|
||||
return HttpResponse(
|
||||
"<h1>Series does not appear to contain dicoms (or field InstanceNumber)</h1>"
|
||||
)
|
||||
|
||||
return redirect("atlas:atlas_series_detail", pk=pk)
|
||||
|
||||
|
||||
@login_required
|
||||
@user_is_author_or_atlas_series_checker
|
||||
def atlas_series_order_dicom_SeriesInstanceUID(request, pk):
|
||||
series = get_object_or_404(Series, pk=pk)
|
||||
try:
|
||||
series.order_by_dicom("SeriesInstanceUID")
|
||||
except:
|
||||
return HttpResponse(
|
||||
"<h1>Series does not appear to contain dicoms (or field SeriesInstanceUID)</h1>"
|
||||
)
|
||||
|
||||
return redirect("atlas:atlas_series_detail", pk=pk)
|
||||
|
||||
|
||||
@login_required
|
||||
@user_is_author_or_atlas_series_checker
|
||||
def atlas_series_order_upload_filename(request, pk):
|
||||
series = get_object_or_404(Series, pk=pk)
|
||||
series.order_by_upload_filename()
|
||||
|
||||
return redirect("atlas: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()]
|
||||
|
||||
Reference in New Issue
Block a user