Files
Evilom fe5505343e feat: 钉钉群聊飞书文档收集与知识图谱系统初始提交
- 通过悟空(dws CLI)拉取dc战略问题研究院+创新组两个群的消息(377条)
- 提取190个飞书链接、18个文件附件
- 下载HTML/MD/XLSX等报告文件到output/downloaded-files/
- 构建知识图谱(JSON+HTML可视化)
- 生成Obsidian知识库(28个页面,7大主题)
- 生成花园世界全量汇总报告
- 所有脚本路径改为相对路径,便于迁移
2026-06-02 20:24:25 +08:00

270 lines
12 KiB
Python

import sys, io, json
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
import os
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
PROJECT_ROOT = os.path.dirname(SCRIPT_DIR)
with open(os.path.join(PROJECT_ROOT, "output", "knowledge-graph", "knowledge_graph.json"), "r", encoding="utf-8") as f:
graph = json.load(f)
# Generate Mermaid diagram
mermaid = []
mermaid.append("graph TB")
mermaid.append("")
# Style definitions
mermaid.append(" classDef person fill:#4A90D9,stroke:#2C5F8A,color:#fff,stroke-width:2px")
mermaid.append(" classDef topic fill:#E8A838,stroke:#B8842C,color:#fff,stroke-width:2px")
mermaid.append(" classDef doc fill:#5CB85C,stroke:#3D8B3D,color:#fff,stroke-width:1px")
mermaid.append(" classDef file fill:#D9534F,stroke:#B94441,color:#fff,stroke-width:1px")
mermaid.append(" classDef group fill:#9B59B6,stroke:#7D3C98,color:#fff,stroke-width:2px")
mermaid.append("")
# Groups
mermaid.append(" subgraph G1[dc战略问题研究院]")
for name in ["li_zj_dc", "huang_jw_dc", "chen_cz_dc", "xia_l_dc", "zhang_jz_dc", "mo_rl_dc", "hu_hj_dc", "wang_j_dc", "fu_my_dc"]:
pass
# Keep it simpler - just key nodes
mermaid.append(" li_zj[李志健]:::person")
mermaid.append(" huang_jw[黄静雯]:::person")
mermaid.append(" chen_cz[陈楚真]:::person")
mermaid.append(" fu_my[傅明游]:::person")
mermaid.append(" xia_l[夏莲]:::person")
mermaid.append(" zhang_jz[张家振]:::person")
mermaid.append(" mo_rl[莫润麟]:::person")
mermaid.append(" hu_hj[胡辉俊]:::person")
mermaid.append(" wang_j[汪季]:::person")
mermaid.append(" end")
mermaid.append("")
mermaid.append(" subgraph G2[创新组]")
mermaid.append(" wang_ym[王雨默]:::person")
mermaid.append(" wei_y[韦译]:::person")
mermaid.append(" liu_p[刘鹏]:::person")
mermaid.append(" zhuo_z[卓泽]:::person")
mermaid.append(" xu_r[徐锐]:::person")
mermaid.append(" end")
mermaid.append("")
# Key cross-group people
mermaid.append(" subgraph G3[跨组人员]")
mermaid.append(" shangguan_c[上官成]:::person")
mermaid.append(" an_d[安东]:::person")
mermaid.append(" lin_f[林峰]:::person")
mermaid.append(" end")
mermaid.append("")
# Topics
mermaid.append(" subgraph T[核心研究主题]")
mermaid.append(" t1[花园世界深度研究]:::topic")
mermaid.append(" t2[云湖项目选择]:::topic")
mermaid.append(" t3[Game Analyst Agent]:::topic")
mermaid.append(" t4[战略思维方法论]:::topic")
mermaid.append(" t5[AI落地与组织变革]:::topic")
mermaid.append(" t6[创新组研发]:::topic")
mermaid.append(" t7[战略组知识产出]:::topic")
mermaid.append(" end")
mermaid.append("")
# Key files
mermaid.append(" subgraph F[关键文件]")
mermaid.append(" f1[主报告-我的花园世界.html]:::file")
mermaid.append(" f2[美国版策略报告.html]:::file")
mermaid.append(" f3[云湖项目选择.html]:::file")
mermaid.append(" f4[game-analyst-agent.html]:::file")
mermaid.append(" f5[取败之道.md]:::file")
mermaid.append(" f6[策划启示.md]:::file")
mermaid.append(" f7[进一步的思考.md]:::file")
mermaid.append(" f8[GOS补充信息.md]:::file")
mermaid.append(" end")
mermaid.append("")
# Relationships
mermaid.append(" %% Person -> Topic")
mermaid.append(" li_zj --> t1")
mermaid.append(" li_zj --> t4")
mermaid.append(" li_zj --> t5")
mermaid.append(" huang_jw --> t1")
mermaid.append(" huang_jw --> t2")
mermaid.append(" huang_jw --> t3")
mermaid.append(" huang_jw --> t7")
mermaid.append(" chen_cz --> t1")
mermaid.append(" chen_cz --> t7")
mermaid.append(" fu_my --> t1")
mermaid.append(" wang_ym --> t6")
mermaid.append(" wei_y --> t6")
mermaid.append(" liu_p --> t6")
mermaid.append(" shangguan_c --> t5")
mermaid.append(" lin_f --> t5")
mermaid.append("")
mermaid.append(" %% Topic -> Files")
mermaid.append(" t1 --> f1")
mermaid.append(" t1 --> f2")
mermaid.append(" t1 --> f6")
mermaid.append(" t1 --> f7")
mermaid.append(" t1 --> f8")
mermaid.append(" t2 --> f3")
mermaid.append(" t3 --> f4")
mermaid.append(" t4 --> f5")
mermaid.append("")
mermaid.append(" %% Daily report flow (研究组)")
mermaid.append(" xia_l -.->|daily report| t7")
mermaid.append(" zhang_jz -.->|daily report| t7")
mermaid.append(" mo_rl -.->|daily report| t7")
mermaid.append(" hu_hj -.->|daily report| t7")
mermaid.append(" wang_j -.->|daily report| t7")
mermaid.append("")
mermaid.append(" %% Cross-group")
mermaid.append(" huang_jw -.->|跨组| t6")
mermaid.append(" li_zj -.->|指导| t6")
mermaid.append(" li_zj -.->|指导| t7")
mermaid_text = "\n".join(mermaid)
# Save mermaid
with open(os.path.join(PROJECT_ROOT, "output", "knowledge-graph", "knowledge_graph_mermaid.md"), "w", encoding="utf-8") as f:
f.write("# 知识图谱 - dc战略问题研究院 & 创新组\n\n")
f.write("```mermaid\n")
f.write(mermaid_text)
f.write("\n```\n")
# Generate comprehensive HTML report
html = []
html.append("""<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>知识图谱 - dc战略问题研究院 & 创新组</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: #0f1419; color: #e7e9ea; line-height: 1.6; }
.container { max-width: 1200px; margin: 0 auto; padding: 20px; }
h1 { font-size: 28px; margin-bottom: 8px; color: #1d9bf0; }
h2 { font-size: 20px; margin: 24px 0 12px; color: #1d9bf0; border-bottom: 1px solid #2f3336; padding-bottom: 8px; }
h3 { font-size: 16px; margin: 16px 0 8px; color: #e7e9ea; }
.subtitle { color: #71767b; margin-bottom: 24px; }
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 12px; margin: 16px 0; }
.stat { background: #16202a; border: 1px solid #2f3336; border-radius: 12px; padding: 16px; text-align: center; }
.stat-num { font-size: 32px; font-weight: bold; color: #1d9bf0; }
.stat-label { font-size: 13px; color: #71767b; }
.card { background: #16202a; border: 1px solid #2f3336; border-radius: 12px; padding: 16px; margin: 8px 0; }
.card:hover { border-color: #1d9bf0; }
.person-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 8px; }
.person { background: #16202a; border: 1px solid #2f3336; border-radius: 8px; padding: 12px; display: flex; align-items: center; gap: 10px; }
.person-avatar { width: 36px; height: 36px; border-radius: 50%; background: #1d9bf0; display: flex; align-items: center; justify-content: center; font-weight: bold; font-size: 14px; flex-shrink: 0; }
.person-info { min-width: 0; }
.person-name { font-weight: 600; font-size: 14px; }
.person-meta { font-size: 12px; color: #71767b; }
.tag { display: inline-block; background: #1d9bf0; color: #fff; padding: 2px 8px; border-radius: 4px; font-size: 11px; margin: 2px; }
.tag-green { background: #00ba7c; }
.tag-orange { background: #ff7a00; }
.tag-purple { background: #7856ff; }
.concept-list { display: flex; flex-wrap: wrap; gap: 4px; margin: 8px 0; }
.file-list { list-style: none; }
.file-list li { padding: 6px 0; border-bottom: 1px solid #2f3336; font-size: 14px; }
.file-list li:last-child { border-bottom: none; }
.file-icon { color: #1d9bf0; margin-right: 6px; }
table { width: 100%%; border-collapse: collapse; margin: 8px 0; }
th, td { padding: 8px 12px; text-align: left; border-bottom: 1px solid #2f3336; font-size: 14px; }
th { color: #71767b; font-weight: 600; font-size: 12px; text-transform: uppercase; }
a { color: #1d9bf0; text-decoration: none; }
a:hover { text-decoration: underline; }
.domain-bar { display: flex; align-items: center; gap: 8px; margin: 4px 0; }
.domain-fill { height: 20px; background: #1d9bf0; border-radius: 4px; min-width: 4px; }
.domain-label { font-size: 13px; white-space: nowrap; }
.domain-count { font-size: 12px; color: #71767b; }
.timeline { border-left: 2px solid #2f3336; padding-left: 16px; margin: 8px 0; }
.timeline-item { position: relative; margin: 12px 0; }
.timeline-item::before { content: ""; position: absolute; left: -21px; top: 6px; width: 10px; height: 10px; border-radius: 50%; background: #1d9bf0; }
.timeline-date { font-size: 12px; color: #71767b; }
.timeline-content { font-size: 14px; }
</style>
</head>
<body>
<div class="container">
<h1>知识图谱</h1>
<p class="subtitle">dc战略问题研究院 & 创新组 | 2026-05-01 ~ 2026-06-02</p>
<div class="stats">
<div class="stat"><div class="stat-num">28</div><div class="stat-label">活跃人员</div></div>
<div class="stat"><div class="stat-num">190</div><div class="stat-label">飞书文档链接</div></div>
<div class="stat"><div class="stat-num">18</div><div class="stat-label">文件附件</div></div>
<div class="stat"><div class="stat-num">7</div><div class="stat-label">核心主题</div></div>
<div class="stat"><div class="stat-num">377</div><div class="stat-label">总消息数</div></div>
</div>
""")
# Persons section
html.append("<h2>人员图谱</h2>")
html.append('<div class="person-grid">')
for name, p in sorted(graph["persons"].items(), key=lambda x: -x[1]["contributions"]):
initial = name[0]
groups = ", ".join(p["groups"])
html.append(f'<div class="person"><div class="person-avatar">{initial}</div><div class="person-info"><div class="person-name">{name}</div><div class="person-meta">{p["contributions"]}条消息 | {groups}</div></div></div>')
html.append("</div>")
# Topics section
html.append("<h2>核心研究主题</h2>")
for t in graph["topics"]:
html.append(f'<div class="card">')
html.append(f'<h3>{t["name"]}</h3>')
html.append(f'<p style="color:#71767b;font-size:14px;margin:4px 0">{t["description"]}</p>')
html.append(f'<p style="font-size:13px;margin:4px 0"><strong>关键人物:</strong> {", ".join(t["key_people"])}</p>')
html.append(f'<div class="concept-list">')
for c in t["key_concepts"]:
html.append(f'<span class="tag">{c}</span>')
html.append(f'</div>')
if t["files"]:
html.append(f'<p style="font-size:13px;margin:8px 0 4px"><strong>关联文件:</strong></p><ul class="file-list">')
for fname in t["files"]:
html.append(f'<li><span class="file-icon">📄</span>{fname}</li>')
html.append(f'</ul>')
html.append(f'</div>')
# Documents by domain
html.append("<h2>文档分布</h2>")
html.append('<div class="card">')
domain_counts = {}
for doc in graph["documents"]:
d = doc["domain"]
domain_counts[d] = domain_counts.get(d, 0) + 1
max_count = max(domain_counts.values()) if domain_counts else 1
for d, c in sorted(domain_counts.items(), key=lambda x: -x[1]):
width = int(c / max_count * 200)
html.append(f'<div class="domain-bar"><div class="domain-label" style="width:200px">{d}</div><div class="domain-fill" style="width:{width}px"></div><div class="domain-count">{c}</div></div>')
html.append('</div>')
# Files section
html.append("<h2>全部文件附件</h2>")
html.append('<div class="card">')
html.append('<table><tr><th>文件名</th><th>分享者</th><th>群组</th><th>时间</th></tr>')
for f in graph["files"]:
html.append(f'<tr><td>{f["name"]}</td><td>{f["sender"]}</td><td>{f["group"]}</td><td>{f["time"]}</td></tr>')
html.append('</table></div>')
# Key concepts
html.append("<h2>关键概念索引</h2>")
all_concepts = {}
for t in graph["topics"]:
for c in t["key_concepts"]:
if c not in all_concepts:
all_concepts[c] = []
all_concepts[c].append(t["name"])
for concept, topics in sorted(all_concepts.items()):
html.append(f'<div class="card" style="padding:10px 16px">')
html.append(f'<strong>{concept}</strong> <span style="color:#71767b;font-size:13px">-> {", ".join(topics)}</span>')
html.append(f'</div>')
html.append("</div></body></html>")
with open(os.path.join(PROJECT_ROOT, "output", "knowledge-graph", "knowledge_graph.html"), "w", encoding="utf-8") as f:
f.write("\n".join(html))
print("Generated knowledge_graph.html and knowledge_graph_mermaid.md")