07-reorder_classes_by_grade.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. """
  4. 07 - 按年级重排序 sys_dept 中专业下的班级 (修改 order_num)
  5. 说明:
  6. - 针对 sys_dept 中 dept_category 以 'zy-' 开头的专业,查找 parent_id 为该专业的子部门(dept_category LIKE 'bj-%')
  7. - 使用 xg_class_msg.enrollment_year 或 sys_dept.dept_name 进行排序(优先 enrollment_year,空值视为 0),然后按 dept_name
  8. - 为每个父专业从 1 开始重新分配 order_num(可修改为起始值为当前最大值 + 1)
  9. - 脚本执行前会交互确认(避免误删/误改);支持 DRY_RUN 模式,仅打印将要更新的改动
  10. 使用方法:
  11. - 运行前确认 `db_config.DB_CONFIG` 正确,执行: python 07-reorder_classes_by_grade.py
  12. """
  13. from __future__ import annotations
  14. from typing import List, Dict, Any, Optional, Tuple
  15. from datetime import datetime
  16. import pymysql
  17. from db_config import DB_CONFIG
  18. DRY_RUN = False
  19. def get_connection():
  20. return pymysql.connect(cursorclass=pymysql.cursors.DictCursor, **DB_CONFIG)
  21. def fetch_all_majors(conn) -> List[Dict[str, Any]]:
  22. sql = "SELECT dept_id, dept_name FROM sys_dept WHERE dept_category LIKE %s ORDER BY dept_name"
  23. with conn.cursor() as c:
  24. c.execute(sql, ("zy-%",))
  25. return c.fetchall()
  26. def fetch_classes_under_major(conn, parent_id: int) -> List[Dict[str, Any]]:
  27. """
  28. 返回列表包含 sys_dept.dept_id, dept_name, xg_class_msg.enrollment_year
  29. """
  30. sql = """
  31. SELECT sd.dept_id, sd.dept_name, xc.enrollment_year
  32. FROM sys_dept sd
  33. LEFT JOIN xg_class_msg xc ON sd.dept_id = xc.id
  34. WHERE sd.parent_id = %s AND sd.dept_category LIKE %s
  35. """
  36. with conn.cursor() as c:
  37. c.execute(sql, (parent_id, "bj-%"))
  38. return c.fetchall()
  39. def normalize_year(val: Any) -> int:
  40. if val is None or val == "":
  41. return 0
  42. try:
  43. return int(val)
  44. except Exception:
  45. return 0
  46. def update_order_nums(conn, updates: List[Tuple[int, int]]):
  47. """
  48. updates: list of (order_num, dept_id)
  49. """
  50. with conn.cursor() as c:
  51. for order_num, dept_id in updates:
  52. if DRY_RUN:
  53. print(f"[DRY_RUN] UPDATE sys_dept SET order_num={order_num} WHERE dept_id={dept_id}")
  54. else:
  55. c.execute("UPDATE sys_dept SET order_num = %s WHERE dept_id = %s", (order_num, dept_id))
  56. if not DRY_RUN:
  57. conn.commit()
  58. def prompt_confirm() -> bool:
  59. try:
  60. ans = input("确认按年级重排序 sys_dept 中专业下的班级并修改 order_num 吗?[y/N]: ").strip().lower()
  61. except (EOFError, KeyboardInterrupt):
  62. print("\n未检测到交互输入,取消操作。")
  63. return False
  64. return ans in ("y", "yes")
  65. def main():
  66. print("开始:07 - 按年级重排序 sys_dept 专业下的班级(修改 order_num)")
  67. if not prompt_confirm():
  68. print("已取消操作。")
  69. return
  70. conn = get_connection()
  71. try:
  72. majors = fetch_all_majors(conn)
  73. print(f"发现 {len(majors)} 个专业,将逐个处理...")
  74. total_changed = 0
  75. for m in majors:
  76. parent_id = m.get("dept_id")
  77. major_name = m.get("dept_name")
  78. classes = fetch_classes_under_major(conn, parent_id)
  79. if not classes:
  80. continue
  81. # sort by enrollment_year(int, 0) asc, then dept_name asc
  82. classes.sort(key=lambda r: (normalize_year(r.get("enrollment_year")), r.get("dept_name") or ""))
  83. updates: List[Tuple[int, int]] = []
  84. for idx, row in enumerate(classes, start=1):
  85. updates.append((idx, row.get("dept_id")))
  86. if updates:
  87. print(f"专业 {major_name}({parent_id}) 将更新 {len(updates)} 条 order_num。")
  88. update_order_nums(conn, updates)
  89. total_changed += len(updates)
  90. print(f"完成:共更新 order_num {total_changed} 条记录。")
  91. except Exception as e:
  92. print("发生异常:", e)
  93. finally:
  94. conn.close()
  95. if __name__ == "__main__":
  96. main()