.
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.PhysicsExamViews.index, name="index"),
|
||||
#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/create/", views.QuestionCreate.as_view(), name="anatomy_question_create"),
|
||||
#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/json/<int:pk>", views.exam_json, name="exam_json"),
|
||||
#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",
|
||||
),
|
||||
]
|
||||
|
||||
+24
-5
@@ -20,6 +20,7 @@ from django.urls import reverse_lazy, reverse
|
||||
from django.http import Http404, JsonResponse
|
||||
|
||||
from django.http import HttpResponseRedirect, HttpResponse
|
||||
from reversion.views import RevisionMixin
|
||||
|
||||
from .models import Question, Category, Exam, CidUserAnswer
|
||||
|
||||
@@ -31,13 +32,15 @@ import json
|
||||
import statistics
|
||||
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.pagination import PageNumberPagination
|
||||
|
||||
from django.core.exceptions import PermissionDenied
|
||||
|
||||
from .forms import ExamForm
|
||||
|
||||
class AuthorOrCheckerRequiredMixin(object):
|
||||
def get_object(self, *args, **kwargs):
|
||||
obj = super().get_object(*args, **kwargs)
|
||||
@@ -253,9 +256,9 @@ def loadJsonAnswer(answer):
|
||||
return False, JsonResponse({"success": False, "error": "invalid"})
|
||||
|
||||
# The model should catch invalid data but this should be less intensive
|
||||
max_int = 10000000
|
||||
if answer["cid"] > max_int or answer["eid"] > max_int:
|
||||
return False, JsonResponse({"success": False})
|
||||
#max_int = 10000000
|
||||
#if answer["cid"] > max_int or answer["eid"] > max_int:
|
||||
# return False, JsonResponse({"success": False})
|
||||
|
||||
exam = get_object_or_404(Exam, pk=answer["eid"])
|
||||
|
||||
@@ -303,4 +306,20 @@ def loadJsonAnswer(answer):
|
||||
|
||||
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:
|
||||
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)
|
||||
else:
|
||||
queryset = Exam.objects.all()
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ urlpatterns = [
|
||||
),
|
||||
path(
|
||||
"user_answers/delete",
|
||||
views.user_answer_delete_multiple,
|
||||
views.GenericViews.user_answer_delete_multiple,
|
||||
name="user_answer_delete_multiple",
|
||||
),
|
||||
]
|
||||
|
||||
+13
-11
@@ -37,7 +37,7 @@ import json
|
||||
import statistics
|
||||
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.pagination import PageNumberPagination
|
||||
@@ -387,15 +387,17 @@ class UserAnswerDelete(SuperuserRequiredMixin, DeleteView):
|
||||
template_name = "user_answer_delete.html"
|
||||
success_url = reverse_lazy("sbas:user_answer_table_view")
|
||||
|
||||
@user_passes_test(lambda u: u.is_superuser)
|
||||
def user_answer_delete_multiple(request):
|
||||
if request.is_ajax():
|
||||
print(request.POST["answer_ids"])
|
||||
answer_ids = json.loads(request.POST["answer_ids"])
|
||||
#@user_passes_test(lambda u: u.is_superuser)
|
||||
#def user_answer_delete_multiple(request):
|
||||
# if request.is_ajax():
|
||||
# print(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