views.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  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 = request.data.get('next_number') or 0 # 下一题序号, 第一题提交为空,不在使用
  59. next_practise = request.data.get('next_practise') # 下一题id,首次加载第一题,传空
  60. try:
  61. with transaction.atomic():
  62. instance = self.get_object()
  63. # 点击下一题,保存、判断当前习题答案
  64. if now_practise:
  65. now_question = ExamQuestion.objects.filter(id=now_practise).first()
  66. if not now_question:
  67. raise CustomError('提交的习题有误,请刷新重试!')
  68. has_answers = len(answers) > 0
  69. if now_question.type == ExamQuestion.FILL:
  70. has_answers = False
  71. for a in range(0, len(answers)):
  72. if answers[a]:
  73. has_answers = True
  74. break
  75. if has_answers > 0:
  76. answer_log, create = PractiseAnswerLog.objects.get_or_create(main=instance,
  77. question=now_question, )
  78. if now_question.type == ExamQuestion.SINGLE:
  79. # 单选
  80. PractiseAnswerOptionLog.objects.filter(main=answer_log).delete()
  81. answer = answers[0]
  82. PractiseAnswerOptionLog.objects.create(main=answer_log, option_id=answer)
  83. right = ExamQuestionOption.objects.filter(main=now_question, id=answer, right=True,
  84. delete=False)
  85. if right:
  86. answer_log.status = PractiseAnswerLog.RIGHT
  87. else:
  88. answer_log.status = PractiseAnswerLog.WRONG
  89. elif now_question.type == ExamQuestion.MULTIPLE:
  90. # 多选
  91. answers.sort()
  92. PractiseAnswerOptionLog.objects.filter(main=answer_log).delete()
  93. for a in answers:
  94. PractiseAnswerOptionLog.objects.create(main=answer_log, option_id=a)
  95. right = ExamQuestionOption.objects.filter(main=now_question, right=True,
  96. delete=False).values_list('id', flat=True)
  97. list(right).sort()
  98. if answers == right:
  99. answer_log.status = PractiseAnswerLog.RIGHT
  100. else:
  101. answer_log.status = PractiseAnswerLog.WRONG
  102. elif now_question.type == ExamQuestion.FILL:
  103. # 填空
  104. answers_len = len(answers)
  105. right = 1
  106. PractiseAnswerFillLog.objects.filter(main=answer_log).delete()
  107. for a in range(0, answers_len):
  108. PractiseAnswerFillLog.objects.create(main=answer_log, content=answers[a] or '', order=a + 1)
  109. right_answer = ExamQuestionFill.objects.filter(main=now_question, content=answers[a],
  110. order=a + 1)
  111. if not right_answer:
  112. # 有一个填空错误,整题错误
  113. right = 0
  114. if right:
  115. answer_log.status = PractiseAnswerLog.RIGHT
  116. else:
  117. answer_log.status = PractiseAnswerLog.WRONG
  118. elif now_question.type == ExamQuestion.DISCUSS:
  119. # 论述题
  120. answer_log.status = PractiseAnswerLog.WAIT_CHECK
  121. answer_log.discuss_answer = answers[0]
  122. answer_log.save()
  123. else:
  124. # 判断
  125. if now_question.judgment == (answers[0] == 1):
  126. answer_log.status = PractiseAnswerLog.RIGHT
  127. else:
  128. answer_log.status = PractiseAnswerLog.WRONG
  129. instance.total_count += 1
  130. # 第一题
  131. if not instance.begin_answer:
  132. instance.begin_answer = answer_log
  133. instance.end_answer = answer_log
  134. instance.submit_time = timezone.now()
  135. instance.save()
  136. answer_log.save()
  137. else:
  138. try:
  139. answer_log = PractiseAnswerLog.objects.get(main=instance, question=now_question, )
  140. PractiseAnswerOptionLog.objects.filter(main=answer_log).delete()
  141. PractiseAnswerFillLog.objects.filter(main=answer_log).delete()
  142. answer_log.status = None
  143. answer_log.save()
  144. except PractiseAnswerLog.DoesNotExist:
  145. # traceback.print_exc()
  146. pass
  147. question_data = {}
  148. if instance.type == PractiseLog.SUBJECT:
  149. questions = ExamQuestion.objects.filter(chapter__subject=instance.subject, delete=False).order_by(
  150. 'type')
  151. else:
  152. questions = ExamQuestion.objects.filter(chapter=instance.chapter, delete=False).order_by('type')
  153. # 返回下一题
  154. if next_practise:
  155. question = questions.filter(id=next_practise).first()
  156. else:
  157. # 首次加载,选择第一个题
  158. question = questions.filter().first()
  159. if question:
  160. question_data = {
  161. 'id': question.id,
  162. 'title': question.title,
  163. 'exam_title': '{}-{}'.format(question.chapter.subject.name,question.chapter.name,),
  164. 'next_type': question.type, # 下一题习题类别
  165. 'next_number': int(next_number) + 1, # 下下一题序号,
  166. 'option': [],
  167. }
  168. answer_log = PractiseAnswerLog.objects.filter(main=instance, question=question).first()
  169. if question.type == ExamQuestion.JUDGMENT:
  170. item1 = {
  171. 'id': 1,
  172. 'content': '正确',
  173. 'answer': True if answer_log and answer_log.status == PractiseAnswerLog.RIGHT else False
  174. }
  175. item0 = {
  176. 'id': 0,
  177. 'content': '错误',
  178. 'answer': True if answer_log and answer_log.status == PractiseAnswerLog.WRONG else False
  179. }
  180. question_data['option'].append(item1)
  181. question_data['option'].append(item0)
  182. elif question.type <= ExamQuestion.MULTIPLE:
  183. rows = ExamQuestionOption.objects.filter(main=question, delete=False)
  184. for row in rows:
  185. option_log = PractiseAnswerOptionLog.objects.filter(main=answer_log, option=row).first()
  186. item = {
  187. 'id': row.id,
  188. 'content': row.content,
  189. 'answer': option_log and True or False
  190. }
  191. question_data['option'].append(item)
  192. elif question.type == ExamQuestion.FILL:
  193. rows = ExamQuestionFill.objects.filter(main=question, delete=False)
  194. for row in rows:
  195. option_log = PractiseAnswerFillLog.objects.filter(main=answer_log, order=row.order).first()
  196. item = {
  197. 'id': row.order, # 填空题序号
  198. 'content': option_log and option_log.content or '',
  199. }
  200. question_data['option'].append(item)
  201. elif question.type == ExamQuestion.DISCUSS: # 论述题
  202. item = {
  203. 'id': question.id, # 论述题序号
  204. 'content': answer_log and answer_log.discuss_answer or '',
  205. }
  206. question_data['option'].append(item)
  207. # 右侧习题类别列表
  208. # 单选题
  209. # 单选、多选、填空。选择答案后,可能会把答案清空,得加上status__isnull=False过滤
  210. questions = questions.values_list('id', flat=True)
  211. single_questions_list = []
  212. for single in questions.filter(type=ExamQuestion.SINGLE):
  213. answer_log = PractiseAnswerLog.objects.filter(main=instance, question_id=single,
  214. status__isnull=False)
  215. single_questions_list.append(
  216. {
  217. 'question_id': single,
  218. 'complete': answer_log and True or False,
  219. }
  220. )
  221. # 多选题
  222. multiple_questions_list = []
  223. for multiple in questions.filter(type=ExamQuestion.MULTIPLE):
  224. answer_log = PractiseAnswerLog.objects.filter(main=instance, question_id=multiple,
  225. status__isnull=False)
  226. multiple_questions_list.append(
  227. {
  228. 'question_id': multiple,
  229. 'complete': answer_log and True or False,
  230. }
  231. )
  232. # 填空题
  233. fill_questions_list = []
  234. for fill in questions.filter(type=ExamQuestion.FILL):
  235. answer_log = PractiseAnswerLog.objects.filter(main=instance, question_id=fill, status__isnull=False)
  236. fill_questions_list.append(
  237. {
  238. 'question_id': fill,
  239. 'complete': answer_log and True or False,
  240. }
  241. )
  242. # 判断题
  243. judgment_questions_list = []
  244. for judgment in questions.filter(type=ExamQuestion.JUDGMENT):
  245. answer_log = PractiseAnswerLog.objects.filter(main=instance, question_id=judgment)
  246. judgment_questions_list.append(
  247. {
  248. 'question_id': judgment,
  249. 'complete': answer_log and True or False,
  250. }
  251. )
  252. # 论述题
  253. discuss_questions_list = []
  254. for discuss in questions.filter(type=ExamQuestion.DISCUSS):
  255. answer_log = PractiseAnswerLog.objects.filter(main=instance, question_id=discuss)
  256. discuss_questions_list.append(
  257. {
  258. 'question_id': discuss,
  259. 'complete': answer_log and True or False,
  260. }
  261. )
  262. result = {
  263. 'question_data': question_data, # 下一题练习题
  264. 'single_questions_list': single_questions_list, # 单选
  265. 'multiple_questions_list': multiple_questions_list, # 多选
  266. 'fill_questions_list': fill_questions_list, # 填空
  267. 'judgment_questions_list': judgment_questions_list, # 判断
  268. 'discuss_questions_list': discuss_questions_list, # 论述
  269. }
  270. return response_ok(result)
  271. except CustomError as e:
  272. return response_error(e.get_error_msg())
  273. except Exception as e:
  274. traceback.print_exc()
  275. return response_error(str(e))
  276. @action(methods=['post'], detail=True)
  277. def submit_practise(self, request, pk):
  278. # 习题交卷,把上个接口的判断答案,放到此处
  279. try:
  280. instance = self.get_object()
  281. with transaction.atomic():
  282. right_count = PractiseAnswerLog.objects.filter(main=instance, status=PractiseAnswerLog.RIGHT).count()
  283. wrong_count = PractiseAnswerLog.objects.filter(main=instance, status=PractiseAnswerLog.WRONG).count()
  284. instance.right_count = right_count
  285. instance.wrong_count = wrong_count
  286. instance.submit_time = timezone.now()
  287. instance.save()
  288. SysLog.objects.addnew(request.user, SysLog.INSERT, u"提交习题答案,id=%d" % (instance.id))
  289. except CustomError as e:
  290. return response_error(e.get_error_msg())
  291. except Exception as e:
  292. traceback.print_exc()
  293. return response_error(str(e))
  294. return response_ok()
  295. class DictView(APIView):
  296. permission_classes = [IsStaff, ]
  297. def get(self, request):
  298. data = []
  299. subjects = Subject.objects.filter(delete=False).values('id', 'name').order_by('id')
  300. for subject in subjects:
  301. chapters = ChapterSimpleSerializer(
  302. Chapter.objects.filter(delete=False, subject__delete=False, subject_id=subject['id']).order_by('id'),
  303. many=True).data
  304. item = {
  305. 'id': subject['id'],
  306. 'name': subject['name'],
  307. 'chapters': chapters,
  308. }
  309. data.append(item)
  310. return response_ok(data)