625 lines
20 KiB
Python
Executable File
625 lines
20 KiB
Python
Executable File
from django import forms
|
|
from django.contrib.admin import widgets
|
|
from django.forms import (
|
|
BaseInlineFormSet,
|
|
Form,
|
|
HiddenInput,
|
|
ModelForm,
|
|
ModelMultipleChoiceField,
|
|
ModelChoiceField,
|
|
ChoiceField,
|
|
CharField,
|
|
ValidationError,
|
|
modelformset_factory,
|
|
CheckboxSelectMultiple
|
|
)
|
|
from django.forms import inlineformset_factory
|
|
from django.shortcuts import get_object_or_404
|
|
|
|
from atlas.models import (
|
|
Case,
|
|
CaseCollection,
|
|
CaseDetail,
|
|
CidReportAnswer,
|
|
Differential,
|
|
Finding,
|
|
SelfReview,
|
|
Series,
|
|
SeriesImage,
|
|
SeriesFinding,
|
|
Condition,
|
|
Structure,
|
|
Subspecialty,
|
|
UncategorisedDicom,
|
|
UserReportAnswer,
|
|
)
|
|
|
|
from anatomy.models import Modality
|
|
|
|
from generic.models import Examination#, Sign
|
|
|
|
# from generic.models import Examination, Site, Condition, Sign
|
|
|
|
from django.contrib.admin.widgets import FilteredSelectMultiple
|
|
from django.forms.widgets import RadioSelect, TextInput, Textarea
|
|
|
|
from tinymce.widgets import TinyMCE
|
|
|
|
from dal import autocomplete
|
|
|
|
from autocomplete import widgets as htmx_widgets
|
|
|
|
import logging
|
|
|
|
class ConditionForm(ModelForm):
|
|
class Meta:
|
|
model = Condition
|
|
exclude = ["rcr_curriculum"]
|
|
|
|
widgets = {
|
|
"synonym": autocomplete.ModelSelect2Multiple(
|
|
url="atlas:condition-autocomplete"
|
|
),
|
|
"parent": autocomplete.ModelSelect2Multiple(
|
|
url="atlas:condition-autocomplete"
|
|
),
|
|
"rcr_curriculum_map": autocomplete.ModelSelect2Multiple(
|
|
url="atlas:condition-autocomplete-rcr-curriculum"
|
|
),
|
|
}
|
|
|
|
|
|
class CaseCollectionForm(ModelForm):
|
|
class Meta:
|
|
model = CaseCollection
|
|
exclude = ["cases", "valid_cid_users"]
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
self.user = kwargs.pop(
|
|
"user"
|
|
) # To get request.user. Do not use kwargs.pop('user', None) due to potential security hole
|
|
if kwargs.get("instance"):
|
|
# We get the 'initial' keyword argument or initialize it
|
|
# as a dict if it didn't exist.
|
|
initial = kwargs.setdefault("initial", {})
|
|
# The widget for a ModelMultipleChoiceField expects
|
|
# a list of primary key for the selected data.
|
|
# initial["exams"] = [t.pk for t in kwargs["instance"].exams.all()]
|
|
|
|
ModelForm.__init__(self, *args, **kwargs)
|
|
|
|
super(CaseCollectionForm, self).__init__(*args, **kwargs)
|
|
|
|
|
|
class FindingForm(ModelForm):
|
|
class Meta:
|
|
model = Finding
|
|
exclude = []
|
|
|
|
widgets = {
|
|
"synonym": autocomplete.ModelSelect2Multiple(
|
|
url="atlas:finding-autocomplete"
|
|
),
|
|
}
|
|
|
|
|
|
class StructureForm(ModelForm):
|
|
class Meta:
|
|
model = Structure
|
|
exclude = []
|
|
|
|
widgets = {
|
|
"synonym": autocomplete.ModelSelect2Multiple(
|
|
url="atlas:structure-autocomplete"
|
|
),
|
|
}
|
|
|
|
|
|
class SeriesFindingForm(ModelForm):
|
|
class Meta:
|
|
model = SeriesFinding
|
|
exclude = ["series", "annotation_json", "viewport_json"]
|
|
|
|
widgets = {
|
|
"findings": autocomplete.ModelSelect2Multiple(
|
|
url="atlas:finding-autocomplete"
|
|
),
|
|
"structures": autocomplete.ModelSelect2Multiple(
|
|
url="atlas:structure-autocomplete"
|
|
),
|
|
}
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
if kwargs.get("series_id"):
|
|
# We get the 'initial' keyword argument or initialize it
|
|
# as a dict if it didn't exist.
|
|
initial = kwargs.setdefault("initial", {})
|
|
# The widget for a ModelMultipleChoiceField expects
|
|
# a list of primary key for the selected data.
|
|
initial["series"] = kwargs.pop("series_id")
|
|
# elif kwargs.get("instance"):
|
|
# # We get the 'initial' keyword argument or initialize it
|
|
# # as a dict if it didn't exist.
|
|
# initial = kwargs.setdefault("initial", {})
|
|
# # The widget for a ModelMultipleChoiceField expects
|
|
# # a list of primary key for the selected data.
|
|
# #initial["structures"] = [t.pk for t in kwargs["structures"].case.all()]
|
|
# #initial["structures"] = [t.pk for t in kwargs["structures"].case.all()]
|
|
|
|
super(SeriesFindingForm, self).__init__(*args, **kwargs)
|
|
|
|
ModelForm.__init__(self, *args, **kwargs)
|
|
|
|
|
|
class SeriesForm(ModelForm):
|
|
class Media:
|
|
# Django also includes a few javascript files necessary
|
|
# for the operation of this form element. You need to
|
|
# include <script src="/admin/jsi18n"></script>
|
|
# in the template.
|
|
css = {
|
|
"all": ["css/widgets.css"],
|
|
}
|
|
# Adding this javascript is crucial
|
|
js = ["jsi18n.js", "tesseract.min.js", "js/upload_form_helpers.js"]
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
self.user = kwargs.pop(
|
|
"user"
|
|
) # To get request.user. Do not use kwargs.pop('user', None) due to potential security hole
|
|
if kwargs.get("instance"):
|
|
# We get the 'initial' keyword argument or initialize it
|
|
# as a dict if it didn't exist.
|
|
initial = kwargs.setdefault("initial", {})
|
|
# The widget for a ModelMultipleChoiceField expects
|
|
# a list of primary key for the selected data.
|
|
initial["case"] = [t.pk for t in kwargs["instance"].case.all()]
|
|
|
|
super(SeriesForm, self).__init__(*args, **kwargs)
|
|
# self.fields["examination"] = ModelChoiceField(
|
|
# queryset=Examination.objects.all(),
|
|
# widget=FilteredSelectMultiple(verbose_name="Examination", is_stacked=False),
|
|
# )
|
|
|
|
self.fields["modality"] = ModelChoiceField(
|
|
required=True,
|
|
queryset=Modality.objects.all(),
|
|
)
|
|
|
|
ModelForm.__init__(self, *args, **kwargs)
|
|
|
|
if self.user.groups.filter(name="atlas_editor").exists():
|
|
case_queryset = Case.objects.all()
|
|
else:
|
|
case_queryset = Case.objects.filter(
|
|
author__id=self.user.id
|
|
) # | Case.objects.filter(open_access=True)
|
|
|
|
self.fields["case"] = ModelMultipleChoiceField(
|
|
required=False,
|
|
queryset=case_queryset,
|
|
widget=FilteredSelectMultiple(verbose_name="Case", is_stacked=False),
|
|
)
|
|
|
|
def save(self, commit=True):
|
|
# Get the unsaved Long instance
|
|
instance = ModelForm.save(self, False)
|
|
|
|
# Prepare a 'save_m2m' method for the form,
|
|
old_save_m2m = self.save_m2m
|
|
|
|
def save_m2m():
|
|
old_save_m2m()
|
|
instance.case.clear()
|
|
for case in self.cleaned_data["case"]:
|
|
# We need the id here
|
|
instance.case.add(case.pk)
|
|
|
|
self.save_m2m = save_m2m
|
|
|
|
# Do we need to save all changes now?
|
|
# if commit:
|
|
instance.save()
|
|
self.save_m2m()
|
|
|
|
# instance.anonymise_images()
|
|
|
|
return instance
|
|
|
|
class Meta:
|
|
model = Series
|
|
exclude = ["author", "series_instance_uid"]
|
|
|
|
widgets = {
|
|
"examination": autocomplete.ModelSelect2(
|
|
url="generic:examination-autocomplete"
|
|
),
|
|
"description": Textarea(attrs={"maxlength": 1000, "rows": 5, "cols": 80}),
|
|
}
|
|
|
|
|
|
class CaseForm(ModelForm):
|
|
|
|
# exams = ModelMultipleChoiceField(required=False, queryset=Exam.objects.all(),widget=FilteredSelectMultiple(verbose_name="Exams", is_stacked=False))
|
|
|
|
class Media:
|
|
# Django also includes a few javascript files necessary
|
|
# for the operation of this form element. You need to
|
|
# include <script src="/admin/jsi18n"></script>
|
|
# in the template.
|
|
css = {
|
|
"all": ["css/widgets.css"],
|
|
}
|
|
# Adding this javascript is crucial
|
|
js = ["jsi18n.js", "tesseract.min.js", "js/upload_form_helpers.js"]
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
logging.info("LOG")
|
|
logging.debug(kwargs)
|
|
self.user = kwargs.pop(
|
|
"user"
|
|
) # To get request.user. Do not use kwargs.pop('user', None) due to potential security hole
|
|
|
|
# This is populated if we create the case from an exam/collection
|
|
self.collection = None
|
|
if kwargs["initial"] is not None and "exams" in kwargs["initial"]:
|
|
collections = kwargs["initial"].pop("exams")
|
|
logging.debug(collections)
|
|
|
|
self.collection = get_object_or_404(CaseCollection, pk=collections[0])
|
|
|
|
if kwargs.get("instance"):
|
|
# We get the 'initial' keyword argument or initialize it
|
|
# as a dict if it didn't exist.
|
|
initial = kwargs.setdefault("initial", {})
|
|
# The widget for a ModelMultipleChoiceField expects
|
|
# a list of primary key for the selected data.
|
|
# initial["exams"] = [t.pk for t in kwargs["instance"].exams.all()]
|
|
|
|
|
|
ModelForm.__init__(self, *args, **kwargs)
|
|
|
|
super(CaseForm, self).__init__(*args, **kwargs)
|
|
|
|
# self.fields["series"].queryset =
|
|
|
|
def get_queryset(self, request):
|
|
return (
|
|
super()
|
|
.get_queryset(request)
|
|
.prefetch_related(
|
|
"condition", "subspecialty", "pathological_process", "series"
|
|
)
|
|
)
|
|
|
|
class Meta:
|
|
model = Case
|
|
# fields = ['due_back']
|
|
# fields = '__all__'
|
|
fields = [
|
|
# "examination",
|
|
# "site",
|
|
"title",
|
|
"subspecialty",
|
|
"description",
|
|
"history",
|
|
"presentation",
|
|
"discussion",
|
|
"condition",
|
|
"pathological_process",
|
|
"report",
|
|
"open_access",
|
|
"previous_case",
|
|
"diagnostic_certainty",
|
|
]
|
|
# fields = ['question', 'findings', 'subspecialty', 'references']
|
|
widgets = {
|
|
# "normal": RadioSelect(
|
|
# choices=[(True, 'Yes'),
|
|
# (False, 'No')])
|
|
# "findings": TinyMCE(attrs={"cols": 80, "rows": 20}),
|
|
# "mark_scheme": TinyMCE(attrs={"cols": 80, "rows": 30}),
|
|
"description": Textarea(attrs={"cols": 80, "rows": 5}),
|
|
"history": Textarea(attrs={"cols": 80, "rows": 5}),
|
|
"discussion": Textarea(attrs={"cols": 80, "rows": 5}),
|
|
"condition": autocomplete.ModelSelect2Multiple(
|
|
url="atlas:condition-autocomplete"
|
|
),
|
|
"presentation": autocomplete.ModelSelect2Multiple(
|
|
url="atlas:presentation-autocomplete"
|
|
),
|
|
"subspecialty": CheckboxSelectMultiple(),
|
|
"pathological_process": CheckboxSelectMultiple()
|
|
}
|
|
|
|
def save(self, commit=True):
|
|
# Get the unsaved Case instance
|
|
instance = ModelForm.save(self, False)
|
|
|
|
|
|
|
|
|
|
# Prepare a 'save_m2m' method for the form,
|
|
old_save_m2m = self.save_m2m
|
|
|
|
def save_m2m():
|
|
old_save_m2m()
|
|
# instance.exams.clear()
|
|
# for exam in self.cleaned_data["exams"]:
|
|
# # We need the id here
|
|
# instance.exams.add(exam.pk)
|
|
|
|
self.save_m2m = save_m2m
|
|
|
|
# Do we need to save all changes now?
|
|
# if commit:
|
|
instance.save()
|
|
self.save_m2m()
|
|
|
|
logging.debug(f"{self.collection=}")
|
|
|
|
if self.collection is not None:
|
|
logging.debug(f"{self.collection=}")
|
|
case_no = self.collection.cases.count() + 1
|
|
logging.debug(f"{case_no=}")
|
|
logging.debug(f"{instance=}")
|
|
|
|
# Create through model
|
|
#cd = CaseDetail(case=instance, collection=exam, sort_order=case_no)
|
|
self.collection.cases.add(instance, through_defaults={"sort_order": case_no})
|
|
|
|
return instance
|
|
|
|
|
|
CaseDifferentialFormSet = inlineformset_factory(
|
|
Case,
|
|
Differential,
|
|
exclude=[],
|
|
can_delete=True,
|
|
extra=1,
|
|
max_num=10,
|
|
widgets={
|
|
"condition": autocomplete.ModelSelect2(url="atlas:condition-autocomplete"),
|
|
"text": Textarea(attrs={"maxlength": 1000, "rows": 3, "cols": 80}),
|
|
},
|
|
)
|
|
|
|
|
|
class CaseSeriesForm(ModelForm):
|
|
|
|
def __init__(self, *args, user, **kwargs):
|
|
super(CaseSeriesForm, self).__init__(*args, **kwargs)
|
|
|
|
if not user.groups.filter(name="atlas_editor").exists():
|
|
queryset = Series.objects.filter(author__id=user.id)
|
|
else:
|
|
queryset = Series.objects.all()
|
|
|
|
self.fields["series"] = ModelChoiceField(
|
|
required=False,
|
|
queryset=queryset,
|
|
# widget=Select(verbose_name="Series", is_stacked=False),
|
|
)
|
|
|
|
|
|
|
|
|
|
class CaseCollectionCaseForm(ModelForm):
|
|
def __init__(self, *args, user, **kwargs):
|
|
super(CaseCollectionCaseForm, self).__init__(*args, **kwargs)
|
|
|
|
if not user.groups.filter(name="atlas_editor").exists():
|
|
queryset = Case.objects.filter(author__id=user.id)
|
|
else:
|
|
queryset = Case.objects.all()
|
|
|
|
self.fields["case"] = ModelChoiceField(
|
|
required=False,
|
|
queryset=queryset,
|
|
# widget=Select(verbose_name="Series", is_stacked=False),
|
|
)
|
|
|
|
|
|
SeriesFormSet = inlineformset_factory(
|
|
Case,
|
|
Series.case.through,
|
|
form=CaseSeriesForm,
|
|
exclude=[],
|
|
can_delete=True,
|
|
extra=0,
|
|
max_num=10,
|
|
)
|
|
|
|
CaseCollectionCaseFormSet = inlineformset_factory(
|
|
CaseCollection,
|
|
Case.casecollection_set.through,
|
|
form=CaseCollectionCaseForm,
|
|
exclude=[],
|
|
can_delete=True,
|
|
extra=0,
|
|
max_num=50,
|
|
)
|
|
|
|
|
|
SeriesImageFormSet = inlineformset_factory(
|
|
Series,
|
|
SeriesImage,
|
|
exclude=[],
|
|
can_delete=True,
|
|
extra=0,
|
|
max_num=2000,
|
|
)
|
|
|
|
class BaseReportAnswerForm(ModelForm):
|
|
class Meta:
|
|
fields = ["answer"]
|
|
|
|
widgets = {
|
|
# "normal": RadioSelect(
|
|
# choices=[(True, 'Yes'),
|
|
# (False, 'No')])
|
|
# "findings": TinyMCE(attrs={"cols": 80, "rows": 20}),
|
|
# "mark_scheme": TinyMCE(attrs={"cols": 80, "rows": 30}),
|
|
"answer": Textarea(attrs={"cols": 100, "rows": 5}),
|
|
}
|
|
|
|
# help_texts = {
|
|
# "answer": "Write your answer in here."
|
|
# }
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(BaseReportAnswerForm, self).__init__(*args, **kwargs)
|
|
self.fields["answer"].required = False
|
|
|
|
class CidReportAnswerForm(BaseReportAnswerForm):
|
|
class Meta(BaseReportAnswerForm.Meta):
|
|
model = CidReportAnswer
|
|
|
|
class UserReportAnswerForm(BaseReportAnswerForm):
|
|
class Meta(BaseReportAnswerForm.Meta):
|
|
model = UserReportAnswer
|
|
|
|
class CidReportAnswerMarkForm(ModelForm):
|
|
class Meta:
|
|
model = CidReportAnswer
|
|
fields = ["score", "feedback"]
|
|
|
|
widgets = {
|
|
# "normal": RadioSelect(
|
|
# choices=[(True, 'Yes'),
|
|
# (False, 'No')])
|
|
# "findings": TinyMCE(attrs={"cols": 80, "rows": 20}),
|
|
# "mark_scheme": TinyMCE(attrs={"cols": 80, "rows": 30}),
|
|
"feedback": Textarea(attrs={"cols": 80, "rows": 4}),
|
|
}
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(CidReportAnswerMarkForm, self).__init__(*args, **kwargs)
|
|
self.fields["score"].required = False
|
|
|
|
|
|
#class AddCollectionToCaseForm(Form):
|
|
#
|
|
# def __init__(self, *args, user, **kwargs):
|
|
# super(AddCollectionToCaseForm, self).__init__(*args, **kwargs)
|
|
# print("INIT", args, kwargs)
|
|
#
|
|
# if not user.groups.filter(name="atlas_editor").exists():
|
|
# queryset = CaseCollection.objects.filter(author__id=user.id)
|
|
# else:
|
|
# queryset = CaseCollection.objects.all()
|
|
#
|
|
# self.valid_collections = queryset
|
|
# self.case = get_object_or_404(Case, pk=pk)
|
|
#
|
|
# self.fields["casecollection"] = ModelMultipleChoiceField(
|
|
# required=False,
|
|
# queryset=queryset,
|
|
# # widget=Select(verbose_name="Series", is_stacked=False),
|
|
# )
|
|
#
|
|
# def save(self, *args, **kwargs):
|
|
# print("SAVE", args, kwargs)
|
|
# instance = super(AddCollectionToCaseForm, self).save(*args, **kwargs)
|
|
#
|
|
# # Here we save the modified project selection back into the database
|
|
# instance.casecollection_set.set(self.cleaned_data['casecollection'])
|
|
#
|
|
# print(f"{instance=}")
|
|
#
|
|
# return instance
|
|
#
|
|
# def clean_casecollection(self):
|
|
# collection_set = self.cleaned_data["casecollection"]
|
|
#
|
|
# print(f"{collection_set=}")
|
|
#
|
|
# for col in collection_set:
|
|
# if col not in self.valid_collections:
|
|
# raise ValidationError("Invalid collection")
|
|
#
|
|
# return collection_set
|
|
|
|
class AddCollectionToCaseForm(Form):
|
|
|
|
def __init__(self, *args, user, **kwargs):
|
|
super(AddCollectionToCaseForm, self).__init__(*args, **kwargs)
|
|
print("INIT", args, kwargs)
|
|
|
|
if not user.groups.filter(name="atlas_editor").exists():
|
|
queryset = CaseCollection.objects.filter(author__id=user.id)
|
|
else:
|
|
queryset = CaseCollection.objects.all()
|
|
|
|
self.valid_collections = queryset
|
|
|
|
self.fields["casecollection"] = ModelMultipleChoiceField(
|
|
required=False,
|
|
queryset=queryset,
|
|
# widget=Select(verbose_name="Series", is_stacked=False),
|
|
)
|
|
|
|
def save(self, *args, **kwargs):
|
|
print("SAVE", args, kwargs)
|
|
instance = super(AddCollectionToCaseForm, self).save(*args, **kwargs)
|
|
|
|
# Here we save the modified project selection back into the database
|
|
instance.casecollection_set.set(self.cleaned_data['casecollection'])
|
|
|
|
print(f"{instance=}")
|
|
|
|
return instance
|
|
|
|
def clean_casecollection(self):
|
|
collection_set = self.cleaned_data["casecollection"]
|
|
|
|
print(f"{collection_set=}")
|
|
|
|
for col in collection_set:
|
|
if col not in self.valid_collections:
|
|
raise ValidationError("Invalid collection")
|
|
|
|
return collection_set
|
|
|
|
|
|
class SelfReviewForm(ModelForm):
|
|
|
|
class Meta:
|
|
model = SelfReview
|
|
fields = ["user_exam", "case", "comments", "findings", "interpretation"]
|
|
widgets = {"user_exam": HiddenInput(), "case": HiddenInput()}
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
if "user_exam_id" in kwargs:
|
|
user_exam_id = kwargs.pop("user_exam_id")
|
|
if "case_id" in kwargs:
|
|
case_id = kwargs.pop("case_id")
|
|
|
|
ModelForm.__init__(self, *args, **kwargs)
|
|
super(SelfReviewForm, self).__init__(*args, **kwargs)
|
|
|
|
|
|
class UncategorisedDicomForm(ModelForm):
|
|
class Meta:
|
|
model = UncategorisedDicom
|
|
fields = ["image"]#, "user"]
|
|
|
|
|
|
#UncategorisedDicomFormset = formset_factory(UncategorisedDicomForm)
|
|
|
|
#class ConditionAutocompleteFormModel(forms.ModelForm):
|
|
# class Meta:
|
|
# model = Condition
|
|
# fields = ["name"]
|
|
# widgets = {
|
|
# 'members': widgets.Autocomplete(
|
|
# name='members',
|
|
# options=dict(multiselect=True, model=Condition)
|
|
# )
|
|
# }
|
|
|
|
class ConditionAutocompleteForm(Form):
|
|
condition = ModelChoiceField(queryset=Condition.objects.all(), widget=htmx_widgets.Autocomplete(
|
|
name="conditions", options=dict(model=Condition)
|
|
))
|