views.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. # coding=utf-8
  2. import json
  3. import traceback
  4. from django.db.models import Sum, F
  5. from rest_framework.decorators import action
  6. from rest_framework.serializers import ValidationError
  7. from rest_framework.views import APIView
  8. from django.db import transaction
  9. from django.db.models import Q
  10. from utils.custom_modelviewset import CustomModelViewSet
  11. from utils import response_ok, response_error
  12. from utils.permission import IsAdministrator, IsStaff
  13. from apps.examination.examquestion.serializers import *
  14. from apps.examination.examquestion.filters import *
  15. from apps.system.models import SysLog
  16. from apps.foundation.serializers import SubjectSerializer, ChapterSerializer, SubjectSimpleSerializer, \
  17. ChapterSimpleSerializer, Subject
  18. from apps.practise.practiselog.models import *
  19. from apps.practise.practiselog.serializers import PractiseLogSerializer
  20. class PractiseLogViewSet(CustomModelViewSet):
  21. permission_classes = [IsStaff, ]
  22. queryset = PractiseLog.objects.filter()
  23. serializer_class = PractiseLogSerializer
  24. def filter_queryset(self, queryset):
  25. queryset = queryset.filter(create_user=self.request.user)
  26. return queryset
  27. def create(self, request, *args, **kwargs):
  28. subject = request.data.get('subject')
  29. chapter = request.data.get('chapter')
  30. type = PractiseLog.CHAPTER
  31. try:
  32. with transaction.atomic():
  33. if not chapter:
  34. # 进入练习题,没有选择章节,使用该科目下的第一个章节
  35. # chapter = Chapter.objects.filter(subject=subject, delete=False).order_by('id').first()
  36. type = PractiseLog.SUBJECT
  37. data = {
  38. 'subject': int(subject),
  39. 'chapter': chapter and int(chapter) or None,
  40. 'type': type,
  41. 'create_user': request.user.id,
  42. 'submit_time': timezone.now()
  43. }
  44. serializer = PractiseLogSerializer(data=data)
  45. if serializer.is_valid(raise_exception=True):
  46. instance = serializer.save()
  47. result = {
  48. 'practise': instance.id, # 练习题 id
  49. }
  50. return response_ok(result)
  51. except ValidationError as e:
  52. traceback.print_exc()
  53. return response_error('数据格式有误')
  54. @action(methods=['post'], detail=True)
  55. def get_next_practise(self, request, pk):
  56. now_practise = request.data.get('now_practise') # 当前提交的练习题。第一题或继续答题时,该参数为空
  57. answers = json.loads(request.data.get('answers')) # 答案, 第一题或继续答题时,该参数为空
  58. next_number = int(request.data.get('next_number')) or 0 # 下一题序号, 第一题提交为空
  59. # next_type = int(request.data.get('next_type')) # 下一题题型,默认1为单选题
  60. button_type = request.data.get('button_type') # 上一题、下一题按钮类型,next下一题,previous上一题
  61. next_practise = request.data.get('next_practise') # 下一题id,首次加载第一题,传空
  62. try:
  63. with transaction.atomic():
  64. instance = self.get_object()
  65. # 点击下一题,保存、判断当前习题答案
  66. if now_practise and button_type == 'next':
  67. if len(answers) == 0:
  68. raise CustomError('请选择该题的答案!')
  69. now_question = ExamQuestion.objects.filter(id=now_practise).first()
  70. if not now_question:
  71. raise CustomError('提交的习题有误,请刷新重试!')
  72. answer_log, create = PractiseAnswerLog.objects.get_or_create(main=instance, question=now_question,
  73. status=PractiseAnswerLog.WRONG)
  74. if now_question.type == ExamQuestion.SINGLE:
  75. # 单选
  76. if len(answers) != 1:
  77. raise CustomError(u'请提交一个答案!')
  78. answer = answers[0]
  79. PractiseAnswerOptionLog.objects.create(main=answer_log, option_id=answer)
  80. right = ExamQuestionOption.objects.filter(main=now_question, id=answer, right=True, )
  81. if right:
  82. instance.right_count += 1
  83. answer_log.status = PractiseAnswerLog.RIGHT
  84. else:
  85. instance.wrong_count += 1
  86. elif now_question.type == ExamQuestion.MULTIPLE:
  87. # 多选
  88. if len(answers) <= 1:
  89. raise CustomError(u'请提交两个以上答案!')
  90. answers.sort()
  91. for a in answers:
  92. PractiseAnswerOptionLog.objects.create(main=answer_log, option_id=a)
  93. right = ExamQuestionOption.objects.filter(main=now_question, right=True,
  94. delete=False).values_list('id', flat=True)
  95. right.sort()
  96. if answers == right:
  97. answer_log.status = PractiseAnswerLog.RIGHT
  98. instance.right_count += 1
  99. else:
  100. instance.wrong_count += 1
  101. elif now_question.type == ExamQuestion.FILL:
  102. # 填空
  103. answers_len = len(answers)
  104. right = 1
  105. for a in range(1, answers_len):
  106. PractiseAnswerFillLog.objects.create(main=answer_log, content=answers[a], order=a)
  107. right_answer = ExamQuestionFill.objects.filter(main=now_question, content=answers[a], order=a)
  108. if not right_answer:
  109. # 有一个填空错误,整题错误
  110. right = 0
  111. if right:
  112. answer_log.status = PractiseAnswerLog.RIGHT
  113. instance.right_count += 1
  114. else:
  115. instance.wrong_count += 1
  116. else:
  117. # 判断
  118. if now_question.judgment == (answers[0] == 1):
  119. answer_log.status = PractiseAnswerLog.RIGHT
  120. instance.right_count += 1
  121. else:
  122. instance.wrong_count += 1
  123. instance.total_count += 1
  124. # 第一题
  125. if next_number == 1:
  126. instance.begin_answer = answer_log
  127. instance.end_answer = answer_log
  128. instance.submit_time = timezone.now()
  129. instance.save()
  130. answer_log.save()
  131. question_data = {}
  132. if instance.type == PractiseLog.SUBJECT:
  133. questions = ExamQuestion.objects.filter(chapter__subject=instance.subject, delete=False).order_by(
  134. 'type')
  135. else:
  136. questions = ExamQuestion.objects.filter(chapter=instance.chapter, delete=False).order_by('type')
  137. # 返回下一题
  138. if next_practise:
  139. # 循环查询4个题型的试题,只到查询到试题
  140. # question = None
  141. # while not question and next_type <= ExamQuestion.JUDGMENT:
  142. # question = questions.filter(type=int(next_type))[next_number - 1:next_number].first()
  143. # if question:
  144. # break
  145. # next_type += 1
  146. # next_number = 1
  147. question = questions.filter(id=next_practise).first()
  148. else:
  149. question = questions.filter().first()
  150. if question:
  151. # 查询到下一题,返回题目和答案
  152. # 根据下一题,查询下下一题类型
  153. # next_question = None
  154. # while not next_question and next_type <= ExamQuestion.JUDGMENT:
  155. # next_question = questions.filter(type=int(next_type))[next_number:next_number + 1].first()
  156. # if next_question:
  157. # if next_number == 0:
  158. # next_number = 1
  159. # break
  160. # next_type += 1
  161. # next_number = 0
  162. # next_question = questions.filter()[next_number:next_number + 1]
  163. # if next_question:
  164. # next_number += 1
  165. # else:
  166. # next_number = 0
  167. question_data = {
  168. 'id': question.id,
  169. 'title': question.title,
  170. 'next_type': question.type, # 下一题习题类别
  171. 'next_number': next_number + 1, # 下下一题序号,
  172. 'option': [],
  173. }
  174. answer_log = PractiseAnswerLog.objects.filter(main=instance, question=question).first()
  175. if question.type == ExamQuestion.JUDGMENT:
  176. item1 = {
  177. 'id': 1,
  178. 'content': '正确',
  179. 'answer': True if answer_log.status == PractiseAnswerLog.RIGHT else False
  180. }
  181. item0 = {
  182. 'id': 0,
  183. 'content': '错误',
  184. 'answer': True if answer_log.status == PractiseAnswerLog.WRONG else False
  185. }
  186. question_data['option'].append(item1)
  187. question_data['option'].append(item0)
  188. elif question.type <= ExamQuestion.MULTIPLE:
  189. rows = ExamQuestionOption.objects.filter(main=question, delete=False)
  190. for row in rows:
  191. option_log = PractiseAnswerOptionLog.objects.filter(main=answer_log, option=row).first()
  192. item = {
  193. 'id':row.id,
  194. 'content':row.content,
  195. 'answer': option_log and True or False
  196. }
  197. question_data['option'].append(item)
  198. elif question.type == ExamQuestion.FILL:
  199. rows = ExamQuestionFill.objects.filter(main=question, delete=False)
  200. for row in rows:
  201. option_log = PractiseAnswerFillLog.objects.filter(main=answer_log, order=row.order).first()
  202. item = {
  203. 'id':row.order, # 填空题序号
  204. 'content':option_log and option_log.content or '',
  205. }
  206. question_data['option'].append(item)
  207. # 右侧习题类别列表
  208. # 单选题
  209. questions = questions.values_list('id', flat=True)
  210. single_questions_list = []
  211. for single in questions.filter(type=ExamQuestion.SINGLE):
  212. answer_log = PractiseAnswerLog.objects.filter(main=instance, question_id=single)
  213. single_questions_list.append(
  214. {
  215. 'question_id':single,
  216. 'complete':answer_log and True or False,
  217. }
  218. )
  219. # 多选题
  220. multiple_questions_list = []
  221. for multiple in questions.filter(type=ExamQuestion.MULTIPLE):
  222. answer_log = PractiseAnswerLog.objects.filter(main=instance, question_id=multiple)
  223. multiple_questions_list.append(
  224. {
  225. 'question_id':multiple,
  226. 'complete':answer_log and True or False,
  227. }
  228. )
  229. # 填空题
  230. fill_questions_list = []
  231. for fill in questions.filter(type=ExamQuestion.FILL):
  232. answer_log = PractiseAnswerLog.objects.filter(main=instance, question_id=fill)
  233. fill_questions_list.append(
  234. {
  235. 'question_id':fill,
  236. 'complete':answer_log and True or False,
  237. }
  238. )
  239. # 判断题
  240. judgment_questions_list = []
  241. for judgment in questions.filter(type=ExamQuestion.MULTIPLE):
  242. answer_log = PractiseAnswerLog.objects.filter(main=instance, question_id=judgment)
  243. judgment_questions_list.append(
  244. {
  245. 'question_id':judgment,
  246. 'complete':answer_log and True or False,
  247. }
  248. )
  249. result = {
  250. 'question_data': question_data, # 下一题练习题
  251. 'single_questions_list': single_questions_list, # 单选
  252. 'multiple_questions_list': multiple_questions_list, # 多选
  253. 'fill_questions_list': fill_questions_list, # 填空
  254. 'judgment_questions_list': judgment_questions_list, # 判断
  255. }
  256. return response_ok(result)
  257. except CustomError as e:
  258. return response_error(e.get_error_msg())
  259. except Exception as e:
  260. traceback.print_exc()
  261. return response_error(str(e))
  262. class DictView(APIView):
  263. permission_classes = [IsStaff, ]
  264. def get(self, request):
  265. data = []
  266. subjects = Subject.objects.filter(delete=False).values('id', 'name').order_by('id')
  267. for subject in subjects:
  268. chapters = ChapterSimpleSerializer(
  269. Chapter.objects.filter(delete=False, subject__delete=False, subject_id=subject['id']).order_by('id'),
  270. many=True).data
  271. item = {
  272. 'id': subject['id'],
  273. 'name': subject['name'],
  274. 'chapters': chapters,
  275. }
  276. data.append(item)
  277. return response_ok(data)