# coding=utf-8 import json import traceback from django.utils import timezone from rest_framework.decorators import action from rest_framework.serializers import ValidationError from rest_framework.views import APIView from django.db import transaction from django.db.models import Q, Sum, F from utils.custom_modelviewset import CustomModelViewSet from utils import response_ok, response_error from utils.permission import IsAdministrator, IsStaff from apps.examination.exam.serializers import * from apps.examination.exam.filters import * from apps.system.models import SysLog from apps.foundation.serializers import SubjectSerializer, ChapterSerializer, SubjectSimpleSerializer, \ ChapterSimpleSerializer, Subject from apps.examination.exam.models import ExamAnswerLog, ExamAnswerOptionLog, ExamAnswerFillLog from apps.examination.exampaper.models import ExamPaper, ExamPaperDetail, ExamQuestion from apps.examination.exampaper.filters import ExamPaperFilter from apps.examination.exampaper.serializers import StaffExamPaperSerializer from apps.examination.examquestion.models import ExamQuestionOption, ExamQuestionFill class ExamPaperViewSet(CustomModelViewSet): permission_classes = [IsStaff, ] queryset = ExamPaper.objects.filter(delete=False, type=ExamPaper.MOCK) serializer_class = StaffExamPaperSerializer def filter_queryset(self, queryset): f = ExamPaperFilter(self.request.GET, queryset=queryset, request=self.request) return f.qs class ExamLogViewSet(CustomModelViewSet): permission_classes = [IsStaff, ] queryset = ExamLog.objects.filter(delete=False) serializer_class = StaffExamLogSerializer def filter_queryset(self, queryset): queryset = queryset.filter(user=self.request.user) return queryset def create(self, request, *args, **kwargs): exampaper_id = request.data.get('exampaper') try: with transaction.atomic(): exampaper = ExamPaper.getById(exampaper_id) data = { 'subject': exampaper.subject.id, 'type': ExamPaper.MOCK, 'exampaper': exampaper_id, 'user': request.user.id, 'exam_time': timezone.now() } serializer = StaffExamLogSerializer(data=data) if serializer.is_valid(raise_exception=True): instance = serializer.save() result = { 'exam_log': instance.id, # 模拟考试 id } return response_ok(result) except ValidationError as e: traceback.print_exc() return response_error('数据格式有误') @action(methods=['post'], detail=True) def get_next_practise(self, request, pk): now_practise = request.data.get('now_practise') # 当前提交的模拟考试明细id。第一题或继续答题时,该参数为空 answers = json.loads(request.data.get('answers')) # 答案, 第一题或继续答题时,该参数为空 next_practise = request.data.get('next_practise') # 下一题id,首次加载第一题,传空 try: with transaction.atomic(): instance = self.get_object() # 点击下一题,保存、判断当前习题答案 if now_practise: detail = ExamPaperDetail.objects.filter(id=now_practise).first() if not detail: raise CustomError('提交的考试习题有误,请刷新重试!') now_question = detail.question if len(answers) > 0: answer_log, create = ExamAnswerLog.objects.get_or_create(main=instance, detail=detail,) if now_question.type == ExamQuestion.SINGLE: # 单选 ExamAnswerOptionLog.objects.filter(main=answer_log).delete() answer = answers[0] ExamAnswerOptionLog.objects.create(main=answer_log, option_id=answer) right = ExamQuestionOption.objects.filter(main=now_question, id=answer, right=True, delete=False) if right: answer_log.status = ExamAnswerLog.RIGHT else: answer_log.status = ExamAnswerLog.WRONG elif now_question.type == ExamQuestion.MULTIPLE: # 多选 answers.sort() ExamAnswerOptionLog.objects.filter(main=answer_log).delete() for a in answers: ExamAnswerOptionLog.objects.create(main=answer_log, option_id=a) right = ExamQuestionOption.objects.filter(main=now_question, right=True, delete=False).values_list('id', flat=True) list(right).sort() if answers == right: answer_log.status = ExamAnswerLog.RIGHT else: answer_log.status = ExamAnswerLog.WRONG elif now_question.type == ExamQuestion.FILL: # 填空 answers_len = len(answers) right = 1 ExamAnswerFillLog.objects.filter(main=answer_log).delete() for a in range(0, answers_len): ExamAnswerFillLog.objects.create(main=answer_log, content=answers[a], order=a + 1) right_answer = ExamQuestionFill.objects.filter(main=now_question, content=answers[a], order=a + 1) if not right_answer: # 有一个填空错误,整题错误 right = 0 if right: answer_log.status = ExamAnswerLog.RIGHT else: answer_log.status = ExamAnswerLog.WRONG else: # 判断 if now_question.judgment == (answers[0] == 1): answer_log.status = ExamAnswerLog.RIGHT else: answer_log.status = ExamAnswerLog.WRONG answer_log.save() else: try: answer_log = ExamAnswerLog.objects.get(main=instance, question=now_question, ) ExamAnswerOptionLog.objects.filter(main=answer_log).delete() ExamAnswerFillLog.objects.filter(main=answer_log).delete() answer_log.status = None answer_log.save() except ExamAnswerLog.DoesNotExist: # traceback.print_exc() pass question_data = {} # 返回下一题 if next_practise: detail = ExamPaperDetail.objects.filter(id=next_practise, main=instance.exampaper, delete=False).first() else: detail = ExamPaperDetail.objects.filter(main=instance.exampaper, delete=False).first() if detail: question = detail.question question_data = { 'id': detail.id, 'title': question.title, 'next_type': question.type, # 下一题习题类别 'next_number': detail.order + 1, # 下下一题序号, 'option': [], } answer_log = ExamAnswerLog.objects.filter(main=instance, detail=detail).first() if question.type == ExamQuestion.JUDGMENT: item1 = { 'id': 1, 'content': '正确', 'answer': True if answer_log and answer_log.status == ExamAnswerLog.RIGHT else False } item0 = { 'id': 0, 'content': '错误', 'answer': True if answer_log and answer_log.status == ExamAnswerLog.WRONG else False } question_data['option'].append(item1) question_data['option'].append(item0) elif question.type <= ExamQuestion.MULTIPLE: rows = ExamQuestionOption.objects.filter(main=question, delete=False) for row in rows: option_log = ExamAnswerOptionLog.objects.filter(main=answer_log, option=row).first() item = { 'id': row.id, 'content': row.content, 'answer': option_log and True or False } question_data['option'].append(item) elif question.type == ExamQuestion.FILL: rows = ExamQuestionFill.objects.filter(main=question, delete=False) for row in rows: option_log = ExamAnswerFillLog.objects.filter(main=answer_log, order=row.order).first() item = { 'id': row.order, # 填空题序号 'content': option_log and option_log.content or '', } question_data['option'].append(item) # 右侧习题类别列表 # 单选、多选、填空。选择答案后,可能会把答案清空,得加上NOTDONE过滤 questions = ExamPaperDetail.objects.filter(main=instance.exampaper, delete=False).values_list('id', flat=True) single_questions_list = [] for single in questions.filter(question__type=ExamQuestion.SINGLE): answer_log = ExamAnswerLog.objects.filter(main=instance, detail=single).exclude( status=ExamAnswerLog.NOTDONE) single_questions_list.append( { 'question_id': single, 'complete': answer_log and True or False, } ) # 多选题 multiple_questions_list = [] for multiple in questions.filter(question__type=ExamQuestion.MULTIPLE): answer_log = ExamAnswerLog.objects.filter(main=instance, detail=multiple).exclude( status=ExamAnswerLog.NOTDONE) multiple_questions_list.append( { 'question_id': multiple, 'complete': answer_log and True or False, } ) # 填空题 fill_questions_list = [] for fill in questions.filter(question__type=ExamQuestion.FILL): answer_log = ExamAnswerLog.objects.filter(main=instance, detail=fill).exclude( status=ExamAnswerLog.NOTDONE) fill_questions_list.append( { 'question_id': fill, 'complete': answer_log and True or False, } ) # 判断题 judgment_questions_list = [] for judgment in questions.filter(question__type=ExamQuestion.JUDGMENT): answer_log = ExamAnswerLog.objects.filter(main=instance, detail=judgment).exclude( status=ExamAnswerLog.NOTDONE) judgment_questions_list.append( { 'question_id': judgment, 'complete': answer_log and True or False, } ) result = { 'question_data': question_data, # 下一题练习题 'single_questions_list': single_questions_list, # 单选 'multiple_questions_list': multiple_questions_list, # 多选 'fill_questions_list': fill_questions_list, # 填空 'judgment_questions_list': judgment_questions_list, # 判断 } return response_ok(result) except CustomError as e: return response_error(e.get_error_msg()) except Exception as e: traceback.print_exc() return response_error(str(e)) @action(methods=['post'], detail=True) def submit_practise(self, request, pk): # 习题交卷,把上个接口的判断答案,放到此处 try: instance = self.get_object() with transaction.atomic(): answer_logs = ExamAnswerLog.objects.filter(main=instance) for answer_log in answer_logs: question = answer_log.detail.question if question.type == ExamQuestion.SINGLE: # 单选 answers = ExamAnswerOptionLog.objects.filter(main=answer_log).first() if answers: if answers.option.right: answer_log.status = ExamAnswerLog.RIGHT else: answer_log.status = ExamAnswerLog.WRONG elif question.type == ExamQuestion.MULTIPLE: # 多选 answers = ExamAnswerOptionLog.objects.filter(main=answer_log).order_by('option_id').values_list('option_id', flat=True) right = ExamQuestionOption.objects.filter(main=question, right=True, delete=False).values_list('id', flat=True) if list(answers) == list(right): answer_log.status = ExamAnswerLog.RIGHT else: answer_log.status = ExamAnswerLog.WRONG elif question.type == ExamQuestion.FILL: # 填空 fill_logs = ExamAnswerFillLog.objects.filter(main=answer_log) right = True for fill_log in fill_logs: right_answer = ExamQuestionFill.objects.filter(main=question, content=fill_log.content, order=fill_log.order, delete=False) if not right_answer: right = False break if right: answer_log.status = ExamAnswerLog.RIGHT else: answer_log.status = ExamAnswerLog.WRONG else: # 判断 if question.judgment == (answer_log.status == ExamAnswerLog.RIGHT): answer_log.status = ExamAnswerLog.RIGHT else: answer_log.status = ExamAnswerLog.WRONG answer_log.save() instance.submit_time = timezone.now() instance.save() SysLog.objects.addnew(request.user, SysLog.INSERT, u"提交模拟考试题答案,id=%d" % (instance.id)) except CustomError as e: return response_error(e.get_error_msg()) except Exception as e: traceback.print_exc() return response_error(str(e)) return response_ok() class DictView(APIView): permission_classes = [IsStaff, ] def get(self, request): subjects = Subject.objects.filter(delete=False) serializers = SubjectSerializer(subjects, many=True) return response_ok(serializers.data)