export_.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. """导出路由"""
  2. from fastapi import APIRouter, Request, HTTPException, Depends
  3. from fastapi.responses import FileResponse
  4. from app.database import get_connection, get_problem
  5. from app.routers.auth import verify_token
  6. from app.services.docx_builder import build_export_docx
  7. router = APIRouter()
  8. @router.post("/export/chapter")
  9. def export_chapter(request: Request, body: dict, _=Depends(verify_token)):
  10. """按章节+类型导出"""
  11. config = request.app.state.config
  12. chapter = body.get("chapter")
  13. import_type = body.get("import_type")
  14. title = body.get("title", "章节导出")
  15. conn = get_connection(config.db_path)
  16. try:
  17. # 查询符合条件的题目
  18. conditions = []
  19. params = []
  20. if chapter:
  21. conditions.append("chapters LIKE ?")
  22. params.append(f'%"{chapter}"%')
  23. if import_type:
  24. conditions.append("import_type = ?")
  25. params.append(import_type)
  26. where = ("WHERE " + " AND ".join(conditions)) if conditions else ""
  27. rows = conn.execute(
  28. f"SELECT id, question_type, difficulty FROM problems {where} ORDER BY question_type, difficulty DESC"
  29. ).fetchall()
  30. # 题目 ID 列表
  31. problem_ids = [r["id"] for r in rows]
  32. if not problem_ids:
  33. raise HTTPException(status_code=404, detail={
  34. "error": {"code": "no_problems", "message": "没有符合条件的题目"}
  35. })
  36. # 生成学生版
  37. student_path = build_export_docx(
  38. problem_ids, config.problems_dir, title, include_solution=False
  39. )
  40. # 生成教师版
  41. teacher_path = build_export_docx(
  42. problem_ids, config.problems_dir, title, include_solution=True
  43. )
  44. return {
  45. "student": student_path.name,
  46. "teacher": teacher_path.name,
  47. "count": len(problem_ids),
  48. }
  49. finally:
  50. conn.close()
  51. @router.post("/export/cart/{cart_id}")
  52. def export_cart(request: Request, cart_id: int, body: dict | None = None, _=Depends(verify_token)):
  53. """导出试题篮"""
  54. config = request.app.state.config
  55. title = (body or {}).get("title", "试题篮导出")
  56. conn = get_connection(config.db_path)
  57. try:
  58. # 获取试题篮中的题目
  59. rows = conn.execute(
  60. "SELECT p.id, p.question_type, p.difficulty FROM cart_items ci "
  61. "JOIN problems p ON ci.problem_id = p.id "
  62. "WHERE ci.cart_id = ? ORDER BY p.question_type, p.difficulty DESC",
  63. (cart_id,)
  64. ).fetchall()
  65. problem_ids = [r["id"] for r in rows]
  66. if not problem_ids:
  67. raise HTTPException(status_code=404, detail={
  68. "error": {"code": "empty_cart", "message": "试题篮为空"}
  69. })
  70. # 获取篮子名称
  71. cart_row = conn.execute("SELECT name FROM carts WHERE id = ?", (cart_id,)).fetchone()
  72. if cart_row:
  73. title = f"{cart_row['name']}_{title}"
  74. student_path = build_export_docx(
  75. problem_ids, config.problems_dir, title, include_solution=False
  76. )
  77. teacher_path = build_export_docx(
  78. problem_ids, config.problems_dir, title, include_solution=True
  79. )
  80. return {
  81. "student": student_path.name,
  82. "teacher": teacher_path.name,
  83. "count": len(problem_ids),
  84. }
  85. finally:
  86. conn.close()
  87. @router.get("/exports/{filename}")
  88. def download_export(request: Request, filename: str, _=Depends(verify_token)):
  89. """下载导出文件"""
  90. config = request.app.state.config
  91. file_path = config.papers_dir / filename
  92. if not file_path.exists():
  93. raise HTTPException(status_code=404, detail={
  94. "error": {"code": "not_found", "message": f"文件 {filename} 不存在"}
  95. })
  96. return FileResponse(
  97. str(file_path),
  98. media_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
  99. filename=filename,
  100. )