paper_consumer.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. import json
  4. import os
  5. import random
  6. import time
  7. from multiprocessing import Process, Queue
  8. from shutil import move
  9. from sqlalchemy import create_engine, and_,func
  10. from sqlalchemy.orm import sessionmaker
  11. from core.config import settings
  12. from models.marktask import *
  13. from models.paper import *
  14. from models.problem import *
  15. from utils.imgtool import crop_img_local, crop_img_remote
  16. from utils.fileuploader import ossfile_uploader
  17. from .recpaper import RecPaper
  18. from utils.cv2img import get_std_points,rec_ans
  19. engine = create_engine(settings.SYNC_MYSQL_URI)
  20. session = sessionmaker(bind=engine)
  21. def upload_simgs(simgs):
  22. """
  23. """
  24. urls = []
  25. for img in simgs:
  26. ossfile = os.path.split(img)[-1]
  27. url = ossfile_uploader.upload_from_file(img,ossfile,{"Content-Type":"image/png"})
  28. urls.append(url)
  29. return urls
  30. def get_marktask(task_id):
  31. """
  32. """
  33. dbsession = session()
  34. task_id = int(task_id)
  35. task = dbsession.query(MarkTask).get(task_id)
  36. dbsession.close()
  37. if task:
  38. return task
  39. return None
  40. def get_paper(pid):
  41. """
  42. """
  43. dbsession = session()
  44. pid = int(pid)
  45. paper = dbsession.query(Paper).get(pid)
  46. dbsession.close()
  47. if paper:
  48. return paper
  49. return None
  50. def get_paper_pieces(pid):
  51. """
  52. """
  53. dbsession = session()
  54. pid = int(pid)
  55. ques = dbsession.query(PaperQuestion).filter(and_(PaperQuestion.pid == pid,PaperQuestion.usage.in_([1,2]))).all()
  56. dbsession.close()
  57. if ques:
  58. return ques
  59. return None
  60. def get_student_marktask(task_id, sno):
  61. """
  62. """
  63. dbsession = session()
  64. task_id = int(task_id)
  65. smarktask = dbsession.query(StudentMarkTask).filter(
  66. and_(StudentMarkTask.task_id == task_id, StudentMarkTask.student_sno == sno)).scalar()
  67. dbsession.close()
  68. if smarktask:
  69. return smarktask
  70. return None
  71. def update_student_marktask(task_id, sno, imgs):
  72. """
  73. """
  74. dbsession = session()
  75. task = dbsession.query(StudentMarkTask).filter(StudentMarkTask.task_id==task_id)\
  76. .filter(StudentMarkTask.student_sno==sno).one()
  77. task.pimgs = json.dumps(imgs)
  78. dbsession.commit()
  79. dbsession.close()
  80. def get_student_answer(student_task_id,student_id,qid):
  81. """
  82. """
  83. dbsession = session()
  84. student_answer = dbsession.query(StudentAnswer).filter(
  85. and_(
  86. StudentAnswer.student_task_id==student_task_id,
  87. StudentAnswer.student_id==student_id,
  88. StudentAnswer.qid==qid,
  89. )).scalar()
  90. dbsession.close()
  91. if student_answer:
  92. return student_answer
  93. return None
  94. def create_student_answer(**info):
  95. """
  96. """
  97. dbsession = session()
  98. answer = StudentAnswer(**info)
  99. dbsession.add(answer)
  100. dbsession.commit()
  101. dbsession.refresh(answer)
  102. dbsession.close()
  103. return answer
  104. def update_student_answer(said,info):
  105. """更新学生答卷
  106. """
  107. dbsession = session()
  108. dbsession.query(StudentAnswer).filter(StudentAnswer.id==said).update(info)
  109. dbsession.commit()
  110. dbsession.close()
  111. def update_task(tid,info):
  112. """更新阅卷任务
  113. """
  114. dbsession = session()
  115. dbsession.query(MarkTask).filter(MarkTask.id==tid).update(info)
  116. dbsession.commit()
  117. dbsession.close()
  118. def update_student_marktask_status(said,info):
  119. """更新学生任务
  120. """
  121. dbsession = session()
  122. dbsession.query(StudentMarkTask).filter(StudentMarkTask.id==said).update(info)
  123. dbsession.commit()
  124. dbsession.close()
  125. def get_uploaded_amount(task_id):
  126. """获取已上传数量
  127. """
  128. dbsession = session()
  129. allsmt = dbsession.query(func.count(StudentMarkTask.id)).filter(StudentMarkTask.status==2).scalar()
  130. dbsession.close()
  131. return allsmt
  132. def update_class_error_questions(task_id,qid,answer,error_count,minus_answer):
  133. """更新班级错题记录
  134. """
  135. dbsession = session()
  136. db_class_error = dbsession.query(ClassErrorQuestion).filter(and_(ClassErrorQuestion.task_id==task_id,ClassErrorQuestion.qid==qid)).one()
  137. if db_class_error:
  138. #单题班级错题率
  139. error_count = db_class_error.error_count + error_count
  140. error_ratio = round(error_count / db_class_error.student_count * 100, 2)
  141. #错题分布
  142. answer_dist = json.loads(db_class_error.answer_dist) if db_class_error.answer_dist else {}
  143. if not answer_dist:
  144. answer_dist[answer] = 1
  145. else:
  146. answer_dist[answer] = answer_dist.get(answer, 0) + 1
  147. if minus_answer and answer_dist.get(minus_answer):
  148. answer_dist[minus_answer] = answer_dist[minus_answer] - 1
  149. db_class_error.error_count = error_count
  150. db_class_error.error_ratio = error_ratio
  151. db_class_error.answer_dist = json.dumps(answer_dist)
  152. dbsession.add(db_class_error)
  153. dbsession.commit()
  154. dbsession.refresh(db_class_error)
  155. dbsession.close()
  156. def update_student_error_questions(task,smarktask,error_count):
  157. """更新学生错题统计
  158. """
  159. task_id = task.id
  160. student_task_id = smarktask.id
  161. student_id = smarktask.student_id
  162. dbsession = session()
  163. db_student_error = dbsession.query(StudentErrorQuestion).filter(
  164. and_(
  165. StudentErrorQuestion.task_id==task_id,
  166. StudentErrorQuestion.student_task_id==student_task_id,
  167. StudentErrorQuestion.student_id==student_id
  168. )).one_or_none()
  169. if db_student_error:
  170. work_error_count = db_student_error.work_error_count + error_count
  171. total_errors = work_error_count
  172. error_ratio = round(total_errors/db_student_error.total_questions,2)*100
  173. db_student_error.work_error_count = work_error_count
  174. db_student_error.total_errors = total_errors
  175. db_student_error.error_ratio = error_ratio
  176. dbsession.add(db_student_error)
  177. dbsession.commit()
  178. dbsession.refresh(db_student_error)
  179. dbsession.close()
  180. def produce_papers(q):
  181. """
  182. """
  183. simgs = []
  184. while True:
  185. if not os.path.exists(settings.PAPERS_PATH):
  186. os.makedirs(settings.PAPERS_PATH)
  187. imglist = os.listdir(settings.PAPERS_PATH)
  188. imglist.sort()
  189. for pimg in imglist:
  190. if pimg.startswith("T"):
  191. pfile = os.path.join(settings.PAPERS_PATH, pimg)
  192. if os.path.isdir(pfile):
  193. ppimgs = os.listdir(pfile)
  194. if ppimgs:
  195. ppimgs.sort()
  196. task_id = os.path.split(pfile)[-1].split("-")[0].lstrip("T")
  197. task = get_marktask(task_id)
  198. if task:
  199. pages = task.pages
  200. for ppimg in ppimgs:
  201. src = os.path.join(pfile, ppimg)
  202. if os.path.isfile(src):
  203. workspace = os.path.join(pfile, "workspace")
  204. if not os.path.exists(workspace):
  205. os.makedirs(workspace)
  206. target = os.path.join(workspace, ppimg)
  207. move(src, target)
  208. simgs.append(target)
  209. if len(simgs) < pages:
  210. continue
  211. else:
  212. q.put(simgs)
  213. simgs = []
  214. time.sleep(0.1)
  215. def rec_papers(simgs, task):
  216. """
  217. """
  218. task_id = task.id
  219. pid = task.pid
  220. ans_points = json.loads(task.ans_points)
  221. # 识别考号和答案
  222. ans_img = simgs[0]
  223. std_points = get_std_points(ans_img)
  224. std_x = std_points[0]["x"]
  225. std_y = std_points[0]["y"]
  226. x = ans_points["x"] + std_x
  227. y = ans_points["y"] + std_y
  228. w = ans_points["w"]
  229. h = ans_points["h"]
  230. cut_point = (x, y, x + w, y + h)
  231. ans_img_card = crop_img_local(ans_img, cut_point)
  232. # 别考号和客观题
  233. qno_list = []
  234. # 获取标准切割的试题
  235. std_ques = get_paper_pieces(pid)
  236. for que in std_ques:
  237. if que.qtype in ["单选题","多选题"]:
  238. qno_list.append(que.qno)
  239. sno = random.choice(["010001","010002","010003","010004","010005"])
  240. # 单个学生阅卷任务
  241. smarktask = get_student_marktask(task_id, sno)
  242. if smarktask:
  243. pimgs = upload_simgs(simgs)
  244. update_student_marktask_status(smarktask.id,{"status":1,"pimgs":pimgs})
  245. # 生产学生作答库
  246. for que in std_ques:
  247. page = que.page
  248. qno = que.qno
  249. sqno = que.sqno
  250. qtype = que.qtype
  251. points = que.points
  252. std_ans = que.answer
  253. std_score = que.score
  254. marked_score = 0
  255. incorrect = 0
  256. status = 0
  257. answer = ""
  258. if qtype in ["单选题","多选题"]:
  259. std_ans_points = que.std_points
  260. answer = rec_ans(ans_img_card,std_ans_points)
  261. print(qno,answer,99999999999999)
  262. status = 1
  263. if answer != std_ans:
  264. marked_score = 0
  265. incorrect = 1
  266. else:
  267. marked_score = std_score
  268. ans_imgs = []
  269. for point in points:
  270. x = point["x"] + std_x
  271. y = point["y"] + std_y
  272. w = point["w"]
  273. h = point["h"]
  274. cut_point = (x, y, x + w, y + h)
  275. url = crop_img_remote(simgs[page], cut_point, qno)
  276. ans_imgs.append(url)
  277. answerdict = {
  278. "student_id": smarktask.student_id,
  279. "student_sno": smarktask.student_sno,
  280. "student_name": smarktask.student_name,
  281. "class_id": task.class_id,
  282. "task_id": task_id,
  283. "task_name": task.name,
  284. "student_task_id": smarktask.id,
  285. "pid": pid,
  286. "pno": smarktask.pno,
  287. "pimgs": smarktask.pimgs,
  288. "pname": smarktask.pname,
  289. "qimg": ans_imgs[0],
  290. "qtype": qtype,
  291. "mtype": smarktask.mtype,
  292. "qno": qno,
  293. "sqno": sqno,
  294. "answer": answer,
  295. "score": que.score,
  296. "qid": que.id,
  297. "stem": que.stem,
  298. "score": std_score,
  299. "std_answer": std_ans,
  300. "status": status,
  301. "incorrect": incorrect,
  302. "marked_score": marked_score,
  303. "creator_id": 1,
  304. "creator_name": "admin",
  305. "editor_id": 1,
  306. "editor_name": "admin",
  307. "school_name": smarktask.school_name,
  308. "grade_name": smarktask.grade_name,
  309. "class_name": smarktask.class_name
  310. }
  311. error_count = 0
  312. minus_answer = ""
  313. student_error_count = 0
  314. student_answer = get_student_answer(smarktask.id,smarktask.student_id,que.id)
  315. if not student_answer:
  316. student_answer = create_student_answer(**answerdict)
  317. if student_answer.incorrect:
  318. error_count += 1
  319. student_error_count += 1
  320. else:
  321. if student_answer.incorrect != incorrect:
  322. if student_answer.incorrect:
  323. if error_count:
  324. error_count += -1
  325. else:
  326. error_count = 0
  327. #学生错题
  328. if student_error_count:
  329. student_error_count += -1
  330. else:
  331. student_error_count = 0
  332. else:
  333. error_count += 1
  334. student_error_count += 1
  335. if qtype in ["单选题","多选题"]:
  336. if student_answer.answer != answer:
  337. minus_answer = student_answer.answer
  338. update_student_answer(student_answer.id,answerdict)
  339. if qtype in ["单选题","多选题"]:
  340. #更新班级错题
  341. update_class_error_questions(task_id,que.id,answer,error_count,minus_answer)
  342. #更新学生错题
  343. update_student_error_questions(task,smarktask,student_error_count)
  344. update_student_marktask_status(smarktask.id,{"status":2})
  345. uploaded_count = get_uploaded_amount(task_id)
  346. update_task(task_id,{"uploaded_amount":uploaded_count})
  347. for img in simgs:
  348. os.remove(img)
  349. def consumer_papers(q):
  350. """
  351. """
  352. simgs = []
  353. while True:
  354. simgs = q.get()
  355. if simgs:
  356. print(f"start consumer simgs:{simgs}")
  357. pfile = simgs[0]
  358. task_id = pfile.split("workspace")[0].split("-")[0].split("/")[-1].lstrip("T")
  359. task = get_marktask(task_id)
  360. if task:
  361. rec_papers(simgs, task)
  362. time.sleep(0.1)
  363. if __name__ == "__main__":
  364. q = Queue()
  365. p1 = Process(target=produce_papers, args=(q,))
  366. p1.start()
  367. for i in range(4):
  368. p2 = Process(target=consumer_papers, args=(q,))
  369. p2.start()