|
@@ -0,0 +1,291 @@
|
|
|
+# 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 = int(request.data.get('next_number')) # 下一题序号,最后一题提交时,此参数为0
|
|
|
+ next_type = int(request.data.get('next_type')) # 下一题题型,默认1为单选题
|
|
|
+ button_type = request.data.get('button_type') # 上一题、下一题按钮类型,next下一题,previous上一题
|
|
|
+ previous_practise = request.data.get('previous_practise') # 上一题id
|
|
|
+ try:
|
|
|
+ with transaction.atomic():
|
|
|
+ instance = self.get_object()
|
|
|
+ # 点击下一题,保存、判断当前习题答案
|
|
|
+ if now_practise and button_type == 'next':
|
|
|
+ if len(answers) == 0:
|
|
|
+ raise CustomError('请选择该题的答案!')
|
|
|
+ now_question = ExamQuestion.objects.filter(id=now_practise).first()
|
|
|
+ if not now_question:
|
|
|
+ raise CustomError('提交的习题有误,请刷新重试!')
|
|
|
+ answer_log, create = PractiseAnswerLog.objects.get_or_create(main=instance, question=now_question,
|
|
|
+ status=PractiseAnswerLog.WRONG)
|
|
|
+ if now_question.type == ExamQuestion.SINGLE:
|
|
|
+ # 单选
|
|
|
+ if len(answers) != 1:
|
|
|
+ raise CustomError(u'请提交一个答案!')
|
|
|
+ answer = answers[0]
|
|
|
+ PractiseAnswerOptionLog.objects.create(main=answer_log, option_id=answer)
|
|
|
+ right = ExamQuestionOption.objects.filter(main=now_question, id=answer, right=True, )
|
|
|
+ if right:
|
|
|
+ instance.right_count += 1
|
|
|
+ answer_log.status = PractiseAnswerLog.RIGHT
|
|
|
+ else:
|
|
|
+ instance.wrong_count += 1
|
|
|
+ elif now_question.type == ExamQuestion.MULTIPLE:
|
|
|
+ # 多选
|
|
|
+ if len(answers) <= 1:
|
|
|
+ raise CustomError(u'请提交两个以上答案!')
|
|
|
+ answers.sort()
|
|
|
+ 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)
|
|
|
+ right.sort()
|
|
|
+ if answers == right:
|
|
|
+ answer_log.status = PractiseAnswerLog.RIGHT
|
|
|
+ instance.right_count += 1
|
|
|
+ else:
|
|
|
+ instance.wrong_count += 1
|
|
|
+ elif now_question.type == ExamQuestion.FILL:
|
|
|
+ # 填空
|
|
|
+ answers_len = len(answers)
|
|
|
+ right = 1
|
|
|
+ for a in range(1, answers_len):
|
|
|
+ PractiseAnswerFillLog.objects.create(main=answer_log, content=answers[a], order=a)
|
|
|
+ right_answer = ExamQuestionFill.objects.filter(main=now_question, content=answers[a], order=a)
|
|
|
+ if not right_answer:
|
|
|
+ # 有一个填空错误,整题错误
|
|
|
+ right = 0
|
|
|
+ if right:
|
|
|
+ answer_log.status = PractiseAnswerLog.RIGHT
|
|
|
+ instance.right_count += 1
|
|
|
+ else:
|
|
|
+ instance.wrong_count += 1
|
|
|
+ else:
|
|
|
+ # 判断
|
|
|
+ if now_question.judgment == (answers[0] == 1):
|
|
|
+ answer_log.status = PractiseAnswerLog.RIGHT
|
|
|
+ instance.right_count += 1
|
|
|
+ else:
|
|
|
+ instance.wrong_count += 1
|
|
|
+ instance.total_count += 1
|
|
|
+ # 第一题
|
|
|
+ if next_number == 1:
|
|
|
+ instance.begin_answer = answer_log
|
|
|
+ instance.end_answer = answer_log
|
|
|
+ instance.submit_time = timezone.now()
|
|
|
+ instance.save()
|
|
|
+ answer_log.order = next_number
|
|
|
+ answer_log.save()
|
|
|
+
|
|
|
+ question_data = {}
|
|
|
+ if previous_practise:
|
|
|
+ # 上一题
|
|
|
+ questions = ExamQuestion.objects.filter(id=previous_practise)
|
|
|
+ elif 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_number:
|
|
|
+ # 循环查询4个题型的试题,只到查询到试题
|
|
|
+ # question = None
|
|
|
+ # while not question and next_type <= ExamQuestion.JUDGMENT:
|
|
|
+ # question = questions.filter(type=int(next_type))[next_number - 1:next_number].first()
|
|
|
+ # if question:
|
|
|
+ # break
|
|
|
+ # next_type += 1
|
|
|
+ # next_number = 1
|
|
|
+ question = questions.filter()[next_number - 1:next_number].first()
|
|
|
+
|
|
|
+ if question:
|
|
|
+ # 查询到下一题,返回题目和答案
|
|
|
+ # 根据下一题,查询下下一题类型
|
|
|
+ # next_question = None
|
|
|
+ # while not next_question and next_type <= ExamQuestion.JUDGMENT:
|
|
|
+ # next_question = questions.filter(type=int(next_type))[next_number:next_number + 1].first()
|
|
|
+ # if next_question:
|
|
|
+ # if next_number == 0:
|
|
|
+ # next_number = 1
|
|
|
+ # break
|
|
|
+ # next_type += 1
|
|
|
+ # next_number = 0
|
|
|
+ # next_question = questions.filter()[next_number:next_number + 1]
|
|
|
+ # if next_question:
|
|
|
+ # next_number += 1
|
|
|
+ # else:
|
|
|
+ # next_number = 0
|
|
|
+ question_data = {
|
|
|
+ 'id': question.id,
|
|
|
+ 'title': question.title,
|
|
|
+ 'next_type': question.type, # 下一题类别 id,不在使用
|
|
|
+ 'next_number': next_number + 1, # 下下一题序号,如果为0,则没有下一题了
|
|
|
+ 'previous_practise': now_practise, # 上一题 id
|
|
|
+ '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.status == PractiseAnswerLog.RIGHT else False
|
|
|
+ }
|
|
|
+ item0 = {
|
|
|
+ 'id': 0,
|
|
|
+ 'content': '错误',
|
|
|
+ 'answer': True if 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)
|
|
|
+
|
|
|
+ # 右侧习题类别列表
|
|
|
+ # 单选题
|
|
|
+ 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)
|
|
|
+ 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)
|
|
|
+ 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)
|
|
|
+ fill_questions_list.append(
|
|
|
+ {
|
|
|
+ 'question_id':fill,
|
|
|
+ 'complete':answer_log and True or False,
|
|
|
+ }
|
|
|
+ )
|
|
|
+ # 判断题
|
|
|
+ judgment_questions_list = []
|
|
|
+ for judgment in questions.filter(type=ExamQuestion.MULTIPLE):
|
|
|
+ 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))
|
|
|
+
|
|
|
+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)
|