factory/apps/edu/views.py

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)