177 lines
7.2 KiB
Python
177 lines
7.2 KiB
Python
from apps.utils.viewsets import CustomModelViewSet, CustomGenericViewSet
|
|
from apps.utils.mixins import ListModelMixin, DestroyModelMixin
|
|
from rest_framework.mixins import RetrieveModelMixin
|
|
from rest_framework.exceptions import ParseError
|
|
from rest_framework.decorators import action
|
|
from rest_framework.serializers import Serializer
|
|
from django.db import transaction
|
|
from .models import Questioncat, Question, Paper, PaperQuestion, Exam, ExamRecord, AnswerDetail
|
|
from .serializers import (QuestioncatSerializer, QuestionSerializer, ExamSerializer,
|
|
ExamRecordInitSerizlier, ExamRecordSerializer, ExamRecordDetailSerializer, ExamRecordSubmitSerializer,
|
|
PaperSerializer, PaperListSerializer, PaperPatchSerializer)
|
|
from django.utils import timezone
|
|
from rest_framework.response import Response
|
|
from rest_framework.permissions import IsAuthenticated
|
|
from drf_yasg.utils import swagger_auto_schema
|
|
from apps.utils.permission import has_perm
|
|
from .filters import ExamFilter, ExamRecordFilter
|
|
from apps.system.models import User
|
|
|
|
# Create your views here.
|
|
class QuestioncatViewSet(CustomModelViewSet):
|
|
queryset = Questioncat.objects.all()
|
|
serializer_class = QuestioncatSerializer
|
|
|
|
|
|
class QuestionViewSet(CustomModelViewSet):
|
|
queryset = Question.objects.all()
|
|
serializer_class = QuestionSerializer
|
|
filterset_fields = ["questioncat", "type", "enabled"]
|
|
|
|
def update(self, request, *args, **kwargs):
|
|
obj: Question = self.get_object()
|
|
if AnswerDetail.objects.filter(question=obj).exists():
|
|
raise ParseError("存在答题,该题目不可编辑")
|
|
return super().update(request, *args, **kwargs)
|
|
|
|
|
|
class PaperViewSet(CustomModelViewSet):
|
|
queryset = Paper.objects.all()
|
|
serializer_class = PaperSerializer
|
|
list_serializer_class = PaperListSerializer
|
|
partial_update_serializer_class = PaperPatchSerializer
|
|
|
|
def update(self, request, *args, **kwargs):
|
|
obj: Paper = self.get_object()
|
|
if Exam.objects.filter(paper=obj).exists():
|
|
raise ParseError("存在考试,该试卷不可编辑")
|
|
return super().update(request, *args, **kwargs)
|
|
|
|
class ExamViewSet(CustomModelViewSet):
|
|
queryset = Exam.objects.all()
|
|
serializer_class = ExamSerializer
|
|
filterset_class = ExamFilter
|
|
|
|
def get_queryset(self):
|
|
qs = super().get_queryset()
|
|
if has_perm(self.request.user, 'exam.view'):
|
|
return qs
|
|
user:User = self.request.user
|
|
dept = user.belong_dept
|
|
qs = qs.filter(is_public=True)
|
|
qs = qs|qs.filter(p_users=user)
|
|
if dept:
|
|
qs = qs|qs.filter(p_depts=dept)
|
|
return qs
|
|
|
|
def destroy(self, request, *args, **kwargs):
|
|
instance = self.get_object()
|
|
if ExamRecord.objects.filter(exam=instance).exists():
|
|
raise ParseError('存在考试记录,禁止删除')
|
|
return super().destroy(request, *args, **kwargs)
|
|
|
|
@swagger_auto_schema(request_body=Serializer, responses={200: ExamRecordInitSerizlier})
|
|
@action(methods=['post'], detail=True, perms_map=[{'post': '*'}], serializer_class=Serializer)
|
|
def attend(self, request, *args, **kwargs):
|
|
"""
|
|
参加考试
|
|
|
|
返回考试具体题目信息
|
|
"""
|
|
exam: Exam = self.get_object()
|
|
now = timezone.now()
|
|
if now < exam.open_time or now > exam.close_time:
|
|
raise ParseError('不在考试时间范围')
|
|
tests = ExamRecord.objects.filter(
|
|
exam=exam, create_by=request.user)
|
|
chance_used = tests.count()
|
|
if chance_used > exam.chance:
|
|
raise ParseError('考试机会已用完')
|
|
if exam.paper:
|
|
with transaction.atomic():
|
|
tests.update(is_last=False)
|
|
er = ExamRecord()
|
|
er.start_time = now
|
|
er.is_pass = False
|
|
er.is_submited = False
|
|
er.exam = exam
|
|
er.create_by = request.user
|
|
er.is_last = True
|
|
er.save()
|
|
pqs = PaperQuestion.objects.filter(paper=exam.paper).order_by('sort', 'id')
|
|
details = []
|
|
for i in pqs:
|
|
details.append(AnswerDetail(examrecord=er, question=i.question, total_score=i.total_score))
|
|
AnswerDetail.objects.bulk_create(details)
|
|
sr = ExamRecordInitSerizlier(er)
|
|
res_data = sr.data
|
|
res_data.update({"chance_used": chance_used})
|
|
return Response(sr.data, status=201)
|
|
raise ParseError('暂不支持')
|
|
|
|
|
|
class ExamRecordViewSet(ListModelMixin, DestroyModelMixin, RetrieveModelMixin, CustomGenericViewSet):
|
|
"""
|
|
考试记录
|
|
"""
|
|
perms_map = {"get": "*", "delete": "examrecord.delete"}
|
|
queryset = ExamRecord.objects.all()
|
|
list_serializer_class = ExamRecordSerializer
|
|
retrieve_serializer_class = ExamRecordDetailSerializer
|
|
search_fields = ('create_by__name', 'create_by__username', 'exam__name')
|
|
filterset_class = ExamRecordFilter
|
|
|
|
def get_queryset(self):
|
|
qs = super().get_queryset()
|
|
if has_perm(self.request.user, "examrecord.view"):
|
|
return qs
|
|
return qs.filter(create_by=self.request.user)
|
|
|
|
|
|
@swagger_auto_schema(request_body=ExamRecordSubmitSerializer, responses={200: ExamRecordSerializer})
|
|
@action(methods=['post'], detail=True, perms_map=[{'post': '*'}], serializer_class=ExamRecordSubmitSerializer, permission_classes = [IsAuthenticated])
|
|
@transaction.atomic
|
|
def submit(self, request, pk=None):
|
|
'''
|
|
提交答卷
|
|
|
|
提交答卷
|
|
'''
|
|
er: ExamRecord = self.get_object()
|
|
now = timezone.now()
|
|
if er.is_submited:
|
|
raise ParseError('该考试记录已提交')
|
|
exam:Exam = er.exam
|
|
if not exam:
|
|
raise ParseError('暂不支持')
|
|
took = (now - er.start_time).total_seconds()
|
|
if took > exam.paper.limit * 60:
|
|
raise ParseError('答题时间超时,提交失败')
|
|
serializer = ExamRecordSubmitSerializer(data = request.data)
|
|
serializer.is_valid(raise_exception=True)
|
|
vdata = serializer.validated_data
|
|
questions_ = vdata['detail_']
|
|
# 后端判卷
|
|
ads = AnswerDetail.objects.select_related('question').filter(examrecord=er).order_by('id')
|
|
total_score = 0
|
|
try:
|
|
for index, ad in enumerate(ads):
|
|
ad.user_answer = questions_[index]['user_answer']
|
|
if ad.question.type == '多选':
|
|
if set(ad.question.right) == set(ad.user_answer):
|
|
ad.is_right = True
|
|
ad.score = ad.total_score
|
|
else:
|
|
if ad.question.right == ad.user_answer:
|
|
ad.is_right = True
|
|
ad.score = ad.total_score
|
|
ad.save()
|
|
total_score = total_score + ad.score
|
|
except Exception as e:
|
|
raise ParseError('判卷失败, 请检查试卷:' + str(e))
|
|
er.score = total_score
|
|
er.took = took
|
|
er.end_time = now
|
|
er.is_submited = True
|
|
er.save()
|
|
return Response(ExamRecordSerializer(er).data) |