from __future__ import annotations from io import BytesIO from openpyxl import Workbook from openpyxl.styles import Alignment, Font, PatternFill, Border, Side def build_export(result: dict) -> bytes: wb = Workbook() thin = Border( left=Side(style="thin", color="D9D1C3"), right=Side(style="thin", color="D9D1C3"), top=Side(style="thin", color="D9D1C3"), bottom=Side(style="thin", color="D9D1C3"), ) head = PatternFill("solid", fgColor="12233D") head_font = Font(color="F4EFE6", bold=True) wrap = Alignment(wrap_text=True, vertical="center") first = True for g in result["groups"]: for t in g["targets"]: title = f"{g['group']}-{t['name']}"[:31] ws = wb.active if first else wb.create_sheet(title) if first: ws.title = title first = False ws.append( ["班级", "测算人数", "最近一次在线", "滤波期望", "下达指标", "保底下界", "冲刺上界", "相对最近一次"] ) for cell in ws[1]: cell.fill = head cell.font = head_font for row in t["classes"]: ws.append( [ row["class_name"], row["n"], row["current"], row["expected"], row["assigned"], row["floor"], row["stretch"], row["delta"], ] ) ws.append([]) ws.append(["说明:下达指标为预估中位水平,班级应努力超越该人数。保底/冲刺来自模拟分布的约20%/80%分位。"]) ws = wb.create_sheet("学生明细") exams = result["exams"] headers = ["学号", "姓名", "班级", "方向", "学籍", "选科"] headers += [f"{e}-排名" for e in exams] # collect target names tcols = [] for g in result["groups"]: for t in g["targets"]: tcols.append((g["group"], t["id"], f"{g['group']}-{t['name']}概率")) headers += [c[2] for c in tcols] headers += ["滤波水平", "预测方差"] ws.append(headers) for cell in ws[1]: cell.fill = head cell.font = head_font cell.alignment = wrap for s in result["students"]: row = [s["sid"], s["name"], s["class_name"], s["group"], s.get("status") or "", s.get("subjects") or ""] ranks = s.get("ranks") or [] for i, _e in enumerate(exams): row.append(ranks[i] if i < len(ranks) else None) for gname, tid, _ in tcols: if s["group"] == gname: row.append((s.get("probs") or {}).get(tid)) else: row.append(None) row.append(s.get("pred_mean")) row.append(s.get("pred_var")) ws.append(row) bio = BytesIO() wb.save(bio) return bio.getvalue()