Files
server-core/app/training/opc_engine.py
T
Pine 8b43d3df52 feat: 培训子应用迁入(app/training,/api/*)
- 报名/测评/政策/调研,独立 data/opc.db
- 源自原培训后端,路径调整至 server-core
2026-08-23 22:36:00 +08:00

111 lines
4.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""OPC 创业基因测评 · 计分逻辑(移植自 website/server/opcTest.js"""
from . import opc_data
QUESTIONS = opc_data.QUESTIONS
RULES = opc_data.RULES
PROFILES = opc_data.PROFILES
RULE_MAP = {r["id"]: r for r in RULES}
ADAPT_DIMS = ["IND", "RISK", "DRIVE", "SOLO", "AI", "STABLE"]
ADAPT_LABELS = {"IND": "独立自主", "RISK": "风险承受", "DRIVE": "自驱动力", "SOLO": "单兵多面", "AI": "AI 意愿", "STABLE": "安全垫"}
AXIS_PAIRS = [("E", "I"), ("V", "G"), ("R", "T"), ("P", "F")]
AXIS_NAMES = {"EI": "能量", "VG": "视野", "RT": "价值", "PF": "节奏"}
SECTIONS = {
"P1": "第一部分 · 创业内核(独立 / 风险 / 自驱 / 单兵 / AI / 安全垫)",
"P2": "第二部分 · 特质倾向(能量 / 视野 / 价值 / 节奏)",
"P3": "第三部分 · 赛道偏好(文旅 / 内容IP / 咨询 / 电商 / 跨境 / 本地)",
"P4": "第四部分 · 角色偏向(产品 / 内容 / 商务 / 运营 / 架构)",
"P5": "第五部分 · 人机协作(对话 / 自动化 / 智能体 / 外包)",
}
def current_questions(version):
return [q for q in QUESTIONS if q["quick"]] if version == "quick" else QUESTIONS
def _get_ans(answers, qid):
"""兼容:answers 键可能是字符串或数字"""
if qid in answers:
return answers[qid]
if str(qid) in answers:
return answers[str(qid)]
return None
def calculate(answers, version="full"):
ids = [q["id"] for q in current_questions(version)]
counts = {}
for qid in ids:
ans = _get_ans(answers, qid)
if ans not in ("A", "B"):
continue
rule = RULE_MAP.get(qid)
if not rule:
continue
code = rule["A"] if ans == "A" else rule["B"]
if code:
counts[code] = counts.get(code, 0) + 1
adapt_dims = []
adapt_sum = 0
for dim in ADAPT_DIMS:
mx = sum(1 for qid in ids for r in [RULE_MAP.get(qid)] if r and (r.get("A") == dim or r.get("B") == dim))
votes = counts.get(dim, 0)
score = round(votes / mx * 100) if mx > 0 else 0
adapt_sum += score
adapt_dims.append({"code": dim, "label": ADAPT_LABELS[dim], "score": score, "votes": votes, "max": mx})
adapt_index = round(adapt_sum / len(ADAPT_DIMS))
adapt_level = next((lv for lv in PROFILES["adaptLevels"] if lv["range"][0] <= adapt_index <= lv["range"][1]), None)
weakest_dims = sorted(adapt_dims, key=lambda d: d["score"])[:2]
type_code = ""
axes_detail = []
for left, right in AXIS_PAIRS:
l = counts.get(left, 0)
r = counts.get(right, 0)
total = l + r
letter = left if l >= r else right
type_code += letter
axes_detail.append({
"pair": left + right, "left": l, "right": r,
"leftPct": round(l / total * 100) if total > 0 else 50,
"rightPct": round(r / total * 100) if total > 0 else 50,
"winner": letter, "lCount": l, "rCount": r,
})
persona = next((p for p in PROFILES["personas"] if p["code"] == type_code), None)
def rank(dims):
total = sum(counts.get(d, 0) for d in dims)
res = []
for d in dims:
v = counts.get(d, 0)
res.append({"code": d, "votes": v, "pct": round(v / total * 100) if total > 0 else 0})
res.sort(key=lambda x: (-x["votes"], x["code"]))
return res
tracks = [t for t in rank([t["code"] for t in PROFILES["tracks"]]) if t["pct"] >= 10][:3]
roles = [x for x in rank([x["code"] for x in PROFILES["roles"]]) if x["pct"] >= 5][:2]
tools = [t for t in rank([t["code"] for t in PROFILES["toolModes"]]) if t["pct"] >= 5][:2]
answered_count = sum(1 for qid in ids if _get_ans(answers, qid) in ("A", "B"))
return {
"version": version, "answeredCount": answered_count, "typeCode": type_code, "persona": persona,
"adaptIndex": adapt_index, "adaptLevel": adapt_level, "adaptDims": adapt_dims, "weakestDims": weakest_dims,
"axesDetail": axes_detail, "tracks": tracks, "roles": roles, "tools": tools,
}
def expand_result(r):
def find(lst, code):
return next((x for x in lst if x["code"] == code), {})
return {
**r,
"tracks": [{**t, **find(PROFILES["tracks"], t["code"])} for t in r["tracks"]],
"roles": [{**x, **find(PROFILES["roles"], x["code"])} for x in r["roles"]],
"tools": [{**x, **find(PROFILES["toolModes"], x["code"])} for x in r["tools"]],
}