"""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"]], }