views.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  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.views import APIView
  7. from django.db import transaction
  8. from rest_framework.viewsets import ReadOnlyModelViewSet
  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
  13. from utils.format import ExcelImporter, WordImporter
  14. from apps.examination.examquestion.serializers import *
  15. from apps.examination.examquestion.filters import *
  16. from apps.system.models import SysLog
  17. from apps.foundation.models import Subject, Chapter
  18. class ExamQuestionViewSet(CustomModelViewSet):
  19. permission_classes = [IsAdministrator, ]
  20. queryset = ExamQuestion.objects.filter(delete=False)
  21. serializer_class = ExamQuestionSerializer
  22. def filter_queryset(self, queryset):
  23. queryset = queryset.filter()
  24. f = ExamQuestionFilter(self.request.GET, queryset=queryset)
  25. return f.qs
  26. def perform_create(self, serializer):
  27. super(ExamQuestionViewSet, self).perform_create(serializer)
  28. instance = serializer.instance
  29. validated_data = serializer.validated_data
  30. SysLog.objects.addnew(self.request.user, SysLog.INSERT, u'添加试题库试题,id=%d' % (instance.id), validated_data)
  31. def perform_update(self, serializer):
  32. super(ExamQuestionViewSet, self).perform_update(serializer)
  33. instance = serializer.instance
  34. validated_data = serializer.validated_data
  35. SysLog.objects.addnew(self.request.user, SysLog.UPDATE, u'修改试题库试题,id=%d' % (instance.id), validated_data)
  36. if 'feedback' in self.request.data and self.request.data['feedback']:
  37. feedback = ExamQuestionFeedback.getById(self.request.data['feedback'])
  38. feedback.process(self.request.user)
  39. feedback.save()
  40. SysLog.objects.addnew(self.request.user, SysLog.UPDATE, u'处理试题错误反馈,id=%d' % (feedback.id))
  41. def destroy(self, request, *args, **kwargs):
  42. with transaction.atomic():
  43. instance = self.get_object()
  44. instance.delete = True
  45. instance.save()
  46. SysLog.objects.addnew(request.user, SysLog.INSERT, u"删除试题库试题,id=%d" % instance.id)
  47. return response_ok()
  48. @action(methods=['post'], detail=False)
  49. def batch_del(self, request):
  50. ids = request.data
  51. try:
  52. if ids:
  53. with transaction.atomic():
  54. ExamQuestion.objects.filter(id__in=ids).update(delete=True)
  55. except CustomError as e:
  56. return response_error(e.get_error_msg())
  57. except Exception as e:
  58. traceback.print_exc()
  59. return response_error(str(e))
  60. return response_ok()
  61. @action(methods=['get'], detail=False)
  62. def export(self, request):
  63. queryset = self.filter_queryset(self.queryset)
  64. serializer = self.get_serializer(queryset, many=True)
  65. return response_ok(serializer.data)
  66. @action(methods=['post'], detail=False)
  67. def import_single(self, request):
  68. file = request.FILES.get('excel_file')
  69. status = ExcelImporter.validity(file)
  70. if not status['success']:
  71. return response_error(status['errors'])
  72. data = status['data']
  73. question_count = 0
  74. line = 2
  75. try:
  76. with transaction.atomic():
  77. cur_question = None
  78. for row in data:
  79. try:
  80. title = row['试题内容']
  81. subject = row['科目']
  82. chapter = row['章节']
  83. difficulty_text = row['难度']
  84. scores= row['分数']
  85. analysis = row['解析']
  86. content = row['单选选项']
  87. right = row['正确答案']
  88. except:
  89. raise CustomError(u'模版文件不正确,请检查模版文件或重新下载')
  90. if title and content:
  91. raise CustomError(u'第%d行:数据格式不正确' % line)
  92. if title and not content:
  93. if not subject:
  94. raise CustomError(u'第%d行:科目不能为空' % line)
  95. if not chapter:
  96. raise CustomError(u'第%d行:章节不能为空' % line)
  97. if not difficulty_text:
  98. raise CustomError(u'第%d行:难度不能为空' % line)
  99. if not scores:
  100. raise CustomError(u'第%d行:分数不能为空' % line)
  101. if difficulty_text == u'简单':
  102. difficulty = ExamQuestion.SIMPLE
  103. elif difficulty_text == u'中等':
  104. difficulty = ExamQuestion.MID
  105. elif difficulty_text == u'困难':
  106. difficulty = ExamQuestion.HARD
  107. else:
  108. raise CustomError(u'第%d行:难度为无效数据' % line)
  109. subject = Subject.objects.filter(name=subject, delete=False).first()
  110. if not subject:
  111. raise CustomError(u'第%d行:科目为无效数据' % line)
  112. chapter = Chapter.objects.filter(subject=subject, name=chapter, delete=False).first()
  113. if not chapter:
  114. raise CustomError(u'第%d行:章节为无效数据' % line)
  115. try:
  116. scores = int(scores)
  117. except:
  118. raise CustomError(u'第%d行:分数为无效数据' % line)
  119. cur_question = ExamQuestion.objects.create(
  120. chapter=chapter,
  121. type=ExamQuestion.SINGLE,
  122. difficulty=difficulty,
  123. scores=scores,
  124. title=title,
  125. analysis=analysis,
  126. create_user=request.user,
  127. create_time=timezone.now()
  128. )
  129. question_count += 1
  130. if not title and not cur_question:
  131. raise CustomError(u'第%d行:试题内容不能为空' % line)
  132. if content and not title:
  133. if not cur_question:
  134. continue
  135. if right:
  136. ExamQuestionOption.objects.create(main=cur_question, content=content, right=True)
  137. else:
  138. ExamQuestionOption.objects.create(main=cur_question, content=content)
  139. line += 1
  140. SysLog.objects.addnew(request.user, SysLog.IMPORT, u"导入[%d]道单选题" % (question_count))
  141. except CustomError as e:
  142. return response_error(e.get_error_msg())
  143. except Exception as e:
  144. traceback.print_exc()
  145. return response_error(str(e))
  146. return response_ok()
  147. @action(methods=['post'], detail=False)
  148. def import_multiple(self, request):
  149. file = request.FILES.get('excel_file')
  150. status = ExcelImporter.validity(file)
  151. if not status['success']:
  152. return response_error(status['errors'])
  153. data = status['data']
  154. question_count = 0
  155. line = 2
  156. try:
  157. with transaction.atomic():
  158. cur_question = None
  159. for row in data:
  160. try:
  161. title = row['试题内容']
  162. subject = row['科目']
  163. chapter = row['章节']
  164. difficulty_text = row['难度']
  165. scores = row['分数']
  166. analysis = row['解析']
  167. content = row['多选选项']
  168. right = row['正确答案']
  169. except:
  170. raise CustomError(u'模版文件不正确,请检查模版文件或重新下载')
  171. if title and content:
  172. raise CustomError(u'第%d行:数据格式不正确' % line)
  173. if title and not content:
  174. if not subject:
  175. raise CustomError(u'第%d行:科目不能为空' % line)
  176. if not chapter:
  177. raise CustomError(u'第%d行:章节不能为空' % line)
  178. if not difficulty_text:
  179. raise CustomError(u'第%d行:难度不能为空' % line)
  180. if not scores:
  181. raise CustomError(u'第%d行:分数不能为空' % line)
  182. if difficulty_text == u'简单':
  183. difficulty = ExamQuestion.SIMPLE
  184. elif difficulty_text == u'中等':
  185. difficulty = ExamQuestion.MID
  186. elif difficulty_text == u'困难':
  187. difficulty = ExamQuestion.HARD
  188. else:
  189. raise CustomError(u'第%d行:难度为无效数据' % line)
  190. subject = Subject.objects.filter(name=subject, delete=False).first()
  191. if not subject:
  192. raise CustomError(u'第%d行:科目为无效数据' % line)
  193. chapter = Chapter.objects.filter(subject=subject, name=chapter, delete=False).first()
  194. if not chapter:
  195. raise CustomError(u'第%d行:章节为无效数据' % line)
  196. try:
  197. scores = int(scores)
  198. except:
  199. raise CustomError(u'第%d行:分数为无效数据' % line)
  200. cur_question = ExamQuestion.objects.create(
  201. chapter=chapter,
  202. type=ExamQuestion.MULTIPLE,
  203. difficulty=difficulty,
  204. scores=scores,
  205. title=title,
  206. analysis=analysis,
  207. create_user=request.user,
  208. create_time=timezone.now()
  209. )
  210. question_count += 1
  211. if not title and not cur_question:
  212. raise CustomError(u'第%d行:试题内容不能为空' % line)
  213. if content and not title:
  214. if not cur_question:
  215. continue
  216. if right:
  217. ExamQuestionOption.objects.create(main=cur_question, content=content, right=True)
  218. else:
  219. ExamQuestionOption.objects.create(main=cur_question, content=content)
  220. line += 1
  221. SysLog.objects.addnew(request.user, SysLog.IMPORT, u"导入[%d]道多选题" % (question_count))
  222. except CustomError as e:
  223. return response_error(e.get_error_msg())
  224. except Exception as e:
  225. traceback.print_exc()
  226. return response_error(str(e))
  227. return response_ok()
  228. @action(methods=['post'], detail=False)
  229. def import_fill(self, request):
  230. file = request.FILES.get('excel_file')
  231. status = ExcelImporter.validity(file)
  232. if not status['success']:
  233. return response_error(status['errors'])
  234. data = status['data']
  235. question_count = 0
  236. line = 2
  237. try:
  238. with transaction.atomic():
  239. cur_question = None
  240. order = 0
  241. for row in data:
  242. try:
  243. title = row['试题内容']
  244. subject = row['科目']
  245. chapter = row['章节']
  246. difficulty_text = row['难度']
  247. scores = row['分数']
  248. analysis = row['解析']
  249. content = row['填空答案']
  250. except:
  251. raise CustomError(u'模版文件不正确,请检查模版文件或重新下载')
  252. if title and not content:
  253. raise CustomError(u'第%d行:答案不能为空' % line)
  254. if title and content:
  255. if not subject:
  256. raise CustomError(u'第%d行:科目不能为空' % line)
  257. if not chapter:
  258. raise CustomError(u'第%d行:章节不能为空' % line)
  259. if not difficulty_text:
  260. raise CustomError(u'第%d行:难度不能为空' % line)
  261. if not scores:
  262. raise CustomError(u'第%d行:分数不能为空' % line)
  263. if difficulty_text == u'简单':
  264. difficulty = ExamQuestion.SIMPLE
  265. elif difficulty_text == u'中等':
  266. difficulty = ExamQuestion.MID
  267. elif difficulty_text == u'困难':
  268. difficulty = ExamQuestion.HARD
  269. else:
  270. raise CustomError(u'第%d行:难度为无效数据' % line)
  271. subject = Subject.objects.filter(name=subject, delete=False).first()
  272. if not subject:
  273. raise CustomError(u'第%d行:科目为无效数据' % line)
  274. chapter = Chapter.objects.filter(subject=subject, name=chapter, delete=False).first()
  275. if not chapter:
  276. raise CustomError(u'第%d行:章节为无效数据' % line)
  277. try:
  278. scores = int(scores)
  279. except:
  280. raise CustomError(u'第%d行:分数为无效数据' % line)
  281. order = 1
  282. cur_question = ExamQuestion.objects.create(
  283. chapter=chapter,
  284. type=ExamQuestion.FILL,
  285. difficulty=difficulty,
  286. scores=scores,
  287. title=title,
  288. analysis=analysis,
  289. create_user=request.user,
  290. create_time=timezone.now()
  291. )
  292. ExamQuestionFill.objects.create(main=cur_question, content=content, order=order)
  293. question_count += 1
  294. order += 1
  295. if not title and not cur_question:
  296. raise CustomError(u'第%d行:试题内容不能为空' % line)
  297. if content and not title:
  298. if (not cur_question) or (not order):
  299. continue
  300. ExamQuestionFill.objects.create(main=cur_question, content=content, order=order)
  301. order += 1
  302. line += 1
  303. SysLog.objects.addnew(request.user, SysLog.IMPORT, u"导入[%d]道填空题" % (question_count))
  304. except CustomError as e:
  305. return response_error(e.get_error_msg())
  306. except Exception as e:
  307. traceback.print_exc()
  308. return response_error(str(e))
  309. return response_ok()
  310. @action(methods=['post'], detail=False)
  311. def import_judgment(self, request):
  312. file = request.FILES.get('excel_file')
  313. status = ExcelImporter.validity(file)
  314. if not status['success']:
  315. return response_error(status['errors'])
  316. data = status['data']
  317. line = 2
  318. try:
  319. with transaction.atomic():
  320. for row in data:
  321. try:
  322. title = row['试题内容']
  323. subject = row['科目']
  324. chapter = row['章节']
  325. difficulty_text = row['难度']
  326. scores = row['分数']
  327. analysis = row['解析']
  328. judgment_text = row['判断答案']
  329. except:
  330. raise CustomError(u'模版文件不正确,请检查模版文件或重新下载')
  331. if not title:
  332. raise CustomError(u'第%d行:试题内容不能为空' % line)
  333. if not subject:
  334. raise CustomError(u'第%d行:科目不能为空' % line)
  335. if not chapter:
  336. raise CustomError(u'第%d行:章节不能为空' % line)
  337. if not difficulty_text:
  338. raise CustomError(u'第%d行:难度不能为空' % line)
  339. if not scores:
  340. raise CustomError(u'第%d行:分数不能为空' % line)
  341. if not judgment_text:
  342. raise CustomError(u'第%d行:答案不能为空' % line)
  343. if difficulty_text == u'简单':
  344. difficulty = ExamQuestion.SIMPLE
  345. elif difficulty_text == u'中等':
  346. difficulty = ExamQuestion.MID
  347. elif difficulty_text == u'困难':
  348. difficulty = ExamQuestion.HARD
  349. else:
  350. raise CustomError(u'第%d行:难度为无效数据' % line)
  351. if judgment_text == u'正确':
  352. judgment = True
  353. elif judgment_text == u'错误':
  354. judgment = False
  355. else:
  356. raise CustomError(u'第%d行:答案为无效数据' % line)
  357. subject = Subject.objects.filter(name=subject, delete=False).first()
  358. if not subject:
  359. raise CustomError(u'第%d行:科目为无效数据' % line)
  360. chapter = Chapter.objects.filter(subject=subject, name=chapter, delete=False).first()
  361. if not chapter:
  362. raise CustomError(u'第%d行:章节为无效数据' % line)
  363. try:
  364. scores = int(scores)
  365. except:
  366. raise CustomError(u'第%d行:分数为无效数据' % line)
  367. ExamQuestion.objects.create(
  368. chapter=chapter,
  369. type=ExamQuestion.JUDGMENT,
  370. difficulty=difficulty,
  371. scores=scores,
  372. title=title,
  373. judgment=judgment,
  374. analysis=analysis,
  375. create_user=request.user,
  376. create_time=timezone.now()
  377. )
  378. line += 1
  379. SysLog.objects.addnew(request.user, SysLog.IMPORT, u"导入[%d]道判断题" % (line - 2))
  380. except CustomError as e:
  381. return response_error(e.get_error_msg())
  382. except Exception as e:
  383. traceback.print_exc()
  384. return response_error(str(e))
  385. return response_ok()
  386. @action(methods=['post'], detail=False)
  387. def import_discuss(self, request):
  388. file = request.FILES.get('excel_file')
  389. status = ExcelImporter.validity(file)
  390. if not status['success']:
  391. return response_error(status['errors'])
  392. data = status['data']
  393. line = 2
  394. try:
  395. with transaction.atomic():
  396. for row in data:
  397. try:
  398. title = row['试题内容']
  399. subject = row['科目']
  400. chapter = row['章节']
  401. difficulty_text = row['难度']
  402. scores = row['分数']
  403. analysis = row['解析']
  404. discuss_text = row['正确答案']
  405. except:
  406. raise CustomError(u'模版文件不正确,请检查模版文件或重新下载')
  407. if not title:
  408. raise CustomError(u'第%d行:试题内容不能为空' % line)
  409. if not subject:
  410. raise CustomError(u'第%d行:科目不能为空' % line)
  411. if not chapter:
  412. raise CustomError(u'第%d行:章节不能为空' % line)
  413. if not difficulty_text:
  414. raise CustomError(u'第%d行:难度不能为空' % line)
  415. if not scores:
  416. raise CustomError(u'第%d行:分数不能为空' % line)
  417. if not discuss_text:
  418. raise CustomError(u'第%d行:答案不能为空' % line)
  419. if difficulty_text == u'简单':
  420. difficulty = ExamQuestion.SIMPLE
  421. elif difficulty_text == u'中等':
  422. difficulty = ExamQuestion.MID
  423. elif difficulty_text == u'困难':
  424. difficulty = ExamQuestion.HARD
  425. else:
  426. raise CustomError(u'第%d行:难度为无效数据' % line)
  427. subject = Subject.objects.filter(name=subject, delete=False).first()
  428. if not subject:
  429. raise CustomError(u'第%d行:科目为无效数据' % line)
  430. chapter = Chapter.objects.filter(subject=subject, name=chapter, delete=False).first()
  431. if not chapter:
  432. raise CustomError(u'第%d行:章节为无效数据' % line)
  433. try:
  434. scores = int(scores)
  435. except:
  436. raise CustomError(u'第%d行:分数为无效数据' % line)
  437. ExamQuestion.objects.create(
  438. chapter=chapter,
  439. type=ExamQuestion.DISCUSS,
  440. difficulty=difficulty,
  441. scores=scores,
  442. title=title,
  443. discuss_answer=discuss_text,
  444. analysis=analysis,
  445. create_user=request.user,
  446. create_time=timezone.now()
  447. )
  448. line += 1
  449. SysLog.objects.addnew(request.user, SysLog.IMPORT, u"导入[%d]道论述题" % (line - 2))
  450. except CustomError as e:
  451. return response_error(e.get_error_msg())
  452. except Exception as e:
  453. traceback.print_exc()
  454. return response_error(str(e))
  455. return response_ok()
  456. @action(methods=['post'], detail=False)
  457. @transaction.atomic
  458. def import_word(self, request):
  459. file = request.FILES.get('word_file')
  460. data = WordImporter.parse(file)
  461. main_data = request.data.get('main_data')
  462. main_data = json.loads(main_data)
  463. # print(data)
  464. subject_id = main_data.get('subject')
  465. subject = Subject.objects.filter(id=subject_id).first()
  466. if not subject:
  467. return response_error('科目不存在')
  468. ExamQuestion.batch_create(subject, data, request.user)
  469. return response_ok(data)
  470. class ExamQuestionFeedbackViewSet(ReadOnlyModelViewSet):
  471. permission_classes = [IsAdministrator, ]
  472. queryset = ExamQuestionFeedback.objects.filter().order_by('status', '-id')
  473. serializer_class = ExamQuestionFeedbackSerializer
  474. def filter_queryset(self, queryset):
  475. f = ExamQuestionFeedbackFilter(self.request.GET, queryset=queryset)
  476. return f.qs