.
This commit is contained in:
+6
-21
@@ -50,7 +50,7 @@ from .models import (
|
|||||||
# IncorrectAnswers,
|
# IncorrectAnswers,
|
||||||
)
|
)
|
||||||
from generic.models import CidUser, Examination
|
from generic.models import CidUser, Examination
|
||||||
from generic.views import AuthorRequiredMixin, ExamCloneMixin, ExamViews, GenericViewBase
|
from generic.views import AuthorRequiredMixin, ExamCloneMixin, ExamCreateBase, ExamDeleteBase, ExamUpdateBase, ExamViews, GenericViewBase
|
||||||
from reversion.views import RevisionMixin
|
from reversion.views import RevisionMixin
|
||||||
|
|
||||||
from .decorators import user_is_author_or_anatomy_checker
|
from .decorators import user_is_author_or_anatomy_checker
|
||||||
@@ -963,30 +963,19 @@ class QuestionDelete(AuthorOrCheckerRequiredMixin, DeleteView):
|
|||||||
success_url = reverse_lazy("anatomy:question_list")
|
success_url = reverse_lazy("anatomy:question_list")
|
||||||
|
|
||||||
|
|
||||||
class ExamCreate(RevisionMixin, LoginRequiredMixin, CreateView):
|
class ExamCreate(ExamCreateBase):
|
||||||
model = Exam
|
model = Exam
|
||||||
form_class = ExamForm
|
form_class = ExamForm
|
||||||
|
|
||||||
def form_valid(self, form):
|
|
||||||
self.object = form.save(commit=False)
|
|
||||||
self.object.save()
|
|
||||||
|
|
||||||
form.instance.author.add(self.request.user.id)
|
class ExamUpdate(ExamUpdateBase, AuthorOrCheckerRequiredMixin):
|
||||||
return super().form_valid(form)
|
|
||||||
|
|
||||||
|
|
||||||
class ExamUpdate(
|
|
||||||
RevisionMixin, LoginRequiredMixin, AuthorOrCheckerRequiredMixin, UpdateView
|
|
||||||
):
|
|
||||||
model = Exam
|
model = Exam
|
||||||
form_class = ExamForm
|
form_class = ExamForm
|
||||||
|
|
||||||
def form_valid(self, form):
|
|
||||||
self.object = form.save(commit=False)
|
|
||||||
self.object.save()
|
|
||||||
|
|
||||||
form.instance.author.add(self.request.user.id)
|
class ExamDelete(AuthorOrCheckerRequiredMixin, ExamDeleteBase):
|
||||||
return super().form_valid(form)
|
model = Exam
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ExamViewSet(RevisionMixin, viewsets.ModelViewSet):
|
class ExamViewSet(RevisionMixin, viewsets.ModelViewSet):
|
||||||
@@ -1009,10 +998,6 @@ class ExamAuthorUpdate(RevisionMixin, LoginRequiredMixin, AuthorRequiredMixin, U
|
|||||||
model = Exam
|
model = Exam
|
||||||
form_class = ExamAuthorForm
|
form_class = ExamAuthorForm
|
||||||
|
|
||||||
class ExamDelete(RevisionMixin, AuthorOrCheckerRequiredMixin, DeleteView):
|
|
||||||
model = Exam
|
|
||||||
template_name = "exam_confirm_delete.html"
|
|
||||||
success_url = reverse_lazy("anatomy:index")
|
|
||||||
|
|
||||||
|
|
||||||
GenericViews = GenericViewBase("anatomy", AnatomyQuestion, CidUserAnswer, Exam)
|
GenericViews = GenericViewBase("anatomy", AnatomyQuestion, CidUserAnswer, Exam)
|
||||||
|
|||||||
+30
-7
@@ -32,7 +32,7 @@ import urllib
|
|||||||
from django.views import View
|
from django.views import View
|
||||||
from django.views.generic.edit import CreateView, UpdateView, DeleteView
|
from django.views.generic.edit import CreateView, UpdateView, DeleteView
|
||||||
from django_filters.views import FilterView
|
from django_filters.views import FilterView
|
||||||
from django_filters import FilterSet
|
from django_filters import FilterSet, OrderingFilter
|
||||||
from django_tables2.views import SingleTableMixin
|
from django_tables2.views import SingleTableMixin
|
||||||
from reversion.views import RevisionMixin
|
from reversion.views import RevisionMixin
|
||||||
from atlas.models import CaseCollection
|
from atlas.models import CaseCollection
|
||||||
@@ -294,6 +294,8 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
# exam_group = group_map[self.app_name]
|
# exam_group = group_map[self.app_name]
|
||||||
|
|
||||||
class ExamFilter(FilterSet):
|
class ExamFilter(FilterSet):
|
||||||
|
sort_order = OrderingFilter(fields=(("name", "name"), ("exam_mode", "exam_mode")))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = exam
|
model = exam
|
||||||
fields = {
|
fields = {
|
||||||
@@ -329,6 +331,8 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
# filter param is either missing or empty, use initial as default
|
# filter param is either missing or empty, use initial as default
|
||||||
if not data.get("archive"):
|
if not data.get("archive"):
|
||||||
data["archive"] = False
|
data["archive"] = False
|
||||||
|
if not data.get("sort_order"):
|
||||||
|
data["sort_order"] = "name"
|
||||||
|
|
||||||
super().__init__(data, *args, **kwargs)
|
super().__init__(data, *args, **kwargs)
|
||||||
|
|
||||||
@@ -523,15 +527,18 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
|
|
||||||
@method_decorator(login_required)
|
@method_decorator(login_required)
|
||||||
def exam_list(self, request, all=False):
|
def exam_list(self, request, all=False):
|
||||||
if all:
|
|
||||||
exams = self.Exam.objects.all().order_by("name")
|
if not self.check_user_access(request.user):
|
||||||
|
#raise PermissionDenied
|
||||||
|
exam_list = self.Exam.objects.filter(author__id=request.user.id).order_by("name")
|
||||||
else:
|
else:
|
||||||
exams = self.Exam.objects.filter(exam_mode=True, archive=False).order_by(
|
exam_list = self.Exam.objects.all().order_by("name")
|
||||||
|
|
||||||
|
if not all:
|
||||||
|
exams = exam_list.filter(exam_mode=True, archive=False).order_by(
|
||||||
"name"
|
"name"
|
||||||
)
|
)
|
||||||
|
|
||||||
if not self.check_user_access(request.user):
|
|
||||||
raise PermissionDenied
|
|
||||||
|
|
||||||
marking = False
|
marking = False
|
||||||
|
|
||||||
@@ -1021,7 +1028,7 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
|
|
||||||
view_feedback = False
|
view_feedback = False
|
||||||
if (
|
if (
|
||||||
#request.user.groups.filter(name=self.checker_group).exists() or
|
# request.user.groups.filter(name=self.checker_group).exists() or
|
||||||
request.user.groups.filter(name="feedback_checker").exists()
|
request.user.groups.filter(name="feedback_checker").exists()
|
||||||
or request.user in question.author.all()
|
or request.user in question.author.all()
|
||||||
):
|
):
|
||||||
@@ -2095,6 +2102,14 @@ class ExamCreateBase(RevisionMixin, LoginRequiredMixin, CreateView):
|
|||||||
form.instance.author.add(self.request.user.id)
|
form.instance.author.add(self.request.user.id)
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
# Call the base implementation first to get a context
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
print(dir(context["view"]))
|
||||||
|
print(context["view"].template_name_suffix)
|
||||||
|
print(context["view"].model.app_name)
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
class ExamUpdateBase(RevisionMixin, LoginRequiredMixin, UpdateView):
|
class ExamUpdateBase(RevisionMixin, LoginRequiredMixin, UpdateView):
|
||||||
template_name = "exam_update_form.html"
|
template_name = "exam_update_form.html"
|
||||||
@@ -2106,3 +2121,11 @@ class ExamUpdateBase(RevisionMixin, LoginRequiredMixin, UpdateView):
|
|||||||
form.instance.author.add(self.request.user.id)
|
form.instance.author.add(self.request.user.id)
|
||||||
|
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
|
class ExamDeleteBase(RevisionMixin, DeleteView):
|
||||||
|
template_name = "exam_confirm_delete.html"
|
||||||
|
|
||||||
|
def get_success_url(self) -> str:
|
||||||
|
# return super().get_success_url()(self):
|
||||||
|
return reverse_lazy(f"{self.model.app_name}:index")
|
||||||
|
|||||||
+8
-27
@@ -75,7 +75,7 @@ from django.forms.models import model_to_dict
|
|||||||
from longs.forms import LongCreationDefaultForm
|
from longs.forms import LongCreationDefaultForm
|
||||||
from longs.models import LongCreationDefault
|
from longs.models import LongCreationDefault
|
||||||
|
|
||||||
from generic.views import AuthorRequiredMixin, ExamCloneMixin, ExamViews
|
from generic.views import AuthorRequiredMixin, ExamCloneMixin, ExamCreateBase, ExamDeleteBase, ExamUpdateBase, ExamViews
|
||||||
from reversion.views import RevisionMixin
|
from reversion.views import RevisionMixin
|
||||||
import reversion
|
import reversion
|
||||||
|
|
||||||
@@ -1112,47 +1112,28 @@ def long_series_order_upload_filename(request, pk):
|
|||||||
|
|
||||||
GenericExamViews = ExamViews(Exam, Long, None, CidUserAnswer, "longs", "long")
|
GenericExamViews = ExamViews(Exam, Long, None, CidUserAnswer, "longs", "long")
|
||||||
|
|
||||||
|
class ExamCreate(ExamCreateBase):
|
||||||
class ExamCreate(RevisionMixin, LoginRequiredMixin, CreateView):
|
|
||||||
model = Exam
|
model = Exam
|
||||||
form_class = ExamForm
|
form_class = ExamForm
|
||||||
|
|
||||||
def form_valid(self, form):
|
|
||||||
self.object = form.save(commit=False)
|
|
||||||
self.object.save()
|
|
||||||
|
|
||||||
form.instance.author.add(self.request.user.id)
|
class ExamUpdate(ExamUpdateBase, AuthorOrCheckerRequiredMixin):
|
||||||
return super().form_valid(form)
|
model = Exam
|
||||||
|
form_class = ExamForm
|
||||||
|
|
||||||
|
|
||||||
|
class ExamDelete(AuthorOrCheckerRequiredMixin, ExamDeleteBase):
|
||||||
|
model = Exam
|
||||||
|
|
||||||
class ExamClone(ExamCloneMixin, ExamCreate):
|
class ExamClone(ExamCloneMixin, ExamCreate):
|
||||||
"""Clone exam view"""
|
"""Clone exam view"""
|
||||||
|
|
||||||
|
|
||||||
class ExamUpdate(
|
|
||||||
RevisionMixin, LoginRequiredMixin, AuthorOrCheckerRequiredMixin, UpdateView
|
|
||||||
):
|
|
||||||
model = Exam
|
|
||||||
form_class = ExamForm
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
|
||||||
self.object = form.save(commit=False)
|
|
||||||
self.object.save()
|
|
||||||
|
|
||||||
form.instance.author.add(self.request.user.id)
|
|
||||||
return super().form_valid(form)
|
|
||||||
|
|
||||||
class ExamAuthorUpdate(RevisionMixin, LoginRequiredMixin, AuthorRequiredMixin, UpdateView):
|
class ExamAuthorUpdate(RevisionMixin, LoginRequiredMixin, AuthorRequiredMixin, UpdateView):
|
||||||
model = Exam
|
model = Exam
|
||||||
form_class = ExamAuthorForm
|
form_class = ExamAuthorForm
|
||||||
|
|
||||||
|
|
||||||
class ExamDelete(RevisionMixin, AuthorOrCheckerRequiredMixin, DeleteView):
|
|
||||||
model = Exam
|
|
||||||
template_name = "exam_confirm_delete.html"
|
|
||||||
success_url = reverse_lazy("longs:index")
|
|
||||||
|
|
||||||
|
|
||||||
class ExamViewSet(RevisionMixin, viewsets.ModelViewSet):
|
class ExamViewSet(RevisionMixin, viewsets.ModelViewSet):
|
||||||
queryset = Exam.objects.all().order_by("name")
|
queryset = Exam.objects.all().order_by("name")
|
||||||
serializer_class = ExamSerializer
|
serializer_class = ExamSerializer
|
||||||
|
|||||||
+12
-25
@@ -45,6 +45,9 @@ import plotly.express as px
|
|||||||
from generic.views import (
|
from generic.views import (
|
||||||
AuthorRequiredMixin,
|
AuthorRequiredMixin,
|
||||||
ExamCloneMixin,
|
ExamCloneMixin,
|
||||||
|
ExamCreateBase,
|
||||||
|
ExamDeleteBase,
|
||||||
|
ExamUpdateBase,
|
||||||
ExamViews,
|
ExamViews,
|
||||||
GenericViewBase,
|
GenericViewBase,
|
||||||
)
|
)
|
||||||
@@ -377,17 +380,19 @@ GenericExamViews = ExamViews(Exam, Question, None, CidUserAnswer, "physics", "ph
|
|||||||
|
|
||||||
GenericViews = GenericViewBase("physics", Question, CidUserAnswer, Exam)
|
GenericViews = GenericViewBase("physics", Question, CidUserAnswer, Exam)
|
||||||
|
|
||||||
|
class ExamCreate(ExamCreateBase):
|
||||||
class ExamCreate(RevisionMixin, LoginRequiredMixin, CreateView):
|
|
||||||
model = Exam
|
model = Exam
|
||||||
form_class = ExamForm
|
form_class = ExamForm
|
||||||
|
|
||||||
def form_valid(self, form):
|
|
||||||
self.object = form.save(commit=False)
|
|
||||||
self.object.save()
|
|
||||||
|
|
||||||
form.instance.author.add(self.request.user.id)
|
class ExamUpdate(ExamUpdateBase, AuthorOrCheckerRequiredMixin):
|
||||||
return super().form_valid(form)
|
model = Exam
|
||||||
|
form_class = ExamForm
|
||||||
|
|
||||||
|
|
||||||
|
class ExamDelete(AuthorOrCheckerRequiredMixin, ExamDeleteBase):
|
||||||
|
model = Exam
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ExamClone(AuthorOrCheckerRequiredMixin, ExamCloneMixin, ExamCreate):
|
class ExamClone(AuthorOrCheckerRequiredMixin, ExamCloneMixin, ExamCreate):
|
||||||
@@ -401,24 +406,6 @@ class ExamAuthorUpdate(
|
|||||||
form_class = ExamAuthorForm
|
form_class = ExamAuthorForm
|
||||||
|
|
||||||
|
|
||||||
class ExamDelete(RevisionMixin, AuthorOrCheckerRequiredMixin, DeleteView):
|
|
||||||
model = Exam
|
|
||||||
template_name = "exam_confirm_delete.html"
|
|
||||||
success_url = reverse_lazy("physics:index")
|
|
||||||
|
|
||||||
|
|
||||||
class ExamUpdate(
|
|
||||||
RevisionMixin, LoginRequiredMixin, AuthorOrCheckerRequiredMixin, UpdateView
|
|
||||||
):
|
|
||||||
model = Exam
|
|
||||||
form_class = ExamForm
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
|
||||||
self.object = form.save(commit=False)
|
|
||||||
self.object.save()
|
|
||||||
|
|
||||||
form.instance.author.add(self.request.user.id)
|
|
||||||
return super().form_valid(form)
|
|
||||||
|
|
||||||
|
|
||||||
class QuestionView(
|
class QuestionView(
|
||||||
|
|||||||
+5
-1
@@ -1,12 +1,16 @@
|
|||||||
|
|
||||||
|
from cProfile import label
|
||||||
import django_filters
|
import django_filters
|
||||||
|
|
||||||
|
from generic.models import UserGrades
|
||||||
|
|
||||||
|
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
class UserListFilter(django_filters.FilterSet):
|
class UserListFilter(django_filters.FilterSet):
|
||||||
|
|
||||||
userprofile__grade = django_filters.CharFilter(label="Grade")
|
userprofile__grade = django_filters.ModelChoiceFilter(label="Grade", queryset=UserGrades.objects.all())
|
||||||
|
#test2 = django_filters.CharFilter()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model= User
|
model= User
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
<button id="add-to-exam" data-exam_json_edit_url="{% url 'generic:generic_exam_json_edit' %}"
|
<button id="add-to-exam" data-exam_json_edit_url="{% url 'generic:generic_exam_json_edit' %}"
|
||||||
data-exam_list_url="{% url 'rapid-exam-list' %}" data-type="rapid" data-csrf="{{ csrf_token}}"
|
data-exam_list_url="{% url 'rapid-exam-list' %}" data-type="rapid" data-csrf="{{ csrf_token}}"
|
||||||
data-qid="{{question.pk}}">Add to exam</button>
|
data-qid="{{question.pk}}">Add to exam</button>
|
||||||
|
<button id="cancel-add-to-exam" style="display:none">Cancel</button>
|
||||||
<span id="exam-options"></span>
|
<span id="exam-options"></span>
|
||||||
</div>
|
</div>
|
||||||
<p class="pre-whitespace"><b>Open Access:</b> {{ question.open_access }}</p>
|
<p class="pre-whitespace"><b>Open Access:</b> {{ question.open_access }}</p>
|
||||||
@@ -156,8 +157,15 @@
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
$("#cancel-add-to-exam").click(function (evt) {
|
||||||
|
$("#add-to-exam").toggle();
|
||||||
|
$("#cancel-add-to-exam").toggle();
|
||||||
|
$("#exam-options").empty();
|
||||||
|
})
|
||||||
|
|
||||||
$("#add-to-exam").click(function (evt) {
|
$("#add-to-exam").click(function (evt) {
|
||||||
|
$("#add-to-exam").toggle();
|
||||||
|
$("#cancel-add-to-exam").toggle();
|
||||||
|
|
||||||
var jqxhr = $.get(evt.target.dataset.exam_list_url, function (data) {
|
var jqxhr = $.get(evt.target.dataset.exam_list_url, function (data) {
|
||||||
console.log(data);
|
console.log(data);
|
||||||
@@ -380,11 +388,18 @@
|
|||||||
<style>
|
<style>
|
||||||
.toggle-button {
|
.toggle-button {
|
||||||
font-size: x-small;
|
font-size: x-small;
|
||||||
opacity: 10%;
|
opacity: 30%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toggle-button:hover {
|
.toggle-button:hover {
|
||||||
font-size: x-small;
|
font-size: x-small;
|
||||||
opacity: 100%;
|
opacity: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#cancel-add-to-exam {
|
||||||
|
border-style: dashed;
|
||||||
|
}
|
||||||
|
#cancel-add-to-exam:hover {
|
||||||
|
background-color: purple;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -33,6 +33,18 @@ def create_superuser(db, django_user_model):
|
|||||||
"admin", "ross@xkjq.uk", "adminpassword"
|
"admin", "ross@xkjq.uk", "adminpassword"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@pytest.fixture(params=EXAM_VIEWS)
|
||||||
|
def set_up_exams(request, django_user_model, create_superuser):
|
||||||
|
basic_user = django_user_model.objects.create_user(
|
||||||
|
"user1", "user@user.net", "pass2"
|
||||||
|
)
|
||||||
|
|
||||||
|
e1 = request.param.Exam.objects.create(name="test exam", exam_mode=True)
|
||||||
|
e = request.param.Exam.objects.create(name="test exam 2", exam_mode=True)
|
||||||
|
e.author.add(basic_user)
|
||||||
|
return request.param
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# @pytest.fixture
|
# @pytest.fixture
|
||||||
# def create_exam(db):
|
# def create_exam(db):
|
||||||
@@ -40,6 +52,18 @@ def create_superuser(db, django_user_model):
|
|||||||
|
|
||||||
# Very basic tests, make sure we can hit key urls without error
|
# Very basic tests, make sure we can hit key urls without error
|
||||||
|
|
||||||
|
def test_index2(client, set_up_exams):
|
||||||
|
client.login(username="admin", password="adminpassword")
|
||||||
|
response = client.get(reverse(f"{set_up_exams.app_name}:index"))
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
assert (
|
||||||
|
BeautifulSoup(response.content, "html.parser")
|
||||||
|
.find("h4", {"class": "exam-number-title"})
|
||||||
|
.getText()
|
||||||
|
== "2 exams found."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("exam_views", EXAM_VIEWS)
|
@pytest.mark.parametrize("exam_views", EXAM_VIEWS)
|
||||||
def test_index(client, create_superuser, exam_views, django_user_model):
|
def test_index(client, create_superuser, exam_views, django_user_model):
|
||||||
@@ -63,6 +87,15 @@ def test_index(client, create_superuser, exam_views, django_user_model):
|
|||||||
== "2 exams found."
|
== "2 exams found."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
response = client.get(reverse(f"{exam_views.app_name}:exam_list"))
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert (
|
||||||
|
len(BeautifulSoup(response.content, "html.parser")
|
||||||
|
.select("a.exam-name"))
|
||||||
|
== 2
|
||||||
|
)
|
||||||
|
|
||||||
if exam_views.app_name in JSON_APPS:
|
if exam_views.app_name in JSON_APPS:
|
||||||
response = client.get(reverse(f"{exam_views.app_name}:active_exams"))
|
response = client.get(reverse(f"{exam_views.app_name}:active_exams"))
|
||||||
|
|
||||||
@@ -86,7 +119,6 @@ def test_index(client, create_superuser, exam_views, django_user_model):
|
|||||||
HTTP_X_REQUESTED_WITH="XMLHttpRequest",
|
HTTP_X_REQUESTED_WITH="XMLHttpRequest",
|
||||||
)
|
)
|
||||||
|
|
||||||
print(f"{e1.valid_user_users.all()=}")
|
|
||||||
|
|
||||||
assert json.loads(add_user_response.content)["status"] == "success"
|
assert json.loads(add_user_response.content)["status"] == "success"
|
||||||
|
|
||||||
@@ -103,6 +135,15 @@ def test_index(client, create_superuser, exam_views, django_user_model):
|
|||||||
== "1 exams found."
|
== "1 exams found."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
response = client.get(reverse(f"{exam_views.app_name}:exam_list"))
|
||||||
|
|
||||||
|
assert (
|
||||||
|
len(BeautifulSoup(response.content, "html.parser")
|
||||||
|
.select("a.exam-name"))
|
||||||
|
== 1
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
if exam_views.app_name in JSON_APPS:
|
if exam_views.app_name in JSON_APPS:
|
||||||
response = client.get(reverse(f"{exam_views.app_name}:active_exams"))
|
response = client.get(reverse(f"{exam_views.app_name}:active_exams"))
|
||||||
|
|
||||||
|
|||||||
+6
-7
@@ -46,6 +46,7 @@ from generic.views import (
|
|||||||
AuthorRequiredMixin,
|
AuthorRequiredMixin,
|
||||||
ExamCloneMixin,
|
ExamCloneMixin,
|
||||||
ExamCreateBase,
|
ExamCreateBase,
|
||||||
|
ExamDeleteBase,
|
||||||
ExamUpdateBase,
|
ExamUpdateBase,
|
||||||
ExamViews,
|
ExamViews,
|
||||||
GenericViewBase,
|
GenericViewBase,
|
||||||
@@ -787,6 +788,9 @@ class ExamUpdate(ExamUpdateBase, AuthorOrCheckerRequiredMixin):
|
|||||||
form_class = ExamForm
|
form_class = ExamForm
|
||||||
|
|
||||||
|
|
||||||
|
class ExamDelete(AuthorOrCheckerRequiredMixin, ExamDeleteBase):
|
||||||
|
model = Exam
|
||||||
|
|
||||||
class ExamAuthorUpdate(
|
class ExamAuthorUpdate(
|
||||||
RevisionMixin, LoginRequiredMixin, AuthorRequiredMixin, UpdateView
|
RevisionMixin, LoginRequiredMixin, AuthorRequiredMixin, UpdateView
|
||||||
):
|
):
|
||||||
@@ -794,11 +798,6 @@ class ExamAuthorUpdate(
|
|||||||
form_class = ExamAuthorForm
|
form_class = ExamAuthorForm
|
||||||
|
|
||||||
|
|
||||||
class ExamDelete(RevisionMixin, AuthorOrCheckerRequiredMixin, DeleteView):
|
|
||||||
model = Exam
|
|
||||||
template_name = "exam_confirm_delete.html"
|
|
||||||
success_url = reverse_lazy("rapids:index")
|
|
||||||
|
|
||||||
|
|
||||||
class UserAnswerView(LoginRequiredMixin, DetailView):
|
class UserAnswerView(LoginRequiredMixin, DetailView):
|
||||||
model = CidUserAnswer
|
model = CidUserAnswer
|
||||||
@@ -840,9 +839,9 @@ class ExamViewSet(RevisionMixin, viewsets.ModelViewSet):
|
|||||||
"""
|
"""
|
||||||
user = self.request.user
|
user = self.request.user
|
||||||
if user.groups.filter(name="rapid_checker").exists():
|
if user.groups.filter(name="rapid_checker").exists():
|
||||||
return Exam.objects.all()
|
return Exam.objects.filter(archive=False).order_by('name')
|
||||||
|
|
||||||
return Exam.objects.filter(author__id=user.id)
|
return Exam.objects.filter(author__id=user.id, archive=False).order_by('name')
|
||||||
|
|
||||||
|
|
||||||
class QuestionAnswerViewSet(viewsets.ModelViewSet):
|
class QuestionAnswerViewSet(viewsets.ModelViewSet):
|
||||||
|
|||||||
+5
-43
@@ -40,7 +40,7 @@ import json
|
|||||||
import statistics
|
import statistics
|
||||||
import plotly.express as px
|
import plotly.express as px
|
||||||
|
|
||||||
from generic.views import AuthorRequiredMixin, ExamCloneMixin, ExamViews, GenericViewBase
|
from generic.views import AuthorRequiredMixin, ExamCloneMixin, ExamCreateBase, ExamDeleteBase, ExamUpdateBase, ExamViews, GenericViewBase
|
||||||
|
|
||||||
from rest_framework import viewsets, permissions
|
from rest_framework import viewsets, permissions
|
||||||
from rest_framework.pagination import PageNumberPagination
|
from rest_framework.pagination import PageNumberPagination
|
||||||
@@ -342,31 +342,17 @@ class QuestionDelete(AuthorOrCheckerRequiredMixin, DeleteView):
|
|||||||
success_url = reverse_lazy("sbas:question_list")
|
success_url = reverse_lazy("sbas:question_list")
|
||||||
|
|
||||||
|
|
||||||
class ExamCreate(RevisionMixin, LoginRequiredMixin, CreateView):
|
class ExamCreate(ExamCreateBase):
|
||||||
model = Exam
|
model = Exam
|
||||||
form_class = ExamForm
|
form_class = ExamForm
|
||||||
|
|
||||||
def form_valid(self, form):
|
|
||||||
self.object = form.save(commit=False)
|
|
||||||
self.object.save()
|
|
||||||
|
|
||||||
form.instance.author.add(self.request.user.id)
|
class ExamUpdate(ExamUpdateBase, AuthorOrCheckerRequiredMixin):
|
||||||
return super().form_valid(form)
|
|
||||||
|
|
||||||
|
|
||||||
class ExamUpdate(
|
|
||||||
RevisionMixin, LoginRequiredMixin, AuthorOrCheckerRequiredMixin, UpdateView
|
|
||||||
):
|
|
||||||
model = Exam
|
model = Exam
|
||||||
form_class = ExamForm
|
form_class = ExamForm
|
||||||
|
|
||||||
def form_valid(self, form):
|
class ExamDelete(AuthorOrCheckerRequiredMixin, ExamDeleteBase):
|
||||||
self.object = form.save(commit=False)
|
model = Exam
|
||||||
self.object.save()
|
|
||||||
|
|
||||||
form.instance.author.add(self.request.user.id)
|
|
||||||
return super().form_valid(form)
|
|
||||||
|
|
||||||
|
|
||||||
# class ExamViewSet(RevisionMixin, viewsets.ModelViewSet):
|
# class ExamViewSet(RevisionMixin, viewsets.ModelViewSet):
|
||||||
# # queryset = Exam.objects.all().order_by('name')
|
# # queryset = Exam.objects.all().order_by('name')
|
||||||
@@ -388,30 +374,6 @@ class ExamAuthorUpdate(RevisionMixin, LoginRequiredMixin, AuthorRequiredMixin, U
|
|||||||
form_class = ExamAuthorForm
|
form_class = ExamAuthorForm
|
||||||
|
|
||||||
|
|
||||||
class ExamDelete(RevisionMixin, AuthorOrCheckerRequiredMixin, DeleteView):
|
|
||||||
model = Exam
|
|
||||||
template_name = "exam_confirm_delete.html"
|
|
||||||
success_url = reverse_lazy("sbas:index")
|
|
||||||
|
|
||||||
|
|
||||||
class ExamClone(ExamCloneMixin, ExamCreate):
|
class ExamClone(ExamCloneMixin, ExamCreate):
|
||||||
"""Clone exam view"""
|
"""Clone exam view"""
|
||||||
|
|
||||||
class ExamDelete(RevisionMixin, AuthorOrCheckerRequiredMixin, DeleteView):
|
|
||||||
model = Exam
|
|
||||||
template_name = "exam_confirm_delete.html"
|
|
||||||
success_url = reverse_lazy("sbas:index")
|
|
||||||
|
|
||||||
|
|
||||||
class ExamUpdate(
|
|
||||||
RevisionMixin, LoginRequiredMixin, AuthorOrCheckerRequiredMixin, UpdateView
|
|
||||||
):
|
|
||||||
model = Exam
|
|
||||||
form_class = ExamForm
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
|
||||||
self.object = form.save(commit=False)
|
|
||||||
self.object.save()
|
|
||||||
|
|
||||||
form.instance.author.add(self.request.user.id)
|
|
||||||
return super().form_valid(form)
|
|
||||||
@@ -1,12 +1,10 @@
|
|||||||
{% extends "base.html" %}
|
{% extends view.model.app_name|add:"/base.html" %}
|
||||||
{% comment %} {% extends exam.app_name|add:"/base.html" %} {% endcomment %}
|
|
||||||
|
|
||||||
{% block js %}
|
{% block js %}
|
||||||
{{ form.media }}
|
{{ form.media }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{{form.instance}}
|
|
||||||
|
|
||||||
<h2>Add Exam</h2>
|
<h2>Add Exam</h2>
|
||||||
<form action="" method="post">
|
<form action="" method="post">
|
||||||
|
|||||||
@@ -4,11 +4,11 @@
|
|||||||
<h1>Examinations</h1>
|
<h1>Examinations</h1>
|
||||||
<div class="{{app_name}}">
|
<div class="{{app_name}}">
|
||||||
Active exams:<br/>
|
Active exams:<br/>
|
||||||
<ul class="exam-list">
|
<ul class="exam-list exam-list-active">
|
||||||
{% for exam in exams %}
|
{% for exam in exams %}
|
||||||
{% if exam.active %}
|
{% if exam.active %}
|
||||||
<li class="exam-item">
|
<li class="exam-item">
|
||||||
<a href="{% url app_name|add:':exam_overview' pk=exam.pk %}" class="flex-col-2">{{exam.name}}</a>
|
<a href="{% url app_name|add:':exam_overview' pk=exam.pk %}" class="flex-col-2 exam-name">{{exam.name}}</a>
|
||||||
{% if marking %}<a href="{% url app_name|add:':mark_overview' pk=exam.pk %}" class="flex-col">Mark</a>{% endif %}
|
{% if marking %}<a href="{% url app_name|add:':mark_overview' pk=exam.pk %}" class="flex-col">Mark</a>{% endif %}
|
||||||
<a href="{% url app_name|add:':exam_cids' exam_id=exam.pk %}" class="flex-col">Candidates</a>
|
<a href="{% url app_name|add:':exam_cids' exam_id=exam.pk %}" class="flex-col">Candidates</a>
|
||||||
<a href="{% url app_name|add:':exam_scores_cid' pk=exam.pk %}" class="flex-col">Scores</a>
|
<a href="{% url app_name|add:':exam_scores_cid' pk=exam.pk %}" class="flex-col">Scores</a>
|
||||||
@@ -22,11 +22,11 @@
|
|||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
Inactive exams:<br/>
|
Inactive exams:<br/>
|
||||||
<ul class="exam-list">
|
<ul class="exam-list exam-list-inactive">
|
||||||
{% for exam in exams %}
|
{% for exam in exams %}
|
||||||
{% if not exam.active %}
|
{% if not exam.active %}
|
||||||
<li class="exam-item">
|
<li class="exam-item">
|
||||||
<a href="{% url app_name|add:':exam_overview' pk=exam.pk %}" class="flex-col-2">{{exam.name}}</a>
|
<a href="{% url app_name|add:':exam_overview' pk=exam.pk %}" class="flex-col-2 exam-name">{{exam.name}}</a>
|
||||||
{% if marking %}<a href="{% url app_name|add:':mark_overview' pk=exam.pk %}" class="flex-col">Mark</a>{% endif %}
|
{% if marking %}<a href="{% url app_name|add:':mark_overview' pk=exam.pk %}" class="flex-col">Mark</a>{% endif %}
|
||||||
<a href="{% url app_name|add:':exam_cids' exam_id=exam.pk %}" class="flex-col">Candidates</a>
|
<a href="{% url app_name|add:':exam_cids' exam_id=exam.pk %}" class="flex-col">Candidates</a>
|
||||||
<a href="{% url app_name|add:':exam_scores_cid' pk=exam.pk %}" class="flex-col">Scores</a>
|
<a href="{% url app_name|add:':exam_scores_cid' pk=exam.pk %}" class="flex-col">Scores</a>
|
||||||
|
|||||||
Reference in New Issue
Block a user