fdbw commited on
Commit
f0b4edd
·
verified ·
1 Parent(s): 11a820d

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +182 -0
app.py ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json, gradio as gr, os, re
2
+ from collections import deque
3
+
4
+ # ==========================================
5
+ # 1. 基础配置与数据加载 (严格执行约定)
6
+ # ==========================================
7
+ PROJECT_NAME = "家族亲戚关系网查询系统"
8
+ DATA_FILE = "family_data.jsonl"
9
+ PHOTO_ROOT = "photos"
10
+ ARCHIVE_ROOT = "archives"
11
+
12
+ def load_family_data():
13
+ db = {}
14
+ if not os.path.exists(DATA_FILE): return db
15
+ with open(DATA_FILE, "r", encoding="utf-8") as f:
16
+ for line in f:
17
+ try:
18
+ data = json.loads(line.strip())
19
+ if "n" in data: db[str(data["n"]).strip()] = data
20
+ except: continue
21
+ return db
22
+
23
+ db = load_family_data()
24
+
25
+ def get_year(name):
26
+ rec = db.get(name, {})
27
+ match = re.search(r'(\d{4})', str(rec.get("info", "")))
28
+ return int(match.group(1)) if match else 9999
29
+
30
+ def fmt_member(n):
31
+ """格式化成员显示:姓名 + 生卒/在世 + 配偶"""
32
+ r = db.get(n, {})
33
+ if not r: return n
34
+ info = str(r.get("info", ""))
35
+ # 提取年份
36
+ years = ""
37
+ y_match = re.search(r'(\d{4}-\d{4}|\d{4}-)', info)
38
+ if y_match: years = f" ({y_match.group()})"
39
+ elif re.search(r'\d{4}', info): years = f" ({re.search(r'\d{4}', info).group()}-)"
40
+
41
+ sps = r.get("sp", [])
42
+ sp_str = f" [配:{''.join(sps)}]" if sps and sps != ["无"] else ""
43
+ return f"{n}{sp_str}{years}"
44
+
45
+ # ==========================================
46
+ # 2. 核心算法:六大维度推演与自然语言解析
47
+ # ==========================================
48
+
49
+ def run_inference(start_name, cate):
50
+ start_name = start_name.strip()
51
+ me = db.get(start_name)
52
+ if not me: return f"❌ 未找到【{start_name}】的记录"
53
+
54
+ res = f"📜 {start_name} 的【{cate}】系关系网\n" + "="*40 + "\n"
55
+ content = ""
56
+
57
+ # 获取父母与子女
58
+ my_f, my_m = me.get("f"), me.get("m")
59
+ children = [n for n, d in db.items() if d.get("f") == start_name or d.get("m") == start_name]
60
+
61
+ if cate == "晚辈":
62
+ def get_descendants(p, level=1):
63
+ kids = [n for n, d in db.items() if d.get("f") == p or d.get("m") == p]
64
+ out = ""
65
+ for k in sorted(kids, key=get_year):
66
+ out += f"{' '*level} ┕ {fmt_member(k)}\n" + get_descendants(k, level + 1)
67
+ return out
68
+ content = get_descendants(start_name)
69
+
70
+ elif cate == "前辈":
71
+ def get_ancestors(p, level=1):
72
+ if level > 4 or not p or p == "待考": return ""
73
+ r = db.get(p, {})
74
+ line = f"{' '*level} 第{level}代: {fmt_member(p)}\n"
75
+ return line + get_ancestors(r.get("f"), level+1) + get_ancestors(r.get("m"), level+1)
76
+ content = get_ancestors(my_f) + get_ancestors(my_m)
77
+
78
+ elif cate == "亲兄妹":
79
+ siblings = [n for n, d in db.items() if (d.get("f") == my_f and my_f != "待考") or (d.get("m") == my_m and my_m != "待考")]
80
+ for s in sorted(set(siblings), key=get_year):
81
+ if s != start_name: content += f" 🚩 {fmt_member(s)}\n"
82
+
83
+ elif cate == "姻兄妹":
84
+ for sp in me.get("sp", []):
85
+ sp_d = db.get(sp)
86
+ if sp_d:
87
+ s_f, s_m = sp_d.get("f"), sp_d.get("m")
88
+ sibs = [n for n, d in db.items() if (d.get("f") == s_f or d.get("m") == s_m) and n != sp]
89
+ content += f"\n💍 配偶【{sp}】的同胞:\n"
90
+ for s in sorted(set(sibs), key=get_year): content += f" └── {fmt_member(s)}\n"
91
+
92
+ return res + (content if content else "(暂无数据记录)")
93
+
94
+ # ==========================================
95
+ # 3. 自然语言查询与档案展示
96
+ # ==========================================
97
+
98
+ def advanced_query(text):
99
+ """解析:张三的舅舅 / 张三,李四"""
100
+ text = text.replace(",", ",").strip()
101
+ if "的" in text:
102
+ name, rel = text.split("的")
103
+ p = db.get(name.strip())
104
+ if not p: return "找不到该人"
105
+ # 舅舅逻辑:母亲的兄弟
106
+ if "舅" in rel and p.get("m"):
107
+ m_d = db.get(p["m"])
108
+ if m_d and m_d.get("f"):
109
+ uncles = [n for n, d in db.items() if d.get("f") == m_d["f"] and d.get("s") == "男" and n != p["m"]]
110
+ return f"🔍 {name} 的舅舅有:\n" + "\n".join([fmt_member(u) for u in uncles])
111
+ return "暂未支持该特定称谓的自动解析"
112
+
113
+ if "," in text:
114
+ n1, n2 = text.split(",")
115
+ # BFS 路径查找
116
+ queue = deque([(n1.strip(), [])])
117
+ visited = {n1.strip()}
118
+ while queue:
119
+ curr, path = queue.popleft()
120
+ if curr == n2.strip():
121
+ return "🔗 关系链:\n" + " ➔ ".join(path + [n2.strip()])
122
+ for neighbor, d in db.items():
123
+ if neighbor not in visited:
124
+ # 检查父子或配偶关系
125
+ if d.get("f") == curr or d.get("m") == curr or curr in d.get("sp", []):
126
+ visited.add(neighbor)
127
+ queue.append((neighbor, path + [curr]))
128
+ return "未发现关联路径"
129
+ return "格式错误,请输入:张三的舅舅 或 张三,李四"
130
+
131
+ def get_personal_tab(name):
132
+ name = name.strip(); p = db.get(name)
133
+ if not p: return "❌ 找不到档案", None, gr.update(choices=[])
134
+
135
+ info_md = f"### {name} 的个人资料\n- **性别**: {p.get('s','?')}\n- **生平**: {p.get('info','')}\n- **父母**: {p.get('f','待考')} & {p.get('m','待考')}\n- **配偶**: {'、'.join(p.get('sp', []))}"
136
+
137
+ # 照片与过滤.gitkeep
138
+ photo = f"{PHOTO_ROOT}/{name}.jpg"
139
+ photo = photo if os.path.exists(photo) else None
140
+ files = []
141
+ if os.path.exists(f"{ARCHIVE_ROOT}/{name}"):
142
+ files = [f for f in os.listdir(f"{ARCHIVE_ROOT}/{name}") if not f.startswith(".")]
143
+ return info_md, photo, gr.update(choices=files)
144
+
145
+ # ==========================================
146
+ # 4. 统一 UI 界面
147
+ # ==========================================
148
+ with gr.Blocks(title=PROJECT_NAME, css="footer {display: none !important;}") as demo:
149
+ gr.Markdown(f"# 📜 {PROJECT_NAME}")
150
+
151
+ with gr.Tabs():
152
+ with gr.Tab("📋 个人资料"):
153
+ name_in = gr.Textbox(label="输入姓名 (回车搜索)", placeholder="例如:冯宗德")
154
+ with gr.Row():
155
+ with gr.Column(scale=1):
156
+ p_photo = gr.Image(label="人物像")
157
+ with gr.Column(scale=2):
158
+ p_info = gr.Markdown("等待输入...")
159
+ file_sel = gr.Dropdown(label="📁 档案资料库 (自动过滤.gitkeep)")
160
+
161
+ # 档案内容展示区
162
+ out_txt = gr.Textbox(label="文字档案", lines=10, visible=False)
163
+ out_img = gr.Image(label="图片档案", visible=False)
164
+
165
+ name_in.submit(get_personal_tab, inputs=name_in, outputs=[p_info, p_photo, file_sel])
166
+
167
+ with gr.Tab("🧬 族网推演"):
168
+ with gr.Row():
169
+ t_name = gr.Textbox(label="起点姓名", value="冯宗德")
170
+ t_cate = gr.Radio(["晚辈", "前辈", "亲兄妹", "姻兄妹"], label="维度", value="晚辈")
171
+ btn_run = gr.Button("开始推演", variant="primary")
172
+ t_out = gr.Textbox(label="推演结果", lines=20)
173
+ btn_run.click(run_inference, inputs=[t_name, t_cate], outputs=t_out)
174
+
175
+ with gr.Tab("🔍 关系溯源"):
176
+ q_in = gr.Textbox(label="输入查询内容", placeholder="例如:冯宗德的舅舅 或 冯仁杰,何宗涓")
177
+ q_btn = gr.Button("解析关系")
178
+ q_out = gr.Textbox(label="查询结果", lines=10)
179
+ q_btn.click(advanced_query, inputs=q_in, outputs=q_out)
180
+
181
+ if __name__ == "__main__":
182
+ demo.launch()