question.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. import re
  4. from bs4 import BeautifulSoup
  5. from fastapi import Depends, Query
  6. from sqlalchemy.ext.asyncio import AsyncSession
  7. from crud.paper import crud_question, crud_paper
  8. from models.paper import PaperQuestion
  9. from models.user import Admin
  10. from schemas.base import ReturnField
  11. from schemas.paper.questions import QuestionInfo, SaveQuePieceInfo
  12. from utils.depends import get_async_db, get_current_user
  13. from utils.imgtool import download_remote_img, crop_img,crop_img_local,crop_img_remote
  14. from utils.cv2img import CV2img
  15. from utils.ansDetect import rec_std_ans
  16. async def create_question(info: QuestionInfo,
  17. db: AsyncSession = Depends(get_async_db),
  18. current_user: Admin = Depends(get_current_user)):
  19. info = info.dict(exclude_none=True)
  20. if not info:
  21. return {"errcode": 400, "mess": "请求参数为空!"}
  22. db_exist = await crud_question.find_one(db,
  23. filters={
  24. "pid": info["pid"],
  25. "pno": info["pno"],
  26. "qno": info["qno"],
  27. "sqno": info["sqno"]
  28. })
  29. if db_exist:
  30. return {"errcode": 400, "mess": "试题重复!"}
  31. info["creator_id"] = current_user.id
  32. info["creator_name"] = current_user.username
  33. db_obj = await crud_question.insert_one(db, info)
  34. return {"data": db_obj}
  35. async def update_question(info: SaveQuePieceInfo,
  36. db: AsyncSession = Depends(get_async_db),
  37. current_user: Admin = Depends(get_current_user)):
  38. info_dict = info.dict(exclude_none=True)
  39. if not info_dict:
  40. return {"errcode": 400, "mess": "请求参数为空!"}
  41. pid = info_dict["id"]
  42. pno = info_dict["pno"]
  43. ques = info_dict["questions"]
  44. obj_questions = info_dict["obj_questions"]
  45. ques.extend(obj_questions)
  46. total_score = 0
  47. for que in ques:
  48. qno = que["qno"]
  49. sqno = que["sqno"]
  50. qtype = que.get("qtype","")
  51. answer = que.get("answer",[])
  52. answer = ",".join(answer) if answer else ""
  53. score = float(que.get("score",0))
  54. total_score += score
  55. db_question = await crud_question.find_one(db,
  56. filters={
  57. "pid": pid,
  58. "pno": pno,
  59. "qno": qno,
  60. "sqno": sqno
  61. })
  62. if db_question:
  63. update_info = {"qtype": qtype, "answer": answer, "score": score}
  64. await crud_question.update(db, db_question, update_info)
  65. # 更新分数
  66. db_paper = await crud_paper.find_one(db, filters={"id": pid})
  67. await crud_paper.update(db, db_paper, {"score": total_score})
  68. # 更新试卷试题数量
  69. existed = await crud_question.count(db, filters={"pid": pid})
  70. if existed:
  71. await crud_paper.update(db, db_paper, {"question_amount": existed})
  72. return {"data": None}
  73. async def delete_question(qid: int,
  74. db: AsyncSession = Depends(get_async_db),
  75. current_user: Admin = Depends(get_current_user)):
  76. existed = await crud_question.count(db, filters={"id": qid})
  77. if not existed:
  78. return {"errcode": 400, "mess": "试题不存在!"}
  79. await crud_question.delete(db, qid)
  80. return {"data": None}
  81. async def get_question(qid: int,
  82. db: AsyncSession = Depends(get_async_db),
  83. current_user: Admin = Depends(get_current_user)):
  84. db_exist = await crud_question.find_one(db, filters={"id": qid})
  85. if not db_exist:
  86. return {"errcode": 400, "mess": "角色不存在!"}
  87. return {"data": db_exist}
  88. async def get_question_list(page: int = 1,
  89. size: int = 10,
  90. name: str = "",
  91. res: ReturnField = Query("", description="控制返回字段,字段逗号分隔"),
  92. db: AsyncSession = Depends(get_async_db),
  93. current_user: Admin = Depends(get_current_user)):
  94. _q = []
  95. if name:
  96. _q.append(PaperQuestion.name.like(f"{name}%"))
  97. offset = (page - 1) * size
  98. total, items = await crud_question.find_all(db,
  99. filters=_q,
  100. offset=offset,
  101. limit=size,
  102. return_fields=res)
  103. return {"total": total, "data": items}
  104. async def cut_imgs(std_img, points,std_points):
  105. """根据points完成图片切割
  106. """
  107. cut_urls = []
  108. for idx, point in enumerate(points):
  109. x = point["x"] + std_points["x"]
  110. y = point["y"] + std_points["y"]
  111. w = point["w"]
  112. h = point["h"]
  113. point = (x, y, x + w, y + h)
  114. original_image = await download_remote_img(std_img)
  115. url = await crop_img(original_image, point, idx)
  116. cut_urls.append(url)
  117. return cut_urls
  118. async def get_cut_imgs(db, current_user, info: SaveQuePieceInfo):
  119. """
  120. """
  121. db_paper = await crud_paper.find_one(db, filters={"id": info.pid})
  122. if not db_paper:
  123. return {"errcode": 404, "mess": "试卷不存在!"}
  124. if info.studentno_points:
  125. db_paper = await crud_paper.update(db, db_paper, {"studentno_points": info.studentno_points})
  126. if info.ans_points:
  127. #这里获取标准点
  128. std_points = db_paper.points[0][0]
  129. ans_url = await cut_imgs(db_paper.imgs[0], [info.ans_points],std_points)
  130. tmp_ans_img = await download_remote_img(ans_url[0])
  131. std_ans_data = rec_std_ans(tmp_ans_img)
  132. db_paper = await crud_paper.update(db, db_paper, {"ans_points": info.ans_points,"ans_url":ans_url})
  133. #写入questions
  134. for qno,std_points in std_ans_data.items():
  135. std_que = await crud_question.find_one(db,filters={
  136. "pid":info.pid,
  137. "pno":info.pno,
  138. "qno":qno,
  139. "sqno":1
  140. })
  141. if std_que:
  142. update_info = {"std_points":std_points}
  143. await crud_question.update(db,std_que,update_info)
  144. else:
  145. question_info = QuestionInfo(
  146. **{
  147. "pid": info.pid,
  148. "pno": info.pno,
  149. "qno": qno,
  150. "sqno": 1,
  151. "page": 0,
  152. "points": [info.ans_points],
  153. "qtype":"单选题",
  154. "imgtype": 0,
  155. "creator_id": current_user.id,
  156. "creator_name": current_user.username,
  157. "editor_id": current_user.id,
  158. "editor_name": current_user.username,
  159. "usage": 0,
  160. "std_points": std_points
  161. })
  162. db_question = await crud_question.insert_one(db,question_info)
  163. cut_data = []
  164. added_questions = info.ques
  165. # 计算要删除的
  166. old_questions = await crud_question.fetch_all(db, filters={"pid": info.pid,"usage__in":[0,1],"qtype__in":["填空题","解答题"]})
  167. old_qids = [x.id for x in old_questions]
  168. now_questions = filter(lambda x: x["imgtype"] == 0 and x["id"], added_questions)
  169. now_qids = [x["id"] for x in now_questions]
  170. del_ids = list(set(old_qids) - set(now_qids))
  171. del_filter = [PaperQuestion.id.in_(del_ids)]
  172. await crud_question.delete(db, where_clauses=del_filter)
  173. # 试题切割
  174. questions = filter(lambda x: x["imgtype"] == 0 and not x["id"], added_questions)
  175. # 按qno和sqno聚合points
  176. cnt_dct = {}
  177. new_questions = []
  178. for que in questions:
  179. key = (que["qno"],que["sqno"])
  180. if cnt_dct.get(key):
  181. points = cnt_dct[key]["points"]
  182. points.extend(que["points"])
  183. que["points"] = points
  184. cnt_dct[key] = que
  185. else:
  186. cnt_dct[key] = que
  187. for k,v in cnt_dct.items():
  188. new_questions.append(v)
  189. for item in new_questions:
  190. if not ("-" in item["qno"]):
  191. question_info = QuestionInfo(
  192. **{
  193. "pid": info.pid,
  194. "pno": info.pno,
  195. "qno": item["qno"],
  196. "sqno": item["sqno"],
  197. "page": item.get("page",0),
  198. "points": item["points"],
  199. "qtype": "解答题",
  200. "imgtype": item["imgtype"],
  201. "creator_id": current_user.id,
  202. "creator_name": current_user.username,
  203. "editor_id": current_user.id,
  204. "editor_name": current_user.username,
  205. "usage": 1
  206. })
  207. cut_data.append(question_info)
  208. else:
  209. question_info = QuestionInfo(
  210. **{
  211. "pid": info.pid,
  212. "pno": info.pno,
  213. "qno": item["qno"],
  214. "sqno": item["sqno"],
  215. "page": item.get("page",0),
  216. "points": item["points"],
  217. "qtype": "解答题",
  218. "imgtype": item["imgtype"],
  219. "creator_id": current_user.id,
  220. "creator_name": current_user.username,
  221. "editor_id": current_user.id,
  222. "editor_name": current_user.username,
  223. "usage": 0
  224. })
  225. cut_data.append(question_info)
  226. for i in range(int(item["qno"].split("-")[0]),int(item["qno"].split("-")[1])+1):
  227. question_info = QuestionInfo(
  228. **{
  229. "pid": info.pid,
  230. "pno": info.pno,
  231. "qno": i,
  232. "sqno": item["sqno"],
  233. "page": item.get("page",0),
  234. "points": item["points"],
  235. "qtype": "解答题",
  236. "imgtype": item["imgtype"],
  237. "creator_id": current_user.id,
  238. "creator_name": current_user.username,
  239. "editor_id": current_user.id,
  240. "editor_name": current_user.username,
  241. "usage": 2
  242. })
  243. cut_data.append(question_info)
  244. await crud_question.insert_many(db, cut_data)
  245. # 材料切割
  246. stuffs = filter(lambda x: x["imgtype"] == 1, added_questions)
  247. # 按qno和sqno聚合points
  248. cnt_dct = {}
  249. new_stuffs = []
  250. for que in stuffs:
  251. key = (que["qno"],que["sqno"])
  252. if cnt_dct.get(key):
  253. points = cnt_dct[key]["points"]
  254. points.extend(que["points"])
  255. que["points"] = points
  256. cnt_dct[key] = que
  257. else:
  258. cnt_dct[key] = que
  259. for k,v in cnt_dct.items():
  260. new_stuffs.append(v)
  261. for item in new_stuffs:
  262. db_question = await crud_question.find_one(db,
  263. filters={
  264. "pid": info.pid,
  265. "qno": item["qno"],
  266. "sqno": item["sqno"]
  267. })
  268. if db_question:
  269. update_info = {"stuff_points": item["points"]}
  270. await crud_question.update(db, db_question, update_info)
  271. # 更新切割状态
  272. filters = {"pid": info.pid}
  273. questions = await crud_question.fetch_all(db, filters=filters)
  274. if questions:
  275. await crud_paper.update(db, db_paper, {"cut": True})
  276. return len(cut_data)
  277. async def save_pieces(info: SaveQuePieceInfo,
  278. db: AsyncSession = Depends(get_async_db),
  279. current_user: Admin = Depends(get_current_user)):
  280. info_dict = info.dict(exclude_none=True)
  281. if not info_dict:
  282. return {"errcode": 400, "mess": "请求参数为空!"}
  283. cut_data = await get_cut_imgs(db, current_user, info)
  284. return {"data": cut_data}
  285. async def save_docs(info: SaveQuePieceInfo,
  286. db: AsyncSession = Depends(get_async_db),
  287. current_user: Admin = Depends(get_current_user)):
  288. info_dict = info.dict(exclude_none=True)
  289. if not info_dict:
  290. return {"errcode": 400, "mess": "请求参数为空!"}
  291. new_ques = []
  292. pid = info_dict["pid"]
  293. pno = info_dict["pno"]
  294. ques = info_dict["ques"]
  295. for que in ques:
  296. qno = que["qno"]
  297. tx = que["tx"].replace("【题型】", "")
  298. tg = que["tg"].replace("【题干】", "")
  299. nd = re.findall("[\u4e00-\u9fa5]+", que["nd"])[-1]
  300. zsd = re.findall("[\u4e00-\u9fa5]+", que["zsd"])[-1]
  301. soup = BeautifulSoup(que["da"], "html.parser")
  302. da = soup.text.replace("【答案】", "")
  303. jx = que["jx"].replace("【解析】", "")
  304. db_question = await crud_question.find_one(db,
  305. filters={
  306. "pid": pid,
  307. "pno": pno,
  308. "qno": qno,
  309. "sqno": 1
  310. })
  311. if db_question:
  312. update_info = {
  313. "qtype": tx,
  314. "stem": tg,
  315. "level": nd,
  316. "answer": da,
  317. "lpoints": zsd,
  318. "analysis": jx
  319. }
  320. await crud_question.update(db, db_question, update_info)
  321. else:
  322. question_info = QuestionInfo(
  323. **{
  324. "pid": pid,
  325. "pno": pno,
  326. "qno": qno,
  327. "sqno": 0,
  328. "qtype": tx,
  329. "stem": tg,
  330. "level": nd,
  331. "lpoints": zsd,
  332. "answer": da,
  333. "analysis": jx,
  334. "creator_id": current_user.id,
  335. "creator_name": current_user.username,
  336. })
  337. new_ques.append(question_info)
  338. # 更新电子卷状态
  339. db_paper = await crud_paper.find_one(db, filters={"id": pid})
  340. await crud_paper.update(db, db_paper, {"attached": True})
  341. return {"data": None}