使用梅校的算法
This commit is contained in:
76
app/main.py
76
app/main.py
@@ -32,9 +32,9 @@ from .db import (
|
||||
)
|
||||
from .excel_parse import parse_workbook, summarize_parse
|
||||
from .export_xlsx import build_export
|
||||
from .indicators import analyze, class_comment, default_targets, infer_track_group, select_students
|
||||
from .layer_method import analyze, class_comment, default_line_settings, infer_track_group, select_students
|
||||
|
||||
app = FastAPI(title="航标 · 班级达线指标预估")
|
||||
app = FastAPI(title="指标测算 · 相邻层次法")
|
||||
app.add_middleware(SessionMiddleware, secret_key=SECRET_KEY, max_age=60 * 60 * 12)
|
||||
app.mount("/static", StaticFiles(directory=Path(__file__).parent / "static"), name="static")
|
||||
templates = Jinja2Templates(directory=str(Path(__file__).parent / "templates"))
|
||||
@@ -136,17 +136,20 @@ def demo(request: Request):
|
||||
def _settings_from(parsed: dict, stored: dict | None) -> dict:
|
||||
summary = summarize_parse(parsed)
|
||||
groups = sorted({infer_track_group(s["track"]) for s in parsed["students"]})
|
||||
line_settings = default_line_settings(groups)
|
||||
base = {
|
||||
"exams": parsed["exams"],
|
||||
"include_art": False,
|
||||
"include_leave": False,
|
||||
"exclude_moves": False,
|
||||
"include_mingyuan": True,
|
||||
"include_other": True,
|
||||
"horizon": 1,
|
||||
"targets": default_targets(groups),
|
||||
"line_settings": line_settings,
|
||||
}
|
||||
if stored:
|
||||
for k in ("exams", "include_art", "include_leave", "include_mingyuan", "include_other", "horizon", "targets"):
|
||||
# 考试列必须始终来自本次上传的工作簿,避免旧分析设置把不存在的列带进来。
|
||||
valid_exam_set = set(parsed["exams"])
|
||||
stored_exams = [e for e in (stored.get("exams") or []) if e in valid_exam_set]
|
||||
base["exams"] = stored_exams or parsed["exams"]
|
||||
for k in ("exclude_moves", "include_mingyuan", "include_other", "line_settings"):
|
||||
if k in stored:
|
||||
base[k] = stored[k]
|
||||
base["summary"] = summary
|
||||
@@ -154,6 +157,7 @@ def _settings_from(parsed: dict, stored: dict | None) -> dict:
|
||||
return base
|
||||
|
||||
|
||||
|
||||
@app.get("/analysis/{aid}", response_class=HTMLResponse)
|
||||
def analysis_page(request: Request, aid: int, step: int = 1):
|
||||
if (redir := require_login(request)):
|
||||
@@ -189,34 +193,39 @@ async def save_settings(request: Request, aid: int):
|
||||
return HTMLResponse("分析不存在", status_code=404)
|
||||
parsed = load_json(item["parsed_path"])
|
||||
form = await request.form()
|
||||
exams = form.getlist("exams")
|
||||
valid_exam_set = set(parsed["exams"])
|
||||
exams = [e for e in form.getlist("exams") if e in valid_exam_set]
|
||||
if not exams:
|
||||
exams = parsed["exams"]
|
||||
if len(exams) < 2:
|
||||
return HTMLResponse("至少需要勾选两次考试才能计算相邻转移概率。", status_code=400)
|
||||
groups = sorted({infer_track_group(s["track"]) for s in parsed["students"]})
|
||||
targets = {}
|
||||
defaults = default_line_settings(groups)
|
||||
line_settings = {}
|
||||
for g in groups:
|
||||
names = form.getlist(f"t_name_{g}")
|
||||
ks = form.getlist(f"t_k_{g}")
|
||||
ids = form.getlist(f"t_id_{g}")
|
||||
rows = []
|
||||
for i, name in enumerate(names):
|
||||
k_raw = ks[i] if i < len(ks) else "0"
|
||||
try:
|
||||
k = int(float(k_raw))
|
||||
except ValueError:
|
||||
k = 0
|
||||
tid = ids[i] if i < len(ids) and ids[i] else f"t{i+1}"
|
||||
if name.strip() and k > 0:
|
||||
rows.append({"id": tid, "name": name.strip(), "k": k})
|
||||
targets[g] = rows or default_targets([g])[g]
|
||||
try:
|
||||
top = int(float(form.get(f"top_k_{g}", defaults[g]["top"])))
|
||||
except ValueError:
|
||||
top = defaults[g]["top"]
|
||||
try:
|
||||
control = int(float(form.get(f"control_k_{g}", defaults[g]["control"])))
|
||||
except ValueError:
|
||||
control = defaults[g]["control"]
|
||||
try:
|
||||
layer_size = int(float(form.get(f"layer_size_{g}", defaults[g]["layer_size"])))
|
||||
except ValueError:
|
||||
layer_size = defaults[g]["layer_size"]
|
||||
line_settings[g] = {
|
||||
"top": max(1, top),
|
||||
"control": max(1, max(top, control)),
|
||||
"layer_size": max(1, layer_size),
|
||||
}
|
||||
settings = {
|
||||
"exams": list(exams),
|
||||
"include_art": form.get("include_art") == "on",
|
||||
"include_leave": form.get("include_leave") == "on",
|
||||
"exclude_moves": form.get("exclude_moves") == "on",
|
||||
"include_mingyuan": form.get("include_mingyuan") == "on",
|
||||
"include_other": form.get("include_other") == "on",
|
||||
"horizon": int(form.get("horizon") or 1),
|
||||
"targets": targets,
|
||||
"line_settings": line_settings,
|
||||
}
|
||||
update_analysis(aid, settings_json=json.dumps(settings, ensure_ascii=False), step=2)
|
||||
action = form.get("action") or "next"
|
||||
@@ -230,6 +239,7 @@ async def save_settings(request: Request, aid: int):
|
||||
return RedirectResponse(f"/analysis/{aid}?step=2", status_code=303)
|
||||
|
||||
|
||||
|
||||
@app.get("/analysis/{aid}/api/student/{sid}")
|
||||
def student_api(request: Request, aid: int, sid: str):
|
||||
if not current_user(request):
|
||||
@@ -257,8 +267,14 @@ def class_api(request: Request, aid: int, group: str, target: str, class_name: s
|
||||
for s in result["students"]
|
||||
if s["group"] == group and s["class_name"] == class_name
|
||||
]
|
||||
rows.sort(key=lambda s: -((s.get("probs") or {}).get(target) or 0))
|
||||
return {"exams": result["exams"], "students": rows[:80], "n": len(rows)}
|
||||
rows.sort(key=lambda s: (s.get("ranks") or {}).get(result["exams"][-1], 10**9))
|
||||
return {
|
||||
"exams": result["exams"],
|
||||
"layer_size": result.get("layer_size"),
|
||||
"last_exam": result["exams"][-1],
|
||||
"students": rows,
|
||||
"n": len(rows),
|
||||
}
|
||||
|
||||
|
||||
@app.get("/analysis/{aid}/export")
|
||||
@@ -269,6 +285,8 @@ def export_analysis(request: Request, aid: int):
|
||||
if not item or not item.get("result_path"):
|
||||
return HTMLResponse("请先完成测算", status_code=400)
|
||||
result = load_json(item["result_path"])
|
||||
if result.get("algorithm") != "adjacent-layer-markov-v2":
|
||||
return HTMLResponse("结果为旧算法,请回到第二步重新测算后再导出。", status_code=400)
|
||||
data = build_export(result)
|
||||
ascii_name = f"hangbiao-{aid}.xlsx"
|
||||
star = quote(f"{item['title']}-下达指标.xlsx")
|
||||
|
||||
Reference in New Issue
Block a user