first commit
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Part0: 把 downloads 表里 node_key 为空的导出文件回填到对应功能页, 实现导出↔页面一一对应。
|
||||
匹配策略:
|
||||
1) 普通导出: 文件目录(如 downloads/02_人事/员工关系) 对应功能页 screenshot 目录(模块/分组);
|
||||
在该分组下的候选页中, 选文件名里能命中的页面名(最长命中优先)。
|
||||
2) _任务中心 收割文件: 无目录线索, 用文件名关键词对全站页面名/路径做全局匹配。
|
||||
仅回填 node_key 为空者; 打印每条匹配结果与未匹配项供人工核对。
|
||||
"""
|
||||
import os, sqlite3, re, collections
|
||||
|
||||
BASE = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
DB = os.path.join(BASE, "db", "research.db")
|
||||
|
||||
|
||||
def db():
|
||||
c = sqlite3.connect(DB); c.row_factory = sqlite3.Row; return c
|
||||
|
||||
|
||||
def strip_num(s):
|
||||
return re.sub(r"^\d+_", "", s or "").strip()
|
||||
|
||||
|
||||
def load_pages():
|
||||
c = db()
|
||||
rows = [dict(r) for r in c.execute(
|
||||
"SELECT node_key,name,full_path,screenshot_path FROM features "
|
||||
"WHERE screenshot_path IS NOT NULL AND screenshot_path<>''")]
|
||||
c.close()
|
||||
for r in rows:
|
||||
# screenshots/02_人事/员工管理/01_员工花名册.jpg -> 目录 02_人事/员工管理
|
||||
d = os.path.dirname(r["screenshot_path"]).replace("screenshots/", "", 1)
|
||||
r["shot_dir"] = d
|
||||
r["name_core"] = strip_num(r["name"]).replace("员工", "").strip()
|
||||
return rows
|
||||
|
||||
|
||||
def best_page_in_group(fname, candidates):
|
||||
"""在同分组候选页里, 选页面名能在文件名中命中且命中最长的。"""
|
||||
best, blen = None, 0
|
||||
for p in candidates:
|
||||
for token in (p["name"], strip_num(p["name"]), p["name_core"]):
|
||||
token = (token or "").strip()
|
||||
if len(token) >= 2 and token in fname and len(token) > blen:
|
||||
best, blen = p, len(token)
|
||||
return best
|
||||
|
||||
|
||||
def global_match(fname, full_path_hint, pages):
|
||||
"""任务中心文件: 按文件名关键词对全站页面做匹配。"""
|
||||
key = fname
|
||||
best, blen = None, 0
|
||||
for p in pages:
|
||||
hay = (p["full_path"] or "") + " " + (p["name"] or "")
|
||||
for token in (strip_num(p["name"]), p["name_core"]):
|
||||
token = (token or "").strip()
|
||||
if len(token) >= 2 and token in key and len(token) > blen:
|
||||
best, blen = p, len(token)
|
||||
return best
|
||||
|
||||
|
||||
# 任务中心文件名 -> 目标页面关键词(人工先验, 提高命中)
|
||||
TC_HINTS = {
|
||||
"参保": ["参保档案", "社保"],
|
||||
"薪酬档案": ["薪酬档案", "薪资档案"],
|
||||
"已签署": ["已签署"],
|
||||
"电子合同": ["已签署", "合同"],
|
||||
}
|
||||
|
||||
# 显式关键词->页面 node_key (第一遍模糊未命中时的人工规则, 按顺序取首个命中)
|
||||
EXPLICIT = [
|
||||
("异动", "人事::/personnel/relation/unusual-action"),
|
||||
("谈话", "人事::/personnel/relation/talk"),
|
||||
("实习生", "人事::/personnel/relation/internship"),
|
||||
("已签订合同", "人事::/personnel/relation/contract"),
|
||||
("合同人员", "人事::/personnel/relation/contract"),
|
||||
("待入职", "人事::/personnel/relation/enrollment"),
|
||||
("待离职", "人事::/personnel/relation/departure"),
|
||||
("试用期", "人事::/personnel/relation/probation"),
|
||||
("考勤明细", "考勤假期::/attendance/stat/records"),
|
||||
("打卡记录", "考勤假期::/attendance/stat/records"),
|
||||
("工资档案", "薪酬::/payroll/salary/profiles"),
|
||||
]
|
||||
# 目录兜底: 该目录剩余未匹配文件统一归属(员工管理各类统计报表 -> 人事报表)
|
||||
DIR_FALLBACK = {
|
||||
"02_人事/员工管理": "人事::/personnel/employee/personnel-report",
|
||||
"02_人事/员工关系": "人事::/personnel/relation/office",
|
||||
}
|
||||
|
||||
|
||||
def map_explicit():
|
||||
"""第二遍: 对仍为空的行用显式关键词规则 + 目录兜底回填。"""
|
||||
c = db()
|
||||
fp = {r["node_key"]: r["full_path"] for r in c.execute("SELECT node_key,full_path FROM features")}
|
||||
rows = [dict(r) for r in c.execute(
|
||||
"SELECT id,file_path FROM downloads WHERE node_key IS NULL OR node_key=''")]
|
||||
done, left = 0, 0
|
||||
for r in rows:
|
||||
rel = r["file_path"].replace("downloads/", "", 1)
|
||||
parts = rel.split("/"); fname = parts[-1]; dir_key = "/".join(parts[:-1])
|
||||
nk = None
|
||||
for sub, node in EXPLICIT:
|
||||
if sub in fname:
|
||||
nk = node; break
|
||||
if not nk:
|
||||
nk = DIR_FALLBACK.get(dir_key)
|
||||
if nk and nk in fp:
|
||||
c.execute("UPDATE downloads SET node_key=?, full_path=? WHERE id=?", (nk, fp[nk], r["id"]))
|
||||
done += 1
|
||||
print(" [OK2] %-40s -> %s" % (fname[:40], fp[nk]))
|
||||
else:
|
||||
left += 1
|
||||
print(" [??2] %-40s (dir=%s)" % (fname[:40], dir_key))
|
||||
c.execute("PRAGMA journal_mode=DELETE")
|
||||
c.commit(); c.close()
|
||||
print("显式回填: 命中 %d, 仍未匹配 %d" % (done, left))
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
pages = load_pages()
|
||||
by_dir = collections.defaultdict(list)
|
||||
for p in pages:
|
||||
by_dir[p["shot_dir"]].append(p)
|
||||
|
||||
c = db()
|
||||
rows = [dict(r) for r in c.execute(
|
||||
"SELECT id,file_path FROM downloads WHERE node_key IS NULL OR node_key=''")]
|
||||
print("待回填导出文件: %d" % len(rows))
|
||||
matched, unmatched = 0, []
|
||||
for r in rows:
|
||||
rel = r["file_path"].replace("downloads/", "", 1)
|
||||
parts = rel.split("/")
|
||||
fname = parts[-1]
|
||||
dir_key = "/".join(parts[:-1])
|
||||
page = None
|
||||
if dir_key.startswith("_任务") or dir_key.startswith("_"):
|
||||
# 任务中心: 关键词优先 + 全局匹配
|
||||
hint_pages = None
|
||||
for kw, targets in TC_HINTS.items():
|
||||
if kw in fname:
|
||||
hint_pages = [p for p in pages if any(t in (p["full_path"] or "") + (p["name"] or "") for t in targets)]
|
||||
break
|
||||
if hint_pages:
|
||||
page = best_page_in_group(fname, hint_pages) or hint_pages[0]
|
||||
if not page:
|
||||
page = global_match(fname, dir_key, pages)
|
||||
else:
|
||||
cands = by_dir.get(dir_key, [])
|
||||
page = best_page_in_group(fname, cands)
|
||||
if not page and len(cands) == 1:
|
||||
page = cands[0]
|
||||
if page:
|
||||
c.execute("UPDATE downloads SET node_key=?, full_path=? WHERE id=?",
|
||||
(page["node_key"], page["full_path"], r["id"]))
|
||||
matched += 1
|
||||
print(" [OK] %-46s -> %s" % (fname[:46], page["full_path"]))
|
||||
else:
|
||||
unmatched.append((fname, dir_key))
|
||||
print(" [??] %-46s (dir=%s) 未匹配" % (fname[:46], dir_key))
|
||||
c.execute("PRAGMA journal_mode=DELETE")
|
||||
c.commit(); c.close()
|
||||
print("\n回填完成: 命中 %d, 未匹配 %d" % (matched, len(unmatched)))
|
||||
if unmatched:
|
||||
print("--- 第二遍: 显式关键词 + 目录兜底 ---")
|
||||
map_explicit()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user