views.py 23 KB

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