Delete app.py
Browse files
app.py
DELETED
|
@@ -1,157 +0,0 @@
|
|
| 1 |
-
import json, os, re, gradio as gr
|
| 2 |
-
|
| 3 |
-
# ================= 配置 =================
|
| 4 |
-
PROJECT_NAME = "家族亲戚关系网查询系统"
|
| 5 |
-
DATA_FILE = "family_data.jsonl"
|
| 6 |
-
PHOTO_ROOT = "photos"
|
| 7 |
-
ARCHIVE_ROOT = "archives"
|
| 8 |
-
|
| 9 |
-
CUSTOM_CSS = """
|
| 10 |
-
footer {display: none !important;}
|
| 11 |
-
.gradio-container {max-width: 100% !important; background: #fdf5e6;}
|
| 12 |
-
#title-md { text-align: center; color: #8b0000; font-family: "Microsoft YaHei", sans-serif; }
|
| 13 |
-
.output-box { border: 2px solid #8b0000 !important; border-radius: 8px !important; }
|
| 14 |
-
"""
|
| 15 |
-
|
| 16 |
-
def load_db():
|
| 17 |
-
db = {}
|
| 18 |
-
if not os.path.exists(DATA_FILE): return db
|
| 19 |
-
with open(DATA_FILE, "r", encoding="utf-8") as f:
|
| 20 |
-
for line in f:
|
| 21 |
-
try:
|
| 22 |
-
item = json.loads(line.strip())
|
| 23 |
-
if "n" in item: db[item["n"].strip()] = item
|
| 24 |
-
except: continue
|
| 25 |
-
return db
|
| 26 |
-
|
| 27 |
-
# ================= 称谓推演引擎 =================
|
| 28 |
-
|
| 29 |
-
def get_relation_label(a, b, db):
|
| 30 |
-
"""判断 A 是 B 的什么(单步关系)"""
|
| 31 |
-
rec_b = db.get(b, {})
|
| 32 |
-
if rec_b.get("f") == a: return "父亲"
|
| 33 |
-
if rec_b.get("m") == a: return "母亲"
|
| 34 |
-
if a in rec_b.get("sp", []): return "配偶"
|
| 35 |
-
# 反向找子女
|
| 36 |
-
rec_a = db.get(a, {})
|
| 37 |
-
if rec_a.get("f") == b or rec_a.get("m") == b: return "子女"
|
| 38 |
-
return "关联"
|
| 39 |
-
|
| 40 |
-
def fmt_n(name, db):
|
| 41 |
-
if name not in db: return name
|
| 42 |
-
info = db[name].get("info", "")
|
| 43 |
-
alive = " (已故)" if re.search(r'\d{4}.*卒|\d{4}-\d{4}', str(info)) else ""
|
| 44 |
-
return f"{name}{alive} [{info}]"
|
| 45 |
-
|
| 46 |
-
# ================= 核心功能函数 =================
|
| 47 |
-
|
| 48 |
-
def ui_infer(name, mode):
|
| 49 |
-
db = load_db(); name = name.strip()
|
| 50 |
-
if name not in db: return "❌ 找不到起点"
|
| 51 |
-
res = f"🔍 【{name}】的{mode}推演:\n" + "="*30 + "\n"
|
| 52 |
-
|
| 53 |
-
# 1. 晚辈递归(子孙满堂)
|
| 54 |
-
if mode == "晚辈":
|
| 55 |
-
def get_desc(root, d):
|
| 56 |
-
kids = [n for n, dat in db.items() if dat.get("f")==root or dat.get("m")==root]
|
| 57 |
-
out = ""
|
| 58 |
-
for k in sorted(kids, key=lambda x: str(db[x].get("info"))):
|
| 59 |
-
prefix = " " * d + ("┗━ " if d>0 else "")
|
| 60 |
-
label = {1:"子/女", 2:"孙", 3:"曾孙", 4:"玄孙"}.get(d, "后代")
|
| 61 |
-
out += f"{prefix}第{d}代({label}): {fmt_n(k, db)}\n"
|
| 62 |
-
out += get_desc(k, d+1)
|
| 63 |
-
return out
|
| 64 |
-
return res + (get_desc(name, 1) or "暂无记录")
|
| 65 |
-
|
| 66 |
-
# 2. 堂表亲精细推演
|
| 67 |
-
me = db.get(name)
|
| 68 |
-
f, m = me.get("f"), me.get("m")
|
| 69 |
-
|
| 70 |
-
if mode == "堂兄妹":
|
| 71 |
-
if not f or f not in db: return res + "父亲记录缺失"
|
| 72 |
-
f_sibs = [n for n, d in db.items() if (d.get("f")==db[f].get("f") or d.get("m")==db[f].get("m")) and n!=f and d.get("s")=="男"]
|
| 73 |
-
content = ""
|
| 74 |
-
for fs in f_sibs:
|
| 75 |
-
kids = [n for n, d in db.items() if d.get("f")==fs or d.get("m")==fs]
|
| 76 |
-
for k in kids: content += f" - {fmt_n(k, db)} (伯叔{fs}之子)\n"
|
| 77 |
-
return res + (content or "未找到堂亲")
|
| 78 |
-
|
| 79 |
-
if mode == "表兄妹":
|
| 80 |
-
# 父亲的姐妹的孩子 + 母亲的兄弟姐妹的孩子
|
| 81 |
-
content = ""
|
| 82 |
-
# 姑表
|
| 83 |
-
if f and f in db:
|
| 84 |
-
f_sibs_f = [n for n, d in db.items() if (d.get("f")==db[f].get("f") or d.get("m")==db[f].get("m")) and n!=f and d.get("s")=="女"]
|
| 85 |
-
for fs in f_sibs_f:
|
| 86 |
-
kids = [n for n, d in db.items() if d.get("f")==fs or d.get("m")==fs]
|
| 87 |
-
for k in kids: content += f" - {fmt_n(k, db)} (姑母{fs}之子)\n"
|
| 88 |
-
# 舅表/姨表
|
| 89 |
-
if m and m in db:
|
| 90 |
-
m_sibs = [n for n, d in db.items() if (d.get("f")==db[m].get("f") or d.get("m")==db[m].get("m")) and n!=m]
|
| 91 |
-
for ms in m_sibs:
|
| 92 |
-
kids = [n for n, d in db.items() if d.get("f")==ms or d.get("m")==ms]
|
| 93 |
-
for k in kids: content += f" - {fmt_n(k, db)} ({'舅' if db[ms].get('s')=='男' else '姨'}{ms}之子)\n"
|
| 94 |
-
return res + (content or "未找到表亲")
|
| 95 |
-
|
| 96 |
-
return res + "逻辑开发中..."
|
| 97 |
-
|
| 98 |
-
def ui_rel_query(name1, name2):
|
| 99 |
-
db = load_db()
|
| 100 |
-
n1, n2 = name1.strip(), name2.strip()
|
| 101 |
-
if n1 not in db or n2 not in db: return "❌ 姓名不全"
|
| 102 |
-
|
| 103 |
-
# 广度优先搜寻路径
|
| 104 |
-
queue = [(n1, [])]
|
| 105 |
-
visited = {n1}
|
| 106 |
-
while queue:
|
| 107 |
-
curr, path = queue.pop(0)
|
| 108 |
-
if curr == n2:
|
| 109 |
-
# 构建带称谓的链条
|
| 110 |
-
full_path = ""
|
| 111 |
-
for i in range(len(path)):
|
| 112 |
-
prev = n1 if i==0 else path[i-1]
|
| 113 |
-
rel = get_relation_label(prev, path[i], db)
|
| 114 |
-
full_path += f"{prev} → ({rel}) → "
|
| 115 |
-
full_path += n2
|
| 116 |
-
return f"🏁 找到关系链:\n{full_path}\n\n💡 提示:该链条基于血缘与配偶逻辑自动推演。"
|
| 117 |
-
|
| 118 |
-
# 寻找邻居:父、母、配、子
|
| 119 |
-
rec = db[curr]
|
| 120 |
-
nb = []
|
| 121 |
-
if rec.get("f"): nb.append(rec["f"])
|
| 122 |
-
if rec.get("m"): nb.append(rec["m"])
|
| 123 |
-
for s in rec.get("sp", []): nb.append(s)
|
| 124 |
-
children = [n for n, d in db.items() if d.get("f")==curr or d.get("m")==curr]
|
| 125 |
-
nb += children
|
| 126 |
-
|
| 127 |
-
for n in nb:
|
| 128 |
-
if n and n not in visited and n in db:
|
| 129 |
-
visited.add(n)
|
| 130 |
-
queue.append((n, path + [n]))
|
| 131 |
-
return "未找到直接或间接关联。"
|
| 132 |
-
|
| 133 |
-
# ================= 界面构建 =================
|
| 134 |
-
with gr.Blocks(title=PROJECT_NAME, css=CUSTOM_CSS) as demo:
|
| 135 |
-
gr.Markdown(f"### 🏮 {PROJECT_NAME}", elem_id="title-md")
|
| 136 |
-
|
| 137 |
-
with gr.Row():
|
| 138 |
-
curr_n = gr.Textbox(label="操作人姓名", value="冯宗德", scale=2)
|
| 139 |
-
|
| 140 |
-
with gr.Tabs():
|
| 141 |
-
with gr.Tab("🧬 族网推演"):
|
| 142 |
-
with gr.Row():
|
| 143 |
-
mode = gr.Radio(["亲兄妹", "堂兄妹", "表兄妹", "前辈", "晚辈"], label="选择维度", value="晚辈")
|
| 144 |
-
run_btn = gr.Button("开始推演", variant="primary")
|
| 145 |
-
out_infer = gr.Textbox(label="推演结论 (自动递归深度后代)", lines=15, elem_classes="output-box")
|
| 146 |
-
run_btn.click(ui_infer, [curr_n, mode], out_infer)
|
| 147 |
-
|
| 148 |
-
with gr.Tab("🔍 关系查询"):
|
| 149 |
-
with gr.Row():
|
| 150 |
-
tar1 = gr.Textbox(label="起点 (如:冯宗德)")
|
| 151 |
-
tar2 = gr.Textbox(label="终点 (如:某某某)")
|
| 152 |
-
q_btn = gr.Button("分析亲属链条", variant="primary")
|
| 153 |
-
out_q = gr.Textbox(label="分析结论", lines=8, elem_classes="output-box")
|
| 154 |
-
q_btn.click(ui_rel_query, [tar1, tar2], out_q)
|
| 155 |
-
|
| 156 |
-
if __name__ == "__main__":
|
| 157 |
-
demo.launch()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|