from django.forms import ( Form, ModelForm, ModelMultipleChoiceField, ModelChoiceField, ChoiceField, CharField, ) from django.forms import inlineformset_factory from generic.forms import ExamAuthorFormMixin, ExamFormMixin, ExamGroupsFormMixin, ExamMarkerFormMixin from shorts.models import ( Abnormality, AnswerMarks, Examination, Question, QuestionFinding, QuestionImage, Region, SampleAnswer, UserAnswer, Exam, ) from django.contrib.admin.widgets import FilteredSelectMultiple from django.forms.widgets import RadioSelect, TextInput, Textarea from django.contrib.auth.models import User from crispy_forms.helper import FormHelper from crispy_forms.layout import Submit, Layout, Div, Field, HTML from crispy_forms.bootstrap import InlineRadios from dal import autocomplete from autocomplete import widgets as htmx_widgets, Autocomplete, AutocompleteWidget, ModelAutocomplete, register as autocomplete_register class QuestionSampleAnswerForm(ModelForm): class Meta: model = Question fields = [] class MarkQuestionForm(Form): # correct = forms.CharField(required=False) # half_correct = forms.CharField(required=False) # incorrect = forms.CharField(required=False) marked_answers = CharField(required=False) #class QuestionAnswerForm(ModelForm): # class Meta: # model = Question # fields = [] class QuestionForm(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 # 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["exams"] = [t.pk for t in kwargs["instance"].exams.all()] ModelForm.__init__(self, *args, **kwargs) super(QuestionForm, self).__init__(*args, **kwargs) self.helper = FormHelper() self.helper.form_id = 'id-anatomy-question-form' self.helper.form_tag = False self.helper.layout = Layout( Div( HTML("

Examination*

"), Field('examination', css_class='form-control'), Field('history', css_class='form-control'), Field('marking_guidance', css_class='form-control'), Field('feedback', css_class='form-control'), Field('open_access', css_class='form-control'), HTML("

Exams

"), Field('exams', css_class='form-control'), )) self.fields["examination"] = ModelMultipleChoiceField( queryset=Examination.objects.all(), widget=FilteredSelectMultiple(verbose_name="Examination", is_stacked=False), ) self.fields["examination"].label = "" if self.user.groups.filter(name="shorts_checker").exists(): exam_queryset = Exam.objects.all() else: exam_queryset = Exam.objects.filter( author__id=self.user.id ) | Exam.objects.filter(open_access=True) self.fields["exams"] = ModelMultipleChoiceField( required=False, queryset=exam_queryset.distinct(), widget=FilteredSelectMultiple(verbose_name="Exams", is_stacked=False), ) self.fields["exams"].label = "" self.fields["history"].widget = Textarea( attrs={"required": "false", "minlength": 2, "maxlength": 500, "rows": 1} ) def save(self, commit=True): instance = ModelForm.save(self, False) instance.save() old_exams = instance.exams.all() new_exams = self.cleaned_data["exams"] for exam in old_exams: if exam not in new_exams: exam.exam_questions.remove(instance) for exam in new_exams: exam.exam_questions.add(instance) #exam.save() # # Prepare a 'save_m2m' method for the form, # old_save_m2m = self.save_m2m # # def save_m2m(): # old_save_m2m() # # This is where we actually link the pizza with toppings # instance.exams.clear() # for exam in self.cleaned_data["exams"]: # instance.exams.add(exam) # # self.save_m2m = save_m2m # # # Do we need to save all changes now? # # if commit: # instance.save() self.save_m2m() return instance class Meta: model = Question # fields = ['due_back'] # fields = '__all__' fields = [ "examination", # "site", "feedback", "history", "marking_guidance", "open_access", ] # fields = ['question', 'feedback', 'subspecialty', 'references'] ImageFormSet = inlineformset_factory( Question, QuestionImage, fields=["image", "feedback_image", "filename", "description"], exclude=[], can_delete=True, extra=1, max_num=10, ) #AnswerFormSet = inlineformset_factory( # Question, # SampleAnswer, # fields=["answer", "status"], # widgets={ # "answer": Textarea( # attrs={"required": "false", "minlength": 2, "maxlength": 500, "rows": 3} # ) # }, # exclude=[], # can_delete=True, # extra=0, # max_num=10, #) # SampleAnswerUpdateFormSet = inlineformset_factory( Question, SampleAnswer, fields=["answer", "score", "proposed"], widgets={ "answer": Textarea( attrs={"required": "false", "minlength": 2, "maxlength": 500, "rows": 3} ) }, exclude=[], can_delete=True, extra=0, max_num=10, ) # This should be made generic? class ExamForm(ExamFormMixin, ModelForm): class Meta(ExamFormMixin.Meta): model = Exam class ExamAuthorForm(ExamAuthorFormMixin): class Meta(ExamAuthorFormMixin.Meta): model = Exam class ExamMarkerForm(ExamMarkerFormMixin): class Meta(ExamMarkerFormMixin.Meta): model = Exam class ExamGroupsForm(ExamGroupsFormMixin): class Meta(ExamGroupsFormMixin.Meta): model = Exam class QuestionFindingForm(ModelForm): class Meta: model = QuestionFinding exclude = ["question", "annotation_json", "viewport_json", "current_image_id_index"] 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("question_id"): initial = kwargs.setdefault("initial", {}) initial["question"] = kwargs.pop("question_id") super(QuestionFindingForm, self).__init__(*args, **kwargs) ModelForm.__init__(self, *args, **kwargs) class MarkQuestionSingleForm(ModelForm): class Meta: model = UserAnswer fields = ["score", "candidate_feedback"] widgets = { "candidate_feedback": Textarea(attrs={"rows": 3}), } class MarkQuestionDoubleForm(ModelForm): class Meta: model = AnswerMarks fields = ["score", "mark_reason"]