Files
penracourses/atlas/forms.py
T

674 lines
21 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,
Resource,
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
from generic.forms import ExamAuthorFormMixin
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", "author"]
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)
def save(self, commit=True):
# Get the unsaved Case instance
instance = ModelForm.save(self, False)
# Do we need to save all changes now?
# if commit:
instance.save()
self.save_m2m()
return instance
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"
),
"conditions": autocomplete.ModelSelect2Multiple(
url="atlas:condition-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 CaseResourceForm(ModelForm):
def __init__(self, *args, user, **kwargs):
super(CaseResourceForm, self).__init__(*args, **kwargs)
if not user.groups.filter(name="atlas_editor").exists():
queryset = Resource.objects.filter(author__id=user.id)
else:
queryset = Resource.objects.all()
self.fields["resource"] = ModelChoiceField(
required=False,
queryset=queryset,
# widget=Select(verbose_name="Series", is_stacked=False),
)
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,
)
CaseResourceFormSet = inlineformset_factory(
Case,
Resource.case_set.through,
form=CaseResourceForm,
exclude=[],
can_delete=True,
extra=0,
max_num=10,
)
SeriesImageFormSet = inlineformset_factory(
Series,
SeriesImage,
exclude=["dicom_tags_ohif", "replaced", "image_md5_hash", "image_blake3_hash", "is_dicom"],
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)
))
class CaseCollectionAuthorForm(ExamAuthorFormMixin):
class Meta(ExamAuthorFormMixin.Meta):
model = CaseCollection
class CaseAuthorForm(ExamAuthorFormMixin):
class Meta(ExamAuthorFormMixin.Meta):
model = Case