123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420 |
- # coding=utf-8
- import json
- import traceback
- from django.utils import timezone
- from rest_framework.decorators import action
- from django.db import transaction
- from django.db.models import F
- from utils.custom_modelviewset import CustomModelViewSet
- from utils import response_ok, response_error
- from utils.permission import IsStaff
- from apps.examination.exam.serializers import *
- from apps.examination.exam.filters import *
- from apps.system.models import SysLog
- from apps.examination.exam.models import ExamAnswerLog, ExamAnswerOptionLog, ExamAnswerFillLog
- from apps.examination.exampaper.models import ExamPaperDetail, ExamQuestion
- from apps.examination.examquestion.models import ExamQuestionOption, ExamQuestionFill, ExamQuestionFeedback
- from apps.examination.examquestion.serializers import ExamQuestionFeedbackSerializer
- from apps.practise.errorbook.models import ErrorBook
- from utils.format import strftime
- class ExamLogViewSet(CustomModelViewSet):
- permission_classes = [IsStaff, ]
- queryset = ExamLog.objects.filter(delete=False, exam__delete=False, type=ExamLog.FORMAL,)
- serializer_class = StaffExamLogSerializer
- def filter_queryset(self, queryset):
- queryset = queryset.filter(user=self.request.user)
- return queryset
- def retrieve(self, request, *args, **kwargs):
- instance = self.get_object()
- serializer = StaffExamLogRetrieveSerializer(instance)
- answer_log = []
- answer_logs = ExamAnswerLog.objects.filter(main=instance).order_by('detail__order')
- for al in answer_logs:
- item = {
- 'id':al.id,
- 'status':al.status,
- }
- answer_log.append(item)
- ranks = []
- rank_data = ExamLog.objects.filter(exam=instance.exam, rank__isnull=False, delete=False).order_by('rank').values('user__name','scores','rank')
- for rank in rank_data:
- item = {
- 'name':rank['user__name'],
- 'scores':rank['scores'],
- 'rank':rank['rank'],
- }
- ranks.append(item)
- result = {
- 'question': serializer.data,
- 'answer_log': answer_log,
- 'ranks': ranks,
- }
- return response_ok(result)
- @action(methods=['get'], detail=False)
- def get_home_exam(self, request):
- # 首页,考试提示
- start_time = timezone.now() + datetime.timedelta(minutes=30)
- queryset = ExamLog.objects.filter(delete=False,
- type=ExamLog.FORMAL,
- exam__exam_time__lte=start_time,
- user=request.user,
- exam__exam_end_time__gte=timezone.now()
- )
- data = []
- for exam_log in queryset:
- if exam_log.exam.exam_time < timezone.now() < exam_log.exam.exam_end_time:
- name = '您有一个考试,正在进行中,点击这里立即参加!'
- id = exam_log.id
- else:
- time = (exam_log.exam.exam_time - timezone.now()).seconds
- name = '您有一个考试,将在{}分钟后开始,请做好准备!'.format(int(time/60) + 1)
- id = ''
- item = {
- 'id':id,
- 'name':name,
- 'exam_end_time':exam_log.exam.exam_end_time,
- }
- data.append(item)
- return response_ok(data)
- @action(methods=['get'], detail=False)
- def get_exam(self, request):
- # 正式考试,获取考试列表,提前半小时
- start_time = timezone.now() + datetime.timedelta(minutes=30)
- queryset = ExamLog.objects.filter(delete=False,
- type=ExamLog.FORMAL,
- exam__exam_time__lte=start_time,
- user=request.user,
- exam__exam_end_time__gte=timezone.now()
- )
- data = []
- for exam_log in queryset:
- item = {
- 'id':exam_log.id,
- 'name':exam_log.exam.name,
- 'subject':exam_log.exam.subject and exam_log.exam.subject.name or '',
- 'exam_time':strftime(exam_log.exam.exam_time),
- 'exam_end_time':strftime(exam_log.exam.exam_end_time),
- 'duration':exam_log.exam.duration,
- 'submit_time':exam_log.submit_time and True or False,
- }
- data.append(item)
- return response_ok(data)
- @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,首次加载第一题,传空
- submit = request.data.get('submit') # 交卷1,下一题为空
- try:
- with transaction.atomic():
- instance = self.get_object()
- if instance.submit_time:
- raise CustomError('您已交卷,禁止重复答题!')
- if timezone.now() < instance.exam.exam_time:
- raise CustomError('还未到考试时间,请稍等!')
- if not submit and timezone.now() > instance.exam.exam_end_time:
- raise CustomError('考试已结束,禁止答题!')
- # 点击下一题,保存
- if now_practise:
- detail = ExamPaperDetail.objects.filter(id=now_practise).first()
- if not detail:
- raise CustomError('提交的考试习题有误,请刷新重试!')
- now_question = detail.question
- has_answers = len(answers) > 0
- # 填空题,判断至少填了一个空
- if now_question.type == ExamQuestion.FILL:
- has_answers = False
- for a in range(0, len(answers)):
- if answers[a]:
- has_answers = True
- break
- if has_answers > 0:
- answer_log, create = ExamAnswerLog.objects.get_or_create(main=instance,
- detail=detail, )
- if 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)
- elif now_question.type == ExamQuestion.FILL:
- # 填空
- answers_len = len(answers)
- ExamAnswerFillLog.objects.filter(main=answer_log).delete()
- for a in range(0, answers_len):
- ExamAnswerFillLog.objects.create(main=answer_log, content=answers[a] or '', order=a + 1)
- elif now_question.type == ExamQuestion.DISCUSS:
- # 论述题
- answer_log.status = ExamAnswerLog.WAIT_CHECK
- answer_log.discuss_answer = answers[0]
- answer_log.save()
- else:
- # 判断
- if 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, detail=detail)
- ExamAnswerOptionLog.objects.filter(main=answer_log).delete()
- ExamAnswerFillLog.objects.filter(main=answer_log).delete()
- answer_log.status = ExamAnswerLog.NOTDONE
- 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,
- 'question': question.id,
- 'title': question.title,
- 'exam_title': instance.exampaper.name,
- '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)
- elif question.type == ExamQuestion.DISCUSS: # 论述题
- item = {
- 'id': question.id, # 论述题序号
- 'content': answer_log and answer_log.discuss_answer 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):
- answers = ExamAnswerOptionLog.objects.filter(main__main=instance, main__detail=single).first()
- single_questions_list.append(
- {
- 'question_id': single,
- 'complete': answers and True or False,
- }
- )
- # 多选题
- multiple_questions_list = []
- for multiple in questions.filter(question__type=ExamQuestion.MULTIPLE):
- answers = ExamAnswerOptionLog.objects.filter(main__main=instance, main__detail=multiple).first()
- multiple_questions_list.append(
- {
- 'question_id': multiple,
- 'complete': answers and True or False,
- }
- )
- # 填空题
- fill_questions_list = []
- for fill in questions.filter(question__type=ExamQuestion.FILL):
- answers = ExamAnswerFillLog.objects.filter(main__main=instance, main__detail=fill).first()
- fill_questions_list.append(
- {
- 'question_id': fill,
- 'complete': answers 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,
- }
- )
- # 论述题
- discuss_questions_list = []
- for discuss in questions.filter(question__type=ExamQuestion.DISCUSS):
- answer_log = ExamAnswerLog.objects.filter(main=instance, detail=discuss).exclude(status=ExamAnswerLog.NOTDONE)
- discuss_questions_list.append(
- {
- 'question_id': discuss,
- '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, # 判断
- 'discuss_questions_list': discuss_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()
- end_time = instance.exam.exam_end_time + datetime.timedelta(minutes=1)
- if timezone.now() > end_time:
- raise CustomError('考试已结束!')
- if instance.submit_time:
- raise CustomError('您已交卷,禁止重复交卷!')
- with transaction.atomic():
- paper_details = ExamPaperDetail.objects.filter(main=instance.exampaper, delete=False)
- for detail in paper_details:
- # 创建考试未答题记录,如果没有找到答案记录,则该题保留未答状态
- answer_log, create = ExamAnswerLog.objects.get_or_create(main=instance, detail=detail)
- # answer_logs = ExamAnswerLog.objects.filter(main=instance)
- # for answer_log in answer_logs:
- question = 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
- instance.single_answer_count += 1
- else:
- answer_log.status = ExamAnswerLog.WRONG
- ErrorBook.add_error(question, request.user, answer_log)
- elif question.type == ExamQuestion.MULTIPLE:
- # 多选
- answers = ExamAnswerOptionLog.objects.filter(main=answer_log).order_by('option_id').values_list(
- 'option_id', flat=True)
- if answers:
- 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
- instance.multiple_answer_count += 1
- else:
- answer_log.status = ExamAnswerLog.WRONG
- ErrorBook.add_error(question, request.user, answer_log)
- elif question.type == ExamQuestion.FILL:
- # 填空
- fill_logs = ExamAnswerFillLog.objects.filter(main=answer_log)
- right = True
- if fill_logs:
- 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
- instance.fill_answer_count += 1
- else:
- answer_log.status = ExamAnswerLog.WRONG
- ErrorBook.add_error(question, request.user, answer_log)
- elif question.type == ExamQuestion.JUDGMENT:
- # 判断
- if answer_log.status != ExamAnswerLog.NOTDONE:
- if question.judgment == (answer_log.status == ExamAnswerLog.RIGHT):
- answer_log.status = ExamAnswerLog.RIGHT
- instance.judgment_answer_count += 1
- else:
- answer_log.status = ExamAnswerLog.WRONG
- ErrorBook.add_error(question, request.user, answer_log)
- else:
- # 论述题 需要人工评分
- pass
- answer_log.save()
- instance.submit_time = timezone.now()
- use_time = instance.submit_time - instance.exam_time
- instance.use_time = use_time.seconds
- single_answer_scores = instance.single_answer_count * instance.exampaper.single_scores
- multiple_answer_scores = instance.multiple_answer_count * instance.exampaper.multiple_scores
- fill_answer_scores = instance.fill_answer_count * instance.exampaper.fill_scores
- judgment_answer_scores = instance.judgment_answer_count * instance.exampaper.judgment_scores
- instance.single_answer_scores = single_answer_scores
- instance.multiple_answer_scores = multiple_answer_scores
- instance.fill_answer_scores = fill_answer_scores
- instance.judgment_answer_scores = judgment_answer_scores
- instance.scores = single_answer_scores + judgment_answer_scores + multiple_answer_scores + fill_answer_scores
- # 计算排名
- up_rank = ExamLog.objects.filter(exam=instance.exam, scores__gte=instance.scores).count()
- instance.rank = up_rank + 1
- ExamLog.objects.filter(exam=instance.exam, scores__lte=instance.scores).update(rank=F('rank') + 1)
- instance.save()
- instance.exampaper.did_count += 1
- instance.exampaper.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 ExamQuestionFeedbackViewSet(CustomModelViewSet):
- permission_classes = [IsStaff, ]
- queryset = ExamQuestionFeedback.objects.filter()
- serializer_class = ExamQuestionFeedbackSerializer
- def perform_create(self, serializer):
- super(ExamQuestionFeedbackViewSet, self).perform_create(serializer)
- instance = serializer.instance
- validated_data = serializer.validated_data
- SysLog.objects.addnew(self.request.user, SysLog.INSERT, u'添加试题错误反馈,id=%d' % instance.id, validated_data)
|