_paper.py 8.9 KB

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