views.py 23 KB

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