Files
penracourses/atlas/forms.py
T
2023-07-31 10:25:23 +01:00

590 lines
18 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,
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
import logging
class ConditionForm(ModelForm):
class Meta:
model = Condition
exclude = []
widgets = {
"synonym": autocomplete.ModelSelect2Multiple(
url="atlas:condition-autocomplete"
),
"parent": autocomplete.ModelSelect2Multiple(
url="atlas:condition-autocomplete"
),
}
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"]
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",
]
# 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):
user_exam_id = kwargs.pop("user_exam_id")
case_id = kwargs.pop("case_id")
ModelForm.__init__(self, *args, **kwargs)
super(SelfReviewForm, self).__init__(*args, **kwargs)