.
This commit is contained in:
@@ -0,0 +1,63 @@
|
|||||||
|
import django_filters
|
||||||
|
|
||||||
|
from .models import Category, Question, CidUserAnswer, Exam
|
||||||
|
|
||||||
|
#from generic.filters import UserAnswerFilter
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
|
|
||||||
|
def get_exams(request):
|
||||||
|
if request is None:
|
||||||
|
return Exam.objects.none()
|
||||||
|
|
||||||
|
if not request.user.groups.filter(name="physics_checker").exists():
|
||||||
|
queryset = Exam.objects.filter(author__id=request.user.id)
|
||||||
|
else:
|
||||||
|
queryset = Exam.objects.all()
|
||||||
|
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
def get_authors(request):
|
||||||
|
if request is None:
|
||||||
|
return User.objects.none()
|
||||||
|
|
||||||
|
if not request.user.groups.filter(name="physics_checker").exists():
|
||||||
|
queryset = User.objects.filter(id=request.user.id)
|
||||||
|
else:
|
||||||
|
queryset = User.objects.all()
|
||||||
|
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
|
class QuestionFilter(django_filters.FilterSet):
|
||||||
|
exams = django_filters.ModelMultipleChoiceFilter(queryset=get_exams, null_label="No exam")
|
||||||
|
author = django_filters.ModelMultipleChoiceFilter(queryset=get_authors, null_label="No author")
|
||||||
|
category = django_filters.ModelChoiceFilter(queryset=Category.objects.all(),null_label="Uncategorised")
|
||||||
|
class Meta:
|
||||||
|
model = Question
|
||||||
|
fields = ("exams", "created_date")
|
||||||
|
|
||||||
|
def __init__(self, data=None, queryset=None, prefix=None, strict=None, user=None, request=None):
|
||||||
|
if not request.user.groups.filter(name="physics_checker").exists():
|
||||||
|
queryset = queryset.filter(open_access=True) | queryset.filter(author__id=request.user.id)
|
||||||
|
super(QuestionFilter, self).__init__(data=data, queryset=queryset, prefix=prefix, request=request)
|
||||||
|
pass
|
||||||
|
|
||||||
|
class UserAnswerFilter(django_filters.FilterSet):
|
||||||
|
class Meta:
|
||||||
|
model= CidUserAnswer
|
||||||
|
fields = (
|
||||||
|
"cid",
|
||||||
|
#"question",
|
||||||
|
"answer",
|
||||||
|
#"answer_compare",
|
||||||
|
"exam",
|
||||||
|
"created",
|
||||||
|
"updated",
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, data=None, queryset=None, prefix=None, strict=None, user=None, request=None):
|
||||||
|
if not request.user.groups.filter(name="physics_checker").exists():
|
||||||
|
queryset = queryset.filter(open_access=True) | queryset.filter(author__id=request.user.id)
|
||||||
|
super(UserAnswerFilter, self).__init__(data=data, queryset=queryset, prefix=prefix, request=request)
|
||||||
|
pass
|
||||||
Executable
+34
@@ -0,0 +1,34 @@
|
|||||||
|
from django.forms import (
|
||||||
|
Form,
|
||||||
|
ModelForm,
|
||||||
|
ModelMultipleChoiceField,
|
||||||
|
ModelChoiceField,
|
||||||
|
ChoiceField,
|
||||||
|
CharField,
|
||||||
|
)
|
||||||
|
from django.forms import inlineformset_factory
|
||||||
|
|
||||||
|
from .models import (
|
||||||
|
Question,
|
||||||
|
CidUserAnswer,
|
||||||
|
Exam,
|
||||||
|
)
|
||||||
|
|
||||||
|
from django.contrib.admin.widgets import FilteredSelectMultiple
|
||||||
|
from django.forms.widgets import RadioSelect, TextInput, Textarea
|
||||||
|
|
||||||
|
|
||||||
|
class CidUserAnswerForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = CidUserAnswer
|
||||||
|
fields = ["answer"]
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(CidUserAnswerForm, self).__init__(*args, **kwargs)
|
||||||
|
self.fields['answer'].required = False
|
||||||
|
|
||||||
|
# This should be made generic?
|
||||||
|
class ExamForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Exam
|
||||||
|
fields = ["name", "time_limit", "exam_mode", "active"]
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
from django.utils.safestring import mark_safe
|
||||||
|
import django_tables2 as tables
|
||||||
|
from django_tables2.utils import A
|
||||||
|
|
||||||
|
from .models import Question, CidUserAnswer
|
||||||
|
|
||||||
|
from django.utils.html import format_html
|
||||||
|
|
||||||
|
#from easy_thumbnails.files import get_thumbnailer
|
||||||
|
|
||||||
|
#from easy_thumbnails.exceptions import InvalidImageFormatError
|
||||||
|
|
||||||
|
#from generic.tables import UserAnswerTable
|
||||||
|
|
||||||
|
|
||||||
|
#class ImageColumn(tables.Column):
|
||||||
|
# def render(self, value):
|
||||||
|
# try:
|
||||||
|
# thumbnailer = get_thumbnailer(value)
|
||||||
|
# return format_html('<img src="/media/{}" />', thumbnailer["exam-list"])
|
||||||
|
# except InvalidImageFormatError:
|
||||||
|
# return format_html('<span title="{}">Invalid image url<span>', value)
|
||||||
|
|
||||||
|
|
||||||
|
class QuestionTable(tables.Table):
|
||||||
|
#edit = tables.LinkColumn(
|
||||||
|
# "physics:physics_question_update", text="Edit", args=[A("pk")], orderable=False
|
||||||
|
#)
|
||||||
|
view = tables.LinkColumn(
|
||||||
|
"physics:question_detail", text="View", args=[A("pk")], orderable=False
|
||||||
|
)
|
||||||
|
#delete = tables.LinkColumn(
|
||||||
|
# "physics:question_delete", text="Delete", args=[A("pk")], orderable=False
|
||||||
|
#)
|
||||||
|
exams = tables.ManyToManyColumn(verbose_name="Exams")
|
||||||
|
|
||||||
|
selection = tables.CheckBoxColumn(accessor="pk", orderable=False)
|
||||||
|
|
||||||
|
stem = tables.Column()
|
||||||
|
answers = tables.TemplateColumn("<ol type='A'><li>{{ record.a_answer|safe}}</li><li>{{ record.b_answer|safe}}</li><li>{{ record.c_answer|safe}}</li><li>{{ record.d_answer|safe}}</li><li>{{ record.e_answer|safe}}</li></ol>")
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Question
|
||||||
|
template_name = "django_tables2/bootstrap4.html"
|
||||||
|
fields = (
|
||||||
|
#"stem",
|
||||||
|
#"a_answer",
|
||||||
|
#"b_answer",
|
||||||
|
#"c_answer",
|
||||||
|
#"d_answer",
|
||||||
|
#"e_answer",
|
||||||
|
"best_answer",
|
||||||
|
"created_date",
|
||||||
|
"category",
|
||||||
|
"author",
|
||||||
|
)
|
||||||
|
sequence = ("view", "stem", "answers", "exams")
|
||||||
|
|
||||||
|
def render_stem(self, value):
|
||||||
|
return mark_safe(value)
|
||||||
|
|
||||||
|
|
||||||
|
class UserAnswerTable(tables.Table):
|
||||||
|
select = tables.CheckBoxColumn(accessor=("pk"))
|
||||||
|
#delete = tables.LinkColumn(
|
||||||
|
# "physics:user_answer_delete", text="Delete", args=[A("pk")], orderable=False
|
||||||
|
#)
|
||||||
|
view = tables.LinkColumn('physics:user_answer_view',
|
||||||
|
text='View',
|
||||||
|
args=[A('pk')],
|
||||||
|
orderable=False)
|
||||||
|
class Meta:
|
||||||
|
model = CidUserAnswer
|
||||||
|
template_name = "django_tables2/bootstrap4.html"
|
||||||
|
fields = (
|
||||||
|
"cid",
|
||||||
|
"question",
|
||||||
|
"answer",
|
||||||
|
#"answer_compare",
|
||||||
|
"exam",
|
||||||
|
"created",
|
||||||
|
"updated",
|
||||||
|
)
|
||||||
|
|
||||||
@@ -7,6 +7,7 @@ urlpatterns = [
|
|||||||
# path('', views.question_list, name='question_list'),
|
# path('', views.question_list, name='question_list'),
|
||||||
path("", views.PhysicsExamViews.index, name="index"),
|
path("", views.PhysicsExamViews.index, name="index"),
|
||||||
#path("question/", views.QuestionView.as_view(), name="question_view"),
|
#path("question/", views.QuestionView.as_view(), name="question_view"),
|
||||||
|
path("question/", views.QuestionView.as_view(), name="question_view"),
|
||||||
path("question/<int:pk>/", views.question_detail, name="question_detail"),
|
path("question/<int:pk>/", views.question_detail, name="question_detail"),
|
||||||
#path("question/create/", views.QuestionCreate.as_view(), name="anatomy_question_create"),
|
#path("question/create/", views.QuestionCreate.as_view(), name="anatomy_question_create"),
|
||||||
#path("question/<int:pk>/update", views.QuestionUpdate.as_view(), name="anatomy_question_update"),
|
#path("question/<int:pk>/update", views.QuestionUpdate.as_view(), name="anatomy_question_update"),
|
||||||
@@ -25,4 +26,18 @@ urlpatterns = [
|
|||||||
path("exam/available", views.active_exams, name="active_exams"),
|
path("exam/available", views.active_exams, name="active_exams"),
|
||||||
#path("exam/json/<int:pk>", views.exam_json, name="exam_json"),
|
#path("exam/json/<int:pk>", views.exam_json, name="exam_json"),
|
||||||
#path("exam/json/<int:pk>/recreate", views.exam_json_recreate, name="exam_json_recreate"),
|
#path("exam/json/<int:pk>/recreate", views.exam_json_recreate, name="exam_json_recreate"),
|
||||||
|
path("user_answers/", views.UserAnswerTableView.as_view(), name="user_answer_table_view"),
|
||||||
|
path(
|
||||||
|
"user_answers/<int:pk>", views.UserAnswerView.as_view(), name="user_answer_view"
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"user_answers/<int:pk>/delete",
|
||||||
|
views.UserAnswerDelete.as_view(),
|
||||||
|
name="user_answer_delete",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"user_answers/delete",
|
||||||
|
views.GenericViews.user_answer_delete_multiple,
|
||||||
|
name="user_answer_delete_multiple",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
+23
-4
@@ -20,6 +20,7 @@ from django.urls import reverse_lazy, reverse
|
|||||||
from django.http import Http404, JsonResponse
|
from django.http import Http404, JsonResponse
|
||||||
|
|
||||||
from django.http import HttpResponseRedirect, HttpResponse
|
from django.http import HttpResponseRedirect, HttpResponse
|
||||||
|
from reversion.views import RevisionMixin
|
||||||
|
|
||||||
from .models import Question, Category, Exam, CidUserAnswer
|
from .models import Question, Category, Exam, CidUserAnswer
|
||||||
|
|
||||||
@@ -31,13 +32,15 @@ import json
|
|||||||
import statistics
|
import statistics
|
||||||
import plotly.express as px
|
import plotly.express as px
|
||||||
|
|
||||||
from generic.views import ExamViews
|
from generic.views import ExamCloneMixin, 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
|
||||||
|
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
|
|
||||||
|
from .forms import ExamForm
|
||||||
|
|
||||||
class AuthorOrCheckerRequiredMixin(object):
|
class AuthorOrCheckerRequiredMixin(object):
|
||||||
def get_object(self, *args, **kwargs):
|
def get_object(self, *args, **kwargs):
|
||||||
obj = super().get_object(*args, **kwargs)
|
obj = super().get_object(*args, **kwargs)
|
||||||
@@ -253,9 +256,9 @@ def loadJsonAnswer(answer):
|
|||||||
return False, JsonResponse({"success": False, "error": "invalid"})
|
return False, JsonResponse({"success": False, "error": "invalid"})
|
||||||
|
|
||||||
# The model should catch invalid data but this should be less intensive
|
# The model should catch invalid data but this should be less intensive
|
||||||
max_int = 10000000
|
#max_int = 10000000
|
||||||
if answer["cid"] > max_int or answer["eid"] > max_int:
|
#if answer["cid"] > max_int or answer["eid"] > max_int:
|
||||||
return False, JsonResponse({"success": False})
|
# return False, JsonResponse({"success": False})
|
||||||
|
|
||||||
exam = get_object_or_404(Exam, pk=answer["eid"])
|
exam = get_object_or_404(Exam, pk=answer["eid"])
|
||||||
|
|
||||||
@@ -304,3 +307,19 @@ def loadJsonAnswer(answer):
|
|||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
PhysicsExamViews = ExamViews(Exam, Question, "physics", "physics", loadJsonAnswer)
|
PhysicsExamViews = ExamViews(Exam, Question, "physics", "physics", loadJsonAnswer)
|
||||||
|
|
||||||
|
GenericViews = GenericViewBase("physics", Question, CidUserAnswer, Exam)
|
||||||
|
|
||||||
|
class ExamCreate(RevisionMixin, LoginRequiredMixin, CreateView):
|
||||||
|
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 ExamClone(ExamCloneMixin, ExamCreate):
|
||||||
|
"""Clone exam view"""
|
||||||
+1
-1
@@ -10,7 +10,7 @@ def get_exams(request):
|
|||||||
if request is None:
|
if request is None:
|
||||||
return Exam.objects.none()
|
return Exam.objects.none()
|
||||||
|
|
||||||
if not request.user.groups.filter(name="anatomy_checker").exists():
|
if not request.user.groups.filter(name="sba_checker").exists():
|
||||||
queryset = Exam.objects.filter(author__id=request.user.id)
|
queryset = Exam.objects.filter(author__id=request.user.id)
|
||||||
else:
|
else:
|
||||||
queryset = Exam.objects.all()
|
queryset = Exam.objects.all()
|
||||||
|
|||||||
+1
-1
@@ -39,7 +39,7 @@ urlpatterns = [
|
|||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"user_answers/delete",
|
"user_answers/delete",
|
||||||
views.user_answer_delete_multiple,
|
views.GenericViews.user_answer_delete_multiple,
|
||||||
name="user_answer_delete_multiple",
|
name="user_answer_delete_multiple",
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
+13
-11
@@ -37,7 +37,7 @@ import json
|
|||||||
import statistics
|
import statistics
|
||||||
import plotly.express as px
|
import plotly.express as px
|
||||||
|
|
||||||
from generic.views import ExamViews
|
from generic.views import 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
|
||||||
@@ -387,15 +387,17 @@ class UserAnswerDelete(SuperuserRequiredMixin, DeleteView):
|
|||||||
template_name = "user_answer_delete.html"
|
template_name = "user_answer_delete.html"
|
||||||
success_url = reverse_lazy("sbas:user_answer_table_view")
|
success_url = reverse_lazy("sbas:user_answer_table_view")
|
||||||
|
|
||||||
@user_passes_test(lambda u: u.is_superuser)
|
#@user_passes_test(lambda u: u.is_superuser)
|
||||||
def user_answer_delete_multiple(request):
|
#def user_answer_delete_multiple(request):
|
||||||
if request.is_ajax():
|
# if request.is_ajax():
|
||||||
print(request.POST["answer_ids"])
|
# print(request.POST["answer_ids"])
|
||||||
answer_ids = json.loads(request.POST["answer_ids"])
|
# answer_ids = json.loads(request.POST["answer_ids"])
|
||||||
|
#
|
||||||
|
# # We could probably delete them all at once....
|
||||||
|
# for id in answer_ids:
|
||||||
|
# CidUserAnswer.objects.get(pk=id).delete()
|
||||||
|
# return JsonResponse({"status": "success"})
|
||||||
|
# return JsonResponse({"status": "error"})
|
||||||
|
|
||||||
# We could probably delete them all at once....
|
|
||||||
for id in answer_ids:
|
|
||||||
CidUserAnswer.objects.get(pk=id).delete()
|
|
||||||
return JsonResponse({"status": "success"})
|
|
||||||
return JsonResponse({"status": "error"})
|
|
||||||
|
|
||||||
|
GenericViews = GenericViewBase("sbas", Question, CidUserAnswer, Exam)
|
||||||
Reference in New Issue
Block a user