_paper.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. import os
  4. from fastapi import Query, Depends, File, UploadFile, Path
  5. from openpyxl import load_workbook
  6. from sqlalchemy import asc, desc
  7. from sqlalchemy.ext.asyncio import AsyncSession
  8. from starlette.background import BackgroundTasks
  9. from common.common import delete_oss_file
  10. from core.config import settings
  11. from crud.paper import crud_paper, crud_question
  12. from crud.resource import crud_work_category
  13. from models.paper import Paper
  14. from models.user import Admin
  15. from schemas.base import OrderByField, ReturnField
  16. from schemas.paper import NewPaperInfo, UpdatePaperInfo
  17. from schemas.paper import PaperInDB, UpdatePaper
  18. from utils.cv2img import CV2img,get_std_points
  19. from utils.depends import get_async_db, get_current_user
  20. from utils.imgtool import download_remote_img
  21. async def get_papers(page: int = 1,
  22. size: int = 10,
  23. name: str = "",
  24. pno: str = "",
  25. ptype: str = "",
  26. ctgid: int = Query(0, description="资源分类ID"),
  27. order: OrderByField = Query("-created_at",
  28. description="排序字段,用逗号分隔,升降序以-判断,默认-created_at"),
  29. res: ReturnField = Query("", description="返回字段,默认列表展示字段"),
  30. db: AsyncSession = Depends(get_async_db),
  31. current_user: Admin = Depends(get_current_user)):
  32. _q = {}
  33. if name:
  34. _q["name"] = name
  35. if pno:
  36. _q["pno"] = pno
  37. if ctgid:
  38. _q["category_id"] = ctgid
  39. if ptype:
  40. _q["ptype"] = ptype
  41. offset = (page - 1) * size
  42. order_fields = []
  43. if order:
  44. for x in order.split(","):
  45. field = x.strip()
  46. if field:
  47. if field.startswith("-"):
  48. order_fields.append(desc(getattr(Paper, field[1:])))
  49. else:
  50. order_fields.append(asc(getattr(Paper, field)))
  51. total, items = await crud_paper.find_all(db,
  52. filters=_q,
  53. offset=offset,
  54. limit=size,
  55. order_by=order_fields,
  56. return_fields=res)
  57. return {"data": items, "total": total}
  58. async def get_papers_info(pid: int = Path(..., description="试卷ID"),
  59. usage: int = Query(None,description="用途"),
  60. db: AsyncSession = Depends(get_async_db),
  61. current_user: Admin = Depends(get_current_user)):
  62. db_obj = await crud_paper.find_one(db, filters={"id": pid})
  63. if not db_obj:
  64. return {"errcode": 400, "mess": "试卷不存在!"}
  65. # 查询该试卷的试题列表
  66. if usage == 12:
  67. filters = {"pid": pid,"usage__in":[1,2],"qtype__in":["填空题","解答题",""]}
  68. else:
  69. filters = {"pid": pid,"usage__in":[0,1],"qtype__in":["填空题","解答题",""]}
  70. questions = await crud_question.fetch_all(db, filters=filters, order_by=[asc("id")])
  71. new_questions = []
  72. for q in questions:
  73. q.std_answer = ["A", "B", "C", "D"]
  74. que = q.__dict__
  75. if q.stuff_points:
  76. for sp in q.stuff_points:
  77. stuff = dict(que)
  78. stuff["points"] = [sp]
  79. stuff["imgtype"] = 1
  80. new_questions.append(stuff)
  81. for point in q.points:
  82. que = dict(que)
  83. que["points"] = [point]
  84. new_questions.append(que)
  85. db_obj.questions = new_questions
  86. #客观题
  87. obj_questions = await crud_question.fetch_all(db, filters={"pid":pid,"qtype__in":["单选题","多选题"]}, order_by=[asc("id")])
  88. for item in obj_questions:
  89. item.answer = item.answer.split()
  90. db_obj.obj_questions = obj_questions
  91. return {"data": db_obj}
  92. async def create_paper(info: NewPaperInfo,
  93. db: AsyncSession = Depends(get_async_db),
  94. current_user: Admin = Depends(get_current_user)):
  95. # 判断分类是否存在
  96. if info.ptype == "work":
  97. existed = await crud_work_category.count(db, filters={"id": info.ctgid})
  98. if not existed:
  99. return {"errcode": 404, "mess": "分类不存在!"}
  100. # 判断试卷编号是否重复
  101. existed = await crud_paper.count(db, filters={"pno": info.pno})
  102. if existed:
  103. return {"errcode": 400, "mess": "试卷编号重复!"}
  104. # 标准点处理
  105. std_points = []
  106. if info.imgs:
  107. remote_imgs = info.imgs
  108. for url in remote_imgs:
  109. tmp_ri = await download_remote_img(url)
  110. points = get_std_points(tmp_ri)
  111. std_points.append(points)
  112. if not std_points:
  113. return {"errcode": 400, "mess": "缺少标准点!"}
  114. # 初始化对象
  115. db_obj = PaperInDB(imgs=info.imgs,
  116. ctgid=info.ctgid,
  117. name=info.name,
  118. pno=info.pno,
  119. size=info.size,
  120. pages=info.pages,
  121. points=std_points,
  122. ptype=info.ptype,
  123. subject=info.subject,
  124. creator_id=current_user.id,
  125. creator_name=current_user.username,
  126. editor_id=current_user.id,
  127. editor_name=current_user.username)
  128. if std_points:
  129. db_obj.uploaded = True
  130. if info.attachments:
  131. db_obj.attach_url = ";".join(info.attachments)
  132. db_obj.attached = True
  133. # 创建
  134. db_obj = await crud_paper.insert_one(db, db_obj)
  135. return {"data": db_obj}
  136. async def update_paper(info: UpdatePaperInfo,
  137. bg_task: BackgroundTasks,
  138. pid: int = Path(..., description="试卷ID"),
  139. db: AsyncSession = Depends(get_async_db),
  140. current_user: Admin = Depends(get_current_user)):
  141. # 判断试卷是否存在
  142. db_obj = await crud_paper.find_one(db, filters={"id": pid})
  143. if not db_obj:
  144. return {"errcode": 404, "mess": "试卷不存在!"}
  145. # 提交参数
  146. info_dict = info.dict(exclude_none=True)
  147. if not info_dict:
  148. return {"errcode": 400, "mess": "提交参数为空!"}
  149. if "ctgid" in info_dict: # 判断分类是否存在
  150. existed = await crud_work_category.count(db, filters={"id": info_dict["ctgid"]})
  151. if not existed:
  152. return {"errcode": 404, "mess": "分类不存在!"}
  153. if "imgs" in info_dict: # 试卷图片
  154. info_dict["uploaded"] = True
  155. bg_task.add_task(delete_oss_file, db_obj.imgs) # 删除原始URL
  156. if "attachments" in info_dict: # 电子卷附件
  157. info_dict["attach_url"] = ";".join(info_dict["attachments"])
  158. info_dict["attached"] = True
  159. bg_task.add_task(delete_oss_file, db_obj.attach_url)
  160. info_dict["editor_id"] = current_user.id
  161. info_dict["editor_name"] = current_user.username
  162. info = UpdatePaper(**info_dict)
  163. db_obj = await crud_paper.update(db, db_obj, info)
  164. return {"data": db_obj}
  165. async def delete_paper(bg_task: BackgroundTasks,
  166. pid: int = Path(..., description="试卷ID"),
  167. db: AsyncSession = Depends(get_async_db),
  168. current_user: Admin = Depends(get_current_user)):
  169. # 删除
  170. db_obj = await crud_paper.find_one(db, filters={"id": pid})
  171. if not db_obj:
  172. return {"errcode": 404, "mess": "试卷不存在!"}
  173. else:
  174. await crud_paper.delete(db, obj_id=pid)
  175. bg_task.add_task(delete_oss_file, db_obj.imgs)
  176. return {"data": None}
  177. async def import_paper(datafile: UploadFile = File(..., description="数据文件"),
  178. db: AsyncSession = Depends(get_async_db),
  179. current_user: Admin = Depends(get_current_user)):
  180. # 判断文件格式
  181. if not datafile.filename.endswith(".xlsx"):
  182. return {"errcode": 400, "mess": "文件格式错误!"}
  183. # 把文件写入磁盘,再加载回来
  184. disk_file = os.path.join(settings.UPLOADER_PATH, datafile.filename)
  185. content = await datafile.read()
  186. with open(disk_file, "wb") as f:
  187. f.write(content)
  188. # 解析文件
  189. errors = []
  190. success = 0
  191. papers = []
  192. # 使用openpyxl读取文件
  193. wb = load_workbook(disk_file)
  194. ws = wb.worksheets[0]
  195. for row in ws.iter_rows(min_row=2, max_col=ws.max_column, max_row=ws.max_row, values_only=True):
  196. idx = row[0]
  197. if len(row) != 12:
  198. errors.append(f"第{idx}行: 提交参数错误!")
  199. continue
  200. # 判断必填参数
  201. if not (row[0] and row[1] and row[2] and row[3] and row[4]):
  202. errors.append(f"第{idx}行: 缺少必填字段!")
  203. continue
  204. # 判断分类是否存在
  205. existed = await crud_work_category.count(db, filters={"id": row[1]})
  206. if not existed:
  207. errors.append(f"第{idx}行: 分类不存在!")
  208. continue
  209. # 判断试卷编号是否重复
  210. existed = await crud_paper.count(db, filters={"pno": row[3]})
  211. if existed:
  212. errors.append(f"第{idx}行: 试卷编号重复!")
  213. continue
  214. # 创建
  215. obj_in = PaperInDB(ctgid=row[1],
  216. name=row[2],
  217. pno=row[3],
  218. pages=row[4],
  219. imgs=row[5] if row[5] else [],
  220. uploaded=True if row[6] else False,
  221. points=row[7] if row[7] else [],
  222. attach_url=";".join(row[8]) if row[8] else "",
  223. attached=True if row[9] else False,
  224. cut=True if row[10] else False,
  225. score=row[11] if row[11] else 100,
  226. creator_id=current_user.id,
  227. creator_name=current_user.username,
  228. editor_id=current_user.id,
  229. editor_name=current_user.username)
  230. papers.append(obj_in)
  231. success += 1
  232. if len(papers) == 20:
  233. await crud_paper.insert_many(db, papers)
  234. papers.clear()
  235. if papers:
  236. await crud_paper.insert_many(db, papers)
  237. papers.clear()
  238. os.remove(disk_file)
  239. return {"data": {"success": success, "fail": len(errors), "errors": errors}}