diff --git a/generic/views.py b/generic/views.py index 460a47cf..96768612 100644 --- a/generic/views.py +++ b/generic/views.py @@ -1891,7 +1891,7 @@ class GenericViewBase: "rapids": "rapid_checker", "anatomy": "anatomy_checker", "longs": "longs_checker", - "sbas": "sbas_checker", + "sbas": "sba_checker", "physics": "physics_checker", } diff --git a/sbas/forms.py b/sbas/forms.py index a6801c5c..525d1704 100755 --- a/sbas/forms.py +++ b/sbas/forms.py @@ -14,11 +14,14 @@ from .models import ( Question, CidUserAnswer, Exam, + Category ) from django.contrib.admin.widgets import FilteredSelectMultiple from django.forms.widgets import RadioSelect, TextInput, Textarea +from tinymce.widgets import TinyMCE + class CidUserAnswerForm(ModelForm): class Meta: @@ -36,4 +39,119 @@ class ExamForm(ExamFormMixin, ModelForm): class ExamAuthorForm(ExamAuthorFormMixin): class Meta(ExamAuthorFormMixin.Meta): - model = Exam \ No newline at end of file + model = Exam + + +class QuestionForm(ModelForm): + + # exams = ModelMultipleChoiceField(required=False, queryset=Exam.objects.all()) + + 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"] + + 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.fields['question'].widget.attrs = {'class': 'question-form', 'rows': 10, 'cols': 100} + # self.fields['feedback'].widget.attrs = {'class': 'feedback-form', 'rows': 10, 'cols': 100} + self.fields["category"] = ModelChoiceField( + required=True, + queryset=Category.objects.all(), + initial=1, + ) + + + if self.user.groups.filter(name="sba_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, + widget=FilteredSelectMultiple(verbose_name="Exams", is_stacked=False), + ) + + def save(self, commit=True): + # Get the unsaved Pizza instance + 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) + + self.save_m2m() + + return instance + + class Meta: + model = Question + + fields = [ + "stem", + "feedback", + "category", + "a_answer", + "a_feedback", + "b_answer", + "b_feedback", + "c_answer", + "c_feedback", + "d_answer", + "d_feedback", + "e_answer", + "e_feedback", + "best_answer", + ] + + widgets = { + # "normal": RadioSelect( + # choices=[(True, 'Yes'), + # (False, 'No')]) + "stem" : TinyMCE(attrs={'cols': 80, 'rows': 30}), + "feedback" : TinyMCE(attrs={'cols': 80, 'rows': 30}), + "a_answer" : TinyMCE(attrs={'cols': 80, 'rows': 5}, mce_attrs={'height': 140}), + "a_feedback" : TinyMCE(attrs={'cols': 80, 'rows': 5}, mce_attrs={'height': 140}), + "b_answer" : TinyMCE(attrs={'cols': 80, 'rows': 4}, mce_attrs={'height': 140}), + "c_answer" : TinyMCE(attrs={'cols': 80, 'rows': 4}, mce_attrs={'height': 140}), + "d_answer" : TinyMCE(attrs={'cols': 80, 'rows': 4}, mce_attrs={'height': 140}), + "e_answer" : TinyMCE(attrs={'cols': 80, 'rows': 4}, mce_attrs={'height': 140}), + "b_feedback" : TinyMCE(attrs={'cols': 80, 'rows': 4}, mce_attrs={'height': 140}), + "c_feedback" : TinyMCE(attrs={'cols': 80, 'rows': 4}, mce_attrs={'height': 140}), + "d_feedback" : TinyMCE(attrs={'cols': 80, 'rows': 4}, mce_attrs={'height': 140}), + "e_feedback" : TinyMCE(attrs={'cols': 80, 'rows': 4}, mce_attrs={'height': 140}), + } + + #widgets = { + # "structure": autocomplete.ModelSelect2(url="anatomy:structure-autocomplete") + #} diff --git a/sbas/models.py b/sbas/models.py index 51dce7a2..d961d55b 100644 --- a/sbas/models.py +++ b/sbas/models.py @@ -124,6 +124,19 @@ class Question(models.Model): self.e_answer, ) + def get_answer_by_choice(self, choice): + if choice == "a": + return self.a_answer + if choice == "b": + return self.b_answer + if choice == "c": + return self.c_answer + if choice == "d": + return self.d_answer + if choice == "e": + return self.e_answer + + def get_correct_answer(self): if self.best_answer == "a": return self.a_answer diff --git a/sbas/templates/sbas/base.html b/sbas/templates/sbas/base.html index 1b4c0ea0..44904176 100644 --- a/sbas/templates/sbas/base.html +++ b/sbas/templates/sbas/base.html @@ -8,7 +8,9 @@ Sbas: {% if request.user.is_authenticated %} Exams / - Questions + Create Exam / + Questions / + Create Question {% if request.user.is_superuser %} / Answers {% endif %} diff --git a/sbas/templates/sbas/exam_scores_user.html b/sbas/templates/sbas/exam_scores_user.html index 1dc39ed9..d91ab6b3 100644 --- a/sbas/templates/sbas/exam_scores_user.html +++ b/sbas/templates/sbas/exam_scores_user.html @@ -1,21 +1,28 @@ {% extends 'sbas/base.html' %} {% block content %} -
{{question.stem|safe}}
+