# coding=utf-8 import json import traceback from django.db.models import Sum, F 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 from utils.custom_modelviewset import CustomModelViewSet from utils import response_ok, response_error from utils.permission import IsAdministrator, IsStaff from apps.examination.examquestion.serializers import * from apps.examination.examquestion.filters import * from apps.system.models import SysLog from apps.foundation.serializers import SubjectSerializer, ChapterSerializer, SubjectSimpleSerializer, \ ChapterSimpleSerializer, Subject from apps.practise.practiselog.models import * from apps.practise.practiselog.serializers import PractiseLogSerializer class PractiseLogViewSet(CustomModelViewSet): permission_classes = [IsStaff, ] queryset = PractiseLog.objects.filter() serializer_class = PractiseLogSerializer def filter_queryset(self, queryset): queryset = queryset.filter(create_user=self.request.user) return queryset def create(self, request, *args, **kwargs): subject = request.data.get('subject') chapter = request.data.get('chapter') type = PractiseLog.CHAPTER try: with transaction.atomic(): if not chapter: # 进入练习题,没有选择章节,使用该科目下的第一个章节 # chapter = Chapter.objects.filter(subject=subject, delete=False).order_by('id').first() type = PractiseLog.SUBJECT data = { 'subject': int(subject), 'chapter': chapter and int(chapter) or None, 'type': type, 'create_user': request.user.id, 'submit_time': timezone.now() } serializer = PractiseLogSerializer(data=data) if serializer.is_valid(raise_exception=True): instance = serializer.save() result = { 'practise': 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') # 当前提交的练习题。第一题或继续答题时,该参数为空 answers = json.loads(request.data.get('answers')) # 答案, 第一题或继续答题时,该参数为空 next_number = request.data.get('next_number') or 0 # 下一题序号, 第一题提交为空,不在使用 next_practise = request.data.get('next_practise') # 下一题id,首次加载第一题,传空 try: with transaction.atomic(): instance = self.get_object() # 点击下一题,保存、判断当前习题答案 if now_practise: now_question = ExamQuestion.objects.filter(id=now_practise).first() if not now_question: raise CustomError('提交的习题有误,请刷新重试!') if len(answers) > 0: answer_log, create = PractiseAnswerLog.objects.get_or_create(main=instance, question=now_question, ) if now_question.type == ExamQuestion.SINGLE: # 单选 PractiseAnswerOptionLog.objects.filter(main=answer_log).delete() answer = answers[0] PractiseAnswerOptionLog.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 = PractiseAnswerLog.RIGHT else: answer_log.status = PractiseAnswerLog.WRONG elif now_question.type == ExamQuestion.MULTIPLE: # 多选 answers.sort() PractiseAnswerOptionLog.objects.filter(main=answer_log).delete() for a in answers: PractiseAnswerOptionLog.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 = PractiseAnswerLog.RIGHT else: answer_log.status = PractiseAnswerLog.WRONG elif now_question.type == ExamQuestion.FILL: # 填空 answers_len = len(answers) right = 1 PractiseAnswerFillLog.objects.filter(main=answer_log).delete() for a in range(0, answers_len): PractiseAnswerFillLog.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 = PractiseAnswerLog.RIGHT else: answer_log.status = PractiseAnswerLog.WRONG else: # 判断 if now_question.judgment == (answers[0] == 1): answer_log.status = PractiseAnswerLog.RIGHT else: answer_log.status = PractiseAnswerLog.WRONG instance.total_count += 1 # 第一题 if not instance.begin_answer: instance.begin_answer = answer_log instance.end_answer = answer_log instance.submit_time = timezone.now() instance.save() answer_log.save() else: try: answer_log = PractiseAnswerLog.objects.get(main=instance, question=now_question, ) PractiseAnswerOptionLog.objects.filter(main=answer_log).delete() PractiseAnswerFillLog.objects.filter(main=answer_log).delete() answer_log.status = None answer_log.save() except PractiseAnswerLog.DoesNotExist: # traceback.print_exc() pass question_data = {} if instance.type == PractiseLog.SUBJECT: questions = ExamQuestion.objects.filter(chapter__subject=instance.subject, delete=False).order_by( 'type') else: questions = ExamQuestion.objects.filter(chapter=instance.chapter, delete=False).order_by('type') # 返回下一题 if next_practise: question = questions.filter(id=next_practise).first() else: # 首次加载,选择第一个题 question = questions.filter().first() if question: question_data = { 'id': question.id, 'title': question.title, 'exam_title': '{}-{}'.format(question.chapter.subject.name,question.chapter.name,), 'next_type': question.type, # 下一题习题类别 'next_number': int(next_number) + 1, # 下下一题序号, 'option': [], } answer_log = PractiseAnswerLog.objects.filter(main=instance, question=question).first() if question.type == ExamQuestion.JUDGMENT: item1 = { 'id': 1, 'content': '正确', 'answer': True if answer_log and answer_log.status == PractiseAnswerLog.RIGHT else False } item0 = { 'id': 0, 'content': '错误', 'answer': True if answer_log and answer_log.status == PractiseAnswerLog.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 = PractiseAnswerOptionLog.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 = PractiseAnswerFillLog.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) # 右侧习题类别列表 # 单选题 # 单选、多选、填空。选择答案后,可能会把答案清空,得加上status__isnull=False过滤 questions = questions.values_list('id', flat=True) single_questions_list = [] for single in questions.filter(type=ExamQuestion.SINGLE): answer_log = PractiseAnswerLog.objects.filter(main=instance, question_id=single, status__isnull=False) single_questions_list.append( { 'question_id': single, 'complete': answer_log and True or False, } ) # 多选题 multiple_questions_list = [] for multiple in questions.filter(type=ExamQuestion.MULTIPLE): answer_log = PractiseAnswerLog.objects.filter(main=instance, question_id=multiple, status__isnull=False) multiple_questions_list.append( { 'question_id': multiple, 'complete': answer_log and True or False, } ) # 填空题 fill_questions_list = [] for fill in questions.filter(type=ExamQuestion.FILL): answer_log = PractiseAnswerLog.objects.filter(main=instance, question_id=fill, status__isnull=False) fill_questions_list.append( { 'question_id': fill, 'complete': answer_log and True or False, } ) # 判断题 judgment_questions_list = [] for judgment in questions.filter(type=ExamQuestion.JUDGMENT): answer_log = PractiseAnswerLog.objects.filter(main=instance, question_id=judgment) 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(): right_count = PractiseAnswerLog.objects.filter(main=instance, status=PractiseAnswerLog.RIGHT).count() wrong_count = PractiseAnswerLog.objects.filter(main=instance, status=PractiseAnswerLog.WRONG).count() instance.right_count = right_count instance.wrong_count = wrong_count 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): data = [] subjects = Subject.objects.filter(delete=False).values('id', 'name').order_by('id') for subject in subjects: chapters = ChapterSimpleSerializer( Chapter.objects.filter(delete=False, subject__delete=False, subject_id=subject['id']).order_by('id'), many=True).data item = { 'id': subject['id'], 'name': subject['name'], 'chapters': chapters, } data.append(item) return response_ok(data)