allow merging of examinations

This commit is contained in:
Ross
2023-07-03 11:10:50 +01:00
parent 1ade42d7ba
commit 6b5c3f8914
14 changed files with 178 additions and 39 deletions
+2
View File
@@ -128,6 +128,8 @@ class ExaminationForm(ModelForm):
model = Examination
fields = ["examination", "modality"]
class ExaminationMergeForm(Form):
examination_to_merge_into = ModelChoiceField(queryset=Examination.objects.all())
class QuestionNoteForm(ModelForm):
class Meta:
+54 -3
View File
@@ -1,7 +1,7 @@
from collections import defaultdict
import json
import os
from typing import Tuple
from typing import Self, Tuple
from django.contrib.auth.mixins import LoginRequiredMixin
from django.db import models
from django.http import HttpRequest
@@ -72,15 +72,66 @@ class Contrast(models.Model):
class Examination(models.Model):
"""
Currently used for
- Atlas series
- Longs series
- Anatomy questions
Not used for rapids - yet??
"""
examination = models.CharField(max_length=200, unique=True)
modality = models.ForeignKey(Modality, on_delete=models.SET_NULL, null=True)
class Meta:
ordering = ("examination",)
def __str__(self):
return self.examination
class Meta:
ordering = ("examination",)
def merge_into(self, examination_to_merge_into: Self, delete: bool=True) -> bool:
"""Merges the current examination into another
To do so it replaces all associations (many to many and foreign key relations)
Returns True if successful
"""
# Get all atlas series with the examination
atlas_series = self.atlas_series_examination.all()
longs_series = self.series_examination.all()
anatomy_questions = self.anatomyquestion_set.all()
# Reassign them to the new object
for series in atlas_series:
series.examination = examination_to_merge_into
series.save()
for series in longs_series:
series.examination = examination_to_merge_into
series.save()
for question in anatomy_questions:
question.examination = examination_to_merge_into
question.save()
# quick check to make sure nothing has gone wrong and we have hanging
# associations
querysets = (
self.atlas_series_examination.all(),
self.series_examination.all(),
self.anatomyquestion_set.all(),
)
for q in querysets:
if q.count() > 0:
return False
if delete:
self.delete()
return True
class Site(models.Model):
@@ -16,27 +16,48 @@
<h3>Name: {{examination.examination}}</h3>
Modality: {{examination.modality}}
</div>
{% if examination.atlas_series_examination_set.all %}
<h4>Associated Atlas Cases</h4>
{% if examination.atlas_series_examination.all %}
<h4>Associated Atlas Series</h4>
<ul>
{% for case in examination.atlas_series_examination_set.all %}
<li>{{case}}</li>
{% for series in examination.atlas_series_examination.all %}
<li>{{series.get_link}}</li>
{% endfor %}
</ul>
{% endif %}
{% if examination.longs_series_examination_set.all %}
<h4>Associated Longs Cases</h4>
{% if examination.series_examination.all %}
<h4>Associated Longs Series</h4>
<ul>
{% for case in examination.longs_series_examination_set.all %}
<li>{{case}}</li>
{% for series in examination.series_examination.all %}
<li>{{series.get_link}}</li>
{% endfor %}
</ul>
{% endif %}
<details><summary>Merge</summary>
<form hx-post="{% url 'generic:examination_merge' examination.pk %}"
hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
hx-confirm="Are you sure?"
hx-target="#result">
{{merge_form}}
<p>This will replace all occurances of the "{{examination}}" and with the selected examination and delete it.</p>
<p>It cannot be undone</p>
<button type="submit">
Merge
</button>
</form>
<section>
<p id="result"><em></em></p>
</section>
</details>
{% endblock %}
{% block js %}
@@ -0,0 +1,14 @@
{% if not form.is_valid %}
Please enter a number
{% elif examination_to_merge_into %}
{% if success %}
Merged into {{examination_to_merge_into}} successfully
{% else %}
There was an error merging into {{examination_to_merge_into}}
{% endif %}
{% else %}
Examination to merge into is invalid: {{examination_to_merge_into}}
{% endif %}
+1
View File
@@ -13,6 +13,7 @@ urlpatterns = [
path("examination/<int:pk>", views.examination_detail, name="examination_detail"),
path("examination/<int:pk>/delete", views.ExaminationDelete.as_view(), name="examination_delete"),
path("examination/<int:pk>/update", views.ExaminationUpdate.as_view(), name="examination_update"),
path("examination/<int:pk>/merge", views.examination_merge, name="examination_merge"),
path("examination/create/", views.create_examination, name="create_examination"),
path(
"examination/ajax/get_examination_id",
+55 -17
View File
@@ -54,6 +54,7 @@ from .forms import (
CidUserExamForm,
ExaminationForm,
CidUserGroupForm,
ExaminationMergeForm,
SupervisorForm,
UserUserForm,
UserUserGroupForm,
@@ -178,22 +179,59 @@ def create_examination(request):
request, "rapids/create_simple.html", {"form": form, "name": "Examination"}
)
@login_required
@user_passes_test(lambda u: u.is_superuser)
def examination_detail(request, pk):
examination = get_object_or_404(Examination, pk=pk)
merge_form = ExaminationMergeForm()
# logging.debug(atlas.subspecialty.first().name.all())
return render(
request,
"generic/examination_detail.html",
{"examination": examination, "merge_form": merge_form},
)
@login_required
@user_passes_test(lambda u: u.is_superuser)
def examination_merge(request, pk):
examination = get_object_or_404(Examination, pk=pk)
form = ExaminationMergeForm(request.POST)
if form.is_valid():
examination_to_merge_into = form.cleaned_data["examination_to_merge_into"]
success = False
if examination.merge_into(examination_to_merge_into):
success = True
else:
examination_to_merge_into = False
return render(
request,
"generic/examination_merge.html",
{
"form": form,
"examination": examination,
"examination_to_merge_into": examination_to_merge_into,
"success": success
},
)
# logging.debug(atlas.subspecialty.first().name.all())
return render(
request,
"generic/examination_merge.html",
{
"examination": examination,
},
)
@csrf_exempt
def get_examination_id(request):
if request.accepts("application/json")():
@@ -818,17 +856,17 @@ class ExamViews(View, LoginRequiredMixin):
.prefetch_related(
Prefetch(
"series",
queryset=LongSeries.objects.select_related("modality", "examination", "plane", "contrast").prefetch_related(
Prefetch("images")
),
),
"author"
#to_attr="prefetched_primary_answer"
# queryset=self.Answer.objects.filter(),
#"series",
#"abnormality",
#"region",
#"examination",
queryset=LongSeries.objects.select_related(
"modality", "examination", "plane", "contrast"
).prefetch_related(Prefetch("images")),
),
"author"
# to_attr="prefetched_primary_answer"
# queryset=self.Answer.objects.filter(),
# "series",
# "abnormality",
# "region",
# "examination",
)
.all()
)
@@ -849,7 +887,7 @@ class ExamViews(View, LoginRequiredMixin):
"abnormality",
"region",
"examination",
"author"
"author",
)
)
# questions = (
@@ -1835,7 +1873,7 @@ class ExamViews(View, LoginRequiredMixin):
if not user_answer or user_answer is None:
# skip if no answer
score, text = q.get_unanswered_mark_and_text()
#answers_marks.append(score)
# answers_marks.append(score)
# answers.append("")
answer_score = score
ans = text
@@ -1872,7 +1910,6 @@ class ExamViews(View, LoginRequiredMixin):
case _:
max_score = len(questions)
template_context = {
"exam": exam,
"cid": cid,
@@ -2092,7 +2129,7 @@ class ExamViews(View, LoginRequiredMixin):
exam.save()
missing_user_ids = valid_user_users - user_ids
missing_user_ids = valid_user_users - user_ids
missing_users = []
if missing_user_ids:
@@ -2971,6 +3008,7 @@ class SupervisorCreate(CidManagerRequiredMixin, CreateView):
class SupervisorList(CidManagerRequiredMixin, ListView):
model = Supervisor
class ExaminationView(SuperuserRequiredMixin, SingleTableMixin, FilterView):
model = Examination
table_class = ExaminationTable
@@ -2987,4 +3025,4 @@ class ExaminationDelete(RevisionMixin, SuperuserRequiredMixin, DeleteView):
class ExaminationUpdate(UpdateView, SuperuserRequiredMixin):
model = Examination
form_class = ExaminationForm
form_class = ExaminationForm