115 lines
5.0 KiB
Python
115 lines
5.0 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
读取 db/menu_map.json, 构建功能树写入 SQLite(features 表)。
|
|
层级: level0=顶层模块, level1=分组, level2=菜单页(叶子)
|
|
并为每个叶子分配期望截图路径 screenshots/<序号_模块>/<分组>/<序号_名称>.jpg
|
|
|
|
menu_map.json 结构(由实时爬取生成):
|
|
{
|
|
"<模块名>": {
|
|
"loc": "落地页URL",
|
|
"desc": "模块释义(可选)",
|
|
"understanding": "开发理解(可选)",
|
|
"groups": [ {"title": "分组名", "items": [ {"name":"页名","href":"/路径","desc":"","understanding":""}, ...] } ],
|
|
"single": {"name":"页名","href":"/路径"} # 无侧边栏的单页模块(可选)
|
|
},
|
|
...
|
|
}
|
|
"""
|
|
import json, os, re, sqlite3
|
|
|
|
BASE = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
MAP = os.path.join(BASE, "db", "menu_map.json")
|
|
DB = os.path.join(BASE, "db", "research.db")
|
|
ORIGIN = "https://s-demob.xinrenxinshi.show"
|
|
|
|
|
|
def sanitize(s):
|
|
s = re.sub(r'[\\/:*?"<>|]', "_", s or "").strip()
|
|
return s[:40] or "page"
|
|
|
|
|
|
def full_url(href):
|
|
if not href:
|
|
return ""
|
|
if href.startswith("http"):
|
|
return href
|
|
return ORIGIN + (href if href.startswith("/") else "/" + href)
|
|
|
|
|
|
def main():
|
|
data = json.load(open(MAP, encoding="utf-8"))
|
|
conn = sqlite3.connect(DB)
|
|
conn.row_factory = sqlite3.Row
|
|
cur = conn.cursor()
|
|
cur.execute("DELETE FROM features")
|
|
|
|
def up(obj):
|
|
keys = list(obj.keys()); cols = ",".join(keys); ph = ",".join(["?"] * len(keys))
|
|
cur.execute(f"INSERT OR REPLACE INTO features ({cols}) VALUES ({ph})", [obj[k] for k in keys])
|
|
|
|
for mi, module in enumerate(data.keys(), 1):
|
|
v = data[module]
|
|
mod_key = f"mod::{module}"
|
|
seq_mod = f"{mi:02d}_{sanitize(module)}"
|
|
up(dict(node_key=mod_key, parent_key="", level=0, name=module,
|
|
full_path=module, url=v.get("loc", ""),
|
|
description=v.get("desc", f"{module}模块。"),
|
|
understanding=v.get("understanding", ""), sort_order=mi, keep=1))
|
|
|
|
groups = v.get("groups", [])
|
|
single = v.get("single")
|
|
flat_items = v.get("items")
|
|
# 扁平模式: 模块直接挂功能页(level2), 无中间分组层
|
|
if not groups and flat_items:
|
|
for ii, it in enumerate(flat_items, 1):
|
|
nm = it["name"]; href = it.get("href", "")
|
|
leaf_key = f"{module}::{href or nm}"
|
|
rel = f"screenshots/{seq_mod}/{ii:02d}_{sanitize(nm)}.jpg"
|
|
up(dict(node_key=leaf_key, parent_key=mod_key, level=2, name=nm,
|
|
full_path=f"{module} / {nm}", url=full_url(href),
|
|
description=it.get("desc", f"{module}模块下「{nm}」相关功能页。"),
|
|
understanding=it.get("understanding", ""),
|
|
screenshot_path=rel, sort_order=ii, keep=1))
|
|
continue
|
|
if not groups and single:
|
|
nm = single["name"]; href = single.get("href", "")
|
|
leaf_key = f"{module}::{href or nm}"
|
|
rel = f"screenshots/{seq_mod}/{sanitize(nm)}.jpg"
|
|
up(dict(node_key=leaf_key, parent_key=mod_key, level=2, name=nm,
|
|
full_path=f"{module} / {nm}", url=full_url(href),
|
|
description=single.get("desc", f"{module}模块下「{nm}」相关功能页。"),
|
|
understanding=single.get("understanding", ""),
|
|
screenshot_path=rel, sort_order=1, keep=1))
|
|
continue
|
|
|
|
gi = 0
|
|
for g in groups:
|
|
gtitle = g.get("title") or "常规"
|
|
gi += 1
|
|
grp_key = f"{mod_key}::grp::{gi}_{gtitle}"
|
|
up(dict(node_key=grp_key, parent_key=mod_key, level=1, name=gtitle,
|
|
full_path=f"{module} / {gtitle}", url="",
|
|
description=f"{module}模块下「{gtitle}」功能分组。",
|
|
understanding="", sort_order=gi, keep=1))
|
|
for ii, it in enumerate(g.get("items", []), 1):
|
|
nm = it["name"]; href = it.get("href", "")
|
|
leaf_key = f"{module}::{href or (gtitle + '/' + nm)}"
|
|
rel = f"screenshots/{seq_mod}/{sanitize(gtitle)}/{ii:02d}_{sanitize(nm)}.jpg"
|
|
up(dict(node_key=leaf_key, parent_key=grp_key, level=2, name=nm,
|
|
full_path=f"{module} / {gtitle} / {nm}", url=full_url(href),
|
|
description=it.get("desc", f"{module}模块下「{nm}」相关功能页。"),
|
|
understanding=it.get("understanding", ""),
|
|
screenshot_path=rel, sort_order=ii, keep=1))
|
|
conn.commit()
|
|
for lv in (0, 1, 2):
|
|
n = cur.execute("SELECT COUNT(*) FROM features WHERE level=?", (lv,)).fetchone()[0]
|
|
print(f"level{lv}: {n}")
|
|
print("total:", cur.execute("SELECT COUNT(*) FROM features").fetchone()[0])
|
|
conn.close()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|