Files
penracourses/atlas/views.py
T
2023-12-04 12:19:52 +00:00

2032 lines
63 KiB
Python
Executable File

import json
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.detail import DetailView
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 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_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
@user_is_author_or_atlas_editor
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_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})
@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
def user_uploads(request, case_id: int | None = None):
dicoms = UncategorisedDicom.objects.filter(user=request.user)
data = defaultdict(list)
for d in dicoms:
tags = d.get_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)
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('<script>opener.closePopup(window, "%s", "%s", "#id_abnormality");</script>' % (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
@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("<h1>Series does not appear to contain dicoms</h1>")
return redirect("atlas:series_detail", pk=pk)
@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(
"<h1>Series does not appear to contain dicoms (or field InstanceNumber)</h1>"
)
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(
"<h1>Series does not appear to contain dicoms (or field SeriesInstanceUID)</h1>"
)
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_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_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())
@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)