views.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. # coding=utf-8
  2. import json
  3. import traceback
  4. from django.utils import timezone
  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, Sum, F
  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.exam.serializers import *
  14. from apps.examination.exam.filters import *
  15. from apps.system.models import SysLog
  16. from apps.foundation.serializers import SubjectSerializer, ChapterSerializer, SubjectSimpleSerializer, \
  17. ChapterSimpleSerializer, Subject
  18. from apps.examination.exam.models import ExamAnswerLog, ExamAnswerOptionLog, ExamAnswerFillLog
  19. from apps.examination.exampaper.models import ExamPaper, ExamPaperDetail, ExamQuestion
  20. from apps.examination.exampaper.filters import ExamPaperFilter
  21. from apps.examination.exampaper.serializers import StaffExamPaperSerializer
  22. from apps.examination.examquestion.models import ExamQuestionOption, ExamQuestionFill
  23. from apps.practise.errorbook.models import ErrorBook
  24. class ExamPaperViewSet(CustomModelViewSet):
  25. permission_classes = [IsStaff, ]
  26. queryset = ExamPaper.objects.filter(delete=False, type=ExamPaper.MOCK)
  27. serializer_class = StaffExamPaperSerializer
  28. def list(self, request, *args, **kwargs):
  29. # 底栏合计
  30. # 统计做过多少套
  31. queryset = self.filter_queryset(self.get_queryset())
  32. queryset = ExamPaperFilter(self.request.GET, queryset=queryset, request=self.request).qs
  33. exampaper_ids = ExamLog.objects.filter(user=self.request.user, delete=False).values_list('exampaper_id',
  34. flat=True)
  35. did_paper = queryset.filter(id__in=exampaper_ids).count()
  36. totalRow = {'totalRow': 1, 'did_paper': did_paper, }
  37. page = self.paginate_queryset(queryset)
  38. if page is not None:
  39. serializer = self.get_serializer(page, many=True)
  40. data = serializer.data
  41. # if len(data) > 0:
  42. data.append(totalRow)
  43. return self.get_paginated_response(data)
  44. serializer = self.get_serializer(queryset, many=True)
  45. return response_ok(serializer.data)
  46. # def filter_queryset(self, queryset):
  47. # f = ExamPaperFilter(self.request.GET, queryset=queryset, request=self.request)
  48. # return f.qs
  49. class ExamLogViewSet(CustomModelViewSet):
  50. permission_classes = [IsStaff, ]
  51. queryset = ExamLog.objects.filter(delete=False, type=ExamLog.MOCK)
  52. serializer_class = StaffExamLogSerializer
  53. def filter_queryset(self, queryset):
  54. queryset = queryset.filter(user=self.request.user)
  55. return queryset
  56. def retrieve(self, request, *args, **kwargs):
  57. instance = self.get_object()
  58. serializer = StaffExamLogRetrieveSerializer(instance)
  59. answer_log = []
  60. answer_logs = ExamAnswerLog.objects.filter(main=instance).order_by('detail__order')
  61. for al in answer_logs:
  62. item = {
  63. 'id': al.id,
  64. 'status': al.status,
  65. }
  66. answer_log.append(item)
  67. result = {
  68. 'question': serializer.data,
  69. 'answer_log': answer_log,
  70. 'ranks': [],
  71. }
  72. return response_ok(result)
  73. def create(self, request, *args, **kwargs):
  74. exampaper_id = request.data.get('exampaper')
  75. try:
  76. with transaction.atomic():
  77. data = {
  78. 'type': ExamPaper.MOCK,
  79. 'exampaper': exampaper_id,
  80. 'user': request.user.id,
  81. 'exam_time': timezone.now()
  82. }
  83. serializer = StaffExamLogSerializer(data=data)
  84. if serializer.is_valid(raise_exception=True):
  85. instance = serializer.save()
  86. result = {
  87. 'exam_log': instance.id, # 模拟考试 id
  88. }
  89. return response_ok(result)
  90. except ValidationError as e:
  91. traceback.print_exc()
  92. return response_error('数据格式有误')
  93. @action(methods=['post'], detail=True)
  94. def get_next_practise(self, request, pk):
  95. now_practise = request.data.get('now_practise') # 当前提交的模拟考试明细id。第一题或继续答题时,该参数为空
  96. answers = json.loads(request.data.get('answers')) # 答案, 第一题或继续答题时,该参数为空
  97. next_practise = request.data.get('next_practise') # 下一题id,首次加载第一题,传空
  98. try:
  99. with transaction.atomic():
  100. instance = self.get_object()
  101. if instance.submit_time:
  102. raise CustomError('您已交卷,禁止重复答题!')
  103. # 点击下一题,保存
  104. if now_practise:
  105. detail = ExamPaperDetail.objects.filter(id=now_practise).first()
  106. if not detail:
  107. raise CustomError('提交的考试习题有误,请刷新重试!')
  108. now_question = detail.question
  109. has_answers = len(answers) > 0
  110. if now_question.type == ExamQuestion.FILL:
  111. has_answers = False
  112. for a in range(0, len(answers)):
  113. if answers[a]:
  114. has_answers = True
  115. break
  116. if has_answers > 0:
  117. answer_log, create = ExamAnswerLog.objects.get_or_create(main=instance,
  118. detail=detail, )
  119. if now_question.type <= ExamQuestion.MULTIPLE:
  120. # 单选、多选
  121. answers.sort()
  122. ExamAnswerOptionLog.objects.filter(main=answer_log).delete()
  123. for a in answers:
  124. ExamAnswerOptionLog.objects.create(main=answer_log, option_id=a)
  125. elif now_question.type == ExamQuestion.FILL:
  126. # 填空
  127. answers_len = len(answers)
  128. ExamAnswerFillLog.objects.filter(main=answer_log).delete()
  129. for a in range(0, answers_len):
  130. ExamAnswerFillLog.objects.create(main=answer_log, content=answers[a] or '', order=a + 1)
  131. elif now_question.type == ExamQuestion.DISCUSS:
  132. # 论述题
  133. answer_log.status = ExamAnswerLog.WAIT_CHECK
  134. answer_log.discuss_answer = answers[0]
  135. answer_log.save()
  136. else:
  137. # 判断
  138. if answers[0] == 1:
  139. answer_log.status = ExamAnswerLog.RIGHT
  140. else:
  141. answer_log.status = ExamAnswerLog.WRONG
  142. answer_log.save()
  143. else:
  144. try:
  145. answer_log = ExamAnswerLog.objects.get(main=instance, detail=detail)
  146. ExamAnswerOptionLog.objects.filter(main=answer_log).delete()
  147. ExamAnswerFillLog.objects.filter(main=answer_log).delete()
  148. answer_log.status = ExamAnswerLog.NOTDONE
  149. answer_log.save()
  150. except ExamAnswerLog.DoesNotExist:
  151. # traceback.print_exc()
  152. pass
  153. question_data = {}
  154. # 返回下一题
  155. if next_practise:
  156. detail = ExamPaperDetail.objects.filter(id=next_practise, main=instance.exampaper,
  157. delete=False).first()
  158. else:
  159. detail = ExamPaperDetail.objects.filter(main=instance.exampaper, delete=False).first()
  160. if detail:
  161. question = detail.question
  162. question_data = {
  163. 'id': detail.id,
  164. 'question': question.id,
  165. 'title': question.title,
  166. 'exam_time': instance.exam_time.strftime('%Y-%m-%d %H:%M:%S'),
  167. 'exam_title': instance.exampaper.name,
  168. 'next_type': question.type, # 下一题习题类别
  169. 'next_number': detail.order + 1, # 下下一题序号,
  170. 'option': [],
  171. }
  172. answer_log = ExamAnswerLog.objects.filter(main=instance, detail=detail).first()
  173. if question.type == ExamQuestion.JUDGMENT:
  174. item1 = {
  175. 'id': 1,
  176. 'content': '正确',
  177. 'answer': True if answer_log and answer_log.status == ExamAnswerLog.RIGHT else False
  178. }
  179. item0 = {
  180. 'id': 0,
  181. 'content': '错误',
  182. 'answer': True if answer_log and answer_log.status == ExamAnswerLog.WRONG else False
  183. }
  184. question_data['option'].append(item1)
  185. question_data['option'].append(item0)
  186. elif question.type <= ExamQuestion.MULTIPLE:
  187. rows = ExamQuestionOption.objects.filter(main=question, delete=False)
  188. for row in rows:
  189. option_log = ExamAnswerOptionLog.objects.filter(main=answer_log, option=row).first()
  190. item = {
  191. 'id': row.id,
  192. 'content': row.content,
  193. 'answer': option_log and True or False
  194. }
  195. question_data['option'].append(item)
  196. elif question.type == ExamQuestion.FILL:
  197. rows = ExamQuestionFill.objects.filter(main=question, delete=False)
  198. for row in rows:
  199. option_log = ExamAnswerFillLog.objects.filter(main=answer_log, order=row.order).first()
  200. item = {
  201. 'id': row.order, # 填空题序号
  202. 'content': option_log and option_log.content or '',
  203. }
  204. question_data['option'].append(item)
  205. elif question.type == ExamQuestion.DISCUSS: # 论述题
  206. item = {
  207. 'id': question.id, # 论述题序号
  208. 'content': answer_log and answer_log.discuss_answer or '',
  209. }
  210. question_data['option'].append(item)
  211. # 右侧习题类别列表
  212. # 单选、多选、填空。选择答案后,可能会把答案清空,得加上NOTDONE过滤
  213. questions = ExamPaperDetail.objects.filter(main=instance.exampaper, delete=False).values_list('id',
  214. flat=True)
  215. single_questions_list = []
  216. for single in questions.filter(question__type=ExamQuestion.SINGLE):
  217. answer_log = ExamAnswerLog.objects.filter(main=instance, detail=single)
  218. single_questions_list.append(
  219. {
  220. 'question_id': single,
  221. 'complete': answer_log and True or False,
  222. }
  223. )
  224. # 多选题
  225. multiple_questions_list = []
  226. for multiple in questions.filter(question__type=ExamQuestion.MULTIPLE):
  227. answer_log = ExamAnswerLog.objects.filter(main=instance, detail=multiple)
  228. multiple_questions_list.append(
  229. {
  230. 'question_id': multiple,
  231. 'complete': answer_log and True or False,
  232. }
  233. )
  234. # 填空题
  235. fill_questions_list = []
  236. for fill in questions.filter(question__type=ExamQuestion.FILL):
  237. answer_log = ExamAnswerLog.objects.filter(main=instance, detail=fill)
  238. fill_questions_list.append(
  239. {
  240. 'question_id': fill,
  241. 'complete': answer_log and True or False,
  242. }
  243. )
  244. # 判断题
  245. judgment_questions_list = []
  246. for judgment in questions.filter(question__type=ExamQuestion.JUDGMENT):
  247. answer_log = ExamAnswerLog.objects.filter(main=instance, detail=judgment)
  248. judgment_questions_list.append(
  249. {
  250. 'question_id': judgment,
  251. 'complete': answer_log and True or False,
  252. }
  253. )
  254. # 论述题
  255. discuss_questions_list = []
  256. for discuss in questions.filter(question__type=ExamQuestion.DISCUSS):
  257. answer_log = ExamAnswerLog.objects.filter(main=instance, detail=discuss).exclude(
  258. status=ExamAnswerLog.NOTDONE)
  259. discuss_questions_list.append(
  260. {
  261. 'question_id': discuss,
  262. 'complete': answer_log and True or False,
  263. }
  264. )
  265. result = {
  266. 'question_data': question_data, # 下一题练习题
  267. 'single_questions_list': single_questions_list, # 单选
  268. 'multiple_questions_list': multiple_questions_list, # 多选
  269. 'fill_questions_list': fill_questions_list, # 填空
  270. 'judgment_questions_list': judgment_questions_list, # 判断
  271. 'discuss_questions_list': discuss_questions_list, # 论述
  272. }
  273. return response_ok(result)
  274. except CustomError as e:
  275. return response_error(e.get_error_msg())
  276. except Exception as e:
  277. traceback.print_exc()
  278. return response_error(str(e))
  279. @action(methods=['post'], detail=True)
  280. def submit_practise(self, request, pk):
  281. # 习题交卷,把上个接口的判断答案,放到此处
  282. try:
  283. instance = self.get_object()
  284. if instance.submit_time:
  285. raise CustomError('您已交卷,禁止重复交卷!')
  286. with transaction.atomic():
  287. paper_details = ExamPaperDetail.objects.filter(main=instance.exampaper, delete=False)
  288. for detail in paper_details:
  289. # 创建模拟考试未答题记录,如果没有找到答案记录,则该题保留未答状态
  290. answer_log, create = ExamAnswerLog.objects.get_or_create(main=instance, detail=detail)
  291. # answer_logs = ExamAnswerLog.objects.filter(main=instance)
  292. # for answer_log in answer_logs:
  293. question = detail.question
  294. if question.type == ExamQuestion.SINGLE:
  295. # 单选
  296. answers = ExamAnswerOptionLog.objects.filter(main=answer_log).first()
  297. if answers:
  298. if answers.option.right:
  299. answer_log.status = ExamAnswerLog.RIGHT
  300. instance.single_answer_count += 1
  301. else:
  302. answer_log.status = ExamAnswerLog.WRONG
  303. ErrorBook.add_error(question, request.user, answer_log)
  304. elif question.type == ExamQuestion.MULTIPLE:
  305. # 多选
  306. answers = ExamAnswerOptionLog.objects.filter(main=answer_log).order_by('option_id').values_list(
  307. 'option_id', flat=True)
  308. if answers:
  309. right = ExamQuestionOption.objects.filter(main=question, right=True,
  310. delete=False).values_list('id', flat=True)
  311. if list(answers) == list(right):
  312. answer_log.status = ExamAnswerLog.RIGHT
  313. instance.multiple_answer_count += 1
  314. else:
  315. answer_log.status = ExamAnswerLog.WRONG
  316. ErrorBook.add_error(question, request.user, answer_log)
  317. elif question.type == ExamQuestion.FILL:
  318. # 填空
  319. fill_logs = ExamAnswerFillLog.objects.filter(main=answer_log)
  320. right = True
  321. if fill_logs:
  322. for fill_log in fill_logs:
  323. right_answer = ExamQuestionFill.objects.filter(main=question, content=fill_log.content,
  324. order=fill_log.order, delete=False)
  325. if not right_answer:
  326. right = False
  327. break
  328. if right:
  329. answer_log.status = ExamAnswerLog.RIGHT
  330. instance.fill_answer_count += 1
  331. else:
  332. answer_log.status = ExamAnswerLog.WRONG
  333. ErrorBook.add_error(question, request.user, answer_log)
  334. elif question.type == ExamQuestion.JUDGMENT:
  335. # 判断
  336. if answer_log.status != ExamAnswerLog.NOTDONE:
  337. if question.judgment == (answer_log.status == ExamAnswerLog.RIGHT):
  338. answer_log.status = ExamAnswerLog.RIGHT
  339. instance.judgment_answer_count += 1
  340. else:
  341. answer_log.status = ExamAnswerLog.WRONG
  342. ErrorBook.add_error(question, request.user, answer_log)
  343. else:
  344. # 论述题 需要人工评分
  345. pass
  346. answer_log.save()
  347. instance.submit_time = timezone.now()
  348. use_time = instance.submit_time - instance.exam_time
  349. instance.use_time = use_time.seconds
  350. single_answer_scores = instance.single_answer_count * instance.exampaper.single_scores
  351. multiple_answer_scores = instance.multiple_answer_count * instance.exampaper.multiple_scores
  352. fill_answer_scores = instance.fill_answer_count * instance.exampaper.fill_scores
  353. judgment_answer_scores = instance.judgment_answer_count * instance.exampaper.judgment_scores
  354. instance.single_answer_scores = single_answer_scores
  355. instance.multiple_answer_scores = multiple_answer_scores
  356. instance.fill_answer_scores = fill_answer_scores
  357. instance.judgment_answer_scores = judgment_answer_scores
  358. instance.scores = single_answer_scores + judgment_answer_scores + multiple_answer_scores + fill_answer_scores
  359. instance.save()
  360. instance.exampaper.did_count += 1
  361. instance.exampaper.save()
  362. SysLog.objects.addnew(request.user, SysLog.INSERT, u"提交模拟考试题,id=%d" % (instance.id))
  363. except CustomError as e:
  364. return response_error(e.get_error_msg())
  365. except Exception as e:
  366. traceback.print_exc()
  367. return response_error(str(e))
  368. return response_ok()
  369. class DictView(APIView):
  370. permission_classes = [IsStaff, ]
  371. def get(self, request):
  372. subjects = Subject.objects.filter(delete=False)
  373. serializers = SubjectSerializer(subjects, many=True)
  374. return response_ok(serializers.data)