diff --git a/physics/forms.py b/physics/forms.py
index dd1bd674..b4c28c58 100755
--- a/physics/forms.py
+++ b/physics/forms.py
@@ -47,4 +47,33 @@ class ExamMarkerForm(ExamMarkerFormMixin):
class ExamGroupsForm(ExamGroupsFormMixin):
class Meta(ExamGroupsFormMixin.Meta):
- model = Exam
\ No newline at end of file
+ model = Exam
+
+
+class QuestionForm(ModelForm):
+ class Meta:
+ model = Question
+ fields = [
+ "stem",
+ "a",
+ "a_answer",
+ "a_feedback",
+ "b",
+ "b_answer",
+ "b_feedback",
+ "c",
+ "c_answer",
+ "c_feedback",
+ "d",
+ "d_answer",
+ "d_feedback",
+ "e",
+ "e_answer",
+ "e_feedback",
+ "category",
+ ]
+
+ def __init__(self, *args, **kwargs):
+ # accept an optional user kwarg for parity with other apps
+ self.user = kwargs.pop("user", None)
+ super().__init__(*args, **kwargs)
\ No newline at end of file
diff --git a/physics/templates/physics/question_form.html b/physics/templates/physics/question_form.html
new file mode 100644
index 00000000..e20843da
--- /dev/null
+++ b/physics/templates/physics/question_form.html
@@ -0,0 +1,40 @@
+{% extends "physics/base.html" %}
+{% load crispy_forms_tags %}
+
+{% block content %}
+
+{% endblock %}
diff --git a/physics/urls.py b/physics/urls.py
index 383c1600..d3355101 100644
--- a/physics/urls.py
+++ b/physics/urls.py
@@ -10,6 +10,8 @@ urlpatterns = []
urlpatterns.extend(
[
path("question/", views.QuestionView.as_view(), name="question_view"),
+ path("question/create/", views.QuestionCreate.as_view(), name="question_create"),
+ path("question/create/exam/", views.QuestionCreate.as_view(), name="question_create_exam"),
path("question//", views.question_detail, name="question_detail"),
path("exam//take_old", views.exam_take_old, name="exam_take_old"),
path(
diff --git a/physics/views.py b/physics/views.py
index e705a6fd..64f03bf4 100644
--- a/physics/views.py
+++ b/physics/views.py
@@ -56,7 +56,14 @@ from generic.views import (
from django.core.exceptions import PermissionDenied
-from .forms import ExamGroupsForm, ExamMarkerForm, UserAnswerForm, ExamAuthorForm, ExamForm
+from .forms import (
+ ExamGroupsForm,
+ ExamMarkerForm,
+ UserAnswerForm,
+ ExamAuthorForm,
+ ExamForm,
+ QuestionForm,
+)
from .decorators import (
user_is_author_or_physics_checker,
user_is_exam_author_or_physics_checker,
@@ -534,6 +541,55 @@ class QuestionView(
return context
+class QuestionCreateBase(RevisionMixin, LoginRequiredMixin, CreateView):
+ model = Question
+ form_class = QuestionForm
+
+ def get_form_kwargs(self):
+ kwargs = super(QuestionCreateBase, self).get_form_kwargs()
+ kwargs.update({"user": self.request.user})
+ return kwargs
+
+ def form_valid(self, form):
+ self.object = form.save(commit=False)
+ self.object.save()
+
+ # add the current user as an author
+ form.instance.author.add(self.request.user.id)
+
+ response = super().form_valid(form)
+ return response
+
+
+class QuestionCreate(QuestionCreateBase):
+ def get_initial(self):
+ if "pk" in self.kwargs:
+ initial = super().get_initial()
+ exam = get_object_or_404(Exam, pk=self.kwargs["pk"])
+ # When creating a question from an exam context we don't have an
+ # 'exams' field on the ModelForm (exams is a reverse relation).
+ # Store nothing in the initial data here; attachment is handled
+ # in form_valid below.
+ return initial
+
+ def form_valid(self, form):
+ # Let the base class save the object and set the author
+ response = super().form_valid(form)
+
+ # If the create URL included an exam pk, add the new question to that exam
+ if "pk" in self.kwargs:
+ try:
+ exam = get_object_or_404(Exam, pk=self.kwargs["pk"])
+ # exam.exam_questions is a ManyToMany through relation; add is fine
+ exam.exam_questions.add(self.object)
+ except Exception:
+ # Don't fail the whole request if attaching to the exam fails;
+ # the question has already been created and author set.
+ pass
+
+ return response
+
+
class UserAnswerView(AuthorOrCheckerRequiredMixin, LoginRequiredMixin, DetailView):
model = UserAnswer