import json import sys from pathlib import Path def generate_html_visualization(result_data: dict, cases_data: dict) -> str: merged_demands = result_data.get("merged_demands", []) mount_decisions = result_data.get("mount_decisions", []) # 构建 decision_map:demand_name → mounted_nodes decision_map = {} for d in mount_decisions: dec = d.get("decision", {}) if isinstance(dec, dict): decision_map[d["demand_name"]] = dec.get("mounted_nodes", []) else: decision_map[d["demand_name"]] = [] # 优先从 result_data 读取 case_info,如果没有则从 cases_data 构建 if "case_info" in result_data: case_map = result_data["case_info"] else: case_map = {} for c in cases_data.get("cases", []): case_map[c["case_id"]] = { "title": c.get("title") or c.get("video_title") or c.get("post_title", ""), "images": c.get("images") or c.get("effect_images", []), "link": c.get("source_link") or c.get("video_url") or c.get("post_url", "") } nodes_js = [] edges_js = [] node_id = 0 COLOR_CASE_BG = "#e3f2fd" COLOR_CASE_BORDER = "#2196f3" COLOR_DEMAND_BG = "#fff3e0" COLOR_DEMAND_BORDER = "#ff9800" for md in merged_demands: demand_id = node_id node_id += 1 dn = md["demand_name"] # 从挂载决策中获取最终选择的节点 mounted = decision_map.get(dn, []) node_tags = [] for n in mounted: node_tags.append(f"{n['name']}") tags_str = " | ".join(node_tags) if node_tags else "无挂载节点" demand_label = f"{dn}\n\n[{tags_str}]" demand_title_html = f"""
需求详情

名称: {dn}

描述: {md["description"]}

挂载节点: {tags_str}

{"".join(f"

· {n['name']}({n.get('source_type','')}) — {n.get('reason','')}

" for n in mounted)}
""" nodes_js.append({ "id": demand_id, "label": demand_label, "title": demand_title_html, "group": "demand", "level": 0 # 核心改动1:Demand 改为 Level 0 (根节点) }) for cid in md.get("source_case_ids", []): case_id = node_id node_id += 1 # 类型转换:case_info 的 key 可能是字符串 cid_str = str(cid) case_info = case_map.get(cid_str) or case_map.get(cid, {"title": f"Case {cid}", "images": [], "link": ""}) title_short = case_info['title'][:20] + "..." if len(case_info['title']) > 20 else case_info['title'] case_label = f"Case {cid}\n{title_short}" img_html = "" if case_info['images']: img_url = case_info['images'][0] img_html = f'' case_title_html = f"""
帖子详情 (点击跳转)

ID: {cid}

标题: {case_info["title"]}

{img_html}
""" nodes_js.append({ "id": case_id, "label": case_label, "title": case_title_html, "url": case_info['link'], "group": "case", "level": 1 # 核心改动2:Case 改为 Level 1 (叶子节点) }) # 核心改动3:在数据上把连线方向反转为 Demand -> Case edges_js.append({"from": demand_id, "to": case_id}) html = f""" Case → Demand 语义映射图
""" return html def main(): if len(sys.argv) < 2: print("用法: python visualize_results.py ") sys.exit(1) result_path = Path(sys.argv[1]) if not result_path.exists(): print(f"错误: 文件不存在: {result_path}") sys.exit(1) with open(result_path, "r", encoding="utf-8") as f: result_data = json.load(f) cases_path = result_path.parent / "02_cases.json" if not cases_path.exists(): cases_data = {"cases": []} else: with open(cases_path, "r", encoding="utf-8") as f: cases_data = json.load(f) html = generate_html_visualization(result_data, cases_data) output_path = result_path.parent / "match_nodes_graph.html" with open(output_path, "w", encoding="utf-8") as f: f.write(html) print(f"[OK] Visualization generated: {output_path}") if __name__ == "__main__": main()